From bf5857e7cac46963e8b4e46ff36b3d74968574dd Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 16 Apr 2024 15:25:11 +0200 Subject: [PATCH 01/91] Hubs/Scopes Merge 1 - Introduce `IScopes` interface. (#3297) --- sentry/api/sentry.api | 333 +++++++--- sentry/src/main/java/io/sentry/Hub.java | 1 + .../src/main/java/io/sentry/HubAdapter.java | 22 +- .../main/java/io/sentry/HubScopesWrapper.java | 279 ++++++++ sentry/src/main/java/io/sentry/IHub.java | 595 +----------------- sentry/src/main/java/io/sentry/IScopes.java | 594 +++++++++++++++++ sentry/src/main/java/io/sentry/NoOpHub.java | 7 + .../src/main/java/io/sentry/NoOpScopes.java | 243 +++++++ .../main/java/io/sentry/ScopesAdapter.java | 286 +++++++++ sentry/src/main/java/io/sentry/Sentry.java | 176 +++--- 10 files changed, 1778 insertions(+), 758 deletions(-) create mode 100644 sentry/src/main/java/io/sentry/HubScopesWrapper.java create mode 100644 sentry/src/main/java/io/sentry/IScopes.java create mode 100644 sentry/src/main/java/io/sentry/NoOpScopes.java create mode 100644 sentry/src/main/java/io/sentry/ScopesAdapter.java diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index bcfc1c9194..52eb5df888 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -293,7 +293,7 @@ public final class io/sentry/EnvelopeReader : io/sentry/IEnvelopeReader { } public final class io/sentry/EnvelopeSender : io/sentry/IEnvelopeSender { - public fun (Lio/sentry/IHub;Lio/sentry/ISerializer;Lio/sentry/ILogger;JI)V + public fun (Lio/sentry/IScopes;Lio/sentry/ISerializer;Lio/sentry/ILogger;JI)V public synthetic fun processDirectory (Ljava/io/File;)V public fun processEnvelopeFile (Ljava/lang/String;Lio/sentry/Hint;)V } @@ -520,6 +520,59 @@ public final class io/sentry/HubAdapter : io/sentry/IHub { public fun withScope (Lio/sentry/ScopeCallback;)V } +public final class io/sentry/HubScopesWrapper : io/sentry/IHub { + public fun (Lio/sentry/IScopes;)V + public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V + public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V + public fun bindClient (Lio/sentry/ISentryClient;)V + public fun captureCheckIn (Lio/sentry/CheckIn;)Lio/sentry/protocol/SentryId; + public fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; + public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; + public fun captureUserFeedback (Lio/sentry/UserFeedback;)V + public fun clearBreadcrumbs ()V + public fun clone ()Lio/sentry/IHub; + public synthetic fun clone ()Ljava/lang/Object; + public fun close ()V + public fun close (Z)V + public fun configureScope (Lio/sentry/ScopeCallback;)V + public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; + public fun endSession ()V + public fun flush (J)V + public fun getBaggage ()Lio/sentry/BaggageHeader; + public fun getLastEventId ()Lio/sentry/protocol/SentryId; + public fun getOptions ()Lio/sentry/SentryOptions; + public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; + public fun getSpan ()Lio/sentry/ISpan; + public fun getTraceparent ()Lio/sentry/SentryTraceHeader; + public fun getTransaction ()Lio/sentry/ITransaction; + public fun isCrashedLastRun ()Ljava/lang/Boolean; + public fun isEnabled ()Z + public fun isHealthy ()Z + public fun metrics ()Lio/sentry/metrics/MetricsApi; + public fun popScope ()V + public fun pushScope ()V + public fun removeExtra (Ljava/lang/String;)V + public fun removeTag (Ljava/lang/String;)V + public fun reportFullyDisplayed ()V + public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V + public fun setFingerprint (Ljava/util/List;)V + public fun setLevel (Lio/sentry/SentryLevel;)V + public fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V + public fun setTag (Ljava/lang/String;Ljava/lang/String;)V + public fun setTransaction (Ljava/lang/String;)V + public fun setUser (Lio/sentry/protocol/User;)V + public fun startSession ()V + public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; + public fun traceHeaders ()Lio/sentry/SentryTraceHeader; + public fun withScope (Lio/sentry/ScopeCallback;)V +} + public abstract interface class io/sentry/IConnectionStatusProvider { public abstract fun addConnectionStatusObserver (Lio/sentry/IConnectionStatusProvider$IConnectionStatusObserver;)Z public abstract fun getConnectionStatus ()Lio/sentry/IConnectionStatusProvider$ConnectionStatus; @@ -548,71 +601,7 @@ public abstract interface class io/sentry/IEnvelopeSender { public abstract fun processEnvelopeFile (Ljava/lang/String;Lio/sentry/Hint;)V } -public abstract interface class io/sentry/IHub { - public abstract fun addBreadcrumb (Lio/sentry/Breadcrumb;)V - public abstract fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V - public fun addBreadcrumb (Ljava/lang/String;)V - public fun addBreadcrumb (Ljava/lang/String;Ljava/lang/String;)V - public abstract fun bindClient (Lio/sentry/ISentryClient;)V - public abstract fun captureCheckIn (Lio/sentry/CheckIn;)Lio/sentry/protocol/SentryId; - public fun captureEnvelope (Lio/sentry/SentryEnvelope;)Lio/sentry/protocol/SentryId; - public abstract fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureEvent (Lio/sentry/SentryEvent;)Lio/sentry/protocol/SentryId; - public abstract fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public abstract fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureException (Ljava/lang/Throwable;)Lio/sentry/protocol/SentryId; - public abstract fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public abstract fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureException (Ljava/lang/Throwable;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureMessage (Ljava/lang/String;)Lio/sentry/protocol/SentryId; - public fun captureMessage (Ljava/lang/String;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public abstract fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; - public abstract fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;)Lio/sentry/protocol/SentryId; - public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public abstract fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; - public abstract fun captureUserFeedback (Lio/sentry/UserFeedback;)V - public abstract fun clearBreadcrumbs ()V - public abstract fun clone ()Lio/sentry/IHub; - public abstract fun close ()V - public abstract fun close (Z)V - public abstract fun configureScope (Lio/sentry/ScopeCallback;)V - public abstract fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; - public abstract fun endSession ()V - public abstract fun flush (J)V - public abstract fun getBaggage ()Lio/sentry/BaggageHeader; - public abstract fun getLastEventId ()Lio/sentry/protocol/SentryId; - public abstract fun getOptions ()Lio/sentry/SentryOptions; - public abstract fun getRateLimiter ()Lio/sentry/transport/RateLimiter; - public abstract fun getSpan ()Lio/sentry/ISpan; - public abstract fun getTraceparent ()Lio/sentry/SentryTraceHeader; - public abstract fun getTransaction ()Lio/sentry/ITransaction; - public abstract fun isCrashedLastRun ()Ljava/lang/Boolean; - public abstract fun isEnabled ()Z - public abstract fun isHealthy ()Z - public abstract fun metrics ()Lio/sentry/metrics/MetricsApi; - public abstract fun popScope ()V - public abstract fun pushScope ()V - public abstract fun removeExtra (Ljava/lang/String;)V - public abstract fun removeTag (Ljava/lang/String;)V - public fun reportFullDisplayed ()V - public abstract fun reportFullyDisplayed ()V - public abstract fun setExtra (Ljava/lang/String;Ljava/lang/String;)V - public abstract fun setFingerprint (Ljava/util/List;)V - public abstract fun setLevel (Lio/sentry/SentryLevel;)V - public abstract fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V - public abstract fun setTag (Ljava/lang/String;Ljava/lang/String;)V - public abstract fun setTransaction (Ljava/lang/String;)V - public abstract fun setUser (Lio/sentry/protocol/User;)V - public abstract fun startSession ()V - public fun startTransaction (Lio/sentry/TransactionContext;)Lio/sentry/ITransaction; - public abstract fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; - public fun startTransaction (Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ITransaction; - public fun startTransaction (Ljava/lang/String;Ljava/lang/String;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; - public abstract fun traceHeaders ()Lio/sentry/SentryTraceHeader; - public abstract fun withScope (Lio/sentry/ScopeCallback;)V +public abstract interface class io/sentry/IHub : io/sentry/IScopes { } public abstract interface class io/sentry/ILogger { @@ -731,6 +720,74 @@ public abstract interface class io/sentry/IScopeObserver { public abstract fun setUser (Lio/sentry/protocol/User;)V } +public abstract interface class io/sentry/IScopes { + public abstract fun addBreadcrumb (Lio/sentry/Breadcrumb;)V + public abstract fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V + public fun addBreadcrumb (Ljava/lang/String;)V + public fun addBreadcrumb (Ljava/lang/String;Ljava/lang/String;)V + public abstract fun bindClient (Lio/sentry/ISentryClient;)V + public abstract fun captureCheckIn (Lio/sentry/CheckIn;)Lio/sentry/protocol/SentryId; + public fun captureEnvelope (Lio/sentry/SentryEnvelope;)Lio/sentry/protocol/SentryId; + public abstract fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureEvent (Lio/sentry/SentryEvent;)Lio/sentry/protocol/SentryId; + public abstract fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public abstract fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureException (Ljava/lang/Throwable;)Lio/sentry/protocol/SentryId; + public abstract fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public abstract fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureException (Ljava/lang/Throwable;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureMessage (Ljava/lang/String;)Lio/sentry/protocol/SentryId; + public fun captureMessage (Ljava/lang/String;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public abstract fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; + public abstract fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;)Lio/sentry/protocol/SentryId; + public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public abstract fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; + public abstract fun captureUserFeedback (Lio/sentry/UserFeedback;)V + public abstract fun clearBreadcrumbs ()V + public abstract fun clone ()Lio/sentry/IHub; + public abstract fun close ()V + public abstract fun close (Z)V + public abstract fun configureScope (Lio/sentry/ScopeCallback;)V + public abstract fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; + public abstract fun endSession ()V + public abstract fun flush (J)V + public abstract fun getBaggage ()Lio/sentry/BaggageHeader; + public abstract fun getLastEventId ()Lio/sentry/protocol/SentryId; + public abstract fun getOptions ()Lio/sentry/SentryOptions; + public abstract fun getRateLimiter ()Lio/sentry/transport/RateLimiter; + public abstract fun getSpan ()Lio/sentry/ISpan; + public abstract fun getTraceparent ()Lio/sentry/SentryTraceHeader; + public abstract fun getTransaction ()Lio/sentry/ITransaction; + public abstract fun isCrashedLastRun ()Ljava/lang/Boolean; + public abstract fun isEnabled ()Z + public abstract fun isHealthy ()Z + public fun isNoOp ()Z + public abstract fun metrics ()Lio/sentry/metrics/MetricsApi; + public abstract fun popScope ()V + public abstract fun pushScope ()V + public abstract fun removeExtra (Ljava/lang/String;)V + public abstract fun removeTag (Ljava/lang/String;)V + public fun reportFullDisplayed ()V + public abstract fun reportFullyDisplayed ()V + public abstract fun setExtra (Ljava/lang/String;Ljava/lang/String;)V + public abstract fun setFingerprint (Ljava/util/List;)V + public abstract fun setLevel (Lio/sentry/SentryLevel;)V + public abstract fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V + public abstract fun setTag (Ljava/lang/String;Ljava/lang/String;)V + public abstract fun setTransaction (Ljava/lang/String;)V + public abstract fun setUser (Lio/sentry/protocol/User;)V + public abstract fun startSession ()V + public fun startTransaction (Lio/sentry/TransactionContext;)Lio/sentry/ITransaction; + public abstract fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; + public fun startTransaction (Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ITransaction; + public fun startTransaction (Ljava/lang/String;Ljava/lang/String;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; + public abstract fun traceHeaders ()Lio/sentry/SentryTraceHeader; + public abstract fun withScope (Lio/sentry/ScopeCallback;)V +} + public abstract interface class io/sentry/ISentryClient { public abstract fun captureCheckIn (Lio/sentry/CheckIn;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureEnvelope (Lio/sentry/SentryEnvelope;)Lio/sentry/protocol/SentryId; @@ -853,7 +910,7 @@ public final class io/sentry/Instrumenter : java/lang/Enum { } public abstract interface class io/sentry/Integration { - public abstract fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public abstract fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/IpAddressUtils { @@ -1187,6 +1244,7 @@ public final class io/sentry/NoOpHub : io/sentry/IHub { public fun isCrashedLastRun ()Ljava/lang/Boolean; public fun isEnabled ()Z public fun isHealthy ()Z + public fun isNoOp ()Z public fun metrics ()Lio/sentry/metrics/MetricsApi; public fun popScope ()V public fun pushScope ()V @@ -1270,6 +1328,60 @@ public final class io/sentry/NoOpScope : io/sentry/IScope { public fun withTransaction (Lio/sentry/Scope$IWithTransaction;)V } +public final class io/sentry/NoOpScopes : io/sentry/IScopes { + public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V + public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V + public fun bindClient (Lio/sentry/ISentryClient;)V + public fun captureCheckIn (Lio/sentry/CheckIn;)Lio/sentry/protocol/SentryId; + public fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; + public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; + public fun captureUserFeedback (Lio/sentry/UserFeedback;)V + public fun clearBreadcrumbs ()V + public fun clone ()Lio/sentry/IHub; + public synthetic fun clone ()Ljava/lang/Object; + public fun close ()V + public fun close (Z)V + public fun configureScope (Lio/sentry/ScopeCallback;)V + public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; + public fun endSession ()V + public fun flush (J)V + public fun getBaggage ()Lio/sentry/BaggageHeader; + public static fun getInstance ()Lio/sentry/NoOpScopes; + public fun getLastEventId ()Lio/sentry/protocol/SentryId; + public fun getOptions ()Lio/sentry/SentryOptions; + public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; + public fun getSpan ()Lio/sentry/ISpan; + public fun getTraceparent ()Lio/sentry/SentryTraceHeader; + public fun getTransaction ()Lio/sentry/ITransaction; + public fun isCrashedLastRun ()Ljava/lang/Boolean; + public fun isEnabled ()Z + public fun isHealthy ()Z + public fun isNoOp ()Z + public fun metrics ()Lio/sentry/metrics/MetricsApi; + public fun popScope ()V + public fun pushScope ()V + public fun removeExtra (Ljava/lang/String;)V + public fun removeTag (Ljava/lang/String;)V + public fun reportFullyDisplayed ()V + public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V + public fun setFingerprint (Ljava/util/List;)V + public fun setLevel (Lio/sentry/SentryLevel;)V + public fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V + public fun setTag (Ljava/lang/String;Ljava/lang/String;)V + public fun setTransaction (Ljava/lang/String;)V + public fun setUser (Lio/sentry/protocol/User;)V + public fun startSession ()V + public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; + public fun traceHeaders ()Lio/sentry/SentryTraceHeader; + public fun withScope (Lio/sentry/ScopeCallback;)V +} + public final class io/sentry/NoOpSpan : io/sentry/ISpan { public fun finish ()V public fun finish (Lio/sentry/SpanStatus;)V @@ -1403,7 +1515,7 @@ public final class io/sentry/OptionsContainer { } public final class io/sentry/OutboxSender : io/sentry/IEnvelopeSender { - public fun (Lio/sentry/IHub;Lio/sentry/IEnvelopeReader;Lio/sentry/ISerializer;Lio/sentry/ILogger;JI)V + public fun (Lio/sentry/IScopes;Lio/sentry/IEnvelopeReader;Lio/sentry/ISerializer;Lio/sentry/ILogger;JI)V public synthetic fun processDirectory (Ljava/io/File;)V public fun processEnvelopeFile (Ljava/lang/String;Lio/sentry/Hint;)V } @@ -1668,11 +1780,64 @@ public abstract class io/sentry/ScopeObserverAdapter : io/sentry/IScopeObserver public fun setUser (Lio/sentry/protocol/User;)V } +public final class io/sentry/ScopesAdapter : io/sentry/IScopes { + public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V + public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V + public fun bindClient (Lio/sentry/ISentryClient;)V + public fun captureCheckIn (Lio/sentry/CheckIn;)Lio/sentry/protocol/SentryId; + public fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; + public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; + public fun captureUserFeedback (Lio/sentry/UserFeedback;)V + public fun clearBreadcrumbs ()V + public fun clone ()Lio/sentry/IHub; + public synthetic fun clone ()Ljava/lang/Object; + public fun close ()V + public fun close (Z)V + public fun configureScope (Lio/sentry/ScopeCallback;)V + public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; + public fun endSession ()V + public fun flush (J)V + public fun getBaggage ()Lio/sentry/BaggageHeader; + public static fun getInstance ()Lio/sentry/ScopesAdapter; + public fun getLastEventId ()Lio/sentry/protocol/SentryId; + public fun getOptions ()Lio/sentry/SentryOptions; + public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; + public fun getSpan ()Lio/sentry/ISpan; + public fun getTraceparent ()Lio/sentry/SentryTraceHeader; + public fun getTransaction ()Lio/sentry/ITransaction; + public fun isCrashedLastRun ()Ljava/lang/Boolean; + public fun isEnabled ()Z + public fun isHealthy ()Z + public fun metrics ()Lio/sentry/metrics/MetricsApi; + public fun popScope ()V + public fun pushScope ()V + public fun removeExtra (Ljava/lang/String;)V + public fun removeTag (Ljava/lang/String;)V + public fun reportFullyDisplayed ()V + public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V + public fun setFingerprint (Ljava/util/List;)V + public fun setLevel (Lio/sentry/SentryLevel;)V + public fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V + public fun setTag (Ljava/lang/String;Ljava/lang/String;)V + public fun setTransaction (Ljava/lang/String;)V + public fun setUser (Lio/sentry/protocol/User;)V + public fun startSession ()V + public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; + public fun traceHeaders ()Lio/sentry/SentryTraceHeader; + public fun withScope (Lio/sentry/ScopeCallback;)V +} + public final class io/sentry/SendCachedEnvelopeFireAndForgetIntegration : io/sentry/IConnectionStatusProvider$IConnectionStatusObserver, io/sentry/Integration, java/io/Closeable { public fun (Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForgetFactory;)V public fun close ()V public fun onConnectionStatusChanged (Lio/sentry/IConnectionStatusProvider$ConnectionStatus;)V - public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public abstract interface class io/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForget { @@ -1684,19 +1849,19 @@ public abstract interface class io/sentry/SendCachedEnvelopeFireAndForgetIntegra } public abstract interface class io/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForgetFactory { - public abstract fun create (Lio/sentry/IHub;Lio/sentry/SentryOptions;)Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForget; + public abstract fun create (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForget; public fun hasValidPath (Ljava/lang/String;Lio/sentry/ILogger;)Z public fun processDir (Lio/sentry/DirectoryProcessor;Ljava/lang/String;Lio/sentry/ILogger;)Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForget; } public final class io/sentry/SendFireAndForgetEnvelopeSender : io/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForgetFactory { public fun (Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForgetDirPath;)V - public fun create (Lio/sentry/IHub;Lio/sentry/SentryOptions;)Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForget; + public fun create (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForget; } public final class io/sentry/SendFireAndForgetOutboxSender : io/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForgetFactory { public fun (Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForgetDirPath;)V - public fun create (Lio/sentry/IHub;Lio/sentry/SentryOptions;)Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForget; + public fun create (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForget; } public final class io/sentry/Sentry { @@ -1721,7 +1886,7 @@ public final class io/sentry/Sentry { public static fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; public static fun captureUserFeedback (Lio/sentry/UserFeedback;)V public static fun clearBreadcrumbs ()V - public static fun cloneMainHub ()Lio/sentry/IHub; + public static fun cloneMainHub ()Lio/sentry/IScopes; public static fun close ()V public static fun configureScope (Lio/sentry/ScopeCallback;)V public static fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; @@ -1729,6 +1894,7 @@ public final class io/sentry/Sentry { public static fun flush (J)V public static fun getBaggage ()Lio/sentry/BaggageHeader; public static fun getCurrentHub ()Lio/sentry/IHub; + public static fun getCurrentScopes ()Lio/sentry/IScopes; public static fun getLastEventId ()Lio/sentry/protocol/SentryId; public static fun getSpan ()Lio/sentry/ISpan; public static fun getTraceparent ()Lio/sentry/SentryTraceHeader; @@ -1750,6 +1916,7 @@ public final class io/sentry/Sentry { public static fun reportFullDisplayed ()V public static fun reportFullyDisplayed ()V public static fun setCurrentHub (Lio/sentry/IHub;)V + public static fun setCurrentScopes (Lio/sentry/IScopes;)V public static fun setExtra (Ljava/lang/String;Ljava/lang/String;)V public static fun setFingerprint (Ljava/util/List;)V public static fun setLevel (Lio/sentry/SentryLevel;)V @@ -2500,8 +2667,8 @@ public final class io/sentry/SentryTraceHeader { } public final class io/sentry/SentryTracer : io/sentry/ITransaction { - public fun (Lio/sentry/TransactionContext;Lio/sentry/IHub;)V - public fun (Lio/sentry/TransactionContext;Lio/sentry/IHub;Lio/sentry/TransactionOptions;)V + public fun (Lio/sentry/TransactionContext;Lio/sentry/IScopes;)V + public fun (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;)V public fun finish ()V public fun finish (Lio/sentry/SpanStatus;)V public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V @@ -2630,11 +2797,11 @@ public final class io/sentry/ShutdownHookIntegration : io/sentry/Integration, ja public fun ()V public fun (Ljava/lang/Runtime;)V public fun close ()V - public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/Span : io/sentry/ISpan { - public fun (Lio/sentry/TransactionContext;Lio/sentry/SentryTracer;Lio/sentry/IHub;Lio/sentry/SentryDate;Lio/sentry/SpanOptions;)V + public fun (Lio/sentry/TransactionContext;Lio/sentry/SentryTracer;Lio/sentry/IScopes;Lio/sentry/SentryDate;Lio/sentry/SpanOptions;)V public fun finish ()V public fun finish (Lio/sentry/SpanStatus;)V public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V @@ -2815,7 +2982,7 @@ public final class io/sentry/SpotlightIntegration : io/sentry/Integration, io/se public fun close ()V public fun execute (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)V public fun getSpotlightConnectionUrl ()Ljava/lang/String; - public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/SystemOutLogger : io/sentry/ILogger { @@ -2975,7 +3142,7 @@ public final class io/sentry/TypeCheckHint { public final class io/sentry/UncaughtExceptionHandlerIntegration : io/sentry/Integration, java/io/Closeable, java/lang/Thread$UncaughtExceptionHandler { public fun ()V public fun close ()V - public final fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public final fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V public fun uncaughtException (Ljava/lang/Thread;Ljava/lang/Throwable;)V } @@ -3016,7 +3183,7 @@ public final class io/sentry/UserFeedback$JsonKeys { } public final class io/sentry/backpressure/BackpressureMonitor : io/sentry/backpressure/IBackpressureMonitor, java/lang/Runnable { - public fun (Lio/sentry/SentryOptions;Lio/sentry/IHub;)V + public fun (Lio/sentry/SentryOptions;Lio/sentry/IScopes;)V public fun getDownsampleFactor ()I public fun run ()V public fun start ()V @@ -5102,9 +5269,9 @@ public final class io/sentry/util/StringUtils { public final class io/sentry/util/TracingUtils { public fun ()V public static fun maybeUpdateBaggage (Lio/sentry/IScope;Lio/sentry/SentryOptions;)Lio/sentry/PropagationContext; - public static fun startNewTrace (Lio/sentry/IHub;)V - public static fun trace (Lio/sentry/IHub;Ljava/util/List;Lio/sentry/ISpan;)Lio/sentry/util/TracingUtils$TracingHeaders; - public static fun traceIfAllowed (Lio/sentry/IHub;Ljava/lang/String;Ljava/util/List;Lio/sentry/ISpan;)Lio/sentry/util/TracingUtils$TracingHeaders; + public static fun startNewTrace (Lio/sentry/IScopes;)V + public static fun trace (Lio/sentry/IScopes;Ljava/util/List;Lio/sentry/ISpan;)Lio/sentry/util/TracingUtils$TracingHeaders; + public static fun traceIfAllowed (Lio/sentry/IScopes;Ljava/lang/String;Ljava/util/List;Lio/sentry/ISpan;)Lio/sentry/util/TracingUtils$TracingHeaders; } public final class io/sentry/util/TracingUtils$TracingHeaders { diff --git a/sentry/src/main/java/io/sentry/Hub.java b/sentry/src/main/java/io/sentry/Hub.java index 6b6da88f09..6a98bb2367 100644 --- a/sentry/src/main/java/io/sentry/Hub.java +++ b/sentry/src/main/java/io/sentry/Hub.java @@ -27,6 +27,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +@Deprecated public final class Hub implements IHub, MetricsApi.IMetricsInterface { private volatile @NotNull SentryId lastEventId; diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index b31d853192..746d51f0cc 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -10,6 +10,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +/** + * @deprecated use {@link ScopesAdapter} instead + */ +@Deprecated public final class HubAdapter implements IHub { private static final HubAdapter INSTANCE = new HubAdapter(); @@ -50,7 +54,7 @@ public boolean isEnabled() { @ApiStatus.Internal @Override public @NotNull SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Hint hint) { - return Sentry.getCurrentHub().captureEnvelope(envelope, hint); + return Sentry.getCurrentScopes().captureEnvelope(envelope, hint); } @Override @@ -186,7 +190,7 @@ public void flush(long timeoutMillis) { @Override public @NotNull IHub clone() { - return Sentry.getCurrentHub().clone(); + return Sentry.getCurrentScopes().clone(); } @Override @@ -195,7 +199,7 @@ public void flush(long timeoutMillis) { @Nullable TraceContext traceContext, @Nullable Hint hint, @Nullable ProfilingTraceData profilingTraceData) { - return Sentry.getCurrentHub() + return Sentry.getCurrentScopes() .captureTransaction(transaction, traceContext, hint, profilingTraceData); } @@ -217,23 +221,23 @@ public void setSpanContext( final @NotNull Throwable throwable, final @NotNull ISpan span, final @NotNull String transactionName) { - Sentry.getCurrentHub().setSpanContext(throwable, span, transactionName); + Sentry.getCurrentScopes().setSpanContext(throwable, span, transactionName); } @Override public @Nullable ISpan getSpan() { - return Sentry.getCurrentHub().getSpan(); + return Sentry.getCurrentScopes().getSpan(); } @Override @ApiStatus.Internal public @Nullable ITransaction getTransaction() { - return Sentry.getCurrentHub().getTransaction(); + return Sentry.getCurrentScopes().getTransaction(); } @Override public @NotNull SentryOptions getOptions() { - return Sentry.getCurrentHub().getOptions(); + return Sentry.getCurrentScopes().getOptions(); } @Override @@ -271,11 +275,11 @@ public void reportFullyDisplayed() { @ApiStatus.Internal @Override public @Nullable RateLimiter getRateLimiter() { - return Sentry.getCurrentHub().getRateLimiter(); + return Sentry.getCurrentScopes().getRateLimiter(); } @Override public @NotNull MetricsApi metrics() { - return Sentry.getCurrentHub().metrics(); + return Sentry.getCurrentScopes().metrics(); } } diff --git a/sentry/src/main/java/io/sentry/HubScopesWrapper.java b/sentry/src/main/java/io/sentry/HubScopesWrapper.java new file mode 100644 index 0000000000..9b294d05b7 --- /dev/null +++ b/sentry/src/main/java/io/sentry/HubScopesWrapper.java @@ -0,0 +1,279 @@ +package io.sentry; + +import io.sentry.metrics.MetricsApi; +import io.sentry.protocol.SentryId; +import io.sentry.protocol.SentryTransaction; +import io.sentry.protocol.User; +import io.sentry.transport.RateLimiter; +import java.util.List; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@SuppressWarnings("deprecation") +@Deprecated +public final class HubScopesWrapper implements IHub { + + private final IScopes scopes; + + public HubScopesWrapper(final @NotNull IScopes scopes) { + this.scopes = scopes; + } + + @Override + public boolean isEnabled() { + return scopes.isEnabled(); + } + + @Override + public @NotNull SentryId captureEvent(@NotNull SentryEvent event, @Nullable Hint hint) { + return scopes.captureEvent(event, hint); + } + + @Override + public @NotNull SentryId captureEvent( + @NotNull SentryEvent event, @Nullable Hint hint, @NotNull ScopeCallback callback) { + return scopes.captureEvent(event, hint, callback); + } + + @Override + public @NotNull SentryId captureMessage(@NotNull String message, @NotNull SentryLevel level) { + return scopes.captureMessage(message, level); + } + + @Override + public @NotNull SentryId captureMessage( + @NotNull String message, @NotNull SentryLevel level, @NotNull ScopeCallback callback) { + return scopes.captureMessage(message, level, callback); + } + + @Override + public @NotNull SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Hint hint) { + return scopes.captureEnvelope(envelope, hint); + } + + @Override + public @NotNull SentryId captureException(@NotNull Throwable throwable, @Nullable Hint hint) { + return scopes.captureException(throwable, hint); + } + + @Override + public @NotNull SentryId captureException( + @NotNull Throwable throwable, @Nullable Hint hint, @NotNull ScopeCallback callback) { + return scopes.captureException(throwable, hint, callback); + } + + @Override + public void captureUserFeedback(@NotNull UserFeedback userFeedback) { + scopes.captureUserFeedback(userFeedback); + } + + @Override + public void startSession() { + scopes.startSession(); + } + + @Override + public void endSession() { + scopes.endSession(); + } + + @Override + public void close() { + scopes.close(); + } + + @Override + public void close(boolean isRestarting) { + scopes.close(isRestarting); + } + + @Override + public void addBreadcrumb(@NotNull Breadcrumb breadcrumb, @Nullable Hint hint) { + scopes.addBreadcrumb(breadcrumb, hint); + } + + @Override + public void addBreadcrumb(@NotNull Breadcrumb breadcrumb) { + scopes.addBreadcrumb(breadcrumb); + } + + @Override + public void setLevel(@Nullable SentryLevel level) { + scopes.setLevel(level); + } + + @Override + public void setTransaction(@Nullable String transaction) { + scopes.setTransaction(transaction); + } + + @Override + public void setUser(@Nullable User user) { + scopes.setUser(user); + } + + @Override + public void setFingerprint(@NotNull List fingerprint) { + scopes.setFingerprint(fingerprint); + } + + @Override + public void clearBreadcrumbs() { + scopes.clearBreadcrumbs(); + } + + @Override + public void setTag(@NotNull String key, @NotNull String value) { + scopes.setTag(key, value); + } + + @Override + public void removeTag(@NotNull String key) { + scopes.removeTag(key); + } + + @Override + public void setExtra(@NotNull String key, @NotNull String value) { + scopes.setExtra(key, value); + } + + @Override + public void removeExtra(@NotNull String key) { + scopes.removeExtra(key); + } + + @Override + public @NotNull SentryId getLastEventId() { + return scopes.getLastEventId(); + } + + @Override + public void pushScope() { + scopes.pushScope(); + } + + @Override + public void popScope() { + scopes.popScope(); + } + + @Override + public void withScope(@NotNull ScopeCallback callback) { + scopes.withScope(callback); + } + + @Override + public void configureScope(@NotNull ScopeCallback callback) { + scopes.configureScope(callback); + } + + @Override + public void bindClient(@NotNull ISentryClient client) { + scopes.bindClient(client); + } + + @Override + public boolean isHealthy() { + return scopes.isHealthy(); + } + + @Override + public void flush(long timeoutMillis) { + scopes.flush(timeoutMillis); + } + + @Override + public @NotNull IHub clone() { + return scopes.clone(); + } + + @ApiStatus.Internal + @Override + public @NotNull SentryId captureTransaction( + @NotNull SentryTransaction transaction, + @Nullable TraceContext traceContext, + @Nullable Hint hint, + @Nullable ProfilingTraceData profilingTraceData) { + return scopes.captureTransaction(transaction, traceContext, hint, profilingTraceData); + } + + @Override + public @NotNull ITransaction startTransaction( + @NotNull TransactionContext transactionContext, + @NotNull TransactionOptions transactionOptions) { + return scopes.startTransaction(transactionContext, transactionOptions); + } + + @Override + public @Nullable SentryTraceHeader traceHeaders() { + return scopes.traceHeaders(); + } + + @ApiStatus.Internal + @Override + public void setSpanContext( + @NotNull Throwable throwable, @NotNull ISpan span, @NotNull String transactionName) { + scopes.setSpanContext(throwable, span, transactionName); + } + + @Override + public @Nullable ISpan getSpan() { + return scopes.getSpan(); + } + + @ApiStatus.Internal + @Override + public @Nullable ITransaction getTransaction() { + return scopes.getTransaction(); + } + + @Override + public @NotNull SentryOptions getOptions() { + return scopes.getOptions(); + } + + @Override + public @Nullable Boolean isCrashedLastRun() { + return scopes.isCrashedLastRun(); + } + + @Override + public void reportFullyDisplayed() { + scopes.reportFullyDisplayed(); + } + + @Override + public @Nullable TransactionContext continueTrace( + @Nullable String sentryTrace, @Nullable List baggageHeaders) { + return scopes.continueTrace(sentryTrace, baggageHeaders); + } + + @Override + public @Nullable SentryTraceHeader getTraceparent() { + return scopes.getTraceparent(); + } + + @Override + public @Nullable BaggageHeader getBaggage() { + return scopes.getBaggage(); + } + + @ApiStatus.Experimental + @Override + public @NotNull SentryId captureCheckIn(@NotNull CheckIn checkIn) { + return scopes.captureCheckIn(checkIn); + } + + @ApiStatus.Internal + @Override + public @Nullable RateLimiter getRateLimiter() { + return scopes.getRateLimiter(); + } + + @ApiStatus.Experimental + @Override + public @NotNull MetricsApi metrics() { + return scopes.metrics(); + } +} diff --git a/sentry/src/main/java/io/sentry/IHub.java b/sentry/src/main/java/io/sentry/IHub.java index 684d8ec528..23a7bed7de 100644 --- a/sentry/src/main/java/io/sentry/IHub.java +++ b/sentry/src/main/java/io/sentry/IHub.java @@ -1,590 +1,9 @@ package io.sentry; -import io.sentry.metrics.MetricsApi; -import io.sentry.protocol.SentryId; -import io.sentry.protocol.SentryTransaction; -import io.sentry.protocol.User; -import io.sentry.transport.RateLimiter; -import java.util.List; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** SDK API contract which combines a client and scope management */ -public interface IHub { - - /** - * Check if the Hub is enabled/active. - * - * @return true if its enabled or false otherwise. - */ - boolean isEnabled(); - - /** - * Captures the event. - * - * @param event the event - * @param hint SDK specific but provides high level information about the origin of the event - * @return The Id (SentryId object) of the event - */ - @NotNull - SentryId captureEvent(@NotNull SentryEvent event, @Nullable Hint hint); - - /** - * Captures the event. - * - * @param event the event - * @return The Id (SentryId object) of the event - */ - default @NotNull SentryId captureEvent(@NotNull SentryEvent event) { - return captureEvent(event, new Hint()); - } - - /** - * Captures the event. - * - * @param event the event - * @param callback The callback to configure the scope for a single invocation. - * @return The Id (SentryId object) of the event - */ - default @NotNull SentryId captureEvent( - @NotNull SentryEvent event, final @NotNull ScopeCallback callback) { - return captureEvent(event, new Hint(), callback); - } - - /** - * Captures the event. - * - * @param event the event - * @param hint SDK specific but provides high level information about the origin of the event - * @param callback The callback to configure the scope for a single invocation. - * @return The Id (SentryId object) of the event - */ - @NotNull - SentryId captureEvent( - final @NotNull SentryEvent event, - final @Nullable Hint hint, - final @NotNull ScopeCallback callback); - - /** - * Captures the message. - * - * @param message The message to send. - * @return The Id (SentryId object) of the event - */ - default @NotNull SentryId captureMessage(@NotNull String message) { - return captureMessage(message, SentryLevel.INFO); - } - - /** - * Captures the message. - * - * @param message The message to send. - * @param level The message level. - * @return The Id (SentryId object) of the event - */ - @NotNull - SentryId captureMessage(@NotNull String message, @NotNull SentryLevel level); - - /** - * Captures the message. - * - * @param message The message to send. - * @param level The message level. - * @param callback The callback to configure the scope for a single invocation. - * @return The Id (SentryId object) of the event - */ - @NotNull - SentryId captureMessage( - @NotNull String message, @NotNull SentryLevel level, @NotNull ScopeCallback callback); - - /** - * Captures the message. - * - * @param message The message to send. - * @param callback The callback to configure the scope for a single invocation. - * @return The Id (SentryId object) of the event - */ - default @NotNull SentryId captureMessage( - @NotNull String message, @NotNull ScopeCallback callback) { - return captureMessage(message, SentryLevel.INFO, callback); - } - - /** - * Captures an envelope. - * - * @param envelope the SentryEnvelope to send. - * @param hint SDK specific but provides high level information about the origin of the event - * @return The Id (SentryId object) of the event - */ - @NotNull - SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Hint hint); - - /** - * Captures an envelope. - * - * @param envelope the SentryEnvelope to send. - * @return The Id (SentryId object) of the event - */ - default @NotNull SentryId captureEnvelope(@NotNull SentryEnvelope envelope) { - return captureEnvelope(envelope, new Hint()); - } - - /** - * Captures the exception. - * - * @param throwable The exception. - * @param hint SDK specific but provides high level information about the origin of the event - * @return The Id (SentryId object) of the event - */ - @NotNull - SentryId captureException(@NotNull Throwable throwable, @Nullable Hint hint); - - /** - * Captures the exception. - * - * @param throwable The exception. - * @return The Id (SentryId object) of the event - */ - default @NotNull SentryId captureException(@NotNull Throwable throwable) { - return captureException(throwable, new Hint()); - } - - /** - * Captures the exception. - * - * @param throwable The exception. - * @param callback The callback to configure the scope for a single invocation. - * @return The Id (SentryId object) of the event - */ - default @NotNull SentryId captureException( - @NotNull Throwable throwable, final @NotNull ScopeCallback callback) { - return captureException(throwable, new Hint(), callback); - } - - /** - * Captures the exception. - * - * @param throwable The exception. - * @param hint SDK specific but provides high level information about the origin of the event - * @param callback The callback to configure the scope for a single invocation. - * @return The Id (SentryId object) of the event - */ - @NotNull - SentryId captureException( - final @NotNull Throwable throwable, - final @Nullable Hint hint, - final @NotNull ScopeCallback callback); - - /** - * Captures a manually created user feedback and sends it to Sentry. - * - * @param userFeedback The user feedback to send to Sentry. - */ - void captureUserFeedback(@NotNull UserFeedback userFeedback); - - /** Starts a new session. If there's a running session, it ends it before starting the new one. */ - void startSession(); - - /** Ends the current session */ - void endSession(); - - /** Flushes out the queue for up to timeout seconds and disable the Hub. */ - void close(); - - /** - * Flushes out the queue for up to timeout seconds and disable the Hub. - * - * @param isRestarting if true, avoids locking the main thread when finishing the queue. - */ - void close(boolean isRestarting); - - /** - * Adds a breadcrumb to the current Scope - * - * @param breadcrumb the breadcrumb - * @param hint SDK specific but provides high level information about the origin of the event - */ - void addBreadcrumb(@NotNull Breadcrumb breadcrumb, @Nullable Hint hint); - - /** - * Adds a breadcrumb to the current Scope - * - * @param breadcrumb the breadcrumb - */ - void addBreadcrumb(@NotNull Breadcrumb breadcrumb); - - /** - * Adds a breadcrumb to the current Scope - * - * @param message rendered as text and the whitespace is preserved. - */ - default void addBreadcrumb(@NotNull String message) { - addBreadcrumb(new Breadcrumb(message)); - } - - /** - * Adds a breadcrumb to the current Scope - * - * @param message rendered as text and the whitespace is preserved. - * @param category Categories are dotted strings that indicate what the crumb is or where it comes - * from. - */ - default void addBreadcrumb(@NotNull String message, @NotNull String category) { - Breadcrumb breadcrumb = new Breadcrumb(message); - breadcrumb.setCategory(category); - addBreadcrumb(breadcrumb); - } - - /** - * Sets the level of all events sent within current Scope - * - * @param level the Sentry level - */ - void setLevel(@Nullable SentryLevel level); - - /** - * Sets the name of the current transaction to the current Scope. - * - * @param transaction the transaction - */ - void setTransaction(@Nullable String transaction); - - /** - * Shallow merges user configuration (email, username, etc) to the current Scope. - * - * @param user the user - */ - void setUser(@Nullable User user); - - /** - * Sets the fingerprint to group specific events together to the current Scope. - * - * @param fingerprint the fingerprints - */ - void setFingerprint(@NotNull List fingerprint); - - /** Deletes current breadcrumbs from the current scope. */ - void clearBreadcrumbs(); - - /** - * Sets the tag to a string value to the current Scope, overwriting a potential previous value - * - * @param key the key - * @param value the value - */ - void setTag(@NotNull String key, @NotNull String value); - - /** - * Removes the tag to a string value to the current Scope - * - * @param key the key - */ - void removeTag(@NotNull String key); - - /** - * Sets the extra key to an arbitrary value to the current Scope, overwriting a potential previous - * value - * - * @param key the key - * @param value the value - */ - void setExtra(@NotNull String key, @NotNull String value); - - /** - * Removes the extra key to an arbitrary value to the current Scope - * - * @param key the key - */ - void removeExtra(@NotNull String key); - - /** - * Last event id recorded in the current scope - * - * @return last SentryId - */ - @NotNull - SentryId getLastEventId(); - - /** Pushes a new scope while inheriting the current scope's data. */ - void pushScope(); - - /** Removes the first scope */ - void popScope(); - - /** - * Runs the callback with a new scope which gets dropped at the end. If you're using the Sentry - * SDK in globalHubMode (defaults to true on Android) {@link - * Sentry#init(Sentry.OptionsConfiguration, boolean)} calling withScope is discouraged, as scope - * changes may be dropped when executed in parallel. Use {@link - * IHub#configureScope(ScopeCallback)} instead. - * - * @param callback the callback - */ - void withScope(@NotNull ScopeCallback callback); - - /** - * Configures the scope through the callback. - * - * @param callback The configure scope callback. - */ - void configureScope(@NotNull ScopeCallback callback); - - /** - * Binds a different client to the hub - * - * @param client the client. - */ - void bindClient(@NotNull ISentryClient client); - - /** - * Whether the transport is healthy. - * - * @return true if the transport is healthy - */ - boolean isHealthy(); - - /** - * Flushes events queued up, but keeps the Hub enabled. Not implemented yet. - * - * @param timeoutMillis time in milliseconds - */ - void flush(long timeoutMillis); - - /** - * Clones the Hub - * - * @return the cloned Hub - */ - @NotNull - IHub clone(); - - /** - * Captures the transaction and enqueues it for sending to Sentry server. - * - * @param transaction the transaction - * @param traceContext the trace context - * @param hint the hints - * @param profilingTraceData the profiling trace data - * @return transaction's id - */ - @ApiStatus.Internal - @NotNull - SentryId captureTransaction( - @NotNull SentryTransaction transaction, - @Nullable TraceContext traceContext, - @Nullable Hint hint, - @Nullable ProfilingTraceData profilingTraceData); - - /** - * Captures the transaction and enqueues it for sending to Sentry server. - * - * @param transaction the transaction - * @param traceContext the trace context - * @param hint the hints - * @return transaction's id - */ - @ApiStatus.Internal - @NotNull - default SentryId captureTransaction( - @NotNull SentryTransaction transaction, - @Nullable TraceContext traceContext, - @Nullable Hint hint) { - return captureTransaction(transaction, traceContext, hint, null); - } - - @ApiStatus.Internal - @NotNull - default SentryId captureTransaction(@NotNull SentryTransaction transaction, @Nullable Hint hint) { - return captureTransaction(transaction, null, hint); - } - - /** - * Captures the transaction and enqueues it for sending to Sentry server. - * - * @param transaction the transaction - * @param traceContext the trace context - * @return transaction's id - */ - @ApiStatus.Internal - default @NotNull SentryId captureTransaction( - @NotNull SentryTransaction transaction, @Nullable TraceContext traceContext) { - return captureTransaction(transaction, traceContext, null); - } - - /** - * Creates a Transaction and returns the instance. - * - * @param transactionContexts the transaction contexts - * @return created transaction - */ - default @NotNull ITransaction startTransaction(@NotNull TransactionContext transactionContexts) { - return startTransaction(transactionContexts, new TransactionOptions()); - } - - /** - * Creates a Transaction and returns the instance. Based on the {@link - * SentryOptions#getTracesSampleRate()} the decision if transaction is sampled will be taken by - * {@link TracesSampler}. - * - * @param name the transaction name - * @param operation the operation - * @return created transaction - */ - default @NotNull ITransaction startTransaction( - final @NotNull String name, final @NotNull String operation) { - return startTransaction(name, operation, new TransactionOptions()); - } - - /** - * Creates a Transaction and returns the instance. Based on the {@link - * SentryOptions#getTracesSampleRate()} the decision if transaction is sampled will be taken by - * {@link TracesSampler}. - * - * @param name the transaction name - * @param operation the operation - * @param transactionOptions the transaction options - * @return created transaction - */ - default @NotNull ITransaction startTransaction( - final @NotNull String name, - final @NotNull String operation, - final @NotNull TransactionOptions transactionOptions) { - return startTransaction(new TransactionContext(name, operation), transactionOptions); - } - - /** - * 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, - final @NotNull TransactionOptions transactionOptions); - - /** - * Returns the "sentry-trace" header that allows tracing across services. Can also be used in - * <meta> HTML tags. Also see {@link IHub#getBaggage()}. - * - * @deprecated please use {@link IHub#getTraceparent()} instead. - * @return sentry trace header or null - */ - @Deprecated - @Nullable - SentryTraceHeader traceHeaders(); - - /** - * Associates {@link ISpan} and the transaction name with the {@link Throwable}. Used to determine - * in which trace the exception has been thrown in framework integrations. - * - * @param throwable the throwable - * @param span the span context - * @param transactionName the transaction name - */ - @ApiStatus.Internal - void setSpanContext( - @NotNull Throwable throwable, @NotNull ISpan span, @NotNull String transactionName); - - /** - * Gets the current active transaction or span. - * - * @return the active span or null when no active transaction is running - */ - @Nullable - ISpan getSpan(); - - /** - * Returns the transaction. - * - * @return the transaction or null when no active transaction is running. - */ - @ApiStatus.Internal - @Nullable - ITransaction getTransaction(); - - /** - * Gets the {@link SentryOptions} attached to current scope. - * - * @return the options attached to current scope. - */ - @NotNull - SentryOptions getOptions(); - - /** - * Returns if the App has crashed (Process has terminated) during the last run. It only returns - * true or false if offline caching {{@link SentryOptions#getCacheDirPath()} } is set with a valid - * dir. - * - *

If the call to this method is early in the App lifecycle and the SDK could not check if the - * App has crashed in the background, the check is gonna do IO in the calling thread. - * - * @return true if App has crashed, false otherwise, and null if not evaluated yet - */ - @Nullable - Boolean isCrashedLastRun(); - - /** - * Report a screen has been fully loaded. That means all data needed by the UI was loaded. If - * time-to-full-display tracing {{@link SentryOptions#isEnableTimeToFullDisplayTracing()} } is - * disabled this call is ignored. - * - *

This method is safe to be called multiple times. If the time-to-full-display span is already - * finished, this call will be ignored. - */ - void reportFullyDisplayed(); - - /** - * @deprecated See {@link IHub#reportFullyDisplayed()}. - */ - @Deprecated - default void reportFullDisplayed() { - reportFullyDisplayed(); - } - - /** - * Continue a trace based on HTTP header values. If no "sentry-trace" header is provided a random - * trace ID and span ID is created. - * - * @param sentryTrace "sentry-trace" header - * @param baggageHeaders "baggage" headers - * @return a transaction context for starting a transaction or null if performance is disabled - */ - @Nullable - TransactionContext continueTrace( - final @Nullable String sentryTrace, final @Nullable List baggageHeaders); - - /** - * Returns the "sentry-trace" header that allows tracing across services. Can also be used in - * <meta> HTML tags. Also see {@link IHub#getBaggage()}. - * - * @return sentry trace header or null - */ - @Nullable - SentryTraceHeader getTraceparent(); - - /** - * Returns the "baggage" header that allows tracing across services. Can also be used in - * <meta> HTML tags. Also see {@link IHub#getTraceparent()}. - * - * @return baggage header or null - */ - @Nullable - BaggageHeader getBaggage(); - - @ApiStatus.Experimental - @NotNull - SentryId captureCheckIn(final @NotNull CheckIn checkIn); - - @ApiStatus.Internal - @Nullable - RateLimiter getRateLimiter(); - - @ApiStatus.Experimental - @NotNull - MetricsApi metrics(); -} +/** + * SDK API contract which combines a client and scope management + * + * @deprecated please use {@link IScopes} instead + */ +@Deprecated +public interface IHub extends IScopes {} diff --git a/sentry/src/main/java/io/sentry/IScopes.java b/sentry/src/main/java/io/sentry/IScopes.java new file mode 100644 index 0000000000..03662457e4 --- /dev/null +++ b/sentry/src/main/java/io/sentry/IScopes.java @@ -0,0 +1,594 @@ +package io.sentry; + +import io.sentry.metrics.MetricsApi; +import io.sentry.protocol.SentryId; +import io.sentry.protocol.SentryTransaction; +import io.sentry.protocol.User; +import io.sentry.transport.RateLimiter; +import java.util.List; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface IScopes { + + /** + * Check if the Hub is enabled/active. + * + * @return true if its enabled or false otherwise. + */ + boolean isEnabled(); + + /** + * Captures the event. + * + * @param event the event + * @param hint SDK specific but provides high level information about the origin of the event + * @return The Id (SentryId object) of the event + */ + @NotNull + SentryId captureEvent(@NotNull SentryEvent event, @Nullable Hint hint); + + /** + * Captures the event. + * + * @param event the event + * @return The Id (SentryId object) of the event + */ + default @NotNull SentryId captureEvent(@NotNull SentryEvent event) { + return captureEvent(event, new Hint()); + } + + /** + * Captures the event. + * + * @param event the event + * @param callback The callback to configure the scope for a single invocation. + * @return The Id (SentryId object) of the event + */ + default @NotNull SentryId captureEvent( + @NotNull SentryEvent event, final @NotNull ScopeCallback callback) { + return captureEvent(event, new Hint(), callback); + } + + /** + * Captures the event. + * + * @param event the event + * @param hint SDK specific but provides high level information about the origin of the event + * @param callback The callback to configure the scope for a single invocation. + * @return The Id (SentryId object) of the event + */ + @NotNull + SentryId captureEvent( + final @NotNull SentryEvent event, + final @Nullable Hint hint, + final @NotNull ScopeCallback callback); + + /** + * Captures the message. + * + * @param message The message to send. + * @return The Id (SentryId object) of the event + */ + default @NotNull SentryId captureMessage(@NotNull String message) { + return captureMessage(message, SentryLevel.INFO); + } + + /** + * Captures the message. + * + * @param message The message to send. + * @param level The message level. + * @return The Id (SentryId object) of the event + */ + @NotNull + SentryId captureMessage(@NotNull String message, @NotNull SentryLevel level); + + /** + * Captures the message. + * + * @param message The message to send. + * @param level The message level. + * @param callback The callback to configure the scope for a single invocation. + * @return The Id (SentryId object) of the event + */ + @NotNull + SentryId captureMessage( + @NotNull String message, @NotNull SentryLevel level, @NotNull ScopeCallback callback); + + /** + * Captures the message. + * + * @param message The message to send. + * @param callback The callback to configure the scope for a single invocation. + * @return The Id (SentryId object) of the event + */ + default @NotNull SentryId captureMessage( + @NotNull String message, @NotNull ScopeCallback callback) { + return captureMessage(message, SentryLevel.INFO, callback); + } + + /** + * Captures an envelope. + * + * @param envelope the SentryEnvelope to send. + * @param hint SDK specific but provides high level information about the origin of the event + * @return The Id (SentryId object) of the event + */ + @NotNull + SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Hint hint); + + /** + * Captures an envelope. + * + * @param envelope the SentryEnvelope to send. + * @return The Id (SentryId object) of the event + */ + default @NotNull SentryId captureEnvelope(@NotNull SentryEnvelope envelope) { + return captureEnvelope(envelope, new Hint()); + } + + /** + * Captures the exception. + * + * @param throwable The exception. + * @param hint SDK specific but provides high level information about the origin of the event + * @return The Id (SentryId object) of the event + */ + @NotNull + SentryId captureException(@NotNull Throwable throwable, @Nullable Hint hint); + + /** + * Captures the exception. + * + * @param throwable The exception. + * @return The Id (SentryId object) of the event + */ + default @NotNull SentryId captureException(@NotNull Throwable throwable) { + return captureException(throwable, new Hint()); + } + + /** + * Captures the exception. + * + * @param throwable The exception. + * @param callback The callback to configure the scope for a single invocation. + * @return The Id (SentryId object) of the event + */ + default @NotNull SentryId captureException( + @NotNull Throwable throwable, final @NotNull ScopeCallback callback) { + return captureException(throwable, new Hint(), callback); + } + + /** + * Captures the exception. + * + * @param throwable The exception. + * @param hint SDK specific but provides high level information about the origin of the event + * @param callback The callback to configure the scope for a single invocation. + * @return The Id (SentryId object) of the event + */ + @NotNull + SentryId captureException( + final @NotNull Throwable throwable, + final @Nullable Hint hint, + final @NotNull ScopeCallback callback); + + /** + * Captures a manually created user feedback and sends it to Sentry. + * + * @param userFeedback The user feedback to send to Sentry. + */ + void captureUserFeedback(@NotNull UserFeedback userFeedback); + + /** Starts a new session. If there's a running session, it ends it before starting the new one. */ + void startSession(); + + /** Ends the current session */ + void endSession(); + + /** Flushes out the queue for up to timeout seconds and disable the Hub. */ + void close(); + + /** + * Flushes out the queue for up to timeout seconds and disable the Hub. + * + * @param isRestarting if true, avoids locking the main thread when finishing the queue. + */ + void close(boolean isRestarting); + + /** + * Adds a breadcrumb to the current Scope + * + * @param breadcrumb the breadcrumb + * @param hint SDK specific but provides high level information about the origin of the event + */ + void addBreadcrumb(@NotNull Breadcrumb breadcrumb, @Nullable Hint hint); + + /** + * Adds a breadcrumb to the current Scope + * + * @param breadcrumb the breadcrumb + */ + void addBreadcrumb(@NotNull Breadcrumb breadcrumb); + + /** + * Adds a breadcrumb to the current Scope + * + * @param message rendered as text and the whitespace is preserved. + */ + default void addBreadcrumb(@NotNull String message) { + addBreadcrumb(new Breadcrumb(message)); + } + + /** + * Adds a breadcrumb to the current Scope + * + * @param message rendered as text and the whitespace is preserved. + * @param category Categories are dotted strings that indicate what the crumb is or where it comes + * from. + */ + default void addBreadcrumb(@NotNull String message, @NotNull String category) { + Breadcrumb breadcrumb = new Breadcrumb(message); + breadcrumb.setCategory(category); + addBreadcrumb(breadcrumb); + } + + /** + * Sets the level of all events sent within current Scope + * + * @param level the Sentry level + */ + void setLevel(@Nullable SentryLevel level); + + /** + * Sets the name of the current transaction to the current Scope. + * + * @param transaction the transaction + */ + void setTransaction(@Nullable String transaction); + + /** + * Shallow merges user configuration (email, username, etc) to the current Scope. + * + * @param user the user + */ + void setUser(@Nullable User user); + + /** + * Sets the fingerprint to group specific events together to the current Scope. + * + * @param fingerprint the fingerprints + */ + void setFingerprint(@NotNull List fingerprint); + + /** Deletes current breadcrumbs from the current scope. */ + void clearBreadcrumbs(); + + /** + * Sets the tag to a string value to the current Scope, overwriting a potential previous value + * + * @param key the key + * @param value the value + */ + void setTag(@NotNull String key, @NotNull String value); + + /** + * Removes the tag to a string value to the current Scope + * + * @param key the key + */ + void removeTag(@NotNull String key); + + /** + * Sets the extra key to an arbitrary value to the current Scope, overwriting a potential previous + * value + * + * @param key the key + * @param value the value + */ + void setExtra(@NotNull String key, @NotNull String value); + + /** + * Removes the extra key to an arbitrary value to the current Scope + * + * @param key the key + */ + void removeExtra(@NotNull String key); + + /** + * Last event id recorded in the current scope + * + * @return last SentryId + */ + @NotNull + SentryId getLastEventId(); + + /** Pushes a new scope while inheriting the current scope's data. */ + void pushScope(); + + /** Removes the first scope */ + void popScope(); + + /** + * Runs the callback with a new scope which gets dropped at the end. If you're using the Sentry + * SDK in globalHubMode (defaults to true on Android) {@link + * Sentry#init(Sentry.OptionsConfiguration, boolean)} calling withScope is discouraged, as scope + * changes may be dropped when executed in parallel. Use {@link + * IHub#configureScope(ScopeCallback)} instead. + * + * @param callback the callback + */ + void withScope(@NotNull ScopeCallback callback); + + /** + * Configures the scope through the callback. + * + * @param callback The configure scope callback. + */ + void configureScope(@NotNull ScopeCallback callback); + + /** + * Binds a different client to the hub + * + * @param client the client. + */ + void bindClient(@NotNull ISentryClient client); + + /** + * Whether the transport is healthy. + * + * @return true if the transport is healthy + */ + boolean isHealthy(); + + /** + * Flushes events queued up, but keeps the Hub enabled. Not implemented yet. + * + * @param timeoutMillis time in milliseconds + */ + void flush(long timeoutMillis); + + /** + * Clones the Hub + * + * @return the cloned Hub + */ + @NotNull + @Deprecated + IHub clone(); + + /** + * Captures the transaction and enqueues it for sending to Sentry server. + * + * @param transaction the transaction + * @param traceContext the trace context + * @param hint the hints + * @param profilingTraceData the profiling trace data + * @return transaction's id + */ + @ApiStatus.Internal + @NotNull + SentryId captureTransaction( + @NotNull SentryTransaction transaction, + @Nullable TraceContext traceContext, + @Nullable Hint hint, + @Nullable ProfilingTraceData profilingTraceData); + + /** + * Captures the transaction and enqueues it for sending to Sentry server. + * + * @param transaction the transaction + * @param traceContext the trace context + * @param hint the hints + * @return transaction's id + */ + @ApiStatus.Internal + @NotNull + default SentryId captureTransaction( + @NotNull SentryTransaction transaction, + @Nullable TraceContext traceContext, + @Nullable Hint hint) { + return captureTransaction(transaction, traceContext, hint, null); + } + + @ApiStatus.Internal + @NotNull + default SentryId captureTransaction(@NotNull SentryTransaction transaction, @Nullable Hint hint) { + return captureTransaction(transaction, null, hint); + } + + /** + * Captures the transaction and enqueues it for sending to Sentry server. + * + * @param transaction the transaction + * @param traceContext the trace context + * @return transaction's id + */ + @ApiStatus.Internal + default @NotNull SentryId captureTransaction( + @NotNull SentryTransaction transaction, @Nullable TraceContext traceContext) { + return captureTransaction(transaction, traceContext, null); + } + + /** + * Creates a Transaction and returns the instance. + * + * @param transactionContexts the transaction contexts + * @return created transaction + */ + default @NotNull ITransaction startTransaction(@NotNull TransactionContext transactionContexts) { + return startTransaction(transactionContexts, new TransactionOptions()); + } + + /** + * Creates a Transaction and returns the instance. Based on the {@link + * SentryOptions#getTracesSampleRate()} the decision if transaction is sampled will be taken by + * {@link TracesSampler}. + * + * @param name the transaction name + * @param operation the operation + * @return created transaction + */ + default @NotNull ITransaction startTransaction( + final @NotNull String name, final @NotNull String operation) { + return startTransaction(name, operation, new TransactionOptions()); + } + + /** + * Creates a Transaction and returns the instance. Based on the {@link + * SentryOptions#getTracesSampleRate()} the decision if transaction is sampled will be taken by + * {@link TracesSampler}. + * + * @param name the transaction name + * @param operation the operation + * @param transactionOptions the transaction options + * @return created transaction + */ + default @NotNull ITransaction startTransaction( + final @NotNull String name, + final @NotNull String operation, + final @NotNull TransactionOptions transactionOptions) { + return startTransaction(new TransactionContext(name, operation), transactionOptions); + } + + /** + * 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, + final @NotNull TransactionOptions transactionOptions); + + /** + * Returns the "sentry-trace" header that allows tracing across services. Can also be used in + * <meta> HTML tags. Also see {@link IHub#getBaggage()}. + * + * @deprecated please use {@link IHub#getTraceparent()} instead. + * @return sentry trace header or null + */ + @Deprecated + @Nullable + SentryTraceHeader traceHeaders(); + + /** + * Associates {@link ISpan} and the transaction name with the {@link Throwable}. Used to determine + * in which trace the exception has been thrown in framework integrations. + * + * @param throwable the throwable + * @param span the span context + * @param transactionName the transaction name + */ + @ApiStatus.Internal + void setSpanContext( + @NotNull Throwable throwable, @NotNull ISpan span, @NotNull String transactionName); + + /** + * Gets the current active transaction or span. + * + * @return the active span or null when no active transaction is running + */ + @Nullable + ISpan getSpan(); + + /** + * Returns the transaction. + * + * @return the transaction or null when no active transaction is running. + */ + @ApiStatus.Internal + @Nullable + ITransaction getTransaction(); + + /** + * Gets the {@link SentryOptions} attached to current scope. + * + * @return the options attached to current scope. + */ + @NotNull + SentryOptions getOptions(); + + /** + * Returns if the App has crashed (Process has terminated) during the last run. It only returns + * true or false if offline caching {{@link SentryOptions#getCacheDirPath()} } is set with a valid + * dir. + * + *

If the call to this method is early in the App lifecycle and the SDK could not check if the + * App has crashed in the background, the check is gonna do IO in the calling thread. + * + * @return true if App has crashed, false otherwise, and null if not evaluated yet + */ + @Nullable + Boolean isCrashedLastRun(); + + /** + * Report a screen has been fully loaded. That means all data needed by the UI was loaded. If + * time-to-full-display tracing {{@link SentryOptions#isEnableTimeToFullDisplayTracing()} } is + * disabled this call is ignored. + * + *

This method is safe to be called multiple times. If the time-to-full-display span is already + * finished, this call will be ignored. + */ + void reportFullyDisplayed(); + + /** + * @deprecated See {@link IHub#reportFullyDisplayed()}. + */ + @Deprecated + default void reportFullDisplayed() { + reportFullyDisplayed(); + } + + /** + * Continue a trace based on HTTP header values. If no "sentry-trace" header is provided a random + * trace ID and span ID is created. + * + * @param sentryTrace "sentry-trace" header + * @param baggageHeaders "baggage" headers + * @return a transaction context for starting a transaction or null if performance is disabled + */ + @Nullable + TransactionContext continueTrace( + final @Nullable String sentryTrace, final @Nullable List baggageHeaders); + + /** + * Returns the "sentry-trace" header that allows tracing across services. Can also be used in + * <meta> HTML tags. Also see {@link IHub#getBaggage()}. + * + * @return sentry trace header or null + */ + @Nullable + SentryTraceHeader getTraceparent(); + + /** + * Returns the "baggage" header that allows tracing across services. Can also be used in + * <meta> HTML tags. Also see {@link IHub#getTraceparent()}. + * + * @return baggage header or null + */ + @Nullable + BaggageHeader getBaggage(); + + @ApiStatus.Experimental + @NotNull + SentryId captureCheckIn(final @NotNull CheckIn checkIn); + + @ApiStatus.Internal + @Nullable + RateLimiter getRateLimiter(); + + @ApiStatus.Experimental + @NotNull + MetricsApi metrics(); + + default boolean isNoOp() { + return false; + } +} diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index e51cea8d2d..704bd2b44a 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -11,6 +11,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +@Deprecated public final class NoOpHub implements IHub { private static final NoOpHub instance = new NoOpHub(); @@ -21,6 +22,7 @@ public final class NoOpHub implements IHub { private NoOpHub() {} + @Deprecated public static NoOpHub getInstance() { return instance; } @@ -234,4 +236,9 @@ public void reportFullyDisplayed() {} public @NotNull MetricsApi metrics() { return metricsApi; } + + @Override + public boolean isNoOp() { + return true; + } } diff --git a/sentry/src/main/java/io/sentry/NoOpScopes.java b/sentry/src/main/java/io/sentry/NoOpScopes.java new file mode 100644 index 0000000000..6fef262944 --- /dev/null +++ b/sentry/src/main/java/io/sentry/NoOpScopes.java @@ -0,0 +1,243 @@ +package io.sentry; + +import io.sentry.metrics.MetricsApi; +import io.sentry.metrics.NoopMetricsAggregator; +import io.sentry.protocol.SentryId; +import io.sentry.protocol.SentryTransaction; +import io.sentry.protocol.User; +import io.sentry.transport.RateLimiter; +import java.util.List; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class NoOpScopes implements IScopes { + + private static final NoOpScopes instance = new NoOpScopes(); + + private final @NotNull SentryOptions emptyOptions = SentryOptions.empty(); + private final @NotNull MetricsApi metricsApi = + new MetricsApi(NoopMetricsAggregator.getInstance()); + + private NoOpScopes() {} + + public static NoOpScopes getInstance() { + return instance; + } + + @Override + public boolean isEnabled() { + return false; + } + + @Override + public @NotNull SentryId captureEvent(@NotNull SentryEvent event, @Nullable Hint hint) { + return SentryId.EMPTY_ID; + } + + @Override + public @NotNull SentryId captureEvent( + @NotNull SentryEvent event, @Nullable Hint hint, @NotNull ScopeCallback callback) { + return SentryId.EMPTY_ID; + } + + @Override + public @NotNull SentryId captureMessage(@NotNull String message, @NotNull SentryLevel level) { + return SentryId.EMPTY_ID; + } + + @Override + public @NotNull SentryId captureMessage( + @NotNull String message, @NotNull SentryLevel level, @NotNull ScopeCallback callback) { + return SentryId.EMPTY_ID; + } + + @Override + public @NotNull SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Hint hint) { + return SentryId.EMPTY_ID; + } + + @Override + public @NotNull SentryId captureException(@NotNull Throwable throwable, @Nullable Hint hint) { + return SentryId.EMPTY_ID; + } + + @Override + public @NotNull SentryId captureException( + @NotNull Throwable throwable, @Nullable Hint hint, @NotNull ScopeCallback callback) { + return SentryId.EMPTY_ID; + } + + @Override + public void captureUserFeedback(@NotNull UserFeedback userFeedback) {} + + @Override + public void startSession() {} + + @Override + public void endSession() {} + + @Override + public void close() {} + + @Override + public void close(final boolean isRestarting) {} + + @Override + public void addBreadcrumb(@NotNull Breadcrumb breadcrumb, @Nullable Hint hint) {} + + @Override + public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb) {} + + @Override + public void setLevel(@Nullable SentryLevel level) {} + + @Override + public void setTransaction(@Nullable String transaction) {} + + @Override + public void setUser(@Nullable User user) {} + + @Override + public void setFingerprint(@NotNull List fingerprint) {} + + @Override + public void clearBreadcrumbs() {} + + @Override + public void setTag(@NotNull String key, @NotNull String value) {} + + @Override + public void removeTag(@NotNull String key) {} + + @Override + public void setExtra(@NotNull String key, @NotNull String value) {} + + @Override + public void removeExtra(@NotNull String key) {} + + @Override + public @NotNull SentryId getLastEventId() { + return SentryId.EMPTY_ID; + } + + @Override + public void pushScope() {} + + @Override + public void popScope() {} + + @Override + public void withScope(@NotNull ScopeCallback callback) { + callback.run(NoOpScope.getInstance()); + } + + @Override + public void configureScope(@NotNull ScopeCallback callback) {} + + @Override + public void bindClient(@NotNull ISentryClient client) {} + + @Override + public boolean isHealthy() { + return true; + } + + @Override + public void flush(long timeoutMillis) {} + + @Deprecated + @Override + public @NotNull IHub clone() { + return NoOpHub.getInstance(); + } + + @Override + public @NotNull SentryId captureTransaction( + final @NotNull SentryTransaction transaction, + final @Nullable TraceContext traceContext, + final @Nullable Hint hint, + final @Nullable ProfilingTraceData profilingTraceData) { + return SentryId.EMPTY_ID; + } + + @Override + public @NotNull ITransaction startTransaction( + @NotNull TransactionContext transactionContext, + @NotNull TransactionOptions transactionOptions) { + return NoOpTransaction.getInstance(); + } + + @Override + @Deprecated + @SuppressWarnings("InlineMeSuggester") + public @NotNull SentryTraceHeader traceHeaders() { + return new SentryTraceHeader(SentryId.EMPTY_ID, SpanId.EMPTY_ID, true); + } + + @Override + public void setSpanContext( + final @NotNull Throwable throwable, + final @NotNull ISpan spanContext, + final @NotNull String transactionName) {} + + @Override + public @Nullable ISpan getSpan() { + return null; + } + + @Override + public @Nullable ITransaction getTransaction() { + return null; + } + + @Override + public @NotNull SentryOptions getOptions() { + return emptyOptions; + } + + @Override + public @Nullable Boolean isCrashedLastRun() { + return null; + } + + @Override + public void reportFullyDisplayed() {} + + @Override + public @Nullable TransactionContext continueTrace( + final @Nullable String sentryTrace, final @Nullable List baggageHeaders) { + return null; + } + + @Override + public @Nullable SentryTraceHeader getTraceparent() { + return null; + } + + @Override + public @Nullable BaggageHeader getBaggage() { + return null; + } + + @Override + @ApiStatus.Experimental + public @NotNull SentryId captureCheckIn(final @NotNull CheckIn checkIn) { + return SentryId.EMPTY_ID; + } + + @Override + public @Nullable RateLimiter getRateLimiter() { + return null; + } + + @Override + public @NotNull MetricsApi metrics() { + return metricsApi; + } + + @Override + public boolean isNoOp() { + return true; + } +} diff --git a/sentry/src/main/java/io/sentry/ScopesAdapter.java b/sentry/src/main/java/io/sentry/ScopesAdapter.java new file mode 100644 index 0000000000..1ecd31e248 --- /dev/null +++ b/sentry/src/main/java/io/sentry/ScopesAdapter.java @@ -0,0 +1,286 @@ +package io.sentry; + +import io.sentry.metrics.MetricsApi; +import io.sentry.protocol.SentryId; +import io.sentry.protocol.SentryTransaction; +import io.sentry.protocol.User; +import io.sentry.transport.RateLimiter; +import java.util.List; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class ScopesAdapter implements IScopes { + + private static final ScopesAdapter INSTANCE = new ScopesAdapter(); + + private ScopesAdapter() {} + + public static ScopesAdapter getInstance() { + return INSTANCE; + } + + @Override + public boolean isEnabled() { + return Sentry.isEnabled(); + } + + @Override + public @NotNull SentryId captureEvent(@NotNull SentryEvent event, @Nullable Hint hint) { + return Sentry.captureEvent(event, hint); + } + + @Override + public @NotNull SentryId captureEvent( + @NotNull SentryEvent event, @Nullable Hint hint, @NotNull ScopeCallback callback) { + return Sentry.captureEvent(event, hint, callback); + } + + @Override + public @NotNull SentryId captureMessage(@NotNull String message, @NotNull SentryLevel level) { + return Sentry.captureMessage(message, level); + } + + @Override + public @NotNull SentryId captureMessage( + @NotNull String message, @NotNull SentryLevel level, @NotNull ScopeCallback callback) { + return Sentry.captureMessage(message, level, callback); + } + + @ApiStatus.Internal + @Override + public @NotNull SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Hint hint) { + return Sentry.getCurrentScopes().captureEnvelope(envelope, hint); + } + + @Override + public @NotNull SentryId captureException(@NotNull Throwable throwable, @Nullable Hint hint) { + return Sentry.captureException(throwable, hint); + } + + @Override + public @NotNull SentryId captureException( + @NotNull Throwable throwable, @Nullable Hint hint, @NotNull ScopeCallback callback) { + return Sentry.captureException(throwable, hint, callback); + } + + @Override + public void captureUserFeedback(@NotNull UserFeedback userFeedback) { + Sentry.captureUserFeedback(userFeedback); + } + + @Override + public void startSession() { + Sentry.startSession(); + } + + @Override + public void endSession() { + Sentry.endSession(); + } + + @Override + public void close(final boolean isRestarting) { + Sentry.close(); + } + + @Override + public void close() { + Sentry.close(); + } + + @Override + public void addBreadcrumb(@NotNull Breadcrumb breadcrumb, @Nullable Hint hint) { + Sentry.addBreadcrumb(breadcrumb, hint); + } + + @Override + public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb) { + addBreadcrumb(breadcrumb, new Hint()); + } + + @Override + public void setLevel(@Nullable SentryLevel level) { + Sentry.setLevel(level); + } + + @Override + public void setTransaction(@Nullable String transaction) { + Sentry.setTransaction(transaction); + } + + @Override + public void setUser(@Nullable User user) { + Sentry.setUser(user); + } + + @Override + public void setFingerprint(@NotNull List fingerprint) { + Sentry.setFingerprint(fingerprint); + } + + @Override + public void clearBreadcrumbs() { + Sentry.clearBreadcrumbs(); + } + + @Override + public void setTag(@NotNull String key, @NotNull String value) { + Sentry.setTag(key, value); + } + + @Override + public void removeTag(@NotNull String key) { + Sentry.removeTag(key); + } + + @Override + public void setExtra(@NotNull String key, @NotNull String value) { + Sentry.setExtra(key, value); + } + + @Override + public void removeExtra(@NotNull String key) { + Sentry.removeExtra(key); + } + + @Override + public @NotNull SentryId getLastEventId() { + return Sentry.getLastEventId(); + } + + @Override + public void pushScope() { + Sentry.pushScope(); + } + + @Override + public void popScope() { + Sentry.popScope(); + } + + @Override + public void withScope(@NotNull ScopeCallback callback) { + Sentry.withScope(callback); + } + + @Override + public void configureScope(@NotNull ScopeCallback callback) { + Sentry.configureScope(callback); + } + + @Override + public void bindClient(@NotNull ISentryClient client) { + Sentry.bindClient(client); + } + + @Override + public boolean isHealthy() { + return Sentry.isHealthy(); + } + + @Override + public void flush(long timeoutMillis) { + Sentry.flush(timeoutMillis); + } + + @Override + @SuppressWarnings("deprecation") + public @NotNull IHub clone() { + return Sentry.getCurrentScopes().clone(); + } + + @ApiStatus.Internal + @Override + public @NotNull SentryId captureTransaction( + @NotNull SentryTransaction transaction, + @Nullable TraceContext traceContext, + @Nullable Hint hint, + @Nullable ProfilingTraceData profilingTraceData) { + return Sentry.getCurrentScopes() + .captureTransaction(transaction, traceContext, hint, profilingTraceData); + } + + @Override + public @NotNull ITransaction startTransaction( + @NotNull TransactionContext transactionContext, + @NotNull TransactionOptions transactionOptions) { + return Sentry.startTransaction(transactionContext, transactionOptions); + } + + @Deprecated + @Override + @SuppressWarnings("deprecation") + public @Nullable SentryTraceHeader traceHeaders() { + return Sentry.traceHeaders(); + } + + @ApiStatus.Internal + @Override + public void setSpanContext( + final @NotNull Throwable throwable, + final @NotNull ISpan span, + final @NotNull String transactionName) { + Sentry.getCurrentScopes().setSpanContext(throwable, span, transactionName); + } + + @Override + public @Nullable ISpan getSpan() { + return Sentry.getCurrentScopes().getSpan(); + } + + @Override + @ApiStatus.Internal + public @Nullable ITransaction getTransaction() { + return Sentry.getCurrentScopes().getTransaction(); + } + + @Override + public @NotNull SentryOptions getOptions() { + return Sentry.getCurrentScopes().getOptions(); + } + + @Override + public @Nullable Boolean isCrashedLastRun() { + return Sentry.isCrashedLastRun(); + } + + @Override + public void reportFullyDisplayed() { + Sentry.reportFullyDisplayed(); + } + + @Override + public @Nullable TransactionContext continueTrace( + final @Nullable String sentryTrace, final @Nullable List baggageHeaders) { + return Sentry.continueTrace(sentryTrace, baggageHeaders); + } + + @Override + public @Nullable SentryTraceHeader getTraceparent() { + return Sentry.getTraceparent(); + } + + @Override + public @Nullable BaggageHeader getBaggage() { + return Sentry.getBaggage(); + } + + @Override + @ApiStatus.Experimental + public @NotNull SentryId captureCheckIn(final @NotNull CheckIn checkIn) { + return Sentry.captureCheckIn(checkIn); + } + + @ApiStatus.Internal + @Override + public @Nullable RateLimiter getRateLimiter() { + return Sentry.getCurrentScopes().getRateLimiter(); + } + + @ApiStatus.Experimental + @Override + public @NotNull MetricsApi metrics() { + return Sentry.getCurrentScopes().metrics(); + } +} diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 5196d0f31a..206ffd8d20 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -43,11 +43,11 @@ public final class Sentry { private Sentry() {} - /** Holds Hubs per thread or only mainHub if globalHubMode is enabled. */ - private static final @NotNull ThreadLocal currentHub = new ThreadLocal<>(); + /** Holds Hubs per thread or only mainScopes if globalHubMode is enabled. */ + private static final @NotNull ThreadLocal currentScopes = new ThreadLocal<>(); /** The Main Hub or NoOp if Sentry is disabled. */ - private static volatile @NotNull IHub mainHub = NoOpHub.getInstance(); + private static volatile @NotNull IScopes mainScopes = NoOpScopes.getInstance(); /** Default value for globalHubMode is false */ private static final boolean GLOBAL_HUB_DEFAULT_MODE = false; @@ -66,40 +66,58 @@ private Sentry() {} private static final long classCreationTimestamp = System.currentTimeMillis(); /** - * Returns the current (threads) hub, if none, clones the mainHub and returns it. + * Returns the current (threads) hub, if none, clones the mainScopes and returns it. * * @return the hub */ @ApiStatus.Internal // exposed for the coroutines integration in SentryContext + @SuppressWarnings("deprecation") + @Deprecated public static @NotNull IHub getCurrentHub() { + return new HubScopesWrapper(getCurrentScopes()); + } + + @ApiStatus.Internal // exposed for the coroutines integration in SentryContext + @SuppressWarnings("deprecation") + public static @NotNull IScopes getCurrentScopes() { if (globalHubMode) { - return mainHub; + return mainScopes; } - IHub hub = currentHub.get(); - if (hub == null || hub instanceof NoOpHub) { - hub = mainHub.clone(); - currentHub.set(hub); + IScopes hub = currentScopes.get(); + if (hub == null || hub.isNoOp()) { + // TODO fork instead + hub = mainScopes.clone(); + currentScopes.set(hub); } return hub; } /** - * Returns a new hub which is cloned from the mainHub. + * Returns a new hub which is cloned from the mainScopes. * * @return the hub */ @ApiStatus.Internal @ApiStatus.Experimental - public static @NotNull IHub cloneMainHub() { + @SuppressWarnings("deprecation") + public static @NotNull IScopes cloneMainHub() { if (globalHubMode) { - return mainHub; + return mainScopes; } - return mainHub.clone(); + // TODO fork instead + return mainScopes.clone(); } @ApiStatus.Internal // exposed for the coroutines integration in SentryContext + @Deprecated + @SuppressWarnings("deprecation") public static void setCurrentHub(final @NotNull IHub hub) { - currentHub.set(hub); + currentScopes.set(hub); + } + + @ApiStatus.Internal // exposed for the coroutines integration in SentryContext + public static void setCurrentScopes(final @NotNull IScopes scopes) { + currentScopes.set(scopes); } /** @@ -108,7 +126,7 @@ public static void setCurrentHub(final @NotNull IHub hub) { * @return true if its enabled or false otherwise. */ public static boolean isEnabled() { - return getCurrentHub().isEnabled(); + return getCurrentScopes().isEnabled(); } /** Initializes the SDK */ @@ -217,6 +235,7 @@ public static void init(final @NotNull SentryOptions options) { * @param options options the SentryOptions * @param globalHubMode the globalHubMode */ + @SuppressWarnings("deprecation") private static synchronized void init( final @NotNull SentryOptions options, final boolean globalHubMode) { if (isEnabled()) { @@ -234,10 +253,11 @@ private static synchronized void init( options.getLogger().log(SentryLevel.INFO, "GlobalHubMode: '%s'", String.valueOf(globalHubMode)); Sentry.globalHubMode = globalHubMode; - final IHub hub = getCurrentHub(); - mainHub = new Hub(options); + final IScopes hub = getCurrentScopes(); + // TODO new Scopes() + mainScopes = new Hub(options); - currentHub.set(mainHub); + currentScopes.set(mainScopes); hub.close(true); @@ -252,12 +272,12 @@ private static synchronized void init( // and hub was still NoOp. // Registering integrations here make sure that Hub is already created. for (final Integration integration : options.getIntegrations()) { - integration.register(HubAdapter.getInstance(), options); + integration.register(ScopesAdapter.getInstance(), options); } notifyOptionsObservers(options); - finalizePreviousSession(options, HubAdapter.getInstance()); + finalizePreviousSession(options, ScopesAdapter.getInstance()); handleAppStartProfilingConfig(options, options.getExecutorService()); } @@ -327,12 +347,12 @@ private static void handleAppStartProfilingConfig( @SuppressWarnings("FutureReturnValueIgnored") private static void finalizePreviousSession( - final @NotNull SentryOptions options, final @NotNull IHub hub) { + final @NotNull SentryOptions options, final @NotNull IScopes scopes) { // enqueue a task to finalize previous session. Since the executor // is single-threaded, this task will be enqueued sequentially after all integrations that have // to modify the previous session have done their work, even if they do that async. try { - options.getExecutorService().submit(new PreviousSessionFinalizer(options, hub)); + options.getExecutorService().submit(new PreviousSessionFinalizer(options, scopes)); } catch (Throwable e) { options.getLogger().log(SentryLevel.DEBUG, "Failed to finalize previous session.", e); } @@ -475,7 +495,7 @@ private static boolean initConfigurations(final @NotNull SentryOptions options) } if (options.isEnableBackpressureHandling()) { - options.setBackpressureMonitor(new BackpressureMonitor(options, HubAdapter.getInstance())); + options.setBackpressureMonitor(new BackpressureMonitor(options, ScopesAdapter.getInstance())); options.getBackpressureMonitor().start(); } @@ -484,11 +504,11 @@ private static boolean initConfigurations(final @NotNull SentryOptions options) /** Close the SDK */ public static synchronized void close() { - final IHub hub = getCurrentHub(); - mainHub = NoOpHub.getInstance(); + final IScopes scopes = getCurrentScopes(); + mainScopes = NoOpScopes.getInstance(); // remove thread local to avoid memory leak - currentHub.remove(); - hub.close(false); + currentScopes.remove(); + scopes.close(false); } /** @@ -498,7 +518,7 @@ public static synchronized void close() { * @return The Id (SentryId object) of the event */ public static @NotNull SentryId captureEvent(final @NotNull SentryEvent event) { - return getCurrentHub().captureEvent(event); + return getCurrentScopes().captureEvent(event); } /** @@ -510,7 +530,7 @@ public static synchronized void close() { */ public static @NotNull SentryId captureEvent( final @NotNull SentryEvent event, final @NotNull ScopeCallback callback) { - return getCurrentHub().captureEvent(event, callback); + return getCurrentScopes().captureEvent(event, callback); } /** @@ -522,7 +542,7 @@ public static synchronized void close() { */ public static @NotNull SentryId captureEvent( final @NotNull SentryEvent event, final @Nullable Hint hint) { - return getCurrentHub().captureEvent(event, hint); + return getCurrentScopes().captureEvent(event, hint); } /** @@ -537,7 +557,7 @@ public static synchronized void close() { final @NotNull SentryEvent event, final @Nullable Hint hint, final @NotNull ScopeCallback callback) { - return getCurrentHub().captureEvent(event, hint, callback); + return getCurrentScopes().captureEvent(event, hint, callback); } /** @@ -547,7 +567,7 @@ public static synchronized void close() { * @return The Id (SentryId object) of the event */ public static @NotNull SentryId captureMessage(final @NotNull String message) { - return getCurrentHub().captureMessage(message); + return getCurrentScopes().captureMessage(message); } /** @@ -559,7 +579,7 @@ public static synchronized void close() { */ public static @NotNull SentryId captureMessage( final @NotNull String message, final @NotNull ScopeCallback callback) { - return getCurrentHub().captureMessage(message, callback); + return getCurrentScopes().captureMessage(message, callback); } /** @@ -571,7 +591,7 @@ public static synchronized void close() { */ public static @NotNull SentryId captureMessage( final @NotNull String message, final @NotNull SentryLevel level) { - return getCurrentHub().captureMessage(message, level); + return getCurrentScopes().captureMessage(message, level); } /** @@ -586,7 +606,7 @@ public static synchronized void close() { final @NotNull String message, final @NotNull SentryLevel level, final @NotNull ScopeCallback callback) { - return getCurrentHub().captureMessage(message, level, callback); + return getCurrentScopes().captureMessage(message, level, callback); } /** @@ -596,7 +616,7 @@ public static synchronized void close() { * @return The Id (SentryId object) of the event */ public static @NotNull SentryId captureException(final @NotNull Throwable throwable) { - return getCurrentHub().captureException(throwable); + return getCurrentScopes().captureException(throwable); } /** @@ -608,7 +628,7 @@ public static synchronized void close() { */ public static @NotNull SentryId captureException( final @NotNull Throwable throwable, final @NotNull ScopeCallback callback) { - return getCurrentHub().captureException(throwable, callback); + return getCurrentScopes().captureException(throwable, callback); } /** @@ -620,7 +640,7 @@ public static synchronized void close() { */ public static @NotNull SentryId captureException( final @NotNull Throwable throwable, final @Nullable Hint hint) { - return getCurrentHub().captureException(throwable, hint); + return getCurrentScopes().captureException(throwable, hint); } /** @@ -635,7 +655,7 @@ public static synchronized void close() { final @NotNull Throwable throwable, final @Nullable Hint hint, final @NotNull ScopeCallback callback) { - return getCurrentHub().captureException(throwable, hint, callback); + return getCurrentScopes().captureException(throwable, hint, callback); } /** @@ -644,7 +664,7 @@ public static synchronized void close() { * @param userFeedback The user feedback to send to Sentry. */ public static void captureUserFeedback(final @NotNull UserFeedback userFeedback) { - getCurrentHub().captureUserFeedback(userFeedback); + getCurrentScopes().captureUserFeedback(userFeedback); } /** @@ -655,7 +675,7 @@ public static void captureUserFeedback(final @NotNull UserFeedback userFeedback) */ public static void addBreadcrumb( final @NotNull Breadcrumb breadcrumb, final @Nullable Hint hint) { - getCurrentHub().addBreadcrumb(breadcrumb, hint); + getCurrentScopes().addBreadcrumb(breadcrumb, hint); } /** @@ -664,7 +684,7 @@ public static void addBreadcrumb( * @param breadcrumb the breadcrumb */ public static void addBreadcrumb(final @NotNull Breadcrumb breadcrumb) { - getCurrentHub().addBreadcrumb(breadcrumb); + getCurrentScopes().addBreadcrumb(breadcrumb); } /** @@ -673,7 +693,7 @@ public static void addBreadcrumb(final @NotNull Breadcrumb breadcrumb) { * @param message rendered as text and the whitespace is preserved. */ public static void addBreadcrumb(final @NotNull String message) { - getCurrentHub().addBreadcrumb(message); + getCurrentScopes().addBreadcrumb(message); } /** @@ -684,7 +704,7 @@ public static void addBreadcrumb(final @NotNull String message) { * from. */ public static void addBreadcrumb(final @NotNull String message, final @NotNull String category) { - getCurrentHub().addBreadcrumb(message, category); + getCurrentScopes().addBreadcrumb(message, category); } /** @@ -693,7 +713,7 @@ public static void addBreadcrumb(final @NotNull String message, final @NotNull S * @param level the Sentry level */ public static void setLevel(final @Nullable SentryLevel level) { - getCurrentHub().setLevel(level); + getCurrentScopes().setLevel(level); } /** @@ -702,7 +722,7 @@ public static void setLevel(final @Nullable SentryLevel level) { * @param transaction the transaction */ public static void setTransaction(final @Nullable String transaction) { - getCurrentHub().setTransaction(transaction); + getCurrentScopes().setTransaction(transaction); } /** @@ -711,7 +731,7 @@ public static void setTransaction(final @Nullable String transaction) { * @param user the user */ public static void setUser(final @Nullable User user) { - getCurrentHub().setUser(user); + getCurrentScopes().setUser(user); } /** @@ -720,12 +740,12 @@ public static void setUser(final @Nullable User user) { * @param fingerprint the fingerprints */ public static void setFingerprint(final @NotNull List fingerprint) { - getCurrentHub().setFingerprint(fingerprint); + getCurrentScopes().setFingerprint(fingerprint); } /** Deletes current breadcrumbs from the current scope. */ public static void clearBreadcrumbs() { - getCurrentHub().clearBreadcrumbs(); + getCurrentScopes().clearBreadcrumbs(); } /** @@ -735,7 +755,7 @@ public static void clearBreadcrumbs() { * @param value the value */ public static void setTag(final @NotNull String key, final @NotNull String value) { - getCurrentHub().setTag(key, value); + getCurrentScopes().setTag(key, value); } /** @@ -744,7 +764,7 @@ public static void setTag(final @NotNull String key, final @NotNull String value * @param key the key */ public static void removeTag(final @NotNull String key) { - getCurrentHub().removeTag(key); + getCurrentScopes().removeTag(key); } /** @@ -755,7 +775,7 @@ public static void removeTag(final @NotNull String key) { * @param value the value */ public static void setExtra(final @NotNull String key, final @NotNull String value) { - getCurrentHub().setExtra(key, value); + getCurrentScopes().setExtra(key, value); } /** @@ -764,7 +784,7 @@ public static void setExtra(final @NotNull String key, final @NotNull String val * @param key the key */ public static void removeExtra(final @NotNull String key) { - getCurrentHub().removeExtra(key); + getCurrentScopes().removeExtra(key); } /** @@ -773,14 +793,14 @@ public static void removeExtra(final @NotNull String key) { * @return last SentryId */ public static @NotNull SentryId getLastEventId() { - return getCurrentHub().getLastEventId(); + return getCurrentScopes().getLastEventId(); } /** Pushes a new scope while inheriting the current scope's data. */ public static void pushScope() { // pushScope is no-op in global hub mode if (!globalHubMode) { - getCurrentHub().pushScope(); + getCurrentScopes().pushScope(); } } @@ -788,7 +808,7 @@ public static void pushScope() { public static void popScope() { // popScope is no-op in global hub mode if (!globalHubMode) { - getCurrentHub().popScope(); + getCurrentScopes().popScope(); } } @@ -798,7 +818,7 @@ public static void popScope() { * @param callback the callback */ public static void withScope(final @NotNull ScopeCallback callback) { - getCurrentHub().withScope(callback); + getCurrentScopes().withScope(callback); } /** @@ -807,7 +827,7 @@ public static void withScope(final @NotNull ScopeCallback callback) { * @param callback The configure scope callback. */ public static void configureScope(final @NotNull ScopeCallback callback) { - getCurrentHub().configureScope(callback); + getCurrentScopes().configureScope(callback); } /** @@ -816,11 +836,11 @@ public static void configureScope(final @NotNull ScopeCallback callback) { * @param client the client. */ public static void bindClient(final @NotNull ISentryClient client) { - getCurrentHub().bindClient(client); + getCurrentScopes().bindClient(client); } public static boolean isHealthy() { - return getCurrentHub().isHealthy(); + return getCurrentScopes().isHealthy(); } /** @@ -829,17 +849,17 @@ public static boolean isHealthy() { * @param timeoutMillis time in milliseconds */ public static void flush(final long timeoutMillis) { - getCurrentHub().flush(timeoutMillis); + getCurrentScopes().flush(timeoutMillis); } /** Starts a new session. If there's a running session, it ends it before starting the new one. */ public static void startSession() { - getCurrentHub().startSession(); + getCurrentScopes().startSession(); } /** Ends the current session */ public static void endSession() { - getCurrentHub().endSession(); + getCurrentScopes().endSession(); } /** @@ -851,7 +871,7 @@ public static void endSession() { */ public static @NotNull ITransaction startTransaction( final @NotNull String name, final @NotNull String operation) { - return getCurrentHub().startTransaction(name, operation); + return getCurrentScopes().startTransaction(name, operation); } /** @@ -866,7 +886,7 @@ public static void endSession() { final @NotNull String name, final @NotNull String operation, final @NotNull TransactionOptions transactionOptions) { - return getCurrentHub().startTransaction(name, operation, transactionOptions); + return getCurrentScopes().startTransaction(name, operation, transactionOptions); } /** @@ -884,7 +904,7 @@ public static void endSession() { final @Nullable String description, final @NotNull TransactionOptions transactionOptions) { final ITransaction transaction = - getCurrentHub().startTransaction(name, operation, transactionOptions); + getCurrentScopes().startTransaction(name, operation, transactionOptions); transaction.setDescription(description); return transaction; } @@ -897,7 +917,7 @@ public static void endSession() { */ public static @NotNull ITransaction startTransaction( final @NotNull TransactionContext transactionContexts) { - return getCurrentHub().startTransaction(transactionContexts); + return getCurrentScopes().startTransaction(transactionContexts); } /** @@ -910,7 +930,7 @@ public static void endSession() { public static @NotNull ITransaction startTransaction( final @NotNull TransactionContext transactionContext, final @NotNull TransactionOptions transactionOptions) { - return getCurrentHub().startTransaction(transactionContext, transactionOptions); + return getCurrentScopes().startTransaction(transactionContext, transactionOptions); } /** @@ -923,7 +943,7 @@ public static void endSession() { @Deprecated @SuppressWarnings("InlineMeSuggester") public static @Nullable SentryTraceHeader traceHeaders() { - return getCurrentHub().traceHeaders(); + return getCurrentScopes().traceHeaders(); } /** @@ -935,9 +955,9 @@ public static void endSession() { */ public static @Nullable ISpan getSpan() { if (globalHubMode && Platform.isAndroid()) { - return getCurrentHub().getTransaction(); + return getCurrentScopes().getTransaction(); } else { - return getCurrentHub().getSpan(); + return getCurrentScopes().getSpan(); } } @@ -952,7 +972,7 @@ public static void endSession() { * @return true if App has crashed, false otherwise, and null if not evaluated yet */ public static @Nullable Boolean isCrashedLastRun() { - return getCurrentHub().isCrashedLastRun(); + return getCurrentScopes().isCrashedLastRun(); } /** @@ -964,7 +984,7 @@ public static void endSession() { * finished, this call will be ignored. */ public static void reportFullyDisplayed() { - getCurrentHub().reportFullyDisplayed(); + getCurrentScopes().reportFullyDisplayed(); } /** @@ -980,7 +1000,7 @@ public static void reportFullDisplayed() { @NotNull @ApiStatus.Experimental public static MetricsApi metrics() { - return getCurrentHub().metrics(); + return getCurrentScopes().metrics(); } /** @@ -1009,7 +1029,7 @@ public interface OptionsConfiguration { // return TransactionContext (if performance enabled) or null (if performance disabled) public static @Nullable TransactionContext continueTrace( final @Nullable String sentryTrace, final @Nullable List baggageHeaders) { - return getCurrentHub().continueTrace(sentryTrace, baggageHeaders); + return getCurrentScopes().continueTrace(sentryTrace, baggageHeaders); } /** @@ -1019,7 +1039,7 @@ public interface OptionsConfiguration { * @return sentry trace header or null */ public static @Nullable SentryTraceHeader getTraceparent() { - return getCurrentHub().getTraceparent(); + return getCurrentScopes().getTraceparent(); } /** @@ -1029,11 +1049,11 @@ public interface OptionsConfiguration { * @return baggage header or null */ public static @Nullable BaggageHeader getBaggage() { - return getCurrentHub().getBaggage(); + return getCurrentScopes().getBaggage(); } @ApiStatus.Experimental public static @NotNull SentryId captureCheckIn(final @NotNull CheckIn checkIn) { - return getCurrentHub().captureCheckIn(checkIn); + return getCurrentScopes().captureCheckIn(checkIn); } } From ca5593ebb79159c3c9db33439bc5974f41a99b9a Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 16 Apr 2024 16:37:18 +0200 Subject: [PATCH 02/91] Hubs/Scopes Merge 2 - Replace `IHub` with `IScopes` in core (#3298) * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core --- sentry/src/main/java/io/sentry/Baggage.java | 4 +- .../java/io/sentry/DirectoryProcessor.java | 8 +- .../main/java/io/sentry/EnvelopeSender.java | 10 +- .../src/main/java/io/sentry/Integration.java | 4 +- .../main/java/io/sentry/MonitorConfig.java | 2 +- .../src/main/java/io/sentry/OutboxSender.java | 14 +- .../io/sentry/PreviousSessionFinalizer.java | 8 +- ...achedEnvelopeFireAndForgetIntegration.java | 20 +- .../SendFireAndForgetEnvelopeSender.java | 6 +- .../sentry/SendFireAndForgetOutboxSender.java | 6 +- .../src/main/java/io/sentry/SentryTracer.java | 85 ++-- .../main/java/io/sentry/SentryWrapper.java | 21 +- .../io/sentry/ShutdownHookIntegration.java | 6 +- sentry/src/main/java/io/sentry/Span.java | 30 +- .../java/io/sentry/SpotlightIntegration.java | 2 +- .../UncaughtExceptionHandlerIntegration.java | 12 +- .../backpressure/BackpressureMonitor.java | 11 +- .../file/FileIOSpanManager.java | 6 +- .../file/SentryFileInputStream.java | 42 +- .../file/SentryFileOutputStream.java | 44 +- .../file/SentryFileReader.java | 7 +- .../file/SentryFileWriter.java | 6 +- .../java/io/sentry/util/CheckInUtils.java | 15 +- .../java/io/sentry/util/TracingUtils.java | 18 +- ...aultTransactionPerformanceCollectorTest.kt | 8 +- .../java/io/sentry/DirectoryProcessorTest.kt | 16 +- .../test/java/io/sentry/EnvelopeSenderTest.kt | 20 +- .../src/test/java/io/sentry/HubAdapterTest.kt | 92 ++-- sentry/src/test/java/io/sentry/HubTest.kt | 430 +++++++++--------- .../test/java/io/sentry/JsonSerializerTest.kt | 8 +- .../java/io/sentry/MainEventProcessorTest.kt | 6 +- .../test/java/io/sentry/OutboxSenderTest.kt | 28 +- .../io/sentry/PreviousSessionFinalizerTest.kt | 22 +- sentry/src/test/java/io/sentry/ScopeTest.kt | 24 +- .../test/java/io/sentry/ScopesAdapterTest.kt | 265 +++++++++++ ...hedEnvelopeFireAndForgetIntegrationTest.kt | 34 +- .../test/java/io/sentry/SentryClientTest.kt | 28 +- sentry/src/test/java/io/sentry/SentryTest.kt | 112 ++--- .../test/java/io/sentry/SentryWrapperTest.kt | 32 +- .../io/sentry/ShutdownHookIntegrationTest.kt | 22 +- sentry/src/test/java/io/sentry/SpanTest.kt | 24 +- .../sentry/TraceContextSerializationTest.kt | 6 +- ...UncaughtExceptionHandlerIntegrationTest.kt | 62 +-- .../backpressure/BackpressureMonitorTest.kt | 12 +- .../sentry/clientreport/ClientReportTest.kt | 8 +- .../file/FileIOSpanManagerTest.kt | 14 +- .../file/SentryFileInputStreamTest.kt | 22 +- .../file/SentryFileOutputStreamTest.kt | 12 +- .../file/SentryFileReaderTest.kt | 12 +- .../file/SentryFileWriterTest.kt | 12 +- .../internal/SpotlightIntegrationTest.kt | 10 +- .../java/io/sentry/protocol/SentrySpanTest.kt | 4 +- .../io/sentry/transport/RateLimiterTest.kt | 32 +- .../java/io/sentry/util/CheckInUtilsTest.kt | 91 ++-- .../java/io/sentry/util/TracingUtilsTest.kt | 30 +- 55 files changed, 1092 insertions(+), 793 deletions(-) create mode 100644 sentry/src/test/java/io/sentry/ScopesAdapterTest.kt diff --git a/sentry/src/main/java/io/sentry/Baggage.java b/sentry/src/main/java/io/sentry/Baggage.java index 8e19fceaf8..4a637bacdf 100644 --- a/sentry/src/main/java/io/sentry/Baggage.java +++ b/sentry/src/main/java/io/sentry/Baggage.java @@ -39,13 +39,13 @@ public final class Baggage { @NotNull public static Baggage fromHeader(final @Nullable String headerValue) { return Baggage.fromHeader( - headerValue, false, HubAdapter.getInstance().getOptions().getLogger()); + headerValue, false, ScopesAdapter.getInstance().getOptions().getLogger()); } @NotNull public static Baggage fromHeader(final @Nullable List headerValues) { return Baggage.fromHeader( - headerValues, false, HubAdapter.getInstance().getOptions().getLogger()); + headerValues, false, ScopesAdapter.getInstance().getOptions().getLogger()); } @ApiStatus.Internal diff --git a/sentry/src/main/java/io/sentry/DirectoryProcessor.java b/sentry/src/main/java/io/sentry/DirectoryProcessor.java index 5d60feba60..a6bb258f30 100644 --- a/sentry/src/main/java/io/sentry/DirectoryProcessor.java +++ b/sentry/src/main/java/io/sentry/DirectoryProcessor.java @@ -19,17 +19,17 @@ abstract class DirectoryProcessor { private static final long ENVELOPE_PROCESSING_DELAY = 100L; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @NotNull ILogger logger; private final long flushTimeoutMillis; private final Queue processedEnvelopes; DirectoryProcessor( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull ILogger logger, final long flushTimeoutMillis, final int maxQueueSize) { - this.hub = hub; + this.scopes = scopes; this.logger = logger; this.flushTimeoutMillis = flushTimeoutMillis; this.processedEnvelopes = @@ -86,7 +86,7 @@ public void processDirectory(final @NotNull File directory) { } // in case there's rate limiting active, skip processing - final @Nullable RateLimiter rateLimiter = hub.getRateLimiter(); + final @Nullable RateLimiter rateLimiter = scopes.getRateLimiter(); if (rateLimiter != null && rateLimiter.isActiveForCategory(DataCategory.All)) { logger.log(SentryLevel.INFO, "DirectoryProcessor, rate limiting active."); return; diff --git a/sentry/src/main/java/io/sentry/EnvelopeSender.java b/sentry/src/main/java/io/sentry/EnvelopeSender.java index 598caad280..3a157f59d3 100644 --- a/sentry/src/main/java/io/sentry/EnvelopeSender.java +++ b/sentry/src/main/java/io/sentry/EnvelopeSender.java @@ -17,18 +17,18 @@ @ApiStatus.Internal public final class EnvelopeSender extends DirectoryProcessor implements IEnvelopeSender { - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @NotNull ISerializer serializer; private final @NotNull ILogger logger; public EnvelopeSender( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull ISerializer serializer, final @NotNull ILogger logger, final long flushTimeoutMillis, final int maxQueueSize) { - super(hub, logger, flushTimeoutMillis, maxQueueSize); - this.hub = Objects.requireNonNull(hub, "Hub is required."); + super(scopes, logger, flushTimeoutMillis, maxQueueSize); + this.scopes = Objects.requireNonNull(scopes, "Hub is required."); this.serializer = Objects.requireNonNull(serializer, "Serializer is required."); this.logger = Objects.requireNonNull(logger, "Logger is required."); } @@ -60,7 +60,7 @@ protected void processFile(final @NotNull File file, final @NotNull Hint hint) { logger.log( SentryLevel.ERROR, "Failed to deserialize cached envelope %s", file.getAbsolutePath()); } else { - hub.captureEnvelope(envelope, hint); + scopes.captureEnvelope(envelope, hint); } HintUtils.runIfHasTypeLogIfNot( diff --git a/sentry/src/main/java/io/sentry/Integration.java b/sentry/src/main/java/io/sentry/Integration.java index 54b17e4d51..1b1a520473 100644 --- a/sentry/src/main/java/io/sentry/Integration.java +++ b/sentry/src/main/java/io/sentry/Integration.java @@ -10,8 +10,8 @@ public interface Integration { /** * Registers an integration * - * @param hub the Hub + * @param scopes the Scopes * @param options the options */ - void register(@NotNull IHub hub, @NotNull SentryOptions options); + void register(@NotNull IScopes scopes, @NotNull SentryOptions options); } diff --git a/sentry/src/main/java/io/sentry/MonitorConfig.java b/sentry/src/main/java/io/sentry/MonitorConfig.java index d954a50466..763e3b65a4 100644 --- a/sentry/src/main/java/io/sentry/MonitorConfig.java +++ b/sentry/src/main/java/io/sentry/MonitorConfig.java @@ -21,7 +21,7 @@ public final class MonitorConfig implements JsonUnknown, JsonSerializable { public MonitorConfig(final @NotNull MonitorSchedule schedule) { this.schedule = schedule; - final SentryOptions.Cron defaultCron = HubAdapter.getInstance().getOptions().getCron(); + final SentryOptions.Cron defaultCron = ScopesAdapter.getInstance().getOptions().getCron(); if (defaultCron != null) { this.checkinMargin = defaultCron.getDefaultCheckinMargin(); this.maxRuntime = defaultCron.getDefaultMaxRuntime(); diff --git a/sentry/src/main/java/io/sentry/OutboxSender.java b/sentry/src/main/java/io/sentry/OutboxSender.java index 709cbb8580..f80bf030c8 100644 --- a/sentry/src/main/java/io/sentry/OutboxSender.java +++ b/sentry/src/main/java/io/sentry/OutboxSender.java @@ -36,20 +36,20 @@ public final class OutboxSender extends DirectoryProcessor implements IEnvelopeS @SuppressWarnings("CharsetObjectCanBeUsed") private static final Charset UTF_8 = Charset.forName("UTF-8"); - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @NotNull IEnvelopeReader envelopeReader; private final @NotNull ISerializer serializer; private final @NotNull ILogger logger; public OutboxSender( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull IEnvelopeReader envelopeReader, final @NotNull ISerializer serializer, final @NotNull ILogger logger, final long flushTimeoutMillis, final int maxQueueSize) { - super(hub, logger, flushTimeoutMillis, maxQueueSize); - this.hub = Objects.requireNonNull(hub, "Hub is required."); + super(scopes, logger, flushTimeoutMillis, maxQueueSize); + this.scopes = Objects.requireNonNull(scopes, "Hub is required."); this.envelopeReader = Objects.requireNonNull(envelopeReader, "Envelope reader is required."); this.serializer = Objects.requireNonNull(serializer, "Serializer is required."); this.logger = Objects.requireNonNull(logger, "Logger is required."); @@ -144,7 +144,7 @@ private void processEnvelope(final @NotNull SentryEnvelope envelope, final @NotN logUnexpectedEventId(envelope, event.getEventId(), currentItem); continue; } - hub.captureEvent(event, hint); + scopes.captureEvent(event, hint); logItemCaptured(currentItem); if (!waitFlush(hint)) { @@ -181,7 +181,7 @@ private void processEnvelope(final @NotNull SentryEnvelope envelope, final @NotN .getTrace() .setSamplingDecision(extractSamplingDecision(traceContext)); } - hub.captureTransaction(transaction, traceContext, hint); + scopes.captureTransaction(transaction, traceContext, hint); logItemCaptured(currentItem); if (!waitFlush(hint)) { @@ -197,7 +197,7 @@ private void processEnvelope(final @NotNull SentryEnvelope envelope, final @NotN final SentryEnvelope newEnvelope = new SentryEnvelope( envelope.getHeader().getEventId(), envelope.getHeader().getSdkVersion(), item); - hub.captureEnvelope(newEnvelope, hint); + scopes.captureEnvelope(newEnvelope, hint); logger.log( SentryLevel.DEBUG, "%s item %d is being captured.", diff --git a/sentry/src/main/java/io/sentry/PreviousSessionFinalizer.java b/sentry/src/main/java/io/sentry/PreviousSessionFinalizer.java index 458c6532ba..bda2a14477 100644 --- a/sentry/src/main/java/io/sentry/PreviousSessionFinalizer.java +++ b/sentry/src/main/java/io/sentry/PreviousSessionFinalizer.java @@ -33,11 +33,11 @@ final class PreviousSessionFinalizer implements Runnable { private final @NotNull SentryOptions options; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; - PreviousSessionFinalizer(final @NotNull SentryOptions options, final @NotNull IHub hub) { + PreviousSessionFinalizer(final @NotNull SentryOptions options, final @NotNull IScopes scopes) { this.options = options; - this.hub = hub; + this.scopes = scopes; } @Override @@ -116,7 +116,7 @@ public void run() { // SdkVersion will be outdated. final SentryEnvelope fromSession = SentryEnvelope.from(serializer, session, options.getSdkVersion()); - hub.captureEnvelope(fromSession); + scopes.captureEnvelope(fromSession); } } catch (Throwable e) { options.getLogger().log(SentryLevel.ERROR, "Error processing previous session.", e); diff --git a/sentry/src/main/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegration.java b/sentry/src/main/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegration.java index bc813fdd1e..2160d1607e 100644 --- a/sentry/src/main/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegration.java +++ b/sentry/src/main/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegration.java @@ -18,7 +18,7 @@ public final class SendCachedEnvelopeFireAndForgetIntegration private final @NotNull SendFireAndForgetFactory factory; private @Nullable IConnectionStatusProvider connectionStatusProvider; - private @Nullable IHub hub; + private @Nullable IScopes scopes; private @Nullable SentryOptions options; private @Nullable SendFireAndForget sender; private final AtomicBoolean isInitialized = new AtomicBoolean(false); @@ -35,7 +35,7 @@ public interface SendFireAndForgetDirPath { public interface SendFireAndForgetFactory { @Nullable - SendFireAndForget create(@NotNull IHub hub, @NotNull SentryOptions options); + SendFireAndForget create(@NotNull IScopes scopes, @NotNull SentryOptions options); default boolean hasValidPath(final @Nullable String dirPath, final @NotNull ILogger logger) { if (dirPath == null || dirPath.isEmpty()) { @@ -66,8 +66,8 @@ public SendCachedEnvelopeFireAndForgetIntegration( } @Override - public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { - this.hub = Objects.requireNonNull(hub, "Hub is required"); + public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + this.scopes = Objects.requireNonNull(scopes, "Hub is required"); this.options = Objects.requireNonNull(options, "SentryOptions is required"); final String cachedDir = options.getCacheDirPath(); @@ -81,7 +81,7 @@ public void register(final @NotNull IHub hub, final @NotNull SentryOptions optio .log(SentryLevel.DEBUG, "SendCachedEventFireAndForgetIntegration installed."); addIntegrationToSdkVersion(getClass()); - sendCachedEnvelopes(hub, options); + sendCachedEnvelopes(scopes, options); } @Override @@ -95,14 +95,14 @@ public void close() throws IOException { @Override public void onConnectionStatusChanged( final @NotNull IConnectionStatusProvider.ConnectionStatus status) { - if (hub != null && options != null) { - sendCachedEnvelopes(hub, options); + if (scopes != null && options != null) { + sendCachedEnvelopes(scopes, options); } } @SuppressWarnings({"FutureReturnValueIgnored", "NullAway"}) private synchronized void sendCachedEnvelopes( - final @NotNull IHub hub, final @NotNull SentryOptions options) { + final @NotNull IScopes scopes, final @NotNull SentryOptions options) { try { options .getExecutorService() @@ -122,7 +122,7 @@ private synchronized void sendCachedEnvelopes( connectionStatusProvider = options.getConnectionStatusProvider(); connectionStatusProvider.addConnectionStatusObserver(this); - sender = factory.create(hub, options); + sender = factory.create(scopes, options); } // skip run only if we're certainly disconnected @@ -138,7 +138,7 @@ private synchronized void sendCachedEnvelopes( } // in case there's rate limiting active, skip processing - final @Nullable RateLimiter rateLimiter = hub.getRateLimiter(); + final @Nullable RateLimiter rateLimiter = scopes.getRateLimiter(); if (rateLimiter != null && rateLimiter.isActiveForCategory(DataCategory.All)) { options .getLogger() diff --git a/sentry/src/main/java/io/sentry/SendFireAndForgetEnvelopeSender.java b/sentry/src/main/java/io/sentry/SendFireAndForgetEnvelopeSender.java index e44d18a8d6..155946b95a 100644 --- a/sentry/src/main/java/io/sentry/SendFireAndForgetEnvelopeSender.java +++ b/sentry/src/main/java/io/sentry/SendFireAndForgetEnvelopeSender.java @@ -21,8 +21,8 @@ public SendFireAndForgetEnvelopeSender( @Override public @Nullable SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget create( - final @NotNull IHub hub, final @NotNull SentryOptions options) { - Objects.requireNonNull(hub, "Hub is required"); + final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + Objects.requireNonNull(scopes, "Hub is required"); Objects.requireNonNull(options, "SentryOptions is required"); final String dirPath = sendFireAndForgetDirPath.getDirPath(); @@ -33,7 +33,7 @@ public SendFireAndForgetEnvelopeSender( final EnvelopeSender envelopeSender = new EnvelopeSender( - hub, + scopes, options.getSerializer(), options.getLogger(), options.getFlushTimeoutMillis(), diff --git a/sentry/src/main/java/io/sentry/SendFireAndForgetOutboxSender.java b/sentry/src/main/java/io/sentry/SendFireAndForgetOutboxSender.java index fda41610fd..e7913b5b2f 100644 --- a/sentry/src/main/java/io/sentry/SendFireAndForgetOutboxSender.java +++ b/sentry/src/main/java/io/sentry/SendFireAndForgetOutboxSender.java @@ -21,8 +21,8 @@ public SendFireAndForgetOutboxSender( @Override public @Nullable SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget create( - final @NotNull IHub hub, final @NotNull SentryOptions options) { - Objects.requireNonNull(hub, "Hub is required"); + final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + Objects.requireNonNull(scopes, "Hub is required"); Objects.requireNonNull(options, "SentryOptions is required"); final String dirPath = sendFireAndForgetDirPath.getDirPath(); @@ -33,7 +33,7 @@ public SendFireAndForgetOutboxSender( final OutboxSender outboxSender = new OutboxSender( - hub, + scopes, options.getEnvelopeReader(), options.getSerializer(), options.getLogger(), diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index bc3b5eb531..7d82bbbc4a 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -26,7 +26,7 @@ public final class SentryTracer implements ITransaction { private final @NotNull SentryId eventId = new SentryId(); private final @NotNull Span root; private final @NotNull List children = new CopyOnWriteArrayList<>(); - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private @NotNull String name; /** @@ -52,31 +52,31 @@ public final class SentryTracer implements ITransaction { private final @Nullable TransactionPerformanceCollector transactionPerformanceCollector; private final @NotNull TransactionOptions transactionOptions; - public SentryTracer(final @NotNull TransactionContext context, final @NotNull IHub hub) { - this(context, hub, new TransactionOptions(), null); + public SentryTracer(final @NotNull TransactionContext context, final @NotNull IScopes scopes) { + this(context, scopes, new TransactionOptions(), null); } public SentryTracer( final @NotNull TransactionContext context, - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull TransactionOptions transactionOptions) { - this(context, hub, transactionOptions, null); + this(context, scopes, transactionOptions, null); } SentryTracer( final @NotNull TransactionContext context, - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull TransactionOptions transactionOptions, final @Nullable TransactionPerformanceCollector transactionPerformanceCollector) { Objects.requireNonNull(context, "context is required"); - Objects.requireNonNull(hub, "hub is required"); + Objects.requireNonNull(scopes, "scopes are required"); this.root = - new Span(context, this, hub, transactionOptions.getStartTimestamp(), transactionOptions); + new Span(context, this, scopes, transactionOptions.getStartTimestamp(), transactionOptions); this.name = context.getName(); this.instrumenter = context.getInstrumenter(); - this.hub = hub; + this.scopes = scopes; this.transactionPerformanceCollector = transactionPerformanceCollector; this.transactionNameSource = context.getTransactionNameSource(); this.transactionOptions = transactionOptions; @@ -84,7 +84,7 @@ public SentryTracer( if (context.getBaggage() != null) { this.baggage = context.getBaggage(); } else { - this.baggage = new Baggage(hub.getOptions().getLogger()); + this.baggage = new Baggage(scopes.getOptions().getLogger()); } // We are currently sending the performance data only in profiles, so there's no point in @@ -122,7 +122,8 @@ public void run() { try { timer.schedule(idleTimeoutTask, idleTimeout); } catch (Throwable e) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log(SentryLevel.WARNING, "Failed to schedule finish timer", e); // if we failed to schedule the finish timer for some reason, we finish it here right @@ -156,7 +157,7 @@ private void onDeadlineTimeoutReached() { return; } - final @NotNull SentryDate finishTimestamp = hub.getOptions().getDateProvider().now(); + final @NotNull SentryDate finishTimestamp = scopes.getOptions().getDateProvider().now(); // abort all child-spans first, this ensures the transaction can be finished, // even if waitForChildren is true @@ -186,7 +187,7 @@ public void finish( // if it's not set -> fallback to the current time if (finishTimestamp == null) { - finishTimestamp = hub.getOptions().getDateProvider().now(); + finishTimestamp = scopes.getOptions().getDateProvider().now(); } // auto-finish any idle spans first @@ -207,9 +208,10 @@ public void finish( ProfilingTraceData profilingTraceData = null; if (Boolean.TRUE.equals(isSampled()) && Boolean.TRUE.equals(isProfileSampled())) { profilingTraceData = - hub.getOptions() + scopes + .getOptions() .getTransactionProfiler() - .onTransactionFinish(this, performanceCollectionData, hub.getOptions()); + .onTransactionFinish(this, performanceCollectionData, scopes.getOptions()); } if (performanceCollectionData != null) { performanceCollectionData.clear(); @@ -222,7 +224,7 @@ public void finish( root.finish(finishStatus.spanStatus, finishTimestamp); - hub.configureScope( + scopes.configureScope( scope -> { scope.withTransaction( transaction -> { @@ -251,7 +253,8 @@ public void finish( if (dropIfNoChildren && children.isEmpty() && transactionOptions.getIdleTimeout() != null) { // if it's an idle transaction which has no children, we drop it to save user's quota - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -261,7 +264,7 @@ public void finish( } transaction.getMeasurements().putAll(root.getMeasurements()); - hub.captureTransaction(transaction, traceContext(), hint, profilingTraceData); + scopes.captureTransaction(transaction, traceContext(), hint, profilingTraceData); } } @@ -292,7 +295,8 @@ public void run() { try { timer.schedule(deadlineTimeoutTask, deadlineTimeOut); } catch (Throwable e) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log(SentryLevel.WARNING, "Failed to schedule finish timer", e); // if we failed to schedule the finish timer for some reason, we finish it here right @@ -418,7 +422,7 @@ private ISpan createChild( return NoOpSpan.getInstance(); } - if (children.size() < hub.getOptions().getMaxSpans()) { + if (children.size() < scopes.getOptions().getMaxSpans()) { Objects.requireNonNull(parentSpanId, "parentSpanId is required"); Objects.requireNonNull(operation, "operation is required"); cancelIdleTimer(); @@ -428,7 +432,7 @@ private ISpan createChild( parentSpanId, this, operation, - this.hub, + this.scopes, timestamp, spanOptions, finishingSpan -> { @@ -451,7 +455,7 @@ private ISpan createChild( span.setData(SpanDataConvention.THREAD_ID, String.valueOf(Thread.currentThread().getId())); span.setData( SpanDataConvention.THREAD_NAME, - hub.getOptions().getMainThreadChecker().isMainThread() + scopes.getOptions().getMainThreadChecker().isMainThread() ? "main" : Thread.currentThread().getName()); this.children.add(span); @@ -460,7 +464,8 @@ private ISpan createChild( } return span; } else { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -529,10 +534,11 @@ private ISpan createChild( return NoOpSpan.getInstance(); } - if (children.size() < hub.getOptions().getMaxSpans()) { + if (children.size() < scopes.getOptions().getMaxSpans()) { return root.startChild(operation, description, timestamp, instrumenter, spanOptions); } else { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -567,7 +573,7 @@ public void finish(@Nullable SpanStatus status, @Nullable SentryDate finishDate) @Override public @Nullable TraceContext traceContext() { - if (hub.getOptions().isTraceSampling()) { + if (scopes.getOptions().isTraceSampling()) { updateBaggageValues(); return baggage.toTraceContext(); } else { @@ -579,12 +585,12 @@ private void updateBaggageValues() { synchronized (this) { if (baggage.isMutable()) { final AtomicReference userAtomicReference = new AtomicReference<>(); - hub.configureScope( + scopes.configureScope( scope -> { userAtomicReference.set(scope.getUser()); }); baggage.setValuesFromTransaction( - this, userAtomicReference.get(), hub.getOptions(), this.getSamplingDecision()); + this, userAtomicReference.get(), scopes.getOptions(), this.getSamplingDecision()); baggage.freeze(); } } @@ -592,7 +598,7 @@ private void updateBaggageValues() { @Override public @Nullable BaggageHeader toBaggageHeader(@Nullable List thirdPartyBaggageHeaders) { - if (hub.getOptions().isTraceSampling()) { + if (scopes.getOptions().isTraceSampling()) { updateBaggageValues(); return BaggageHeader.fromBaggageAndOutgoingHeader(baggage, thirdPartyBaggageHeaders); @@ -616,7 +622,8 @@ private boolean hasAllChildrenFinished() { @Override public void setOperation(final @NotNull String operation) { if (root.isFinished()) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -636,7 +643,8 @@ public void setOperation(final @NotNull String operation) { @Override public void setDescription(final @Nullable String description) { if (root.isFinished()) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -656,7 +664,8 @@ public void setDescription(final @Nullable String description) { @Override public void setStatus(final @Nullable SpanStatus status) { if (root.isFinished()) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -676,7 +685,8 @@ public void setStatus(final @Nullable SpanStatus status) { @Override public void setThrowable(final @Nullable Throwable throwable) { if (root.isFinished()) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log(SentryLevel.DEBUG, "The transaction is already finished. Throwable cannot be set"); return; @@ -698,7 +708,8 @@ public void setThrowable(final @Nullable Throwable throwable) { @Override public void setTag(final @NotNull String key, final @NotNull String value) { if (root.isFinished()) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log(SentryLevel.DEBUG, "The transaction is already finished. Tag %s cannot be set", key); return; @@ -720,7 +731,8 @@ public boolean isFinished() { @Override public void setData(@NotNull String key, @NotNull Object value) { if (root.isFinished()) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, "The transaction is already finished. Data %s cannot be set", key); @@ -795,7 +807,8 @@ public void setName(@NotNull String name) { @Override public void setName(@NotNull String name, @NotNull TransactionNameSource transactionNameSource) { if (root.isFinished()) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, diff --git a/sentry/src/main/java/io/sentry/SentryWrapper.java b/sentry/src/main/java/io/sentry/SentryWrapper.java index 1a39adee99..165ace7c83 100644 --- a/sentry/src/main/java/io/sentry/SentryWrapper.java +++ b/sentry/src/main/java/io/sentry/SentryWrapper.java @@ -27,16 +27,18 @@ public final class SentryWrapper { * @return the wrapped {@link Callable} * @param - the result type of the {@link Callable} */ + @SuppressWarnings("deprecation") public static Callable wrapCallable(final @NotNull Callable callable) { - final IHub newHub = Sentry.getCurrentHub().clone(); + // TODO replace with forking + final IScopes newHub = Sentry.getCurrentScopes().clone(); return () -> { - final IHub oldState = Sentry.getCurrentHub(); - Sentry.setCurrentHub(newHub); + final IScopes oldState = Sentry.getCurrentScopes(); + Sentry.setCurrentScopes(newHub); try { return callable.call(); } finally { - Sentry.setCurrentHub(oldState); + Sentry.setCurrentScopes(oldState); } }; } @@ -51,17 +53,18 @@ public static Callable wrapCallable(final @NotNull Callable callable) * @return the wrapped {@link Supplier} * @param - the result type of the {@link Supplier} */ + @SuppressWarnings("deprecation") public static Supplier wrapSupplier(final @NotNull Supplier supplier) { - - final IHub newHub = Sentry.getCurrentHub().clone(); + // TODO replace with forking + final IScopes newHub = Sentry.getCurrentScopes().clone(); return () -> { - final IHub oldState = Sentry.getCurrentHub(); - Sentry.setCurrentHub(newHub); + final IScopes oldState = Sentry.getCurrentScopes(); + Sentry.setCurrentScopes(newHub); try { return supplier.get(); } finally { - Sentry.setCurrentHub(oldState); + Sentry.setCurrentScopes(oldState); } }; } diff --git a/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java b/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java index b144f2d88a..d957a87ccf 100644 --- a/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java +++ b/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java @@ -27,12 +27,12 @@ public ShutdownHookIntegration() { } @Override - public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { - Objects.requireNonNull(hub, "Hub is required"); + public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + Objects.requireNonNull(scopes, "Scopes are required"); Objects.requireNonNull(options, "SentryOptions is required"); if (options.isEnableShutdownHook()) { - thread = new Thread(() -> hub.flush(options.getFlushTimeoutMillis())); + thread = new Thread(() -> scopes.flush(options.getFlushTimeoutMillis())); runtime.addShutdownHook(thread); options.getLogger().log(SentryLevel.DEBUG, "ShutdownHookIntegration installed."); addIntegrationToSdkVersion(getClass()); diff --git a/sentry/src/main/java/io/sentry/Span.java b/sentry/src/main/java/io/sentry/Span.java index 850276dac3..1c9d180b29 100644 --- a/sentry/src/main/java/io/sentry/Span.java +++ b/sentry/src/main/java/io/sentry/Span.java @@ -35,7 +35,7 @@ public final class Span implements ISpan { /** A throwable thrown during the execution of the span. */ private @Nullable Throwable throwable; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @NotNull AtomicBoolean finished = new AtomicBoolean(false); @@ -55,8 +55,8 @@ public final class Span implements ISpan { final @Nullable SpanId parentSpanId, final @NotNull SentryTracer transaction, final @NotNull String operation, - final @NotNull IHub hub) { - this(traceId, parentSpanId, transaction, operation, hub, null, new SpanOptions(), null); + final @NotNull IScopes scopes) { + this(traceId, parentSpanId, transaction, operation, scopes, null, new SpanOptions(), null); } Span( @@ -64,7 +64,7 @@ public final class Span implements ISpan { final @Nullable SpanId parentSpanId, final @NotNull SentryTracer transaction, final @NotNull String operation, - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @Nullable SentryDate startTimestamp, final @NotNull SpanOptions options, final @Nullable SpanFinishedCallback spanFinishedCallback) { @@ -72,30 +72,30 @@ public final class Span implements ISpan { new SpanContext( traceId, new SpanId(), operation, parentSpanId, transaction.getSamplingDecision()); this.transaction = Objects.requireNonNull(transaction, "transaction is required"); - this.hub = Objects.requireNonNull(hub, "hub is required"); + this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); this.options = options; this.spanFinishedCallback = spanFinishedCallback; if (startTimestamp != null) { this.startTimestamp = startTimestamp; } else { - this.startTimestamp = hub.getOptions().getDateProvider().now(); + this.startTimestamp = scopes.getOptions().getDateProvider().now(); } } public Span( final @NotNull TransactionContext context, final @NotNull SentryTracer sentryTracer, - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @Nullable SentryDate startTimestamp, final @NotNull SpanOptions options) { this.context = Objects.requireNonNull(context, "context is required"); this.transaction = Objects.requireNonNull(sentryTracer, "sentryTracer is required"); - this.hub = Objects.requireNonNull(hub, "hub is required"); + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); this.spanFinishedCallback = null; if (startTimestamp != null) { this.startTimestamp = startTimestamp; } else { - this.startTimestamp = hub.getOptions().getDateProvider().now(); + this.startTimestamp = scopes.getOptions().getDateProvider().now(); } this.options = options; } @@ -180,7 +180,7 @@ public void finish() { @Override public void finish(@Nullable SpanStatus status) { - finish(status, hub.getOptions().getDateProvider().now()); + finish(status, scopes.getOptions().getDateProvider().now()); } /** @@ -197,7 +197,7 @@ public void finish(final @Nullable SpanStatus status, final @Nullable SentryDate } this.context.setStatus(status); - this.timestamp = timestamp == null ? hub.getOptions().getDateProvider().now() : timestamp; + this.timestamp = timestamp == null ? scopes.getOptions().getDateProvider().now() : timestamp; if (options.isTrimStart() || options.isTrimEnd()) { @Nullable SentryDate minChildStart = null; @Nullable SentryDate maxChildEnd = null; @@ -230,7 +230,7 @@ public void finish(final @Nullable SpanStatus status, final @Nullable SentryDate } if (throwable != null) { - hub.setSpanContext(throwable, this, this.transaction.getName()); + scopes.setSpanContext(throwable, this, this.transaction.getName()); } if (spanFinishedCallback != null) { spanFinishedCallback.execute(this); @@ -343,7 +343,8 @@ public void setData(final @NotNull String key, final @NotNull Object value) { @Override public void setMeasurement(final @NotNull String name, final @NotNull Number value) { if (isFinished()) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -365,7 +366,8 @@ public void setMeasurement( final @NotNull Number value, final @NotNull MeasurementUnit unit) { if (isFinished()) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, diff --git a/sentry/src/main/java/io/sentry/SpotlightIntegration.java b/sentry/src/main/java/io/sentry/SpotlightIntegration.java index 6d488bcbce..0b69ae79be 100644 --- a/sentry/src/main/java/io/sentry/SpotlightIntegration.java +++ b/sentry/src/main/java/io/sentry/SpotlightIntegration.java @@ -26,7 +26,7 @@ public final class SpotlightIntegration private @NotNull ISentryExecutorService executorService = NoOpSentryExecutorService.getInstance(); @Override - public void register(@NotNull IHub hub, @NotNull SentryOptions options) { + public void register(@NotNull IScopes scopes, @NotNull SentryOptions options) { this.options = options; this.logger = options.getLogger(); diff --git a/sentry/src/main/java/io/sentry/UncaughtExceptionHandlerIntegration.java b/sentry/src/main/java/io/sentry/UncaughtExceptionHandlerIntegration.java index 33e1a4a815..47ceaa084e 100644 --- a/sentry/src/main/java/io/sentry/UncaughtExceptionHandlerIntegration.java +++ b/sentry/src/main/java/io/sentry/UncaughtExceptionHandlerIntegration.java @@ -28,7 +28,7 @@ public final class UncaughtExceptionHandlerIntegration /** Reference to the pre-existing uncaught exception handler. */ private @Nullable Thread.UncaughtExceptionHandler defaultExceptionHandler; - private @Nullable IHub hub; + private @Nullable IScopes scopes; private @Nullable SentryOptions options; private boolean registered = false; @@ -43,7 +43,7 @@ public UncaughtExceptionHandlerIntegration() { } @Override - public final void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { + public final void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { if (registered) { options .getLogger() @@ -54,7 +54,7 @@ public final void register(final @NotNull IHub hub, final @NotNull SentryOptions } registered = true; - this.hub = Objects.requireNonNull(hub, "Hub is required"); + this.scopes = Objects.requireNonNull(scopes, "Hub is required"); this.options = Objects.requireNonNull(options, "SentryOptions is required"); this.options @@ -89,7 +89,7 @@ public final void register(final @NotNull IHub hub, final @NotNull SentryOptions @Override public void uncaughtException(Thread thread, Throwable thrown) { - if (options != null && hub != null) { + if (options != null && scopes != null) { options.getLogger().log(SentryLevel.INFO, "Uncaught exception received."); try { @@ -99,14 +99,14 @@ public void uncaughtException(Thread thread, Throwable thrown) { final SentryEvent event = new SentryEvent(throwable); event.setLevel(SentryLevel.FATAL); - final ITransaction transaction = hub.getTransaction(); + final ITransaction transaction = scopes.getTransaction(); if (transaction == null && event.getEventId() != null) { // if there's no active transaction on scope, this event can trigger flush notification exceptionHint.setFlushable(event.getEventId()); } final Hint hint = HintUtils.createWithTypeCheckHint(exceptionHint); - final @NotNull SentryId sentryId = hub.captureEvent(event, hint); + final @NotNull SentryId sentryId = scopes.captureEvent(event, hint); final boolean isEventDropped = sentryId.equals(SentryId.EMPTY_ID); final EventDropReason eventDropReason = HintUtils.getEventDropReason(hint); // in case the event has been dropped by multithreaded deduplicator, the other threads will diff --git a/sentry/src/main/java/io/sentry/backpressure/BackpressureMonitor.java b/sentry/src/main/java/io/sentry/backpressure/BackpressureMonitor.java index 2008a38c76..6541a7586a 100644 --- a/sentry/src/main/java/io/sentry/backpressure/BackpressureMonitor.java +++ b/sentry/src/main/java/io/sentry/backpressure/BackpressureMonitor.java @@ -1,6 +1,6 @@ package io.sentry.backpressure; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ISentryExecutorService; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -12,12 +12,13 @@ public final class BackpressureMonitor implements IBackpressureMonitor, Runnable private static final int CHECK_INTERVAL_IN_MS = 10 * 1000; private final @NotNull SentryOptions sentryOptions; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private int downsampleFactor = 0; - public BackpressureMonitor(final @NotNull SentryOptions sentryOptions, final @NotNull IHub hub) { + public BackpressureMonitor( + final @NotNull SentryOptions sentryOptions, final @NotNull IScopes scopes) { this.sentryOptions = sentryOptions; - this.hub = hub; + this.scopes = scopes; } @Override @@ -66,6 +67,6 @@ private void reschedule(final int delay) { } private boolean isHealthy() { - return hub.isHealthy(); + return scopes.isHealthy(); } } diff --git a/sentry/src/main/java/io/sentry/instrumentation/file/FileIOSpanManager.java b/sentry/src/main/java/io/sentry/instrumentation/file/FileIOSpanManager.java index 956996ce04..52963413b6 100644 --- a/sentry/src/main/java/io/sentry/instrumentation/file/FileIOSpanManager.java +++ b/sentry/src/main/java/io/sentry/instrumentation/file/FileIOSpanManager.java @@ -1,6 +1,6 @@ package io.sentry.instrumentation.file; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryOptions; @@ -28,8 +28,8 @@ final class FileIOSpanManager { private final @NotNull SentryStackTraceFactory stackTraceFactory; - static @Nullable ISpan startSpan(final @NotNull IHub hub, final @NotNull String op) { - final ISpan parent = Platform.isAndroid() ? hub.getTransaction() : hub.getSpan(); + static @Nullable ISpan startSpan(final @NotNull IScopes scopes, final @NotNull String op) { + final ISpan parent = Platform.isAndroid() ? scopes.getTransaction() : scopes.getSpan(); return parent != null ? parent.startChild(op) : null; } diff --git a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileInputStream.java b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileInputStream.java index 04bb87ae7c..ea7d7f09a5 100644 --- a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileInputStream.java +++ b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileInputStream.java @@ -1,8 +1,8 @@ package io.sentry.instrumentation.file; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ISpan; +import io.sentry.ScopesAdapter; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -25,24 +25,24 @@ public final class SentryFileInputStream extends FileInputStream { private final @NotNull FileIOSpanManager spanManager; public SentryFileInputStream(final @Nullable String name) throws FileNotFoundException { - this(name != null ? new File(name) : null, HubAdapter.getInstance()); + this(name != null ? new File(name) : null, ScopesAdapter.getInstance()); } public SentryFileInputStream(final @Nullable File file) throws FileNotFoundException { - this(file, HubAdapter.getInstance()); + this(file, ScopesAdapter.getInstance()); } public SentryFileInputStream(final @NotNull FileDescriptor fdObj) { - this(fdObj, HubAdapter.getInstance()); + this(fdObj, ScopesAdapter.getInstance()); } - SentryFileInputStream(final @Nullable File file, final @NotNull IHub hub) + SentryFileInputStream(final @Nullable File file, final @NotNull IScopes scopes) throws FileNotFoundException { - this(init(file, null, hub)); + this(init(file, null, scopes)); } - SentryFileInputStream(final @NotNull FileDescriptor fdObj, final @NotNull IHub hub) { - this(init(fdObj, null, hub), fdObj); + SentryFileInputStream(final @NotNull FileDescriptor fdObj, final @NotNull IScopes scopes) { + this(init(fdObj, null, scopes), fdObj); } private SentryFileInputStream( @@ -60,24 +60,24 @@ private SentryFileInputStream(final @NotNull FileInputStreamInitData data) } private static FileInputStreamInitData init( - final @Nullable File file, @Nullable FileInputStream delegate, final @NotNull IHub hub) + final @Nullable File file, @Nullable FileInputStream delegate, final @NotNull IScopes scopes) throws FileNotFoundException { - final ISpan span = FileIOSpanManager.startSpan(hub, "file.read"); + final ISpan span = FileIOSpanManager.startSpan(scopes, "file.read"); if (delegate == null) { delegate = new FileInputStream(file); } - return new FileInputStreamInitData(file, span, delegate, hub.getOptions()); + return new FileInputStreamInitData(file, span, delegate, scopes.getOptions()); } private static FileInputStreamInitData init( final @NotNull FileDescriptor fd, @Nullable FileInputStream delegate, - final @NotNull IHub hub) { - final ISpan span = FileIOSpanManager.startSpan(hub, "file.read"); + final @NotNull IScopes scopes) { + final ISpan span = FileIOSpanManager.startSpan(scopes, "file.read"); if (delegate == null) { delegate = new FileInputStream(fd); } - return new FileInputStreamInitData(null, span, delegate, hub.getOptions()); + return new FileInputStreamInitData(null, span, delegate, scopes.getOptions()); } @Override @@ -128,25 +128,27 @@ public static FileInputStream create( final @NotNull FileInputStream delegate, final @Nullable String name) throws FileNotFoundException { return new SentryFileInputStream( - init(name != null ? new File(name) : null, delegate, HubAdapter.getInstance())); + init(name != null ? new File(name) : null, delegate, ScopesAdapter.getInstance())); } public static FileInputStream create( final @NotNull FileInputStream delegate, final @Nullable File file) throws FileNotFoundException { - return new SentryFileInputStream(init(file, delegate, HubAdapter.getInstance())); + return new SentryFileInputStream(init(file, delegate, ScopesAdapter.getInstance())); } public static FileInputStream create( final @NotNull FileInputStream delegate, final @NotNull FileDescriptor descriptor) { return new SentryFileInputStream( - init(descriptor, delegate, HubAdapter.getInstance()), descriptor); + init(descriptor, delegate, ScopesAdapter.getInstance()), descriptor); } static FileInputStream create( - final @NotNull FileInputStream delegate, final @Nullable File file, final @NotNull IHub hub) + final @NotNull FileInputStream delegate, + final @Nullable File file, + final @NotNull IScopes scopes) throws FileNotFoundException { - return new SentryFileInputStream(init(file, delegate, hub)); + return new SentryFileInputStream(init(file, delegate, scopes)); } } } diff --git a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileOutputStream.java b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileOutputStream.java index 9424710d71..4ef5022e1c 100644 --- a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileOutputStream.java +++ b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileOutputStream.java @@ -1,8 +1,8 @@ package io.sentry.instrumentation.file; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ISpan; +import io.sentry.ScopesAdapter; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; @@ -24,30 +24,31 @@ public final class SentryFileOutputStream extends FileOutputStream { private final @NotNull FileIOSpanManager spanManager; public SentryFileOutputStream(final @Nullable String name) throws FileNotFoundException { - this(name != null ? new File(name) : null, false, HubAdapter.getInstance()); + this(name != null ? new File(name) : null, false, ScopesAdapter.getInstance()); } public SentryFileOutputStream(final @Nullable String name, final boolean append) throws FileNotFoundException { - this(init(name != null ? new File(name) : null, append, null, HubAdapter.getInstance())); + this(init(name != null ? new File(name) : null, append, null, ScopesAdapter.getInstance())); } public SentryFileOutputStream(final @Nullable File file) throws FileNotFoundException { - this(file, false, HubAdapter.getInstance()); + this(file, false, ScopesAdapter.getInstance()); } public SentryFileOutputStream(final @Nullable File file, final boolean append) throws FileNotFoundException { - this(init(file, append, null, HubAdapter.getInstance())); + this(init(file, append, null, ScopesAdapter.getInstance())); } public SentryFileOutputStream(final @NotNull FileDescriptor fdObj) { - this(init(fdObj, null, HubAdapter.getInstance()), fdObj); + this(init(fdObj, null, ScopesAdapter.getInstance()), fdObj); } - SentryFileOutputStream(final @Nullable File file, final boolean append, final @NotNull IHub hub) + SentryFileOutputStream( + final @Nullable File file, final boolean append, final @NotNull IScopes scopes) throws FileNotFoundException { - this(init(file, append, null, hub)); + this(init(file, append, null, scopes)); } private SentryFileOutputStream( @@ -68,22 +69,24 @@ private static FileOutputStreamInitData init( final @Nullable File file, final boolean append, @Nullable FileOutputStream delegate, - @NotNull IHub hub) + @NotNull IScopes scopes) throws FileNotFoundException { - final ISpan span = FileIOSpanManager.startSpan(hub, "file.write"); + final ISpan span = FileIOSpanManager.startSpan(scopes, "file.write"); if (delegate == null) { delegate = new FileOutputStream(file, append); } - return new FileOutputStreamInitData(file, append, span, delegate, hub.getOptions()); + return new FileOutputStreamInitData(file, append, span, delegate, scopes.getOptions()); } private static FileOutputStreamInitData init( - final @NotNull FileDescriptor fd, @Nullable FileOutputStream delegate, @NotNull IHub hub) { - final ISpan span = FileIOSpanManager.startSpan(hub, "file.write"); + final @NotNull FileDescriptor fd, + @Nullable FileOutputStream delegate, + @NotNull IScopes scopes) { + final ISpan span = FileIOSpanManager.startSpan(scopes, "file.write"); if (delegate == null) { delegate = new FileOutputStream(fd); } - return new FileOutputStreamInitData(null, false, span, delegate, hub.getOptions()); + return new FileOutputStreamInitData(null, false, span, delegate, scopes.getOptions()); } @Override @@ -132,31 +135,32 @@ public static FileOutputStream create( final @NotNull FileOutputStream delegate, final @Nullable String name) throws FileNotFoundException { return new SentryFileOutputStream( - init(name != null ? new File(name) : null, false, delegate, HubAdapter.getInstance())); + init(name != null ? new File(name) : null, false, delegate, ScopesAdapter.getInstance())); } public static FileOutputStream create( final @NotNull FileOutputStream delegate, final @Nullable String name, final boolean append) throws FileNotFoundException { return new SentryFileOutputStream( - init(name != null ? new File(name) : null, append, delegate, HubAdapter.getInstance())); + init( + name != null ? new File(name) : null, append, delegate, ScopesAdapter.getInstance())); } public static FileOutputStream create( final @NotNull FileOutputStream delegate, final @Nullable File file) throws FileNotFoundException { - return new SentryFileOutputStream(init(file, false, delegate, HubAdapter.getInstance())); + return new SentryFileOutputStream(init(file, false, delegate, ScopesAdapter.getInstance())); } public static FileOutputStream create( final @NotNull FileOutputStream delegate, final @Nullable File file, final boolean append) throws FileNotFoundException { - return new SentryFileOutputStream(init(file, append, delegate, HubAdapter.getInstance())); + return new SentryFileOutputStream(init(file, append, delegate, ScopesAdapter.getInstance())); } public static FileOutputStream create( final @NotNull FileOutputStream delegate, final @NotNull FileDescriptor fdObj) { - return new SentryFileOutputStream(init(fdObj, delegate, HubAdapter.getInstance()), fdObj); + return new SentryFileOutputStream(init(fdObj, delegate, ScopesAdapter.getInstance()), fdObj); } } } diff --git a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileReader.java b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileReader.java index 0a225e65a5..38a83c7ff6 100644 --- a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileReader.java +++ b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileReader.java @@ -1,6 +1,6 @@ package io.sentry.instrumentation.file; -import io.sentry.IHub; +import io.sentry.IScopes; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; @@ -20,7 +20,8 @@ public SentryFileReader(final @NotNull FileDescriptor fd) { super(new SentryFileInputStream(fd)); } - SentryFileReader(final @NotNull File file, final @NotNull IHub hub) throws FileNotFoundException { - super(new SentryFileInputStream(file, hub)); + SentryFileReader(final @NotNull File file, final @NotNull IScopes scopes) + throws FileNotFoundException { + super(new SentryFileInputStream(file, scopes)); } } diff --git a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileWriter.java b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileWriter.java index 9588984612..93c901ec6c 100644 --- a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileWriter.java +++ b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileWriter.java @@ -1,6 +1,6 @@ package io.sentry.instrumentation.file; -import io.sentry.IHub; +import io.sentry.IScopes; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; @@ -30,8 +30,8 @@ public SentryFileWriter(final @NotNull FileDescriptor fd) { super(new SentryFileOutputStream(fd)); } - SentryFileWriter(final @NotNull File file, final boolean append, final @NotNull IHub hub) + SentryFileWriter(final @NotNull File file, final boolean append, final @NotNull IScopes scopes) throws FileNotFoundException { - super(new SentryFileOutputStream(file, append, hub)); + super(new SentryFileOutputStream(file, append, scopes)); } } diff --git a/sentry/src/main/java/io/sentry/util/CheckInUtils.java b/sentry/src/main/java/io/sentry/util/CheckInUtils.java index e15603adaf..6719e24839 100644 --- a/sentry/src/main/java/io/sentry/util/CheckInUtils.java +++ b/sentry/src/main/java/io/sentry/util/CheckInUtils.java @@ -3,7 +3,7 @@ import io.sentry.CheckIn; import io.sentry.CheckInStatus; import io.sentry.DateUtils; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.MonitorConfig; import io.sentry.Sentry; import io.sentry.protocol.SentryId; @@ -30,18 +30,19 @@ public static U withCheckIn( final @Nullable MonitorConfig monitorConfig, final @NotNull Callable callable) throws Exception { - final @NotNull IHub hub = Sentry.getCurrentHub(); + final @NotNull IScopes scopes = Sentry.getCurrentScopes(); final long startTime = System.currentTimeMillis(); boolean didError = false; - hub.pushScope(); - TracingUtils.startNewTrace(hub); + // TODO fork instead + scopes.pushScope(); + TracingUtils.startNewTrace(scopes); CheckIn inProgressCheckIn = new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS); if (monitorConfig != null) { inProgressCheckIn.setMonitorConfig(monitorConfig); } - @Nullable SentryId checkInId = hub.captureCheckIn(inProgressCheckIn); + @Nullable SentryId checkInId = scopes.captureCheckIn(inProgressCheckIn); try { return callable.call(); } catch (Throwable t) { @@ -51,8 +52,8 @@ public static U withCheckIn( final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK; CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); - hub.captureCheckIn(checkIn); - hub.popScope(); + scopes.captureCheckIn(checkIn); + scopes.popScope(); } } diff --git a/sentry/src/main/java/io/sentry/util/TracingUtils.java b/sentry/src/main/java/io/sentry/util/TracingUtils.java index 2aeb613f2d..67f6459660 100644 --- a/sentry/src/main/java/io/sentry/util/TracingUtils.java +++ b/sentry/src/main/java/io/sentry/util/TracingUtils.java @@ -2,8 +2,8 @@ import io.sentry.Baggage; import io.sentry.BaggageHeader; -import io.sentry.IHub; import io.sentry.IScope; +import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.PropagationContext; import io.sentry.SentryOptions; @@ -14,8 +14,8 @@ public final class TracingUtils { - public static void startNewTrace(final @NotNull IHub hub) { - hub.configureScope( + public static void startNewTrace(final @NotNull IScopes scopes) { + scopes.configureScope( scope -> { scope.withPropagationContext( propagationContext -> { @@ -25,30 +25,30 @@ public static void startNewTrace(final @NotNull IHub hub) { } public static @Nullable TracingHeaders traceIfAllowed( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull String requestUrl, @Nullable List thirdPartyBaggageHeaders, final @Nullable ISpan span) { - final @NotNull SentryOptions sentryOptions = hub.getOptions(); + final @NotNull SentryOptions sentryOptions = scopes.getOptions(); if (sentryOptions.isTraceSampling() && shouldAttachTracingHeaders(requestUrl, sentryOptions)) { - return trace(hub, thirdPartyBaggageHeaders, span); + return trace(scopes, thirdPartyBaggageHeaders, span); } return null; } public static @Nullable TracingHeaders trace( - final @NotNull IHub hub, + final @NotNull IScopes scopes, @Nullable List thirdPartyBaggageHeaders, final @Nullable ISpan span) { - final @NotNull SentryOptions sentryOptions = hub.getOptions(); + final @NotNull SentryOptions sentryOptions = scopes.getOptions(); if (span != null && !span.isNoOp()) { return new TracingHeaders( span.toSentryTrace(), span.toBaggageHeader(thirdPartyBaggageHeaders)); } else { final @NotNull PropagationContextHolder returnValue = new PropagationContextHolder(); - hub.configureScope( + scopes.configureScope( (scope) -> { returnValue.propagationContext = maybeUpdateBaggage(scope, sentryOptions); }); diff --git a/sentry/src/test/java/io/sentry/DefaultTransactionPerformanceCollectorTest.kt b/sentry/src/test/java/io/sentry/DefaultTransactionPerformanceCollectorTest.kt index c0445efb23..1416fbbe3f 100644 --- a/sentry/src/test/java/io/sentry/DefaultTransactionPerformanceCollectorTest.kt +++ b/sentry/src/test/java/io/sentry/DefaultTransactionPerformanceCollectorTest.kt @@ -33,7 +33,7 @@ class DefaultTransactionPerformanceCollectorTest { private class Fixture { lateinit var transaction1: ITransaction lateinit var transaction2: ITransaction - val hub: IHub = mock() + val scopes: IScopes = mock() val options = SentryOptions() var mockTimer: Timer? = null val deferredExecutorService = DeferredExecutorService() @@ -47,7 +47,7 @@ class DefaultTransactionPerformanceCollectorTest { } init { - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) } fun getSut(memoryCollector: IPerformanceSnapshotCollector? = JavaMemoryCollector(), cpuCollector: IPerformanceSnapshotCollector? = mockCpuCollector, executorService: ISentryExecutorService = deferredExecutorService): TransactionPerformanceCollector { @@ -59,8 +59,8 @@ class DefaultTransactionPerformanceCollectorTest { if (memoryCollector != null) { options.addPerformanceCollector(memoryCollector) } - transaction1 = SentryTracer(TransactionContext("", ""), hub) - transaction2 = SentryTracer(TransactionContext("", ""), hub) + transaction1 = SentryTracer(TransactionContext("", ""), scopes) + transaction2 = SentryTracer(TransactionContext("", ""), scopes) val collector = DefaultTransactionPerformanceCollector(options) val timer: Timer = collector.getProperty("timer") ?: Timer(true) mockTimer = spy(timer) diff --git a/sentry/src/test/java/io/sentry/DirectoryProcessorTest.kt b/sentry/src/test/java/io/sentry/DirectoryProcessorTest.kt index e87f4256d5..0507b8499d 100644 --- a/sentry/src/test/java/io/sentry/DirectoryProcessorTest.kt +++ b/sentry/src/test/java/io/sentry/DirectoryProcessorTest.kt @@ -27,7 +27,7 @@ class DirectoryProcessorTest { private class Fixture { - var hub: IHub = mock() + var scopes: IScopes = mock() var envelopeReader: IEnvelopeReader = mock() var serializer: ISerializer = mock() var logger: ILogger = mock() @@ -40,7 +40,7 @@ class DirectoryProcessorTest { fun getSut(isRetryable: Boolean = false, isRateLimitingActive: Boolean = false): OutboxSender { val hintCaptor = argumentCaptor() - whenever(hub.captureEvent(any(), hintCaptor.capture())).then { + whenever(scopes.captureEvent(any(), hintCaptor.capture())).then { HintUtils.runIfHasType( hintCaptor.firstValue, Enqueable::class.java @@ -52,7 +52,7 @@ class DirectoryProcessorTest { val rateLimiter = mock { whenever(mock.isActiveForCategory(any())).thenReturn(true) } - whenever(hub.rateLimiter).thenReturn(rateLimiter) + whenever(scopes.rateLimiter).thenReturn(rateLimiter) } } HintUtils.runIfHasType( @@ -62,7 +62,7 @@ class DirectoryProcessorTest { retryable.isRetry = isRetryable } } - return OutboxSender(hub, envelopeReader, serializer, logger, 500, 30) + return OutboxSender(scopes, envelopeReader, serializer, logger, 500, 30) } } @@ -91,7 +91,7 @@ class DirectoryProcessorTest { whenever(fixture.serializer.deserialize(any(), eq(SentryEvent::class.java))).thenReturn(event) fixture.getSut().processDirectory(file) - verify(fixture.hub).captureEvent(any(), argWhere { !HintUtils.hasType(it, ApplyScopeData::class.java) }) + verify(fixture.scopes).captureEvent(any(), argWhere { !HintUtils.hasType(it, ApplyScopeData::class.java) }) } @Test @@ -100,7 +100,7 @@ class DirectoryProcessorTest { dir.mkdirs() assertTrue(dir.exists()) // sanity check fixture.getSut().processDirectory(file) - verify(fixture.hub, never()).captureEnvelope(any(), any()) + verify(fixture.scopes, never()).captureEnvelope(any(), any()) } @Test @@ -121,7 +121,7 @@ class DirectoryProcessorTest { sut.processDirectory(file) // should only capture once - verify(fixture.hub).captureEvent(any(), anyOrNull()) + verify(fixture.scopes).captureEvent(any(), anyOrNull()) } @Test @@ -139,7 +139,7 @@ class DirectoryProcessorTest { sut.processDirectory(file) // should only capture once - verify(fixture.hub).captureEvent(any(), anyOrNull()) + verify(fixture.scopes).captureEvent(any(), anyOrNull()) } private fun getTempEnvelope(fileName: String): String { diff --git a/sentry/src/test/java/io/sentry/EnvelopeSenderTest.kt b/sentry/src/test/java/io/sentry/EnvelopeSenderTest.kt index 6f0ea9cb8a..d63ee81854 100644 --- a/sentry/src/test/java/io/sentry/EnvelopeSenderTest.kt +++ b/sentry/src/test/java/io/sentry/EnvelopeSenderTest.kt @@ -23,7 +23,7 @@ import kotlin.test.assertFalse class EnvelopeSenderTest { private class Fixture { - var hub: IHub? = mock() + var scopes: IScopes? = mock() var logger: ILogger? = mock() var serializer: ISerializer? = mock() var options = SentryOptions().noFlushTimeout() @@ -35,7 +35,7 @@ class EnvelopeSenderTest { fun getSut(): EnvelopeSender { return EnvelopeSender( - hub!!, + scopes!!, serializer!!, logger!!, options.flushTimeoutMillis, @@ -62,7 +62,7 @@ class EnvelopeSenderTest { val sut = fixture.getSut() sut.processDirectory(File("i don't exist")) verify(fixture.logger)!!.log(eq(SentryLevel.WARNING), eq("Directory '%s' doesn't exist. No cached events to send."), any()) - verifyNoMoreInteractions(fixture.hub) + verifyNoMoreInteractions(fixture.scopes) } @Test @@ -72,7 +72,7 @@ class EnvelopeSenderTest { testFile.deleteOnExit() sut.processDirectory(testFile) verify(fixture.logger)!!.log(eq(SentryLevel.ERROR), eq("Cache dir %s is not a directory."), any()) - verifyNoMoreInteractions(fixture.hub) + verifyNoMoreInteractions(fixture.scopes) } @Test @@ -82,11 +82,11 @@ class EnvelopeSenderTest { sut.processDirectory(File(tempDirectory.toUri())) testFile.deleteOnExit() verify(fixture.logger)!!.log(eq(SentryLevel.DEBUG), eq("File '%s' doesn't match extension expected."), any()) - verify(fixture.hub, never())!!.captureEnvelope(any(), anyOrNull()) + verify(fixture.scopes, never())!!.captureEnvelope(any(), anyOrNull()) } @Test - fun `when directory has event files, processDirectory captures with hub`() { + fun `when directory has event files, processDirectory captures with scopes`() { val event = SentryEvent() val envelope = SentryEnvelope.from(fixture.serializer!!, event, null) whenever(fixture.serializer!!.deserializeEnvelope(any())).thenReturn(envelope) @@ -94,7 +94,7 @@ class EnvelopeSenderTest { val testFile = File(Files.createTempFile(tempDirectory, "send-cached-event-test", EnvelopeCache.SUFFIX_ENVELOPE_FILE).toUri()) testFile.deleteOnExit() sut.processDirectory(File(tempDirectory.toUri())) - verify(fixture.hub)!!.captureEnvelope(eq(envelope), any()) + verify(fixture.scopes)!!.captureEnvelope(eq(envelope), any()) } @Test @@ -108,12 +108,12 @@ class EnvelopeSenderTest { val hints = HintUtils.createWithTypeCheckHint(mock()) sut.processFile(testFile, hints) verify(fixture.logger)!!.log(eq(SentryLevel.ERROR), eq(expected), eq("Failed to capture cached envelope %s"), eq(testFile.absolutePath)) - verifyNoMoreInteractions(fixture.hub) + verifyNoMoreInteractions(fixture.scopes) assertFalse(testFile.exists()) } @Test - fun `when hub throws, file gets deleted`() { + fun `when scopes throws, file gets deleted`() { val expected = RuntimeException() whenever(fixture.serializer!!.deserializeEnvelope(any())).doThrow(expected) val sut = fixture.getSut() @@ -121,6 +121,6 @@ class EnvelopeSenderTest { testFile.deleteOnExit() sut.processFile(testFile, Hint()) verify(fixture.logger)!!.log(eq(SentryLevel.ERROR), eq(expected), eq("Failed to capture cached envelope %s"), eq(testFile.absolutePath)) - verifyNoMoreInteractions(fixture.hub) + verifyNoMoreInteractions(fixture.scopes) } } diff --git a/sentry/src/test/java/io/sentry/HubAdapterTest.kt b/sentry/src/test/java/io/sentry/HubAdapterTest.kt index 9686250d20..0e7e1d0f77 100644 --- a/sentry/src/test/java/io/sentry/HubAdapterTest.kt +++ b/sentry/src/test/java/io/sentry/HubAdapterTest.kt @@ -13,11 +13,11 @@ import kotlin.test.Test class HubAdapterTest { - val hub: Hub = mock() + val scopes: IScopes = mock() @BeforeTest fun `set up`() { - Sentry.setCurrentHub(hub) + Sentry.setCurrentScopes(scopes) } @AfterTest @@ -27,7 +27,7 @@ class HubAdapterTest { @Test fun `isEnabled calls Hub`() { HubAdapter.getInstance().isEnabled - verify(hub).isEnabled + verify(scopes).isEnabled } @Test fun `captureEvent calls Hub`() { @@ -35,27 +35,27 @@ class HubAdapterTest { val hint = mock() val scopeCallback = mock() HubAdapter.getInstance().captureEvent(event, hint) - verify(hub).captureEvent(eq(event), eq(hint)) + verify(scopes).captureEvent(eq(event), eq(hint)) HubAdapter.getInstance().captureEvent(event, hint, scopeCallback) - verify(hub).captureEvent(eq(event), eq(hint), eq(scopeCallback)) + verify(scopes).captureEvent(eq(event), eq(hint), eq(scopeCallback)) } @Test fun `captureMessage calls Hub`() { val scopeCallback = mock() val sentryLevel = mock() HubAdapter.getInstance().captureMessage("message", sentryLevel) - verify(hub).captureMessage(eq("message"), eq(sentryLevel)) + verify(scopes).captureMessage(eq("message"), eq(sentryLevel)) HubAdapter.getInstance().captureMessage("message", sentryLevel, scopeCallback) - verify(hub).captureMessage(eq("message"), eq(sentryLevel), eq(scopeCallback)) + verify(scopes).captureMessage(eq("message"), eq(sentryLevel), eq(scopeCallback)) } @Test fun `captureEnvelope calls Hub`() { val envelope = mock() val hint = mock() HubAdapter.getInstance().captureEnvelope(envelope, hint) - verify(hub).captureEnvelope(eq(envelope), eq(hint)) + verify(scopes).captureEnvelope(eq(envelope), eq(hint)) } @Test fun `captureException calls Hub`() { @@ -63,145 +63,145 @@ class HubAdapterTest { val hint = mock() val scopeCallback = mock() HubAdapter.getInstance().captureException(throwable, hint) - verify(hub).captureException(eq(throwable), eq(hint)) + verify(scopes).captureException(eq(throwable), eq(hint)) HubAdapter.getInstance().captureException(throwable, hint, scopeCallback) - verify(hub).captureException(eq(throwable), eq(hint), eq(scopeCallback)) + verify(scopes).captureException(eq(throwable), eq(hint), eq(scopeCallback)) } @Test fun `captureUserFeedback calls Hub`() { val userFeedback = mock() HubAdapter.getInstance().captureUserFeedback(userFeedback) - verify(hub).captureUserFeedback(eq(userFeedback)) + verify(scopes).captureUserFeedback(eq(userFeedback)) } @Test fun `captureCheckIn calls Hub`() { val checkIn = mock() HubAdapter.getInstance().captureCheckIn(checkIn) - verify(hub).captureCheckIn(eq(checkIn)) + verify(scopes).captureCheckIn(eq(checkIn)) } @Test fun `startSession calls Hub`() { HubAdapter.getInstance().startSession() - verify(hub).startSession() + verify(scopes).startSession() } @Test fun `endSession calls Hub`() { HubAdapter.getInstance().endSession() - verify(hub).endSession() + verify(scopes).endSession() } @Test fun `close calls Hub`() { HubAdapter.getInstance().close() - verify(hub).close(false) + verify(scopes).close(false) } @Test fun `close with isRestarting true calls Hub with isRestarting false`() { HubAdapter.getInstance().close(true) - verify(hub).close(false) + verify(scopes).close(false) } @Test fun `close with isRestarting false calls Hub with isRestarting false`() { HubAdapter.getInstance().close(false) - verify(hub).close(false) + verify(scopes).close(false) } @Test fun `addBreadcrumb calls Hub`() { val breadcrumb = mock() val hint = mock() HubAdapter.getInstance().addBreadcrumb(breadcrumb, hint) - verify(hub).addBreadcrumb(eq(breadcrumb), eq(hint)) + verify(scopes).addBreadcrumb(eq(breadcrumb), eq(hint)) } @Test fun `setLevel calls Hub`() { val sentryLevel = mock() HubAdapter.getInstance().setLevel(sentryLevel) - verify(hub).setLevel(eq(sentryLevel)) + verify(scopes).setLevel(eq(sentryLevel)) } @Test fun `setTransaction calls Hub`() { HubAdapter.getInstance().setTransaction("transaction") - verify(hub).setTransaction(eq("transaction")) + verify(scopes).setTransaction(eq("transaction")) } @Test fun `setUser calls Hub`() { val user = mock() HubAdapter.getInstance().setUser(user) - verify(hub).setUser(eq(user)) + verify(scopes).setUser(eq(user)) } @Test fun `setFingerprint calls Hub`() { val fingerprint = ArrayList() HubAdapter.getInstance().setFingerprint(fingerprint) - verify(hub).setFingerprint(eq(fingerprint)) + verify(scopes).setFingerprint(eq(fingerprint)) } @Test fun `clearBreadcrumbs calls Hub`() { HubAdapter.getInstance().clearBreadcrumbs() - verify(hub).clearBreadcrumbs() + verify(scopes).clearBreadcrumbs() } @Test fun `setTag calls Hub`() { HubAdapter.getInstance().setTag("key", "value") - verify(hub).setTag(eq("key"), eq("value")) + verify(scopes).setTag(eq("key"), eq("value")) } @Test fun `removeTag calls Hub`() { HubAdapter.getInstance().removeTag("key") - verify(hub).removeTag(eq("key")) + verify(scopes).removeTag(eq("key")) } @Test fun `setExtra calls Hub`() { HubAdapter.getInstance().setExtra("key", "value") - verify(hub).setExtra(eq("key"), eq("value")) + verify(scopes).setExtra(eq("key"), eq("value")) } @Test fun `removeExtra calls Hub`() { HubAdapter.getInstance().removeExtra("key") - verify(hub).removeExtra(eq("key")) + verify(scopes).removeExtra(eq("key")) } @Test fun `getLastEventId calls Hub`() { HubAdapter.getInstance().lastEventId - verify(hub).lastEventId + verify(scopes).lastEventId } @Test fun `pushScope calls Hub`() { HubAdapter.getInstance().pushScope() - verify(hub).pushScope() + verify(scopes).pushScope() } @Test fun `popScope calls Hub`() { HubAdapter.getInstance().popScope() - verify(hub).popScope() + verify(scopes).popScope() } @Test fun `withScope calls Hub`() { val scopeCallback = mock() HubAdapter.getInstance().withScope(scopeCallback) - verify(hub).withScope(eq(scopeCallback)) + verify(scopes).withScope(eq(scopeCallback)) } @Test fun `configureScope calls Hub`() { val scopeCallback = mock() HubAdapter.getInstance().configureScope(scopeCallback) - verify(hub).configureScope(eq(scopeCallback)) + verify(scopes).configureScope(eq(scopeCallback)) } @Test fun `bindClient calls Hub`() { val client = mock() HubAdapter.getInstance().bindClient(client) - verify(hub).bindClient(eq(client)) + verify(scopes).bindClient(eq(client)) } @Test fun `flush calls Hub`() { HubAdapter.getInstance().flush(1) - verify(hub).flush(eq(1)) + verify(scopes).flush(eq(1)) } @Test fun `clone calls Hub`() { HubAdapter.getInstance().clone() - verify(hub).clone() + verify(scopes).clone() } @Test fun `captureTransaction calls Hub`() { @@ -210,7 +210,7 @@ class HubAdapterTest { val hint = mock() val profilingTraceData = mock() HubAdapter.getInstance().captureTransaction(transaction, traceContext, hint, profilingTraceData) - verify(hub).captureTransaction(eq(transaction), eq(traceContext), eq(hint), eq(profilingTraceData)) + verify(scopes).captureTransaction(eq(transaction), eq(traceContext), eq(hint), eq(profilingTraceData)) } @Test fun `startTransaction calls Hub`() { @@ -218,48 +218,48 @@ class HubAdapterTest { val samplingContext = mock() val transactionOptions = mock() HubAdapter.getInstance().startTransaction(transactionContext) - verify(hub).startTransaction(eq(transactionContext), any()) + verify(scopes).startTransaction(eq(transactionContext), any()) - reset(hub) + reset(scopes) HubAdapter.getInstance().startTransaction(transactionContext, transactionOptions) - verify(hub).startTransaction(eq(transactionContext), eq(transactionOptions)) + verify(scopes).startTransaction(eq(transactionContext), eq(transactionOptions)) } @Test fun `traceHeaders calls Hub`() { HubAdapter.getInstance().traceHeaders() - verify(hub).traceHeaders() + verify(scopes).traceHeaders() } @Test fun `setSpanContext calls Hub`() { val throwable = mock() val span = mock() HubAdapter.getInstance().setSpanContext(throwable, span, "transactionName") - verify(hub).setSpanContext(eq(throwable), eq(span), eq("transactionName")) + verify(scopes).setSpanContext(eq(throwable), eq(span), eq("transactionName")) } @Test fun `getSpan calls Hub`() { HubAdapter.getInstance().span - verify(hub).span + verify(scopes).span } @Test fun `getTransaction calls Hub`() { HubAdapter.getInstance().transaction - verify(hub).transaction + verify(scopes).transaction } @Test fun `getOptions calls Hub`() { HubAdapter.getInstance().options - verify(hub).options + verify(scopes).options } @Test fun `isCrashedLastRun calls Hub`() { HubAdapter.getInstance().isCrashedLastRun - verify(hub).isCrashedLastRun + verify(scopes).isCrashedLastRun } @Test fun `reportFullyDisplayed calls Hub`() { HubAdapter.getInstance().reportFullyDisplayed() - verify(hub).reportFullyDisplayed() + verify(scopes).reportFullyDisplayed() } } diff --git a/sentry/src/test/java/io/sentry/HubTest.kt b/sentry/src/test/java/io/sentry/HubTest.kt index 8fac963e70..f30fd0a966 100644 --- a/sentry/src/test/java/io/sentry/HubTest.kt +++ b/sentry/src/test/java/io/sentry/HubTest.kt @@ -72,7 +72,7 @@ class HubTest { } @Test - fun `when hub is cloned, integrations are not registered`() { + fun `when scopes is cloned, integrations are not registered`() { val integrationMock = mock() val options = SentryOptions() options.cacheDirPath = file.absolutePath @@ -80,36 +80,36 @@ class HubTest { options.setSerializer(mock()) options.addIntegration(integrationMock) // val expected = HubAdapter.getInstance() - val hub = Hub(options) + val scopes = Hub(options) // verify(integrationMock).register(expected, options) - hub.clone() + scopes.clone() verifyNoMoreInteractions(integrationMock) } @Test - fun `when hub is cloned, scope changes are isolated`() { + fun `when scopes is cloned, scope changes are isolated`() { val options = SentryOptions() options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val hub = Hub(options) + val scopes = Hub(options) var firstScope: IScope? = null - hub.configureScope { + scopes.configureScope { firstScope = it - it.setTag("hub", "a") + it.setTag("scopes", "a") } var cloneScope: IScope? = null - val clone = hub.clone() + val clone = scopes.clone() clone.configureScope { cloneScope = it - it.setTag("hub", "b") + it.setTag("scopes", "b") } - assertEquals("a", firstScope!!.tags["hub"]) - assertEquals("b", cloneScope!!.tags["hub"]) + assertEquals("a", firstScope!!.tags["scopes"]) + assertEquals("b", cloneScope!!.tags["scopes"]) } @Test - fun `when hub is initialized, breadcrumbs are capped as per options`() { + fun `when scopes is initialized, breadcrumbs are capped as per options`() { val options = SentryOptions() options.cacheDirPath = file.absolutePath options.maxBreadcrumbs = 5 @@ -288,7 +288,7 @@ class HubTest { } @Test - fun `when captureEvent is called on disabled hub, lastEventId does not get overwritten`() { + fun `when captureEvent is called on disabled scopes, lastEventId does not get overwritten`() { val (sut, mockClient) = getEnabledHub() whenever(mockClient.captureEvent(any(), any(), anyOrNull())).thenReturn(SentryId(UUID.randomUUID())) val event = SentryEvent() @@ -827,14 +827,14 @@ class HubTest { @Test fun `when withScope throws an exception then it should be caught`() { - val (hub, _, logger) = getEnabledHub() + val (scopes, _, logger) = getEnabledHub() val exception = Exception("scope callback exception") val scopeCallback = ScopeCallback { throw exception } - hub.withScope(scopeCallback) + scopes.withScope(scopeCallback) verify(logger).log(eq(SentryLevel.ERROR), any(), eq(exception)) } @@ -864,25 +864,25 @@ class HubTest { @Test fun `when configureScope throws an exception then it should be caught`() { - val (hub, _, logger) = getEnabledHub() + val (scopes, _, logger) = getEnabledHub() val exception = Exception("scope callback exception") val scopeCallback = ScopeCallback { throw exception } - hub.configureScope(scopeCallback) + scopes.configureScope(scopeCallback) verify(logger).log(eq(SentryLevel.ERROR), any(), eq(exception)) } //endregion @Test - fun `when integration is registered, hub is enabled`() { + fun `when integration is registered, scopes is enabled`() { val mock = mock() var options: SentryOptions? = null - // init main hub and make it enabled + // init main scopes and make it enabled Sentry.init { it.addIntegration(mock) it.dsn = "https://key@sentry.io/proj" @@ -892,8 +892,8 @@ class HubTest { } doAnswer { - val hub = it.arguments[0] as IHub - assertTrue(hub.isEnabled) + val scopes = it.arguments[0] as IScopes + assertTrue(scopes.isEnabled) }.whenever(mock).register(any(), eq(options!!)) verify(mock).register(any(), eq(options!!)) @@ -902,26 +902,26 @@ class HubTest { //region setLevel tests @Test fun `when setLevel is called on disabled client, do nothing`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } - hub.close() + scopes.close() - hub.setLevel(SentryLevel.INFO) + scopes.setLevel(SentryLevel.INFO) assertNull(scope?.level) } @Test fun `when setLevel is called, level is set`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } - hub.setLevel(SentryLevel.INFO) + scopes.setLevel(SentryLevel.INFO) assertEquals(SentryLevel.INFO, scope?.level) } //endregion @@ -929,74 +929,74 @@ class HubTest { //region setTransaction tests @Test fun `when setTransaction is called on disabled client, do nothing`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } - hub.close() + scopes.close() - hub.setTransaction("test") + scopes.setTransaction("test") assertNull(scope?.transactionName) } @Test fun `when setTransaction is called, and transaction is not set, transaction name is changed`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } - hub.setTransaction("test") + scopes.setTransaction("test") assertEquals("test", scope?.transactionName) } @Test fun `when setTransaction is called, and transaction is set, transaction name is changed`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } - val tx = hub.startTransaction("test", "op") - hub.configureScope { it.setTransaction(tx) } + val tx = scopes.startTransaction("test", "op") + scopes.configureScope { it.setTransaction(tx) } assertEquals("test", scope?.transactionName) } @Test fun `when startTransaction is called with different instrumenter, no-op is returned`() { - val hub = generateHub() + val scopes = generateHub() val transactionContext = TransactionContext("test", "op").also { it.instrumenter = Instrumenter.OTEL } val transactionOptions = TransactionOptions() - val tx = hub.startTransaction(transactionContext, transactionOptions) + val tx = scopes.startTransaction(transactionContext, transactionOptions) assertTrue(tx is NoOpTransaction) } @Test fun `when startTransaction is called with different instrumenter, no-op is returned 2`() { - val hub = generateHub() { + val scopes = generateHub() { it.instrumenter = Instrumenter.OTEL } - val tx = hub.startTransaction("test", "op") + val tx = scopes.startTransaction("test", "op") assertTrue(tx is NoOpTransaction) } @Test fun `when startTransaction is called with configured instrumenter, it works`() { - val hub = generateHub() { + val scopes = generateHub() { it.instrumenter = Instrumenter.OTEL } val transactionContext = TransactionContext("test", "op").also { it.instrumenter = Instrumenter.OTEL } val transactionOptions = TransactionOptions() - val tx = hub.startTransaction(transactionContext, transactionOptions) + val tx = scopes.startTransaction(transactionContext, transactionOptions) assertFalse(tx is NoOpTransaction) } @@ -1005,27 +1005,27 @@ class HubTest { //region setUser tests @Test fun `when setUser is called on disabled client, do nothing`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } - hub.close() + scopes.close() - hub.setUser(User()) + scopes.setUser(User()) assertNull(scope?.user) } @Test fun `when setUser is called, user is set`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } val user = User() - hub.setUser(user) + scopes.setUser(user) assertEquals(user, scope?.user) } //endregion @@ -1033,40 +1033,40 @@ class HubTest { //region setFingerprint tests @Test fun `when setFingerprint is called on disabled client, do nothing`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } - hub.close() + scopes.close() val fingerprint = listOf("abc") - hub.setFingerprint(fingerprint) + scopes.setFingerprint(fingerprint) assertEquals(0, scope?.fingerprint?.count()) } @Test fun `when setFingerprint is called with null parameter, do nothing`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } - hub.callMethod("setFingerprint", List::class.java, null) + scopes.callMethod("setFingerprint", List::class.java, null) assertEquals(0, scope?.fingerprint?.count()) } @Test fun `when setFingerprint is called, fingerprint is set`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } val fingerprint = listOf("abc") - hub.setFingerprint(fingerprint) + scopes.setFingerprint(fingerprint) assertEquals(1, scope?.fingerprint?.count()) } //endregion @@ -1074,30 +1074,30 @@ class HubTest { //region clearBreadcrumbs tests @Test fun `when clearBreadcrumbs is called on disabled client, do nothing`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } - hub.addBreadcrumb(Breadcrumb()) + scopes.addBreadcrumb(Breadcrumb()) assertEquals(1, scope?.breadcrumbs?.count()) - hub.close() + scopes.close() assertEquals(0, scope?.breadcrumbs?.count()) } @Test fun `when clearBreadcrumbs is called, clear breadcrumbs`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } - hub.addBreadcrumb(Breadcrumb()) + scopes.addBreadcrumb(Breadcrumb()) assertEquals(1, scope?.breadcrumbs?.count()) - hub.clearBreadcrumbs() + scopes.clearBreadcrumbs() assertEquals(0, scope?.breadcrumbs?.count()) } //endregion @@ -1105,38 +1105,38 @@ class HubTest { //region setTag tests @Test fun `when setTag is called on disabled client, do nothing`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } - hub.close() + scopes.close() - hub.setTag("test", "test") + scopes.setTag("test", "test") assertEquals(0, scope?.tags?.count()) } @Test fun `when setTag is called with null parameters, do nothing`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } - hub.callMethod("setTag", parameterTypes = arrayOf(String::class.java, String::class.java), null, null) + scopes.callMethod("setTag", parameterTypes = arrayOf(String::class.java, String::class.java), null, null) assertEquals(0, scope?.tags?.count()) } @Test fun `when setTag is called, tag is set`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } - hub.setTag("test", "test") + scopes.setTag("test", "test") assertEquals(1, scope?.tags?.count()) } //endregion @@ -1144,38 +1144,38 @@ class HubTest { //region setExtra tests @Test fun `when setExtra is called on disabled client, do nothing`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } - hub.close() + scopes.close() - hub.setExtra("test", "test") + scopes.setExtra("test", "test") assertEquals(0, scope?.extras?.count()) } @Test fun `when setExtra is called with null parameters, do nothing`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } - hub.callMethod("setExtra", parameterTypes = arrayOf(String::class.java, String::class.java), null, null) + scopes.callMethod("setExtra", parameterTypes = arrayOf(String::class.java, String::class.java), null, null) assertEquals(0, scope?.extras?.count()) } @Test fun `when setExtra is called, extra is set`() { - val hub = generateHub() + val scopes = generateHub() var scope: IScope? = null - hub.configureScope { + scopes.configureScope { scope = it } - hub.setExtra("test", "test") + scopes.setExtra("test", "test") assertEquals(1, scope?.extras?.count()) } //endregion @@ -1488,19 +1488,19 @@ class HubTest { val mockTransactionProfiler = mock() val mockClient = mock() whenever(mockTransactionProfiler.onTransactionFinish(any(), anyOrNull(), anyOrNull())).thenAnswer { mockClient.captureEnvelope(mock()) } - val hub = generateHub { + val scopes = generateHub { it.setTransactionProfiler(mockTransactionProfiler) } - hub.bindClient(mockClient) + scopes.bindClient(mockClient) // Transaction is not sampled, so it should not be profiled val contexts = TransactionContext("name", "op", TracesSamplingDecision(false, null, true, null)) - val transaction = hub.startTransaction(contexts) + val transaction = scopes.startTransaction(contexts) transaction.finish() verify(mockClient, never()).captureEnvelope(any()) // Transaction is sampled, so it should be profiled val sampledContexts = TransactionContext("name", "op", TracesSamplingDecision(true, null, true, null)) - val sampledTransaction = hub.startTransaction(sampledContexts) + val sampledTransaction = scopes.startTransaction(sampledContexts) sampledTransaction.finish() verify(mockClient).captureEnvelope(any()) } @@ -1510,13 +1510,13 @@ class HubTest { val mockTransactionProfiler = mock() val mockClient = mock() whenever(mockTransactionProfiler.onTransactionFinish(any(), anyOrNull(), anyOrNull())).thenAnswer { mockClient.captureEnvelope(mock()) } - val hub = generateHub { + val scopes = generateHub { it.profilesSampleRate = 0.0 it.setTransactionProfiler(mockTransactionProfiler) } - hub.bindClient(mockClient) + scopes.bindClient(mockClient) val contexts = TransactionContext("name", "op") - val transaction = hub.startTransaction(contexts) + val transaction = scopes.startTransaction(contexts) transaction.finish() verify(mockClient, never()).captureEnvelope(any()) } @@ -1525,12 +1525,12 @@ class HubTest { fun `when profiler is running and isAppStartTransaction is false, startTransaction does not interact with profiler`() { val mockTransactionProfiler = mock() whenever(mockTransactionProfiler.isRunning).thenReturn(true) - val hub = generateHub { + val scopes = generateHub { it.profilesSampleRate = 1.0 it.setTransactionProfiler(mockTransactionProfiler) } val context = TransactionContext("name", "op") - hub.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = false }) + scopes.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = false }) verify(mockTransactionProfiler, never()).start() verify(mockTransactionProfiler, never()).bindTransaction(any()) } @@ -1539,12 +1539,12 @@ class HubTest { fun `when profiler is running and isAppStartTransaction is true, startTransaction binds current profile`() { val mockTransactionProfiler = mock() whenever(mockTransactionProfiler.isRunning).thenReturn(true) - val hub = generateHub { + val scopes = generateHub { it.profilesSampleRate = 1.0 it.setTransactionProfiler(mockTransactionProfiler) } val context = TransactionContext("name", "op") - val transaction = hub.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = true }) + val transaction = scopes.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = true }) verify(mockTransactionProfiler, never()).start() verify(mockTransactionProfiler).bindTransaction(eq(transaction)) } @@ -1553,12 +1553,12 @@ class HubTest { fun `when profiler is not running, startTransaction starts and binds current profile`() { val mockTransactionProfiler = mock() whenever(mockTransactionProfiler.isRunning).thenReturn(false) - val hub = generateHub { + val scopes = generateHub { it.profilesSampleRate = 1.0 it.setTransactionProfiler(mockTransactionProfiler) } val context = TransactionContext("name", "op") - val transaction = hub.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = false }) + val transaction = scopes.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = false }) verify(mockTransactionProfiler).start() verify(mockTransactionProfiler).bindTransaction(eq(transaction)) } @@ -1567,75 +1567,75 @@ class HubTest { //region startTransaction tests @Test fun `when startTransaction, creates transaction`() { - val hub = generateHub() + val scopes = generateHub() val contexts = TransactionContext("name", "op") - val transaction = hub.startTransaction(contexts) + val transaction = scopes.startTransaction(contexts) assertTrue(transaction is SentryTracer) assertEquals(contexts, transaction.root.spanContext) } @Test fun `when startTransaction with bindToScope set to false, transaction is not attached to the scope`() { - val hub = generateHub() + val scopes = generateHub() - hub.startTransaction("name", "op", TransactionOptions()) + scopes.startTransaction("name", "op", TransactionOptions()) - hub.configureScope { + scopes.configureScope { assertNull(it.span) } } @Test fun `when startTransaction without bindToScope set, transaction is not attached to the scope`() { - val hub = generateHub() + val scopes = generateHub() - hub.startTransaction("name", "op") + scopes.startTransaction("name", "op") - hub.configureScope { + scopes.configureScope { assertNull(it.span) } } @Test fun `when startTransaction with bindToScope set to true, transaction is attached to the scope`() { - val hub = generateHub() + val scopes = generateHub() - val transaction = hub.startTransaction("name", "op", TransactionOptions().also { it.isBindToScope = true }) + val transaction = scopes.startTransaction("name", "op", TransactionOptions().also { it.isBindToScope = true }) - hub.configureScope { + scopes.configureScope { assertEquals(transaction, it.span) } } @Test fun `when startTransaction and no tracing sampling is configured, event is not sampled`() { - val hub = generateHub { + val scopes = generateHub { it.tracesSampleRate = 0.0 } - val transaction = hub.startTransaction("name", "op") + val transaction = scopes.startTransaction("name", "op") assertFalse(transaction.isSampled!!) } @Test fun `when startTransaction and no profile sampling is configured, profile is not sampled`() { - val hub = generateHub { + val scopes = generateHub { it.tracesSampleRate = 1.0 it.profilesSampleRate = 0.0 } - val transaction = hub.startTransaction("name", "op") + val transaction = scopes.startTransaction("name", "op") assertTrue(transaction.isSampled!!) assertFalse(transaction.isProfileSampled!!) } @Test fun `when startTransaction with parent sampled and no traces sampler provided, transaction inherits sampling decision`() { - val hub = generateHub() + val scopes = generateHub() val transactionContext = TransactionContext("name", "op") transactionContext.parentSampled = true - val transaction = hub.startTransaction(transactionContext) + val transaction = scopes.startTransaction(transactionContext) assertNotNull(transaction) assertNotNull(transaction.isSampled) assertTrue(transaction.isSampled!!) @@ -1643,10 +1643,10 @@ class HubTest { @Test fun `when startTransaction with parent profile sampled and no profile sampler provided, transaction inherits profile sampling decision`() { - val hub = generateHub() + val scopes = generateHub() val transactionContext = TransactionContext("name", "op") transactionContext.setParentSampled(true, true) - val transaction = hub.startTransaction(transactionContext) + val transaction = scopes.startTransaction(transactionContext) assertTrue(transaction.isProfileSampled!!) } @@ -1717,11 +1717,11 @@ class HubTest { @Test fun `when tracesSampleRate and tracesSampler are not set on SentryOptions, startTransaction returns NoOp`() { - val hub = generateHub { + val scopes = generateHub { it.tracesSampleRate = null it.tracesSampler = null } - val transaction = hub.startTransaction(TransactionContext("name", "op", TracesSamplingDecision(true))) + val transaction = scopes.startTransaction(TransactionContext("name", "op", TracesSamplingDecision(true))) assertTrue(transaction is NoOpTransaction) } //endregion @@ -1729,81 +1729,81 @@ class HubTest { //region startTransaction tests @Test fun `when traceHeaders and no transaction is active, traceHeaders are generated from scope`() { - val hub = generateHub() + val scopes = generateHub() var spanId: SpanId? = null - hub.configureScope { spanId = it.propagationContext.spanId } + scopes.configureScope { spanId = it.propagationContext.spanId } - val traceHeader = hub.traceHeaders() + val traceHeader = scopes.traceHeaders() assertNotNull(traceHeader) assertEquals(spanId, traceHeader.spanId) } @Test fun `when traceHeaders and there is an active transaction, traceHeaders are not null`() { - val hub = generateHub() - val tx = hub.startTransaction("aTransaction", "op") - hub.configureScope { it.setTransaction(tx) } + val scopes = generateHub() + val tx = scopes.startTransaction("aTransaction", "op") + scopes.configureScope { it.setTransaction(tx) } - assertNotNull(hub.traceHeaders()) + assertNotNull(scopes.traceHeaders()) } //endregion //region getSpan tests @Test fun `when there is no active transaction, getSpan returns null`() { - val hub = generateHub() - assertNull(hub.span) + val scopes = generateHub() + assertNull(scopes.span) } @Test fun `when there is no active transaction, getTransaction returns null`() { - val hub = generateHub() - assertNull(hub.transaction) + val scopes = generateHub() + assertNull(scopes.transaction) } @Test fun `when there is active transaction bound to the scope, getTransaction and getSpan return active transaction`() { - val hub = generateHub() - val tx = hub.startTransaction("aTransaction", "op") - hub.configureScope { it.transaction = tx } + val scopes = generateHub() + val tx = scopes.startTransaction("aTransaction", "op") + scopes.configureScope { it.transaction = tx } - assertEquals(tx, hub.transaction) - assertEquals(tx, hub.span) + assertEquals(tx, scopes.transaction) + assertEquals(tx, scopes.span) } @Test - fun `when there is a transaction but the hub is closed, getTransaction returns null`() { - val hub = generateHub() - hub.startTransaction("name", "op") - hub.close() + fun `when there is a transaction but the scopes is closed, getTransaction returns null`() { + val scopes = generateHub() + scopes.startTransaction("name", "op") + scopes.close() - assertNull(hub.transaction) + assertNull(scopes.transaction) } @Test fun `when there is active span within a transaction bound to the scope, getSpan returns active span`() { - val hub = generateHub() - val tx = hub.startTransaction("aTransaction", "op") - hub.configureScope { it.setTransaction(tx) } - hub.configureScope { it.setTransaction(tx) } + val scopes = generateHub() + val tx = scopes.startTransaction("aTransaction", "op") + scopes.configureScope { it.setTransaction(tx) } + scopes.configureScope { it.setTransaction(tx) } val span = tx.startChild("op") - assertEquals(tx, hub.transaction) - assertEquals(span, hub.span) + assertEquals(tx, scopes.transaction) + assertEquals(span, scopes.span) } // endregion //region setSpanContext @Test fun `associates span context with throwable`() { - val (hub, mockClient) = getEnabledHub() - val transaction = hub.startTransaction("aTransaction", "op") + val (scopes, mockClient) = getEnabledHub() + val transaction = scopes.startTransaction("aTransaction", "op") val span = transaction.startChild("op") val exception = RuntimeException() - hub.setSpanContext(exception, span, "tx-name") - hub.captureEvent(SentryEvent(exception)) + scopes.setSpanContext(exception, span, "tx-name") + scopes.captureEvent(SentryEvent(exception)) verify(mockClient).captureEvent( check { @@ -1816,8 +1816,8 @@ class HubTest { @Test fun `returns null when no span context associated with throwable`() { - val hub = generateHub() as Hub - assertNull(hub.getSpanContext(RuntimeException())) + val scopes = generateHub() as Hub + assertNull(scopes.getSpanContext(RuntimeException())) } // endregion @@ -1826,9 +1826,9 @@ class HubTest { val nativeMarker = File(hashedFolder(), EnvelopeCache.NATIVE_CRASH_MARKER_FILE) nativeMarker.mkdirs() nativeMarker.createNewFile() - val hub = generateHub() as Hub + val scopes = generateHub() as Hub - assertTrue(hub.isCrashedLastRun!!) + assertTrue(scopes.isCrashedLastRun!!) assertTrue(nativeMarker.exists()) } @@ -1837,69 +1837,69 @@ class HubTest { val nativeMarker = File(hashedFolder(), EnvelopeCache.NATIVE_CRASH_MARKER_FILE) nativeMarker.mkdirs() nativeMarker.createNewFile() - val hub = generateHub { + val scopes = generateHub { it.isEnableAutoSessionTracking = false } - assertTrue(hub.isCrashedLastRun!!) + assertTrue(scopes.isCrashedLastRun!!) assertFalse(nativeMarker.exists()) } @Test fun `reportFullyDisplayed is ignored if TimeToFullDisplayTracing is disabled`() { var called = false - val hub = generateHub { + val scopes = generateHub { it.fullyDisplayedReporter.registerFullyDrawnListener { called = !called } } - hub.reportFullyDisplayed() + scopes.reportFullyDisplayed() assertFalse(called) } @Test fun `reportFullyDisplayed calls FullyDisplayedReporter if TimeToFullDisplayTracing is enabled`() { var called = false - val hub = generateHub { + val scopes = generateHub { it.isEnableTimeToFullDisplayTracing = true it.fullyDisplayedReporter.registerFullyDrawnListener { called = !called } } - hub.reportFullyDisplayed() + scopes.reportFullyDisplayed() assertTrue(called) } @Test fun `reportFullyDisplayed calls FullyDisplayedReporter only once`() { var called = false - val hub = generateHub { + val scopes = generateHub { it.isEnableTimeToFullDisplayTracing = true it.fullyDisplayedReporter.registerFullyDrawnListener { called = !called } } - hub.reportFullyDisplayed() + scopes.reportFullyDisplayed() assertTrue(called) - hub.reportFullyDisplayed() + scopes.reportFullyDisplayed() assertTrue(called) } @Test fun `reportFullDisplayed calls reportFullyDisplayed`() { - val hub = spy(generateHub()) - hub.reportFullDisplayed() - verify(hub).reportFullyDisplayed() + val scopes = spy(generateHub()) + scopes.reportFullDisplayed() + verify(scopes).reportFullyDisplayed() } @Test fun `continueTrace creates propagation context from headers and returns transaction context if performance enabled`() { - val hub = generateHub() + val scopes = generateHub() val traceId = SentryId() val parentSpanId = SpanId() - val transactionContext = hub.continueTrace("$traceId-$parentSpanId-1", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) + val transactionContext = scopes.continueTrace("$traceId-$parentSpanId-1", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) - hub.configureScope { scope -> + scopes.configureScope { scope -> assertEquals(traceId, scope.propagationContext.traceId) assertEquals(parentSpanId, scope.propagationContext.parentSpanId) } @@ -1910,16 +1910,16 @@ class HubTest { @Test fun `continueTrace creates new propagation context if header invalid and returns transaction context if performance enabled`() { - val hub = generateHub() + val scopes = generateHub() val traceId = SentryId() var propagationContextHolder = AtomicReference() - hub.configureScope { propagationContextHolder.set(it.propagationContext) } + scopes.configureScope { propagationContextHolder.set(it.propagationContext) } val propagationContextAtStart = propagationContextHolder.get()!! - val transactionContext = hub.continueTrace("invalid", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) + val transactionContext = scopes.continueTrace("invalid", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) - hub.configureScope { scope -> + scopes.configureScope { scope -> assertNotEquals(propagationContextAtStart.traceId, scope.propagationContext.traceId) assertNotEquals(propagationContextAtStart.parentSpanId, scope.propagationContext.parentSpanId) assertNotEquals(propagationContextAtStart.spanId, scope.propagationContext.spanId) @@ -1932,12 +1932,12 @@ class HubTest { @Test fun `continueTrace creates propagation context from headers and returns null if performance disabled`() { - val hub = generateHub { it.enableTracing = false } + val scopes = generateHub { it.enableTracing = false } val traceId = SentryId() val parentSpanId = SpanId() - val transactionContext = hub.continueTrace("$traceId-$parentSpanId-1", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) + val transactionContext = scopes.continueTrace("$traceId-$parentSpanId-1", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) - hub.configureScope { scope -> + scopes.configureScope { scope -> assertEquals(traceId, scope.propagationContext.traceId) assertEquals(parentSpanId, scope.propagationContext.parentSpanId) } @@ -1947,16 +1947,16 @@ class HubTest { @Test fun `continueTrace creates new propagation context if header invalid and returns null if performance disabled`() { - val hub = generateHub { it.enableTracing = false } + val scopes = generateHub { it.enableTracing = false } val traceId = SentryId() var propagationContextHolder = AtomicReference() - hub.configureScope { propagationContextHolder.set(it.propagationContext) } + scopes.configureScope { propagationContextHolder.set(it.propagationContext) } val propagationContextAtStart = propagationContextHolder.get()!! - val transactionContext = hub.continueTrace("invalid", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) + val transactionContext = scopes.continueTrace("invalid", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) - hub.configureScope { scope -> + scopes.configureScope { scope -> assertNotEquals(propagationContextAtStart.traceId, scope.propagationContext.traceId) assertNotEquals(propagationContextAtStart.parentSpanId, scope.propagationContext.parentSpanId) assertNotEquals(propagationContextAtStart.spanId, scope.propagationContext.spanId) @@ -1966,32 +1966,32 @@ class HubTest { } @Test - fun `hub provides no tags for metrics, if metric option is disabled`() { - val hub = generateHub { + fun `scopes provides no tags for metrics, if metric option is disabled`() { + val scopes = generateHub { it.isEnableMetrics = false it.isEnableDefaultTagsForMetrics = true } as Hub assertTrue( - hub.defaultTagsForMetrics.isEmpty() + scopes.defaultTagsForMetrics.isEmpty() ) } @Test - fun `hub provides no tags for metrics, if default tags option is disabled`() { - val hub = generateHub { + fun `scopes provides no tags for metrics, if default tags option is disabled`() { + val scopes = generateHub { it.isEnableMetrics = true it.isEnableDefaultTagsForMetrics = false } as Hub assertTrue( - hub.defaultTagsForMetrics.isEmpty() + scopes.defaultTagsForMetrics.isEmpty() ) } @Test - fun `hub provides minimum default tags for metrics, if nothing is set up`() { - val hub = generateHub { + fun `scopes provides minimum default tags for metrics, if nothing is set up`() { + val scopes = generateHub { it.isEnableMetrics = true it.isEnableDefaultTagsForMetrics = true } as Hub @@ -2000,19 +2000,19 @@ class HubTest { mapOf( "environment" to "production" ), - hub.defaultTagsForMetrics + scopes.defaultTagsForMetrics ) } @Test - fun `hub provides default tags for metrics, based on options and running transaction`() { - val hub = generateHub { + fun `scopes provides default tags for metrics, based on options and running transaction`() { + val scopes = generateHub { it.isEnableMetrics = true it.isEnableDefaultTagsForMetrics = true it.environment = "test" it.release = "1.0" } as Hub - hub.startTransaction( + scopes.startTransaction( "name", "op", TransactionOptions().apply { isBindToScope = true } @@ -2024,72 +2024,72 @@ class HubTest { "release" to "1.0", "transaction" to "name" ), - hub.defaultTagsForMetrics + scopes.defaultTagsForMetrics ) } @Test - fun `hub provides no local metric aggregator if metrics feature is disabled`() { - val hub = generateHub { + fun `scopes provides no local metric aggregator if metrics feature is disabled`() { + val scopes = generateHub { it.isEnableMetrics = false it.isEnableSpanLocalMetricAggregation = true } as Hub - hub.startTransaction( + scopes.startTransaction( "name", "op", TransactionOptions().apply { isBindToScope = true } ) - assertNull(hub.localMetricsAggregator) + assertNull(scopes.localMetricsAggregator) } @Test - fun `hub provides no local metric aggregator if local aggregation feature is disabled`() { - val hub = generateHub { + fun `scopes provides no local metric aggregator if local aggregation feature is disabled`() { + val scopes = generateHub { it.isEnableMetrics = true it.isEnableSpanLocalMetricAggregation = false } as Hub - hub.startTransaction( + scopes.startTransaction( "name", "op", TransactionOptions().apply { isBindToScope = true } ) - assertNull(hub.localMetricsAggregator) + assertNull(scopes.localMetricsAggregator) } @Test - fun `hub provides local metric aggregator if feature is enabled`() { - val hub = generateHub { + fun `scopes provides local metric aggregator if feature is enabled`() { + val scopes = generateHub { it.isEnableMetrics = true it.isEnableSpanLocalMetricAggregation = true } as Hub - hub.startTransaction( + scopes.startTransaction( "name", "op", TransactionOptions().apply { isBindToScope = true } ) - assertNotNull(hub.localMetricsAggregator) + assertNotNull(scopes.localMetricsAggregator) } @Test - fun `hub startSpanForMetric starts a child span`() { - val hub = generateHub { + fun `scopes startSpanForMetric starts a child span`() { + val scopes = generateHub { it.isEnableMetrics = true it.isEnableSpanLocalMetricAggregation = true it.sampleRate = 1.0 } as Hub - val txn = hub.startTransaction( + val txn = scopes.startTransaction( "name.txn", "op.txn", TransactionOptions().apply { isBindToScope = true } ) - val span = hub.startSpanForMetric("op", "key")!! + val span = scopes.startSpanForMetric("op", "key")!! assertEquals("op", span.spanContext.op) assertEquals("key", span.spanContext.description) @@ -2098,7 +2098,7 @@ class HubTest { private val dsnTest = "https://key@sentry.io/proj" - private fun generateHub(optionsConfiguration: Sentry.OptionsConfiguration? = null): IHub { + private fun generateHub(optionsConfiguration: Sentry.OptionsConfiguration? = null): IScopes { val options = SentryOptions().apply { dsn = dsnTest cacheDirPath = file.absolutePath diff --git a/sentry/src/test/java/io/sentry/JsonSerializerTest.kt b/sentry/src/test/java/io/sentry/JsonSerializerTest.kt index 8dc4f804bc..bd3a3c2cff 100644 --- a/sentry/src/test/java/io/sentry/JsonSerializerTest.kt +++ b/sentry/src/test/java/io/sentry/JsonSerializerTest.kt @@ -43,7 +43,7 @@ class JsonSerializerTest { private class Fixture { val logger: ILogger = mock() val serializer: ISerializer - val hub = mock() + val scopes = mock() val traceFile = Files.createTempFile("test", "here").toFile() val options = SentryOptions() @@ -51,7 +51,7 @@ class JsonSerializerTest { options.dsn = "https://key@sentry.io/proj" options.setLogger(logger) options.isDebug = true - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) serializer = JsonSerializer(options) options.setSerializer(serializer) options.setEnvelopeReader(EnvelopeReader(serializer)) @@ -830,7 +830,7 @@ class JsonSerializerTest { trace.status = SpanStatus.OK trace.setTag("myTag", "myValue") trace.sampled = true - val tracer = SentryTracer(trace, fixture.hub) + val tracer = SentryTracer(trace, fixture.scopes) tracer.setData("dataKey", "dataValue") val span = tracer.startChild("child") span.finish(SpanStatus.OK) @@ -1300,7 +1300,7 @@ class JsonSerializerTest { status = SpanStatus.OK setTag("myTag", "myValue") } - val tracer = SentryTracer(trace, fixture.hub) + val tracer = SentryTracer(trace, fixture.scopes) val span = tracer.startChild("child") span.setMeasurement("test_measurement", 1, MeasurementUnit.Custom("test")) span.finish(SpanStatus.OK) diff --git a/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt b/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt index ec932ebc86..682626f08c 100644 --- a/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt +++ b/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt @@ -33,7 +33,7 @@ class MainEventProcessorTest { dist = "dist" sdkVersion = SdkVersion("test", "1.2.3") } - val hub = mock() + val scopes = mock() val getLocalhost = mock() lateinit var sentryTracer: SentryTracer private val hostnameCacheMock = Mockito.mockStatic(HostnameCache::class.java) @@ -72,8 +72,8 @@ class MainEventProcessorTest { } host } - whenever(hub.options).thenReturn(sentryOptions) - sentryTracer = SentryTracer(TransactionContext("", ""), hub) + whenever(scopes.options).thenReturn(sentryOptions) + sentryTracer = SentryTracer(TransactionContext("", ""), scopes) val hostnameCache = HostnameCache(hostnameCacheDuration) { getLocalhost } hostnameCacheMock.`when` { HostnameCache.getInstance() }.thenReturn(hostnameCache) diff --git a/sentry/src/test/java/io/sentry/OutboxSenderTest.kt b/sentry/src/test/java/io/sentry/OutboxSenderTest.kt index ab50e054d0..9274ed4400 100644 --- a/sentry/src/test/java/io/sentry/OutboxSenderTest.kt +++ b/sentry/src/test/java/io/sentry/OutboxSenderTest.kt @@ -30,7 +30,7 @@ class OutboxSenderTest { private class Fixture { val options = mock() - val hub = mock() + val scopes = mock() var envelopeReader = mock() val serializer = mock() val logger = mock() @@ -39,11 +39,11 @@ class OutboxSenderTest { whenever(options.dsn).thenReturn("https://key@sentry.io/proj") whenever(options.dateProvider).thenReturn(SentryNanotimeDateProvider()) whenever(options.mainThreadChecker).thenReturn(NoOpMainThreadChecker.getInstance()) - whenever(hub.options).thenReturn(this.options) + whenever(scopes.options).thenReturn(this.options) } fun getSut(): OutboxSender { - return OutboxSender(hub, envelopeReader, serializer, logger, 15000, 30) + return OutboxSender(scopes, envelopeReader, serializer, logger, 15000, 30) } } @@ -83,7 +83,7 @@ class OutboxSenderTest { val hints = HintUtils.createWithTypeCheckHint(mock()) sut.processEnvelopeFile(path, hints) - verify(fixture.hub).captureEvent(eq(expected), any()) + verify(fixture.scopes).captureEvent(eq(expected), any()) assertFalse(File(path).exists()) // Additionally make sure we have no errors logged verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any()) @@ -94,7 +94,7 @@ class OutboxSenderTest { fun `when parser is EnvelopeReader and serializer return SentryTransaction, transaction captured, transactions sampled, file is deleted`() { fixture.envelopeReader = EnvelopeReader(JsonSerializer(fixture.options)) whenever(fixture.options.maxSpans).thenReturn(1000) - whenever(fixture.hub.options).thenReturn(fixture.options) + whenever(fixture.scopes.options).thenReturn(fixture.options) whenever(fixture.options.transactionProfiler).thenReturn(NoOpTransactionProfiler.getInstance()) val transactionContext = TransactionContext("fixture-name", "http") @@ -102,7 +102,7 @@ class OutboxSenderTest { transactionContext.status = SpanStatus.OK transactionContext.setTag("fixture-tag", "fixture-value") - val sentryTracer = SentryTracer(transactionContext, fixture.hub) + val sentryTracer = SentryTracer(transactionContext, fixture.scopes) val span = sentryTracer.startChild("child") span.finish(SpanStatus.OK) sentryTracer.finish() @@ -120,7 +120,7 @@ class OutboxSenderTest { val hints = HintUtils.createWithTypeCheckHint(mock()) sut.processEnvelopeFile(path, hints) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(expected, it) assertTrue(it.isSampled) @@ -139,7 +139,7 @@ class OutboxSenderTest { fun `restores sampleRate`() { fixture.envelopeReader = EnvelopeReader(JsonSerializer(fixture.options)) whenever(fixture.options.maxSpans).thenReturn(1000) - whenever(fixture.hub.options).thenReturn(fixture.options) + whenever(fixture.scopes.options).thenReturn(fixture.options) whenever(fixture.options.transactionProfiler).thenReturn(NoOpTransactionProfiler.getInstance()) val transactionContext = TransactionContext("fixture-name", "http") @@ -148,7 +148,7 @@ class OutboxSenderTest { transactionContext.setTag("fixture-tag", "fixture-value") transactionContext.samplingDecision = TracesSamplingDecision(true, 0.00000021) - val sentryTracer = SentryTracer(transactionContext, fixture.hub) + val sentryTracer = SentryTracer(transactionContext, fixture.scopes) val span = sentryTracer.startChild("child") span.finish(SpanStatus.OK) sentryTracer.finish() @@ -166,7 +166,7 @@ class OutboxSenderTest { val hints = HintUtils.createWithTypeCheckHint(mock()) sut.processEnvelopeFile(path, hints) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(expected, it) assertTrue(it.isSampled) @@ -207,7 +207,7 @@ class OutboxSenderTest { val hints = HintUtils.createWithTypeCheckHint(mock()) sut.processEnvelopeFile(path, hints) - verify(fixture.hub).captureEvent(any(), any()) + verify(fixture.scopes).captureEvent(any(), any()) assertFalse(File(path).exists()) // Additionally make sure we have no errors logged verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any()) @@ -225,7 +225,7 @@ class OutboxSenderTest { val hints = HintUtils.createWithTypeCheckHint(mock()) sut.processEnvelopeFile(path, hints) - verify(fixture.hub).captureEnvelope(any(), any()) + verify(fixture.scopes).captureEnvelope(any(), any()) assertFalse(File(path).exists()) // Additionally make sure we have no errors logged verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any()) @@ -245,7 +245,7 @@ class OutboxSenderTest { // Additionally make sure we have no errors logged verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), any()) - verify(fixture.hub, never()).captureEvent(any()) + verify(fixture.scopes, never()).captureEvent(any()) assertFalse(File(path).exists()) } @@ -263,7 +263,7 @@ class OutboxSenderTest { // Additionally make sure we have no errors logged verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), any()) - verify(fixture.hub, never()).captureEvent(any()) + verify(fixture.scopes, never()).captureEvent(any()) assertFalse(File(path).exists()) } diff --git a/sentry/src/test/java/io/sentry/PreviousSessionFinalizerTest.kt b/sentry/src/test/java/io/sentry/PreviousSessionFinalizerTest.kt index 239e90905e..87aa5e6715 100644 --- a/sentry/src/test/java/io/sentry/PreviousSessionFinalizerTest.kt +++ b/sentry/src/test/java/io/sentry/PreviousSessionFinalizerTest.kt @@ -21,7 +21,7 @@ class PreviousSessionFinalizerTest { class Fixture { val options = SentryOptions() - val hub = mock() + val scopes = mock() val logger = mock() lateinit var sessionFile: File @@ -61,7 +61,7 @@ class PreviousSessionFinalizerTest { nativeCrashMarker.writeText(nativeCrashTimestamp.toString()) } } - return PreviousSessionFinalizer(options, hub) + return PreviousSessionFinalizer(options, scopes) } fun sessionFromEnvelope(envelope: SentryEnvelope): Session { @@ -80,7 +80,7 @@ class PreviousSessionFinalizerTest { val finalizer = fixture.getSut(null) finalizer.run() - verify(fixture.hub, never()).captureEnvelope(any()) + verify(fixture.scopes, never()).captureEnvelope(any()) } @Test @@ -88,7 +88,7 @@ class PreviousSessionFinalizerTest { val finalizer = fixture.getSut(tmpDir, sessionFileExists = false) finalizer.run() - verify(fixture.hub, never()).captureEnvelope(any()) + verify(fixture.scopes, never()).captureEnvelope(any()) } @Test @@ -96,7 +96,7 @@ class PreviousSessionFinalizerTest { val finalizer = fixture.getSut(tmpDir, sessionFileExists = true, session = null) finalizer.run() - verify(fixture.hub, never()).captureEnvelope(any()) + verify(fixture.scopes, never()).captureEnvelope(any()) } @Test @@ -107,7 +107,7 @@ class PreviousSessionFinalizerTest { ) finalizer.run() - verify(fixture.hub).captureEnvelope( + verify(fixture.scopes).captureEnvelope( argThat { val session = fixture.sessionFromEnvelope(this) session.release == "io.sentry.sample@1.0" && @@ -133,7 +133,7 @@ class PreviousSessionFinalizerTest { ) finalizer.run() - verify(fixture.hub).captureEnvelope( + verify(fixture.scopes).captureEnvelope( argThat { val session = fixture.sessionFromEnvelope(this) session.release == "io.sentry.sample@1.0" && @@ -156,7 +156,7 @@ class PreviousSessionFinalizerTest { ) finalizer.run() - verify(fixture.hub).captureEnvelope( + verify(fixture.scopes).captureEnvelope( argThat { val session = fixture.sessionFromEnvelope(this) session.release == "io.sentry.sample@1.0" && @@ -170,7 +170,7 @@ class PreviousSessionFinalizerTest { val finalizer = fixture.getSut(tmpDir, sessionFileExists = true) finalizer.run() - verify(fixture.hub, never()).captureEnvelope(any()) + verify(fixture.scopes, never()).captureEnvelope(any()) assertFalse(fixture.sessionFile.exists()) } @@ -189,7 +189,7 @@ class PreviousSessionFinalizerTest { argThat { startsWith("Timed out waiting to flush previous session to its own file in session finalizer.") }, any() ) - verify(fixture.hub, never()).captureEnvelope(any()) + verify(fixture.scopes, never()).captureEnvelope(any()) } @Test @@ -202,6 +202,6 @@ class PreviousSessionFinalizerTest { argThat { startsWith("Timed out waiting to flush previous session to its own file in session finalizer.") }, any() ) - verify(fixture.hub, never()).captureEnvelope(any()) + verify(fixture.scopes, never()).captureEnvelope(any()) } } diff --git a/sentry/src/test/java/io/sentry/ScopeTest.kt b/sentry/src/test/java/io/sentry/ScopeTest.kt index 906c897c62..86794d7b19 100644 --- a/sentry/src/test/java/io/sentry/ScopeTest.kt +++ b/sentry/src/test/java/io/sentry/ScopeTest.kt @@ -114,7 +114,7 @@ class ScopeTest { scope.setExtra("extra", "extra") val transaction = - SentryTracer(TransactionContext("transaction-name", "op"), NoOpHub.getInstance()) + SentryTracer(TransactionContext("transaction-name", "op"), NoOpScopes.getInstance()) scope.transaction = transaction val attachment = Attachment("path/log.txt") @@ -192,7 +192,7 @@ class ScopeTest { scope.setTransaction( SentryTracer( TransactionContext("newTransaction", "op"), - NoOpHub.getInstance() + NoOpScopes.getInstance() ) ) @@ -265,7 +265,7 @@ class ScopeTest { fun `clear scope resets scope to default state`() { val scope = Scope(SentryOptions()) scope.level = SentryLevel.WARNING - scope.setTransaction(SentryTracer(TransactionContext("", "op"), NoOpHub.getInstance())) + scope.setTransaction(SentryTracer(TransactionContext("", "op"), NoOpScopes.getInstance())) scope.user = User() scope.request = Request() scope.fingerprint = mutableListOf("finger") @@ -822,7 +822,7 @@ class ScopeTest { @Test fun `Scope getTransaction returns the transaction if there is no active span`() { val scope = Scope(SentryOptions()) - val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) + val transaction = SentryTracer(TransactionContext("name", "op"), NoOpScopes.getInstance()) scope.transaction = transaction assertEquals(transaction, scope.span) } @@ -830,7 +830,7 @@ class ScopeTest { @Test fun `Scope getTransaction returns the current span if there is an unfinished span`() { val scope = Scope(SentryOptions()) - val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) + val transaction = SentryTracer(TransactionContext("name", "op"), NoOpScopes.getInstance()) scope.transaction = transaction val span = transaction.startChild("op") assertEquals(span, scope.span) @@ -839,7 +839,7 @@ class ScopeTest { @Test fun `Scope getTransaction returns the current span if there is a finished span`() { val scope = Scope(SentryOptions()) - val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) + val transaction = SentryTracer(TransactionContext("name", "op"), NoOpScopes.getInstance()) scope.transaction = transaction val span = transaction.startChild("op") span.finish() @@ -849,7 +849,7 @@ class ScopeTest { @Test fun `Scope getTransaction returns the latest span if there is a list of active span`() { val scope = Scope(SentryOptions()) - val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) + val transaction = SentryTracer(TransactionContext("name", "op"), NoOpScopes.getInstance()) scope.transaction = transaction val span = transaction.startChild("op") val innerSpan = span.startChild("op") @@ -859,7 +859,7 @@ class ScopeTest { @Test fun `Scope setTransaction sets transaction name`() { val scope = Scope(SentryOptions()) - val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) + val transaction = SentryTracer(TransactionContext("name", "op"), NoOpScopes.getInstance()) scope.transaction = transaction scope.setTransaction("new-name") assertNotNull(scope.transaction) { @@ -871,7 +871,7 @@ class ScopeTest { @Test fun `Scope setTransaction with null does not clear transaction`() { val scope = Scope(SentryOptions()) - val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) + val transaction = SentryTracer(TransactionContext("name", "op"), NoOpScopes.getInstance()) scope.transaction = transaction scope.callMethod("setTransaction", String::class.java, null) assertNotNull(scope.transaction) @@ -936,7 +936,7 @@ class ScopeTest { fun `when transaction is started, sets transaction name on the transaction object`() { val scope = Scope(SentryOptions()) val sentryTransaction = - SentryTracer(TransactionContext("transaction-name", "op"), NoOpHub.getInstance()) + SentryTracer(TransactionContext("transaction-name", "op"), NoOpScopes.getInstance()) scope.transaction = sentryTransaction assertEquals("transaction-name", scope.transactionName) scope.setTransaction("new-name") @@ -950,7 +950,7 @@ class ScopeTest { val scope = Scope(SentryOptions()) scope.setTransaction("transaction-a") val sentryTransaction = - SentryTracer(TransactionContext("transaction-name", "op"), NoOpHub.getInstance()) + SentryTracer(TransactionContext("transaction-name", "op"), NoOpScopes.getInstance()) scope.setTransaction(sentryTransaction) assertEquals("transaction-name", scope.transactionName) scope.clearTransaction() @@ -961,7 +961,7 @@ class ScopeTest { fun `withTransaction returns the current Transaction bound to the Scope`() { val scope = Scope(SentryOptions()) val sentryTransaction = - SentryTracer(TransactionContext("transaction-name", "op"), NoOpHub.getInstance()) + SentryTracer(TransactionContext("transaction-name", "op"), NoOpScopes.getInstance()) scope.setTransaction(sentryTransaction) scope.withTransaction { diff --git a/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt b/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt new file mode 100644 index 0000000000..85a0b6ef75 --- /dev/null +++ b/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt @@ -0,0 +1,265 @@ +package io.sentry + +import io.sentry.protocol.SentryTransaction +import io.sentry.protocol.User +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.reset +import org.mockito.kotlin.verify +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test + +class ScopesAdapterTest { + + val scopes: IScopes = mock() + + @BeforeTest + fun `set up`() { + Sentry.setCurrentScopes(scopes) + } + + @AfterTest + fun shutdown() { + Sentry.close() + } + + @Test fun `isEnabled calls Hub`() { + ScopesAdapter.getInstance().isEnabled + verify(scopes).isEnabled + } + + @Test fun `captureEvent calls Hub`() { + val event = mock() + val hint = mock() + val scopeCallback = mock() + ScopesAdapter.getInstance().captureEvent(event, hint) + verify(scopes).captureEvent(eq(event), eq(hint)) + + ScopesAdapter.getInstance().captureEvent(event, hint, scopeCallback) + verify(scopes).captureEvent(eq(event), eq(hint), eq(scopeCallback)) + } + + @Test fun `captureMessage calls Hub`() { + val scopeCallback = mock() + val sentryLevel = mock() + ScopesAdapter.getInstance().captureMessage("message", sentryLevel) + verify(scopes).captureMessage(eq("message"), eq(sentryLevel)) + + ScopesAdapter.getInstance().captureMessage("message", sentryLevel, scopeCallback) + verify(scopes).captureMessage(eq("message"), eq(sentryLevel), eq(scopeCallback)) + } + + @Test fun `captureEnvelope calls Hub`() { + val envelope = mock() + val hint = mock() + ScopesAdapter.getInstance().captureEnvelope(envelope, hint) + verify(scopes).captureEnvelope(eq(envelope), eq(hint)) + } + + @Test fun `captureException calls Hub`() { + val throwable = mock() + val hint = mock() + val scopeCallback = mock() + ScopesAdapter.getInstance().captureException(throwable, hint) + verify(scopes).captureException(eq(throwable), eq(hint)) + + ScopesAdapter.getInstance().captureException(throwable, hint, scopeCallback) + verify(scopes).captureException(eq(throwable), eq(hint), eq(scopeCallback)) + } + + @Test fun `captureUserFeedback calls Hub`() { + val userFeedback = mock() + ScopesAdapter.getInstance().captureUserFeedback(userFeedback) + verify(scopes).captureUserFeedback(eq(userFeedback)) + } + + @Test fun `captureCheckIn calls Hub`() { + val checkIn = mock() + ScopesAdapter.getInstance().captureCheckIn(checkIn) + verify(scopes).captureCheckIn(eq(checkIn)) + } + + @Test fun `startSession calls Hub`() { + ScopesAdapter.getInstance().startSession() + verify(scopes).startSession() + } + + @Test fun `endSession calls Hub`() { + ScopesAdapter.getInstance().endSession() + verify(scopes).endSession() + } + + @Test fun `close calls Hub`() { + ScopesAdapter.getInstance().close() + verify(scopes).close(false) + } + + @Test fun `close with isRestarting true calls Hub with isRestarting false`() { + ScopesAdapter.getInstance().close(true) + verify(scopes).close(false) + } + + @Test fun `close with isRestarting false calls Hub with isRestarting false`() { + ScopesAdapter.getInstance().close(false) + verify(scopes).close(false) + } + + @Test fun `addBreadcrumb calls Hub`() { + val breadcrumb = mock() + val hint = mock() + ScopesAdapter.getInstance().addBreadcrumb(breadcrumb, hint) + verify(scopes).addBreadcrumb(eq(breadcrumb), eq(hint)) + } + + @Test fun `setLevel calls Hub`() { + val sentryLevel = mock() + ScopesAdapter.getInstance().setLevel(sentryLevel) + verify(scopes).setLevel(eq(sentryLevel)) + } + + @Test fun `setTransaction calls Hub`() { + ScopesAdapter.getInstance().setTransaction("transaction") + verify(scopes).setTransaction(eq("transaction")) + } + + @Test fun `setUser calls Hub`() { + val user = mock() + ScopesAdapter.getInstance().setUser(user) + verify(scopes).setUser(eq(user)) + } + + @Test fun `setFingerprint calls Hub`() { + val fingerprint = ArrayList() + ScopesAdapter.getInstance().setFingerprint(fingerprint) + verify(scopes).setFingerprint(eq(fingerprint)) + } + + @Test fun `clearBreadcrumbs calls Hub`() { + ScopesAdapter.getInstance().clearBreadcrumbs() + verify(scopes).clearBreadcrumbs() + } + + @Test fun `setTag calls Hub`() { + ScopesAdapter.getInstance().setTag("key", "value") + verify(scopes).setTag(eq("key"), eq("value")) + } + + @Test fun `removeTag calls Hub`() { + ScopesAdapter.getInstance().removeTag("key") + verify(scopes).removeTag(eq("key")) + } + + @Test fun `setExtra calls Hub`() { + ScopesAdapter.getInstance().setExtra("key", "value") + verify(scopes).setExtra(eq("key"), eq("value")) + } + + @Test fun `removeExtra calls Hub`() { + ScopesAdapter.getInstance().removeExtra("key") + verify(scopes).removeExtra(eq("key")) + } + + @Test fun `getLastEventId calls Hub`() { + ScopesAdapter.getInstance().lastEventId + verify(scopes).lastEventId + } + + @Test fun `pushScope calls Hub`() { + ScopesAdapter.getInstance().pushScope() + verify(scopes).pushScope() + } + + @Test fun `popScope calls Hub`() { + ScopesAdapter.getInstance().popScope() + verify(scopes).popScope() + } + + @Test fun `withScope calls Hub`() { + val scopeCallback = mock() + ScopesAdapter.getInstance().withScope(scopeCallback) + verify(scopes).withScope(eq(scopeCallback)) + } + + @Test fun `configureScope calls Hub`() { + val scopeCallback = mock() + ScopesAdapter.getInstance().configureScope(scopeCallback) + verify(scopes).configureScope(eq(scopeCallback)) + } + + @Test fun `bindClient calls Hub`() { + val client = mock() + ScopesAdapter.getInstance().bindClient(client) + verify(scopes).bindClient(eq(client)) + } + + @Test fun `flush calls Hub`() { + ScopesAdapter.getInstance().flush(1) + verify(scopes).flush(eq(1)) + } + + @Test fun `clone calls Hub`() { + ScopesAdapter.getInstance().clone() + verify(scopes).clone() + } + + @Test fun `captureTransaction calls Hub`() { + val transaction = mock() + val traceContext = mock() + val hint = mock() + val profilingTraceData = mock() + ScopesAdapter.getInstance().captureTransaction(transaction, traceContext, hint, profilingTraceData) + verify(scopes).captureTransaction(eq(transaction), eq(traceContext), eq(hint), eq(profilingTraceData)) + } + + @Test fun `startTransaction calls Hub`() { + val transactionContext = mock() + val samplingContext = mock() + val transactionOptions = mock() + ScopesAdapter.getInstance().startTransaction(transactionContext) + verify(scopes).startTransaction(eq(transactionContext), any()) + + reset(scopes) + + ScopesAdapter.getInstance().startTransaction(transactionContext, transactionOptions) + verify(scopes).startTransaction(eq(transactionContext), eq(transactionOptions)) + } + + @Test fun `traceHeaders calls Hub`() { + ScopesAdapter.getInstance().traceHeaders() + verify(scopes).traceHeaders() + } + + @Test fun `setSpanContext calls Hub`() { + val throwable = mock() + val span = mock() + ScopesAdapter.getInstance().setSpanContext(throwable, span, "transactionName") + verify(scopes).setSpanContext(eq(throwable), eq(span), eq("transactionName")) + } + + @Test fun `getSpan calls Hub`() { + ScopesAdapter.getInstance().span + verify(scopes).span + } + + @Test fun `getTransaction calls Hub`() { + ScopesAdapter.getInstance().transaction + verify(scopes).transaction + } + + @Test fun `getOptions calls Hub`() { + ScopesAdapter.getInstance().options + verify(scopes).options + } + + @Test fun `isCrashedLastRun calls Hub`() { + ScopesAdapter.getInstance().isCrashedLastRun + verify(scopes).isCrashedLastRun + } + + @Test fun `reportFullyDisplayed calls Hub`() { + ScopesAdapter.getInstance().reportFullyDisplayed() + verify(scopes).reportFullyDisplayed() + } +} diff --git a/sentry/src/test/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegrationTest.kt b/sentry/src/test/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegrationTest.kt index 78623f90a7..beed31a198 100644 --- a/sentry/src/test/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegrationTest.kt @@ -19,7 +19,7 @@ import kotlin.test.assertTrue class SendCachedEnvelopeFireAndForgetIntegrationTest { private class Fixture { - var hub: IHub = mock() + var scopes: IScopes = mock() var logger: ILogger = mock() var options = SentryOptions() val sender = mock() @@ -45,7 +45,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fun `when cacheDirPath returns null, register logs and exit`() { fixture.options.cacheDirPath = null val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.logger).log(eq(SentryLevel.ERROR), eq("No cache dir path is defined in options.")) verify(fixture.sender, never()).send() } @@ -73,7 +73,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { val sut = SendCachedEnvelopeFireAndForgetIntegration(CustomFactory()) fixture.options.cacheDirPath = "abc" fixture.options.executorService = ImmediateExecutorService() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.logger).log(eq(SentryLevel.ERROR), eq("SendFireAndForget factory is null.")) verify(fixture.sender, never()).send() } @@ -85,7 +85,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { mock() ) val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertNotNull(fixture.options.sdkVersion) assert(fixture.options.sdkVersion!!.integrationSet.contains("SendCachedEnvelopeFireAndForget")) } @@ -96,7 +96,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fixture.options.executorService.close(0) whenever(fixture.callback.create(any(), any())).thenReturn(mock()) val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.logger).log(eq(SentryLevel.ERROR), eq("Failed to call the executor. Cached events will not be sent. Did you call Sentry.close()?"), any()) } @@ -108,7 +108,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fixture.options.cacheDirPath = "cache" val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(connectionStatusProvider).addConnectionStatusObserver(any()) } @@ -122,9 +122,9 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fixture.options.cacheDirPath = "cache" val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.sender, never()).send() } @@ -139,7 +139,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fixture.options.cacheDirPath = "cache" val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.sender).send() } @@ -155,7 +155,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fixture.options.cacheDirPath = "cache" val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) // when there's no connection no factory create call should be done verify(fixture.sender, never()).send() @@ -183,9 +183,9 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { val rateLimiter = mock { whenever(mock.isActiveForCategory(any())).thenReturn(true) } - whenever(fixture.hub.rateLimiter).thenReturn(rateLimiter) + whenever(fixture.scopes.rateLimiter).thenReturn(rateLimiter) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) // no factory call should be done if there's rate limiting active verify(fixture.sender, never()).send() @@ -196,8 +196,8 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fixture.options.executorService = ImmediateExecutorService() fixture.options.cacheDirPath = "cache" val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) - verify(fixture.callback).create(eq(fixture.hub), eq(fixture.options)) + sut.register(fixture.scopes, fixture.options) + verify(fixture.callback).create(eq(fixture.scopes), eq(fixture.options)) } @Test @@ -205,7 +205,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fixture.options.executorService = mock() fixture.options.cacheDirPath = "cache" val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.callback, never()).create(any(), any()) } @@ -215,7 +215,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fixture.options.executorService = deferredExecutorService val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.sender, never()).send() sut.close() @@ -224,7 +224,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { } private class CustomFactory : SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForgetFactory { - override fun create(hub: IHub, options: SentryOptions): SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget? { + override fun create(scopes: IScopes, options: SentryOptions): SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget? { return null } } diff --git a/sentry/src/test/java/io/sentry/SentryClientTest.kt b/sentry/src/test/java/io/sentry/SentryClientTest.kt index de540bf90c..eac2b0be29 100644 --- a/sentry/src/test/java/io/sentry/SentryClientTest.kt +++ b/sentry/src/test/java/io/sentry/SentryClientTest.kt @@ -70,7 +70,7 @@ class SentryClientTest { var transport = mock() var factory = mock() val maxAttachmentSize: Long = (5 * 1024 * 1024).toLong() - val hub = mock() + val scopes = mock() val sentryTracer: SentryTracer var sentryOptions: SentryOptions = SentryOptions().apply { @@ -88,8 +88,8 @@ class SentryClientTest { init { whenever(factory.create(any(), any())).thenReturn(transport) - whenever(hub.options).thenReturn(sentryOptions) - sentryTracer = SentryTracer(TransactionContext("a-transaction", "op"), hub) + whenever(scopes.options).thenReturn(sentryOptions) + sentryTracer = SentryTracer(TransactionContext("a-transaction", "op"), scopes) } var attachment = Attachment("hello".toByteArray(), "hello.txt", "text/plain", true) @@ -1456,9 +1456,9 @@ class SentryClientTest { @Test fun `when captureTransaction with scope, transaction should use user data`() { - val hub: IHub = mock() - whenever(hub.options).thenReturn(SentryOptions()) - val transaction = SentryTransaction(SentryTracer(TransactionContext("tx", "op"), hub)) + val scopes: IScopes = mock() + whenever(scopes.options).thenReturn(SentryOptions()) + val transaction = SentryTransaction(SentryTracer(TransactionContext("tx", "op"), scopes)) val scope = createScope() val sut = fixture.getSut() @@ -1487,7 +1487,7 @@ class SentryClientTest { val event = SentryEvent() val sut = fixture.getSut() val scope = createScope() - val transaction = SentryTracer(TransactionContext("a-transaction", "op"), fixture.hub) + val transaction = SentryTracer(TransactionContext("a-transaction", "op"), fixture.scopes) scope.setTransaction(transaction) val span = transaction.startChild("op") sut.captureEvent(event, scope) @@ -1558,7 +1558,7 @@ class SentryClientTest { fixture.sentryOptions.release = "optionsRelease" fixture.sentryOptions.environment = "optionsEnvironment" val sut = fixture.getSut() - val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.hub) + val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.scopes) val transaction = SentryTransaction(sentryTracer) transaction.release = "transactionRelease" transaction.environment = "transactionEnvironment" @@ -1571,7 +1571,7 @@ class SentryClientTest { fun `when transaction does not have SDK version set, and the SDK version is set on options, options values are applied to transactions`() { fixture.sentryOptions.sdkVersion = SdkVersion("sdk.name", "version") val sut = fixture.getSut() - val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.hub) + val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.scopes) val transaction = SentryTransaction(sentryTracer) sut.captureTransaction(transaction, sentryTracer.traceContext()) assertEquals(fixture.sentryOptions.sdkVersion, transaction.sdk) @@ -1581,7 +1581,7 @@ class SentryClientTest { fun `when transaction has SDK version set, and the SDK version is set on options, options values are not applied to transactions`() { fixture.sentryOptions.sdkVersion = SdkVersion("sdk.name", "version") val sut = fixture.getSut() - val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.hub) + val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.scopes) val transaction = SentryTransaction(sentryTracer) val sdkVersion = SdkVersion("transaction.sdk.name", "version") transaction.sdk = sdkVersion @@ -1593,7 +1593,7 @@ class SentryClientTest { fun `when transaction does not have tags, and tags are set on options, options values are applied to transactions`() { fixture.sentryOptions.setTag("tag1", "value1") val sut = fixture.getSut() - val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.hub) + val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.scopes) val transaction = SentryTransaction(sentryTracer) sut.captureTransaction(transaction, sentryTracer.traceContext()) assertEquals(mapOf("tag1" to "value1"), transaction.tags) @@ -1604,7 +1604,7 @@ class SentryClientTest { fixture.sentryOptions.setTag("tag1", "value1") fixture.sentryOptions.setTag("tag2", "value2") val sut = fixture.getSut() - val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.hub) + val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.scopes) val transaction = SentryTransaction(sentryTracer) transaction.setTag("tag3", "value3") transaction.setTag("tag2", "transaction-tag") @@ -1618,7 +1618,7 @@ class SentryClientTest { @Test fun `captured transactions without a platform, have the default platform set`() { val sut = fixture.getSut() - val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.hub) + val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.scopes) val transaction = SentryTransaction(sentryTracer) sut.captureTransaction(transaction, sentryTracer.traceContext()) assertEquals("java", transaction.platform) @@ -1627,7 +1627,7 @@ class SentryClientTest { @Test fun `captured transactions with a platform, do not get the platform overwritten`() { val sut = fixture.getSut() - val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.hub) + val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.scopes) val transaction = SentryTransaction(sentryTracer) transaction.platform = "abc" sut.captureTransaction(transaction, sentryTracer.traceContext()) diff --git a/sentry/src/test/java/io/sentry/SentryTest.kt b/sentry/src/test/java/io/sentry/SentryTest.kt index 0f4966b44a..70728d2900 100644 --- a/sentry/src/test/java/io/sentry/SentryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTest.kt @@ -63,27 +63,27 @@ class SentryTest { } @Test - fun `init multiple times calls hub close with isRestarting true`() { - val hub = mock() + fun `init multiple times calls scopes close with isRestarting true`() { + val scopes = mock() Sentry.init { it.dsn = dsn } - Sentry.setCurrentHub(hub) + Sentry.setCurrentScopes(scopes) Sentry.init { it.dsn = dsn } - verify(hub).close(eq(true)) + verify(scopes).close(eq(true)) } @Test - fun `close calls hub close with isRestarting false`() { - val hub = mock() + fun `close calls scopes close with isRestarting false`() { + val scopes = mock() Sentry.init { it.dsn = dsn } - Sentry.setCurrentHub(hub) + Sentry.setCurrentScopes(scopes) Sentry.close() - verify(hub).close(eq(false)) + verify(scopes).close(eq(false)) } @Test @@ -213,7 +213,7 @@ class SentryTest { Sentry.init { it.isEnableExternalConfiguration = true } - assertTrue(HubAdapter.getInstance().isEnabled) + assertTrue(ScopesAdapter.getInstance().isEnabled) } finally { temporaryFolder.delete() } @@ -229,10 +229,10 @@ class SentryTest { Sentry.setTag("none", "shouldNotExist") var value: String? = null - Sentry.getCurrentHub().configureScope { + Sentry.getCurrentScopes().configureScope { value = it.tags[value] } - assertTrue(Sentry.getCurrentHub() is NoOpHub) + assertTrue(Sentry.getCurrentScopes().isNoOp) assertNull(value) } @@ -245,10 +245,10 @@ class SentryTest { Sentry.setTag("none", "shouldNotExist") var value: String? = null - Sentry.getCurrentHub().configureScope { + Sentry.getCurrentScopes().configureScope { value = it.tags[value] } - assertTrue(Sentry.getCurrentHub() is NoOpHub) + assertTrue(Sentry.getCurrentScopes().isNoOp) assertNull(value) } @@ -267,7 +267,7 @@ class SentryTest { Sentry.init { it.dsn = dsn } val client = mock() - Sentry.getCurrentHub().bindClient(client) + Sentry.getCurrentScopes().bindClient(client) val userFeedback = UserFeedback(SentryId.EMPTY_ID) Sentry.captureUserFeedback(userFeedback) @@ -369,11 +369,11 @@ class SentryTest { } @Test - fun `using sentry before calling init creates NoOpHub but after init Sentry uses a new clone`() { - // noop as not yet initialized, caches NoOpHub in ThreadLocal + fun `using sentry before calling init creates NoOpScopes but after init Sentry uses a new clone`() { + // noop as not yet initialized, caches NoOpScopes in ThreadLocal Sentry.captureMessage("noop caused") - assertTrue(Sentry.getCurrentHub() is NoOpHub) + assertTrue(Sentry.getCurrentScopes().isNoOp) // init Sentry in another thread val thread = Thread() { @@ -387,18 +387,18 @@ class SentryTest { Sentry.captureMessage("should work now") - val hub = Sentry.getCurrentHub() - assertNotNull(hub) - assertFalse(hub is NoOpHub) + val scopes = Sentry.getCurrentScopes() + assertNotNull(scopes) + assertFalse(scopes.isNoOp) } @Test - fun `main hub can be cloned and does not share scope with current hub`() { - // noop as not yet initialized, caches NoOpHub in ThreadLocal + fun `main scopes can be cloned and does not share scope with current scopes`() { + // noop as not yet initialized, caches NoOpScopes in ThreadLocal Sentry.addBreadcrumb("breadcrumbNoOp") Sentry.captureMessage("messageNoOp") - assertTrue(Sentry.getCurrentHub() is NoOpHub) + assertTrue(Sentry.getCurrentScopes().isNoOp) val capturedEvents = mutableListOf() @@ -418,14 +418,14 @@ class SentryTest { Sentry.addBreadcrumb("breadcrumbCurrent") - val hub = Sentry.getCurrentHub() - assertNotNull(hub) - assertFalse(hub is NoOpHub) + val scopes = Sentry.getCurrentScopes() + assertNotNull(scopes) + assertFalse(Sentry.getCurrentScopes().isNoOp) val newMainHubClone = Sentry.cloneMainHub() newMainHubClone.addBreadcrumb("breadcrumbMainClone") - hub.captureMessage("messageCurrent") + scopes.captureMessage("messageCurrent") newMainHubClone.captureMessage("messageMainClone") assertEquals(2, capturedEvents.size) @@ -444,12 +444,12 @@ class SentryTest { } @Test - fun `main hub is not cloned in global hub mode and shares scope with current hub`() { - // noop as not yet initialized, caches NoOpHub in ThreadLocal + fun `main scopes is not cloned in global scopes mode and shares scope with current scopes`() { + // noop as not yet initialized, caches NoOpScopes in ThreadLocal Sentry.addBreadcrumb("breadcrumbNoOp") Sentry.captureMessage("messageNoOp") - assertTrue(Sentry.getCurrentHub() is NoOpHub) + assertTrue(Sentry.getCurrentScopes().isNoOp) val capturedEvents = mutableListOf() @@ -469,14 +469,14 @@ class SentryTest { Sentry.addBreadcrumb("breadcrumbCurrent") - val hub = Sentry.getCurrentHub() - assertNotNull(hub) - assertFalse(hub is NoOpHub) + val scopes = Sentry.getCurrentScopes() + assertNotNull(scopes) + assertFalse(scopes.isNoOp) val newMainHubClone = Sentry.cloneMainHub() newMainHubClone.addBreadcrumb("breadcrumbMainClone") - hub.captureMessage("messageCurrent") + scopes.captureMessage("messageCurrent") newMainHubClone.captureMessage("messageMainClone") assertEquals(2, capturedEvents.size) @@ -669,25 +669,25 @@ class SentryTest { } @Test - fun `reportFullyDisplayed calls hub reportFullyDisplayed`() { - val hub = mock() + fun `reportFullyDisplayed calls scopes reportFullyDisplayed`() { + val scopes = mock() Sentry.init { it.dsn = dsn } - Sentry.setCurrentHub(hub) + Sentry.setCurrentScopes(scopes) Sentry.reportFullyDisplayed() - verify(hub).reportFullyDisplayed() + verify(scopes).reportFullyDisplayed() } @Test fun `reportFullDisplayed calls reportFullyDisplayed`() { - val hub = mock() + val scopes = mock() Sentry.init { it.dsn = dsn } - Sentry.setCurrentHub(hub) + Sentry.setCurrentScopes(scopes) Sentry.reportFullDisplayed() - verify(hub).reportFullyDisplayed() + verify(scopes).reportFullyDisplayed() } @Test @@ -806,11 +806,11 @@ class SentryTest { it.serializer.deserialize(previousSessionFile.bufferedReader(), Session::class.java)!!.environment ) - it.addIntegration { hub, _ -> + it.addIntegration { scopes, _ -> // this is just a hack to trigger the previousSessionFlush latch, so the finalizer // does not time out waiting. We have to do it as integration, because this is where - // the hub is already initialized - hub.startSession() + // the scopes is already initialized + scopes.startSession() } } @@ -861,7 +861,7 @@ class SentryTest { Sentry.init { it.dsn = dsn } val client = mock() - Sentry.getCurrentHub().bindClient(client) + Sentry.getCurrentScopes().bindClient(client) val checkIn = CheckIn("some_slug", CheckInStatus.OK) Sentry.captureCheckIn(checkIn) @@ -910,18 +910,18 @@ class SentryTest { } @Test - fun `getSpan calls hub getSpan`() { - val hub = mock() + fun `getSpan calls scopes getSpan`() { + val scopes = mock() Sentry.init({ it.dsn = dsn }, false) - Sentry.setCurrentHub(hub) + Sentry.setCurrentScopes(scopes) Sentry.getSpan() - verify(hub).span + verify(scopes).span } @Test - fun `getSpan calls returns root span if globalhub mode is enabled on Android`() { + fun `getSpan calls returns root span if globalscopes mode is enabled on Android`() { PlatformTestManipulator.pretendIsAndroid(true) Sentry.init({ it.dsn = dsn @@ -938,7 +938,7 @@ class SentryTest { } @Test - fun `getSpan calls returns child span if globalhub mode is enabled, but the platform is not Android`() { + fun `getSpan calls returns child span if globalscopes mode is enabled, but the platform is not Android`() { PlatformTestManipulator.pretendIsAndroid(false) Sentry.init({ it.dsn = dsn @@ -954,7 +954,7 @@ class SentryTest { } @Test - fun `getSpan calls returns child span if globalhub mode is disabled`() { + fun `getSpan calls returns child span if globalscopes mode is disabled`() { Sentry.init({ it.dsn = dsn it.enableTracing = true @@ -1140,15 +1140,15 @@ class SentryTest { } @Test - fun `metrics calls hub getMetrics`() { - val hub = mock() + fun `metrics calls scopes getMetrics`() { + val scopes = mock() Sentry.init({ it.dsn = dsn }, false) - Sentry.setCurrentHub(hub) + Sentry.setCurrentScopes(scopes) Sentry.metrics() - verify(hub).metrics() + verify(scopes).metrics() } private class InMemoryOptionsObserver : IOptionsObserver { diff --git a/sentry/src/test/java/io/sentry/SentryWrapperTest.kt b/sentry/src/test/java/io/sentry/SentryWrapperTest.kt index 7f6d449eac..2fb9b38566 100644 --- a/sentry/src/test/java/io/sentry/SentryWrapperTest.kt +++ b/sentry/src/test/java/io/sentry/SentryWrapperTest.kt @@ -35,20 +35,20 @@ class SentryWrapperTest { } } - val mainHub = Sentry.getCurrentHub() - val threadedHub = Sentry.getCurrentHub().clone() + val mainHub = Sentry.getCurrentScopes() + val threadedHub = Sentry.getCurrentScopes().clone() executor.submit { - Sentry.setCurrentHub(threadedHub) + Sentry.setCurrentScopes(threadedHub) }.get() - assertEquals(mainHub, Sentry.getCurrentHub()) + assertEquals(mainHub, Sentry.getCurrentScopes()) val callableFuture = CompletableFuture.supplyAsync( SentryWrapper.wrapSupplier { - assertNotEquals(mainHub, Sentry.getCurrentHub()) - assertNotEquals(threadedHub, Sentry.getCurrentHub()) + assertNotEquals(mainHub, Sentry.getCurrentScopes()) + assertNotEquals(threadedHub, Sentry.getCurrentScopes()) "Result 1" }, executor @@ -57,8 +57,8 @@ class SentryWrapperTest { callableFuture.join() executor.submit { - assertNotEquals(mainHub, Sentry.getCurrentHub()) - assertEquals(threadedHub, Sentry.getCurrentHub()) + assertNotEquals(mainHub, Sentry.getCurrentScopes()) + assertEquals(threadedHub, Sentry.getCurrentScopes()) }.get() } @@ -169,20 +169,20 @@ class SentryWrapperTest { it.dsn = dsn } - val mainHub = Sentry.getCurrentHub() - val threadedHub = Sentry.getCurrentHub().clone() + val mainHub = Sentry.getCurrentScopes() + val threadedHub = Sentry.getCurrentScopes().clone() executor.submit { - Sentry.setCurrentHub(threadedHub) + Sentry.setCurrentScopes(threadedHub) }.get() - assertEquals(mainHub, Sentry.getCurrentHub()) + assertEquals(mainHub, Sentry.getCurrentScopes()) val callableFuture = executor.submit( SentryWrapper.wrapCallable { - assertNotEquals(mainHub, Sentry.getCurrentHub()) - assertNotEquals(threadedHub, Sentry.getCurrentHub()) + assertNotEquals(mainHub, Sentry.getCurrentScopes()) + assertNotEquals(threadedHub, Sentry.getCurrentScopes()) "Result 1" } ) @@ -190,8 +190,8 @@ class SentryWrapperTest { callableFuture.get() executor.submit { - assertNotEquals(mainHub, Sentry.getCurrentHub()) - assertEquals(threadedHub, Sentry.getCurrentHub()) + assertNotEquals(mainHub, Sentry.getCurrentScopes()) + assertEquals(threadedHub, Sentry.getCurrentScopes()) }.get() } } diff --git a/sentry/src/test/java/io/sentry/ShutdownHookIntegrationTest.kt b/sentry/src/test/java/io/sentry/ShutdownHookIntegrationTest.kt index d6ce0ff043..428a34635f 100644 --- a/sentry/src/test/java/io/sentry/ShutdownHookIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/ShutdownHookIntegrationTest.kt @@ -16,7 +16,7 @@ class ShutdownHookIntegrationTest { private class Fixture { val runtime = mock() val options = SentryOptions() - val hub = mock() + val scopes = mock() fun getSut(): ShutdownHookIntegration { return ShutdownHookIntegration(runtime) @@ -29,7 +29,7 @@ class ShutdownHookIntegrationTest { fun `registration attaches shutdown hook to runtime`() { val integration = fixture.getSut() - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) verify(fixture.runtime).addShutdownHook(any()) } @@ -39,7 +39,7 @@ class ShutdownHookIntegrationTest { val integration = fixture.getSut() fixture.options.isEnableShutdownHook = false - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) verify(fixture.runtime, never()).addShutdownHook(any()) } @@ -48,7 +48,7 @@ class ShutdownHookIntegrationTest { fun `registration removes shutdown hook from runtime`() { val integration = fixture.getSut() - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) integration.close() verify(fixture.runtime).removeShutdownHook(any()) @@ -58,13 +58,13 @@ class ShutdownHookIntegrationTest { fun `hook calls flush`() { val integration = fixture.getSut() - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) assertNotNull(integration.hook) { it.start() it.join() } - verify(fixture.hub).flush(any()) + verify(fixture.scopes).flush(any()) } @Test @@ -72,13 +72,13 @@ class ShutdownHookIntegrationTest { val integration = fixture.getSut() fixture.options.flushTimeoutMillis = 10000 - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) assertNotNull(integration.hook) { it.start() it.join() } - verify(fixture.hub).flush(eq(10000)) + verify(fixture.scopes).flush(eq(10000)) } @Test @@ -86,7 +86,7 @@ class ShutdownHookIntegrationTest { val integration = fixture.getSut() whenever(fixture.runtime.removeShutdownHook(any())).thenThrow(java.lang.IllegalStateException("Shutdown in progress")) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) integration.close() verify(fixture.runtime).removeShutdownHook(any()) @@ -97,7 +97,7 @@ class ShutdownHookIntegrationTest { val integration = fixture.getSut() whenever(fixture.runtime.removeShutdownHook(any())).thenThrow(java.lang.IllegalStateException()) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) assertFails { integration.close() @@ -110,7 +110,7 @@ class ShutdownHookIntegrationTest { fun `Integration adds itself to integration list`() { val integration = fixture.getSut() - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) assertTrue( fixture.options.sdkVersion!!.integrationSet.contains("ShutdownHook") diff --git a/sentry/src/test/java/io/sentry/SpanTest.kt b/sentry/src/test/java/io/sentry/SpanTest.kt index ae9d9bd07f..fd36c31933 100644 --- a/sentry/src/test/java/io/sentry/SpanTest.kt +++ b/sentry/src/test/java/io/sentry/SpanTest.kt @@ -21,10 +21,10 @@ import kotlin.test.assertTrue class SpanTest { private class Fixture { - val hub = mock() + val scopes = mock() init { - whenever(hub.options).thenReturn( + whenever(scopes.options).thenReturn( SentryOptions().apply { dsn = "https://key@sentry.io/proj" isTraceSampling = true @@ -36,9 +36,9 @@ class SpanTest { return Span( SentryId(), SpanId(), - SentryTracer(TransactionContext("name", "op"), hub), + SentryTracer(TransactionContext("name", "op"), scopes), "op", - hub, + scopes, null, options, null @@ -46,7 +46,7 @@ class SpanTest { } fun getRootSut(options: TransactionOptions = TransactionOptions()): Span { - return SentryTracer(TransactionContext("name", "op"), hub, options).root + return SentryTracer(TransactionContext("name", "op"), scopes, options).root } } @@ -106,10 +106,10 @@ class SpanTest { parentSpanId, SentryTracer( TransactionContext("name", "op", TracesSamplingDecision(true)), - fixture.hub + fixture.scopes ), "op", - fixture.hub + fixture.scopes ) val sentryTrace = span.toSentryTrace() @@ -163,17 +163,17 @@ class SpanTest { } @Test - fun `when span has throwable set set, it assigns itself to throwable on the Hub`() { + fun `when span has throwable set set, it assigns itself to throwable on the Scopes`() { val transaction = SentryTracer( TransactionContext("name", "op"), - fixture.hub + fixture.scopes ) val span = transaction.startChild("op") val ex = RuntimeException() span.throwable = ex span.finish() - verify(fixture.hub).setSpanContext(ex, span, "name") + verify(fixture.scopes).setSpanContext(ex, span, "name") } @Test @@ -188,7 +188,7 @@ class SpanTest { span.finish(SpanStatus.UNKNOWN_ERROR) // call only once - verify(fixture.hub).setSpanContext(any(), any(), any()) + verify(fixture.scopes).setSpanContext(any(), any(), any()) assertEquals(SpanStatus.OK, span.status) assertEquals(timestamp, span.finishDate) } @@ -496,7 +496,7 @@ class SpanTest { } private fun getTransaction(transactionContext: TransactionContext = TransactionContext("name", "op")): SentryTracer { - return SentryTracer(transactionContext, fixture.hub) + return SentryTracer(transactionContext, fixture.scopes) } private fun startChildFromSpan(): Span { diff --git a/sentry/src/test/java/io/sentry/TraceContextSerializationTest.kt b/sentry/src/test/java/io/sentry/TraceContextSerializationTest.kt index e79e5ebf8c..0847c9448f 100644 --- a/sentry/src/test/java/io/sentry/TraceContextSerializationTest.kt +++ b/sentry/src/test/java/io/sentry/TraceContextSerializationTest.kt @@ -54,10 +54,10 @@ class TraceContextSerializationTest { private fun createTraceContext(sRate: Double): TraceContext { val baggage = Baggage(fixture.logger) - val hub: IHub = mock() - whenever(hub.options).thenReturn(SentryOptions()) + val scopes: IScopes = mock() + whenever(scopes.options).thenReturn(SentryOptions()) baggage.setValuesFromTransaction( - SentryTracer(TransactionContext("name", "op"), hub), + SentryTracer(TransactionContext("name", "op"), scopes), User().apply { id = "user-id" others = mapOf("segment" to "pro") diff --git a/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt b/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt index 01353d5ac0..ec366e1901 100644 --- a/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt @@ -30,7 +30,7 @@ class UncaughtExceptionHandlerIntegrationTest { val defaultHandler = mock() val thread = mock() val throwable = Throwable("test") - val hub = mock() + val scopes = mock() val options = SentryOptions() val logger = mock() @@ -63,17 +63,17 @@ class UncaughtExceptionHandlerIntegrationTest { fun `when uncaughtException is called, sentry captures exception`() { val sut = fixture.getSut(isPrintUncaughtStackTrace = false) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.uncaughtException(fixture.thread, fixture.throwable) - verify(fixture.hub).captureEvent(any(), any()) + verify(fixture.scopes).captureEvent(any(), any()) } @Test fun `when register is called, current handler is not lost`() { val sut = fixture.getSut(hasDefaultHandler = true, isPrintUncaughtStackTrace = false) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.uncaughtException(fixture.thread, fixture.throwable) verify(fixture.defaultHandler).uncaughtException(fixture.thread, fixture.throwable) @@ -81,7 +81,7 @@ class UncaughtExceptionHandlerIntegrationTest { @Test fun `when uncaughtException is called, exception captured has handled=false`() { - whenever(fixture.hub.captureException(any())).thenAnswer { invocation -> + whenever(fixture.scopes.captureException(any())).thenAnswer { invocation -> val e = invocation.getArgument(1) assertNotNull(e) assertNotNull(e.exceptionMechanism) @@ -91,22 +91,22 @@ class UncaughtExceptionHandlerIntegrationTest { val sut = fixture.getSut(isPrintUncaughtStackTrace = false) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.uncaughtException(fixture.thread, fixture.throwable) - verify(fixture.hub).captureEvent(any(), any()) + verify(fixture.scopes).captureEvent(any(), any()) } @Test - fun `when hub is closed, integrations should be closed`() { + fun `when scopes is closed, integrations should be closed`() { val integrationMock = mock() val options = SentryOptions() options.dsn = "https://key@sentry.io/proj" options.addIntegration(integrationMock) options.cacheDirPath = fixture.file.absolutePath options.setSerializer(mock()) - val hub = Hub(options) - hub.close() + val scopes = Hub(options) + scopes.close() verify(integrationMock).close() } @@ -117,7 +117,7 @@ class UncaughtExceptionHandlerIntegrationTest { isPrintUncaughtStackTrace = false ) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.handler, never()).defaultUncaughtExceptionHandler = any() } @@ -126,7 +126,7 @@ class UncaughtExceptionHandlerIntegrationTest { fun `When defaultUncaughtExceptionHandler is enabled, should install Sentry UncaughtExceptionHandler`() { val sut = fixture.getSut(isPrintUncaughtStackTrace = false) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.handler).defaultUncaughtExceptionHandler = argWhere { it is UncaughtExceptionHandlerIntegration } @@ -136,7 +136,7 @@ class UncaughtExceptionHandlerIntegrationTest { fun `When defaultUncaughtExceptionHandler is set and integration is closed, default uncaught exception handler is reset to previous handler`() { val sut = fixture.getSut(hasDefaultHandler = true, isPrintUncaughtStackTrace = false) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) whenever(fixture.handler.defaultUncaughtExceptionHandler) .thenReturn(sut) sut.close() @@ -148,7 +148,7 @@ class UncaughtExceptionHandlerIntegrationTest { fun `When defaultUncaughtExceptionHandler is not set and integration is closed, default uncaught exception handler is reset to null`() { val sut = fixture.getSut(isPrintUncaughtStackTrace = false) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) whenever(fixture.handler.defaultUncaughtExceptionHandler) .thenReturn(sut) sut.close() @@ -165,7 +165,7 @@ class UncaughtExceptionHandlerIntegrationTest { val sut = fixture.getSut(isPrintUncaughtStackTrace = true) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.uncaughtException(fixture.thread, RuntimeException("This should be printed!")) assertTrue( @@ -185,7 +185,7 @@ class UncaughtExceptionHandlerIntegrationTest { fun `waits for event to flush on disk`() { val capturedEventId = SentryId() - whenever(fixture.hub.captureEvent(any(), any())).thenAnswer { invocation -> + whenever(fixture.scopes.captureEvent(any(), any())).thenAnswer { invocation -> val hint = HintUtils.getSentrySdkHint(invocation.getArgument(1)) as DiskFlushNotification thread { @@ -197,10 +197,10 @@ class UncaughtExceptionHandlerIntegrationTest { val sut = fixture.getSut(flushTimeoutMillis = 5000) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.uncaughtException(fixture.thread, fixture.throwable) - verify(fixture.hub).captureEvent(any(), any()) + verify(fixture.scopes).captureEvent(any(), any()) // shouldn't fall into timed out state, because we marked event as flushed on another thread verify(fixture.logger, never()).log( any(), @@ -211,14 +211,14 @@ class UncaughtExceptionHandlerIntegrationTest { @Test fun `does not block flushing when the event was dropped`() { - whenever(fixture.hub.captureEvent(any(), any())).thenReturn(SentryId.EMPTY_ID) + whenever(fixture.scopes.captureEvent(any(), any())).thenReturn(SentryId.EMPTY_ID) val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.uncaughtException(fixture.thread, fixture.throwable) - verify(fixture.hub).captureEvent(any(), any()) + verify(fixture.scopes).captureEvent(any(), any()) // we do not call markFlushed, hence it should time out waiting for flush, but because // we drop the event, it should not even come to this if-check verify(fixture.logger, never()).log( @@ -231,17 +231,17 @@ class UncaughtExceptionHandlerIntegrationTest { @Test fun `waits for event to flush on disk if it was dropped by multithreaded deduplicator`() { val hintCaptor = argumentCaptor() - whenever(fixture.hub.captureEvent(any(), hintCaptor.capture())).thenAnswer { + whenever(fixture.scopes.captureEvent(any(), hintCaptor.capture())).thenAnswer { HintUtils.setEventDropReason(hintCaptor.firstValue, MULTITHREADED_DEDUPLICATION) return@thenAnswer SentryId.EMPTY_ID } val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.uncaughtException(fixture.thread, fixture.throwable) - verify(fixture.hub).captureEvent(any(), any()) + verify(fixture.scopes).captureEvent(any(), any()) // we do not call markFlushed, even though we dropped the event, the reason was // MULTITHREADED_DEDUPLICATION, so it should time out verify(fixture.logger).log( @@ -254,15 +254,15 @@ class UncaughtExceptionHandlerIntegrationTest { @Test fun `when there is no active transaction on scope, sets current event id as flushable`() { val eventCaptor = argumentCaptor() - whenever(fixture.hub.captureEvent(eventCaptor.capture(), any())) + whenever(fixture.scopes.captureEvent(eventCaptor.capture(), any())) .thenReturn(SentryId.EMPTY_ID) val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.uncaughtException(fixture.thread, fixture.throwable) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( any(), argThat { (HintUtils.getSentrySdkHint(this) as UncaughtExceptionHint) @@ -274,16 +274,16 @@ class UncaughtExceptionHandlerIntegrationTest { @Test fun `when there is active transaction on scope, does not set current event id as flushable`() { val eventCaptor = argumentCaptor() - whenever(fixture.hub.transaction).thenReturn(mock()) - whenever(fixture.hub.captureEvent(eventCaptor.capture(), any())) + whenever(fixture.scopes.transaction).thenReturn(mock()) + whenever(fixture.scopes.captureEvent(eventCaptor.capture(), any())) .thenReturn(SentryId.EMPTY_ID) val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.uncaughtException(fixture.thread, fixture.throwable) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( any(), argThat { !(HintUtils.getSentrySdkHint(this) as UncaughtExceptionHint) diff --git a/sentry/src/test/java/io/sentry/backpressure/BackpressureMonitorTest.kt b/sentry/src/test/java/io/sentry/backpressure/BackpressureMonitorTest.kt index c010c97238..cf234574bf 100644 --- a/sentry/src/test/java/io/sentry/backpressure/BackpressureMonitorTest.kt +++ b/sentry/src/test/java/io/sentry/backpressure/BackpressureMonitorTest.kt @@ -1,6 +1,6 @@ package io.sentry.backpressure -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ISentryExecutorService import io.sentry.SentryOptions import io.sentry.backpressure.BackpressureMonitor.MAX_DOWNSAMPLE_FACTOR @@ -17,13 +17,13 @@ class BackpressureMonitorTest { class Fixture { val options = SentryOptions() - val hub = mock() + val scopes = mock() val executor = mock() fun getSut(): BackpressureMonitor { options.executorService = executor whenever(executor.isClosed).thenReturn(false) whenever(executor.schedule(any(), any())).thenReturn(mock>()) - return BackpressureMonitor(options, hub) + return BackpressureMonitor(options, scopes) } } @@ -38,7 +38,7 @@ class BackpressureMonitorTest { @Test fun `downsampleFactor increases with negative health checks up to max`() { val sut = fixture.getSut() - whenever(fixture.hub.isHealthy).thenReturn(false) + whenever(fixture.scopes.isHealthy).thenReturn(false) assertEquals(0, sut.downsampleFactor) (1..MAX_DOWNSAMPLE_FACTOR).forEach { i -> @@ -54,13 +54,13 @@ class BackpressureMonitorTest { @Test fun `downsampleFactor goes back to 0 after positive health check`() { val sut = fixture.getSut() - whenever(fixture.hub.isHealthy).thenReturn(false) + whenever(fixture.scopes.isHealthy).thenReturn(false) assertEquals(0, sut.downsampleFactor) sut.checkHealth() assertEquals(1, sut.downsampleFactor) - whenever(fixture.hub.isHealthy).thenReturn(true) + whenever(fixture.scopes.isHealthy).thenReturn(true) sut.checkHealth() assertEquals(0, sut.downsampleFactor) } diff --git a/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt b/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt index b4615cac76..d27e5c0228 100644 --- a/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt +++ b/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt @@ -7,7 +7,7 @@ import io.sentry.DataCategory import io.sentry.DateUtils import io.sentry.EventProcessor import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.NoOpLogger import io.sentry.ProfilingTraceData import io.sentry.Sentry @@ -47,9 +47,9 @@ class ClientReportTest { @Test fun `lost envelope can be recorded`() { givenClientReportRecorder() - val hub = mock() - whenever(hub.options).thenReturn(opts) - val transaction = SentryTracer(TransactionContext("name", "op"), hub) + val scopes = mock() + whenever(scopes.options).thenReturn(opts) + val transaction = SentryTracer(TransactionContext("name", "op"), scopes) val lostClientReport = ClientReport( DateUtils.getCurrentDateTime(), diff --git a/sentry/src/test/java/io/sentry/instrumentation/file/FileIOSpanManagerTest.kt b/sentry/src/test/java/io/sentry/instrumentation/file/FileIOSpanManagerTest.kt index 00c89f27be..f6271b5b5e 100644 --- a/sentry/src/test/java/io/sentry/instrumentation/file/FileIOSpanManagerTest.kt +++ b/sentry/src/test/java/io/sentry/instrumentation/file/FileIOSpanManagerTest.kt @@ -1,6 +1,6 @@ package io.sentry.instrumentation.file -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ISpan import io.sentry.ITransaction import io.sentry.util.PlatformTestManipulator @@ -20,25 +20,25 @@ class FileIOSpanManagerTest { @Test fun `startSpan uses transaction on Android platform`() { - val hub = mock() + val scopes = mock() val transaction = mock() - whenever(hub.transaction).thenReturn(transaction) + whenever(scopes.transaction).thenReturn(transaction) PlatformTestManipulator.pretendIsAndroid(true) - FileIOSpanManager.startSpan(hub, "op.read") + FileIOSpanManager.startSpan(scopes, "op.read") verify(transaction).startChild(any()) } @Test fun `startSpan uses last span on non-Android platforms`() { - val hub = mock() + val scopes = mock() val span = mock() - whenever(hub.span).thenReturn(span) + whenever(scopes.span).thenReturn(span) PlatformTestManipulator.pretendIsAndroid(false) - FileIOSpanManager.startSpan(hub, "op.read") + FileIOSpanManager.startSpan(scopes, "op.read") verify(span).startChild(any()) } } diff --git a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileInputStreamTest.kt b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileInputStreamTest.kt index 5e27eb451d..063b6d6428 100644 --- a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileInputStreamTest.kt +++ b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileInputStreamTest.kt @@ -1,6 +1,6 @@ package io.sentry.instrumentation.file -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryOptions import io.sentry.SentryTracer import io.sentry.SpanDataConvention @@ -29,7 +29,7 @@ import kotlin.test.assertTrue class SentryFileInputStreamTest { class Fixture { - val hub = mock() + val scopes = mock() lateinit var sentryTracer: SentryTracer private val options = SentryOptions() @@ -40,21 +40,21 @@ class SentryFileInputStreamTest { sendDefaultPii: Boolean = false ): SentryFileInputStream { tmpFile?.writeText("Text") - whenever(hub.options).thenReturn( + whenever(scopes.options).thenReturn( options.apply { isSendDefaultPii = sendDefaultPii mainThreadChecker = MainThreadChecker.getInstance() addInAppInclude("org.junit") } ) - sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) + sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) if (activeTransaction) { - whenever(hub.span).thenReturn(sentryTracer) + whenever(scopes.span).thenReturn(sentryTracer) } return if (fileDescriptor == null) { - SentryFileInputStream(tmpFile, hub) + SentryFileInputStream(tmpFile, scopes) } else { - SentryFileInputStream(fileDescriptor, hub) + SentryFileInputStream(fileDescriptor, scopes) } } @@ -62,13 +62,13 @@ class SentryFileInputStreamTest { tmpFile: File? = null, delegate: FileInputStream ): SentryFileInputStream { - whenever(hub.options).thenReturn(options) - sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) - whenever(hub.span).thenReturn(sentryTracer) + whenever(scopes.options).thenReturn(options) + sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) + whenever(scopes.span).thenReturn(sentryTracer) return SentryFileInputStream.Factory.create( delegate, tmpFile, - hub + scopes ) as SentryFileInputStream } } diff --git a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileOutputStreamTest.kt b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileOutputStreamTest.kt index f6a09830c2..8b175adc2d 100644 --- a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileOutputStreamTest.kt +++ b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileOutputStreamTest.kt @@ -1,6 +1,6 @@ package io.sentry.instrumentation.file -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryOptions import io.sentry.SentryTracer import io.sentry.SpanDataConvention @@ -24,7 +24,7 @@ import kotlin.test.assertTrue class SentryFileOutputStreamTest { class Fixture { - val hub = mock() + val scopes = mock() lateinit var sentryTracer: SentryTracer internal fun getSut( @@ -32,17 +32,17 @@ class SentryFileOutputStreamTest { activeTransaction: Boolean = true, append: Boolean = false ): SentryFileOutputStream { - whenever(hub.options).thenReturn( + whenever(scopes.options).thenReturn( SentryOptions().apply { mainThreadChecker = MainThreadChecker.getInstance() addInAppInclude("org.junit") } ) - sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) + sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) if (activeTransaction) { - whenever(hub.span).thenReturn(sentryTracer) + whenever(scopes.span).thenReturn(sentryTracer) } - return SentryFileOutputStream(tmpFile, append, hub) + return SentryFileOutputStream(tmpFile, append, scopes) } } diff --git a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileReaderTest.kt b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileReaderTest.kt index 2485579e7a..38781d718b 100644 --- a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileReaderTest.kt +++ b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileReaderTest.kt @@ -1,6 +1,6 @@ package io.sentry.instrumentation.file -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryOptions import io.sentry.SentryTracer import io.sentry.SpanDataConvention @@ -17,7 +17,7 @@ import kotlin.test.assertEquals class SentryFileReaderTest { class Fixture { - val hub = mock() + val scopes = mock() lateinit var sentryTracer: SentryTracer internal fun getSut( @@ -25,16 +25,16 @@ class SentryFileReaderTest { activeTransaction: Boolean = true ): SentryFileReader { tmpFile.writeText("TEXT") - whenever(hub.options).thenReturn( + whenever(scopes.options).thenReturn( SentryOptions().apply { mainThreadChecker = MainThreadChecker.getInstance() } ) - sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) + sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) if (activeTransaction) { - whenever(hub.span).thenReturn(sentryTracer) + whenever(scopes.span).thenReturn(sentryTracer) } - return SentryFileReader(tmpFile, hub) + return SentryFileReader(tmpFile, scopes) } } diff --git a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileWriterTest.kt b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileWriterTest.kt index f0738d8725..8f3d96b4d7 100644 --- a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileWriterTest.kt +++ b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileWriterTest.kt @@ -1,6 +1,6 @@ package io.sentry.instrumentation.file -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryOptions import io.sentry.SentryTracer import io.sentry.SpanDataConvention @@ -17,7 +17,7 @@ import kotlin.test.assertEquals class SentryFileWriterTest { class Fixture { - val hub = mock() + val scopes = mock() lateinit var sentryTracer: SentryTracer internal fun getSut( @@ -25,16 +25,16 @@ class SentryFileWriterTest { activeTransaction: Boolean = true, append: Boolean = false ): SentryFileWriter { - whenever(hub.options).thenReturn( + whenever(scopes.options).thenReturn( SentryOptions().apply { mainThreadChecker = MainThreadChecker.getInstance() } ) - sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) + sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) if (activeTransaction) { - whenever(hub.span).thenReturn(sentryTracer) + whenever(scopes.span).thenReturn(sentryTracer) } - return SentryFileWriter(tmpFile, append, hub) + return SentryFileWriter(tmpFile, append, scopes) } } diff --git a/sentry/src/test/java/io/sentry/internal/SpotlightIntegrationTest.kt b/sentry/src/test/java/io/sentry/internal/SpotlightIntegrationTest.kt index 73aa339f26..0a91bec562 100644 --- a/sentry/src/test/java/io/sentry/internal/SpotlightIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/internal/SpotlightIntegrationTest.kt @@ -1,6 +1,6 @@ package io.sentry.internal -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryOptions import io.sentry.SentryOptions.BeforeEnvelopeCallback import io.sentry.SpotlightIntegration @@ -19,7 +19,7 @@ class SpotlightIntegrationTest { } val spotlight = SpotlightIntegration() - spotlight.register(mock(), options) + spotlight.register(mock(), options) assertNull(options.beforeEnvelopeCallback) } @@ -33,7 +33,7 @@ class SpotlightIntegrationTest { } val spotlight = SpotlightIntegration() - spotlight.register(mock(), options) + spotlight.register(mock(), options) assertEquals(envelopeCallback, options.beforeEnvelopeCallback) } @@ -45,7 +45,7 @@ class SpotlightIntegrationTest { } val spotlight = SpotlightIntegration() - spotlight.register(mock(), options) + spotlight.register(mock(), options) assertEquals(options.beforeEnvelopeCallback, spotlight) spotlight.close() @@ -71,7 +71,7 @@ class SpotlightIntegrationTest { } val spotlight = SpotlightIntegration() - spotlight.register(mock(), options) + spotlight.register(mock(), options) assertEquals("http://example.com:1234/stream", spotlight.spotlightConnectionUrl) } diff --git a/sentry/src/test/java/io/sentry/protocol/SentrySpanTest.kt b/sentry/src/test/java/io/sentry/protocol/SentrySpanTest.kt index 27499be0a0..d67b0186be 100644 --- a/sentry/src/test/java/io/sentry/protocol/SentrySpanTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/SentrySpanTest.kt @@ -1,6 +1,6 @@ package io.sentry.protocol -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryLongDate import io.sentry.SentryTracer import io.sentry.Span @@ -19,7 +19,7 @@ class SentrySpanTest { val span = Span( TransactionContext("name", "op"), mock(), - mock(), + mock(), SentryLongDate(1000000), SpanOptions() ) diff --git a/sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt b/sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt index d9e217b0ac..2b1be98611 100644 --- a/sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt +++ b/sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt @@ -4,7 +4,7 @@ import io.sentry.Attachment import io.sentry.CheckIn import io.sentry.CheckInStatus import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ISerializer import io.sentry.NoOpLogger import io.sentry.ProfilingTraceData @@ -80,10 +80,10 @@ class RateLimiterTest { fun `parse X-Sentry-Rate-Limit and set its values and retry after should be true`() { val rateLimiter = fixture.getSUT() whenever(fixture.currentDateProvider.currentTimeMillis).thenReturn(0) - val hub: IHub = mock() - whenever(hub.options).thenReturn(SentryOptions()) + val scopes: IScopes = mock() + whenever(scopes.options).thenReturn(SentryOptions()) val eventItem = SentryEnvelopeItem.fromEvent(fixture.serializer, SentryEvent()) - val transaction = SentryTransaction(SentryTracer(TransactionContext("name", "op"), hub)) + val transaction = SentryTransaction(SentryTracer(TransactionContext("name", "op"), scopes)) val transactionItem = SentryEnvelopeItem.fromEvent(fixture.serializer, transaction) val envelope = SentryEnvelope(SentryEnvelopeHeader(), arrayListOf(eventItem, transactionItem)) @@ -97,10 +97,10 @@ class RateLimiterTest { fun `parse X-Sentry-Rate-Limit and set its values and retry after should be false`() { val rateLimiter = fixture.getSUT() whenever(fixture.currentDateProvider.currentTimeMillis).thenReturn(0, 0, 1001) - val hub: IHub = mock() - whenever(hub.options).thenReturn(SentryOptions()) + val scopes: IScopes = mock() + whenever(scopes.options).thenReturn(SentryOptions()) val eventItem = SentryEnvelopeItem.fromEvent(fixture.serializer, SentryEvent()) - val transaction = SentryTransaction(SentryTracer(TransactionContext("name", "op"), hub)) + val transaction = SentryTransaction(SentryTracer(TransactionContext("name", "op"), scopes)) val transactionItem = SentryEnvelopeItem.fromEvent(fixture.serializer, transaction) val envelope = SentryEnvelope(SentryEnvelopeHeader(), arrayListOf(eventItem, transactionItem)) @@ -191,9 +191,9 @@ class RateLimiterTest { it.setName("John Me") } ) - val hub = mock() - whenever(hub.options).thenReturn(SentryOptions()) - val transaction = SentryTracer(TransactionContext("name", "op"), hub) + val scopes = mock() + whenever(scopes.options).thenReturn(SentryOptions()) + val transaction = SentryTracer(TransactionContext("name", "op"), scopes) val sessionItem = SentryEnvelopeItem.fromSession(fixture.serializer, Session("123", User(), "env", "release")) val attachmentItem = SentryEnvelopeItem.fromAttachment(fixture.serializer, NoOpLogger.getInstance(), Attachment("{ \"number\": 10 }".toByteArray(), "log.json"), 1000) @@ -219,8 +219,8 @@ class RateLimiterTest { @Test fun `records only dropped items as lost`() { val rateLimiter = fixture.getSUT() - val hub = mock() - whenever(hub.options).thenReturn(SentryOptions()) + val scopes = mock() + whenever(scopes.options).thenReturn(SentryOptions()) val eventItem = SentryEnvelopeItem.fromEvent(fixture.serializer, SentryEvent()) val userFeedbackItem = SentryEnvelopeItem.fromUserFeedback( @@ -233,7 +233,7 @@ class RateLimiterTest { it.setName("John Me") } ) - val transaction = SentryTracer(TransactionContext("name", "op"), hub) + val transaction = SentryTracer(TransactionContext("name", "op"), scopes) val profileItem = SentryEnvelopeItem.fromProfilingTrace(ProfilingTraceData(File(""), transaction), 1000, fixture.serializer) val sessionItem = SentryEnvelopeItem.fromSession(fixture.serializer, Session("123", User(), "env", "release")) val attachmentItem = SentryEnvelopeItem.fromAttachment(fixture.serializer, NoOpLogger.getInstance(), Attachment("{ \"number\": 10 }".toByteArray(), "log.json"), 1000) @@ -253,12 +253,12 @@ class RateLimiterTest { @Test fun `drop profile items as lost`() { val rateLimiter = fixture.getSUT() - val hub = mock() - whenever(hub.options).thenReturn(SentryOptions()) + val scopes = mock() + whenever(scopes.options).thenReturn(SentryOptions()) val eventItem = SentryEnvelopeItem.fromEvent(fixture.serializer, SentryEvent()) val f = File.createTempFile("test", "trace") - val transaction = SentryTracer(TransactionContext("name", "op"), hub) + val transaction = SentryTracer(TransactionContext("name", "op"), scopes) val profileItem = SentryEnvelopeItem.fromProfilingTrace(ProfilingTraceData(f, transaction), 1000, fixture.serializer) val envelope = SentryEnvelope(SentryEnvelopeHeader(), arrayListOf(eventItem, profileItem)) diff --git a/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt b/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt index a285cd5832..9f330348b0 100644 --- a/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt @@ -1,7 +1,8 @@ package io.sentry.util import io.sentry.CheckInStatus -import io.sentry.IHub +import io.sentry.HubScopesWrapper +import io.sentry.IScopes import io.sentry.MonitorConfig import io.sentry.MonitorSchedule import io.sentry.MonitorScheduleUnit @@ -56,30 +57,31 @@ class CheckInUtilsTest { @Test fun `sends check-in for wrapped supplier`() { Mockito.mockStatic(Sentry::class.java).use { sentry -> - val hub = mock() - sentry.`when` { Sentry.getCurrentHub() }.thenReturn(hub) - whenever(hub.options).thenReturn(SentryOptions()) + val scopes = mock() + sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) + sentry.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(scopes)) + whenever(scopes.options).thenReturn(SentryOptions()) val returnValue = CheckInUtils.withCheckIn("monitor-1") { return@withCheckIn "test1" } assertEquals("test1", returnValue) - inOrder(hub) { - verify(hub).pushScope() - verify(hub).configureScope(any()) - verify(hub).captureCheckIn( + inOrder(scopes) { + verify(scopes).pushScope() + verify(scopes).configureScope(any()) + verify(scopes).captureCheckIn( check { assertEquals("monitor-1", it.monitorSlug) assertEquals(CheckInStatus.IN_PROGRESS.apiName(), it.status) } ) - verify(hub).captureCheckIn( + verify(scopes).captureCheckIn( check { assertEquals("monitor-1", it.monitorSlug) assertEquals(CheckInStatus.OK.apiName(), it.status) } ) - verify(hub).popScope() + verify(scopes).popScope() } } } @@ -87,8 +89,9 @@ class CheckInUtilsTest { @Test fun `sends check-in for wrapped supplier with exception`() { Mockito.mockStatic(Sentry::class.java).use { sentry -> - val hub = mock() - sentry.`when` { Sentry.getCurrentHub() }.thenReturn(hub) + val scopes = mock() + sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) + sentry.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(scopes)) try { CheckInUtils.withCheckIn("monitor-1") { @@ -99,22 +102,22 @@ class CheckInUtilsTest { assertEquals("thrown on purpose", e.message) } - inOrder(hub) { - verify(hub).pushScope() - verify(hub).configureScope(any()) - verify(hub).captureCheckIn( + inOrder(scopes) { + verify(scopes).pushScope() + verify(scopes).configureScope(any()) + verify(scopes).captureCheckIn( check { assertEquals("monitor-1", it.monitorSlug) assertEquals(CheckInStatus.IN_PROGRESS.apiName(), it.status) } ) - verify(hub).captureCheckIn( + verify(scopes).captureCheckIn( check { assertEquals("monitor-1", it.monitorSlug) assertEquals(CheckInStatus.ERROR.apiName(), it.status) } ) - verify(hub).popScope() + verify(scopes).popScope() } } } @@ -122,32 +125,33 @@ class CheckInUtilsTest { @Test fun `sends check-in for wrapped supplier with upsert`() { Mockito.mockStatic(Sentry::class.java).use { sentry -> - val hub = mock() - sentry.`when` { Sentry.getCurrentHub() }.thenReturn(hub) - whenever(hub.options).thenReturn(SentryOptions()) + val scopes = mock() + sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) + sentry.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(scopes)) + whenever(scopes.options).thenReturn(SentryOptions()) val monitorConfig = MonitorConfig(MonitorSchedule.interval(7, MonitorScheduleUnit.DAY)) val returnValue = CheckInUtils.withCheckIn("monitor-1", monitorConfig) { "test1" } assertEquals("test1", returnValue) - inOrder(hub) { - verify(hub).pushScope() - verify(hub).configureScope(any()) - verify(hub).captureCheckIn( + inOrder(scopes) { + verify(scopes).pushScope() + verify(scopes).configureScope(any()) + verify(scopes).captureCheckIn( check { assertEquals("monitor-1", it.monitorSlug) assertSame(monitorConfig, it.monitorConfig) assertEquals(CheckInStatus.IN_PROGRESS.apiName(), it.status) } ) - verify(hub).captureCheckIn( + verify(scopes).captureCheckIn( check { assertEquals("monitor-1", it.monitorSlug) assertEquals(CheckInStatus.OK.apiName(), it.status) } ) - verify(hub).popScope() + verify(scopes).popScope() } } } @@ -155,9 +159,10 @@ class CheckInUtilsTest { @Test fun `sends check-in for wrapped supplier with upsert and thresholds`() { Mockito.mockStatic(Sentry::class.java).use { sentry -> - val hub = mock() - sentry.`when` { Sentry.getCurrentHub() }.thenReturn(hub) - whenever(hub.options).thenReturn(SentryOptions()) + val scopes = mock() + sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) + sentry.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(scopes)) + whenever(scopes.options).thenReturn(SentryOptions()) val monitorConfig = MonitorConfig(MonitorSchedule.interval(7, MonitorScheduleUnit.DAY)).apply { failureIssueThreshold = 10 recoveryThreshold = 20 @@ -167,23 +172,23 @@ class CheckInUtilsTest { } assertEquals("test1", returnValue) - inOrder(hub) { - verify(hub).pushScope() - verify(hub).configureScope(any()) - verify(hub).captureCheckIn( + inOrder(scopes) { + verify(scopes).pushScope() + verify(scopes).configureScope(any()) + verify(scopes).captureCheckIn( check { assertEquals("monitor-1", it.monitorSlug) assertSame(monitorConfig, it.monitorConfig) assertEquals(CheckInStatus.IN_PROGRESS.apiName(), it.status) } ) - verify(hub).captureCheckIn( + verify(scopes).captureCheckIn( check { assertEquals("monitor-1", it.monitorSlug) assertEquals(CheckInStatus.OK.apiName(), it.status) } ) - verify(hub).popScope() + verify(scopes).popScope() } } } @@ -191,9 +196,10 @@ class CheckInUtilsTest { @Test fun `sets defaults for MonitorConfig from SentryOptions`() { Mockito.mockStatic(Sentry::class.java).use { sentry -> - val hub = mock() - sentry.`when` { Sentry.getCurrentHub() }.thenReturn(hub) - whenever(hub.options).thenReturn( + val scopes = mock() + sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) + sentry.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(scopes)) + whenever(scopes.options).thenReturn( SentryOptions().apply { cron = SentryOptions.Cron().apply { defaultCheckinMargin = 20 @@ -218,9 +224,10 @@ class CheckInUtilsTest { @Test fun `defaults for MonitorConfig from SentryOptions can be overridden`() { Mockito.mockStatic(Sentry::class.java).use { sentry -> - val hub = mock() - sentry.`when` { Sentry.getCurrentHub() }.thenReturn(hub) - whenever(hub.options).thenReturn( + val scopes = mock() + sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) + sentry.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(scopes)) + whenever(scopes.options).thenReturn( SentryOptions().apply { cron = SentryOptions.Cron().apply { defaultCheckinMargin = 20 diff --git a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt index e38410641e..b3f640aeec 100644 --- a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt @@ -1,7 +1,7 @@ package io.sentry.util import io.sentry.Baggage -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.NoOpSpan import io.sentry.Scope import io.sentry.ScopeCallback @@ -29,18 +29,18 @@ class TracingUtilsTest { val options = SentryOptions().apply { dsn = "https://key@sentry.io/proj" } - val hub = mock() + val scopes = mock() val scope = Scope(options) lateinit var span: Span val preExistingBaggage = listOf("some-baggage-key=some-baggage-value") fun setup() { - whenever(hub.options).thenReturn(options) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) + whenever(scopes.options).thenReturn(options) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) span = Span( TransactionContext("name", "op", TracesSamplingDecision(true)), - SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), hub), - hub, + SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), scopes), + scopes, null, SpanOptions() ) @@ -53,7 +53,7 @@ class TracingUtilsTest { fun `returns headers if allowed from scope without span`() { fixture.setup() - val headers = TracingUtils.traceIfAllowed(fixture.hub, "https://sentry.io/hello", fixture.preExistingBaggage, null) + val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, null) assertNotNull(headers) assertNotNull(headers.baggageHeader) @@ -68,7 +68,7 @@ class TracingUtilsTest { fun `returns headers if allowed from scope if span is noop`() { fixture.setup() - val headers = TracingUtils.traceIfAllowed(fixture.hub, "https://sentry.io/hello", fixture.preExistingBaggage, NoOpSpan.getInstance()) + val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, NoOpSpan.getInstance()) assertNotNull(headers) assertNotNull(headers.baggageHeader) @@ -83,7 +83,7 @@ class TracingUtilsTest { fixture.scope.propagationContext.baggage = Baggage.fromHeader("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=2722d9f6ec019ade60c776169d9a8904,sentry-transaction=HTTP%20GET").also { it.freeze() } fixture.setup() - val headers = TracingUtils.traceIfAllowed(fixture.hub, "https://sentry.io/hello", fixture.preExistingBaggage, null) + val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, null) assertNotNull(headers) assertNotNull(headers.baggageHeader) @@ -98,7 +98,7 @@ class TracingUtilsTest { fun `returns headers if allowed from span`() { fixture.setup() - val headers = TracingUtils.traceIfAllowed(fixture.hub, "https://sentry.io/hello", fixture.preExistingBaggage, fixture.span) + val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, fixture.span) assertNotNull(headers) assertNotNull(headers.baggageHeader) @@ -112,7 +112,7 @@ class TracingUtilsTest { fixture.options.isTraceSampling = false fixture.setup() - val headers = TracingUtils.traceIfAllowed(fixture.hub, "https://sentry.io/hello", fixture.preExistingBaggage, null) + val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, null) assertNull(headers) } @@ -122,7 +122,7 @@ class TracingUtilsTest { fixture.options.isTraceSampling = false fixture.setup() - val headers = TracingUtils.traceIfAllowed(fixture.hub, "https://sentry.io/hello", fixture.preExistingBaggage, fixture.span) + val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, fixture.span) assertNull(headers) } @@ -132,7 +132,7 @@ class TracingUtilsTest { fixture.options.setTracePropagationTargets(listOf("some-host-that-does-not-exist")) fixture.setup() - val headers = TracingUtils.traceIfAllowed(fixture.hub, "https://sentry.io/hello", fixture.preExistingBaggage, null) + val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, null) assertNull(headers) } @@ -142,7 +142,7 @@ class TracingUtilsTest { fixture.options.setTracePropagationTargets(listOf("some-host-that-does-not-exist")) fixture.setup() - val headers = TracingUtils.traceIfAllowed(fixture.hub, "https://sentry.io/hello", fixture.preExistingBaggage, fixture.span) + val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, fixture.span) assertNull(headers) } @@ -153,7 +153,7 @@ class TracingUtilsTest { val propagationContextBefore = fixture.scope.propagationContext - TracingUtils.startNewTrace(fixture.hub) + TracingUtils.startNewTrace(fixture.scopes) assertNotEquals(propagationContextBefore.traceId, fixture.scope.propagationContext.traceId) assertNotEquals(propagationContextBefore.spanId, fixture.scope.propagationContext.spanId) From 30990f66150ce33ec88986e1ac22bf7173b3f319 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 16 Apr 2024 16:38:06 +0200 Subject: [PATCH 03/91] Hubs/Scopes Merge 3 - Replace `IHub` with `IScopes` in Android core (#3299) * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core --- .../api/sentry-android-core.api | 28 ++--- .../core/ActivityBreadcrumbsIntegration.java | 17 +-- .../core/ActivityLifecycleIntegration.java | 32 +++--- .../core/AndroidTransactionProfiler.java | 12 +- .../sentry/android/core/AnrIntegration.java | 19 +-- .../sentry/android/core/AnrV2Integration.java | 14 +-- .../AppComponentsBreadcrumbsIntegration.java | 16 +-- .../android/core/AppLifecycleIntegration.java | 14 +-- .../core/CurrentActivityIntegration.java | 4 +- .../core/EnvelopeFileObserverIntegration.java | 14 ++- .../android/core/InternalSentrySdk.java | 24 ++-- .../sentry/android/core/LifecycleWatcher.java | 22 ++-- .../sentry/android/core/NdkIntegration.java | 9 +- .../core/NetworkBreadcrumbsIntegration.java | 21 ++-- .../PhoneStateBreadcrumbsIntegration.java | 20 ++-- .../core/SendCachedEnvelopeIntegration.java | 20 ++-- .../io/sentry/android/core/SentryAndroid.java | 11 +- .../SystemEventsBreadcrumbsIntegration.java | 20 ++-- .../TempSensorBreadcrumbsIntegration.java | 12 +- .../core/UserInteractionIntegration.java | 12 +- .../gestures/SentryGestureListener.java | 18 +-- .../core/AndroidTransactionProfilerTest.kt | 12 +- .../sentry/android/core/AnrIntegrationTest.kt | 28 ++--- .../android/core/AnrV2IntegrationTest.kt | 90 +++++++-------- ...AppComponentsBreadcrumbsIntegrationTest.kt | 48 ++++---- .../core/AppLifecycleIntegrationTest.kt | 16 +-- .../core/CurrentActivityIntegrationTest.kt | 6 +- .../core/DefaultAndroidEventProcessorTest.kt | 8 +- .../EnvelopeFileObserverIntegrationTest.kt | 20 ++-- .../android/core/LifecycleWatcherTest.kt | 48 ++++---- .../sentry/android/core/NdkIntegrationTest.kt | 22 ++-- .../core/NetworkBreadcrumbsIntegrationTest.kt | 108 +++++++++--------- .../PerformanceAndroidEventProcessorTest.kt | 20 ++-- .../PhoneStateBreadcrumbsIntegrationTest.kt | 38 +++--- .../core/SendCachedEnvelopeIntegrationTest.kt | 30 ++--- .../SystemEventsBreadcrumbsIntegrationTest.kt | 24 ++-- .../TempSensorBreadcrumbsIntegrationTest.kt | 32 +++--- .../SentryGestureListenerClickTest.kt | 22 ++-- .../SentryGestureListenerScrollTest.kt | 26 ++--- .../SentryGestureListenerTracingTest.kt | 58 +++++----- 40 files changed, 511 insertions(+), 504 deletions(-) diff --git a/sentry-android-core/api/sentry-android-core.api b/sentry-android-core/api/sentry-android-core.api index 8eb017346d..2afe788ef8 100644 --- a/sentry-android-core/api/sentry-android-core.api +++ b/sentry-android-core/api/sentry-android-core.api @@ -8,7 +8,7 @@ public final class io/sentry/android/core/ActivityBreadcrumbsIntegration : andro public fun onActivitySaveInstanceState (Landroid/app/Activity;Landroid/os/Bundle;)V public fun onActivityStarted (Landroid/app/Activity;)V public fun onActivityStopped (Landroid/app/Activity;)V - public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/ActivityFramesTracker { @@ -33,7 +33,7 @@ public final class io/sentry/android/core/ActivityLifecycleIntegration : android public fun onActivitySaveInstanceState (Landroid/app/Activity;Landroid/os/Bundle;)V public fun onActivityStarted (Landroid/app/Activity;)V public fun onActivityStopped (Landroid/app/Activity;)V - public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/AndroidCpuCollector : io/sentry/IPerformanceSnapshotCollector { @@ -87,7 +87,7 @@ public class io/sentry/android/core/AndroidProfiler$ProfileStartData { public final class io/sentry/android/core/AnrIntegration : io/sentry/Integration, java/io/Closeable { public fun (Landroid/content/Context;)V public fun close ()V - public final fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public final fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/AnrIntegrationFactory { @@ -104,7 +104,7 @@ public final class io/sentry/android/core/AnrV2EventProcessor : io/sentry/Backfi public class io/sentry/android/core/AnrV2Integration : io/sentry/Integration, java/io/Closeable { public fun (Landroid/content/Context;)V public fun close ()V - public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/AnrV2Integration$AnrV2Hint : io/sentry/hints/BlockingFlushHint, io/sentry/hints/AbnormalExit, io/sentry/hints/Backfillable { @@ -123,13 +123,13 @@ public final class io/sentry/android/core/AppComponentsBreadcrumbsIntegration : public fun onConfigurationChanged (Landroid/content/res/Configuration;)V public fun onLowMemory ()V public fun onTrimMemory (I)V - public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/AppLifecycleIntegration : io/sentry/Integration, java/io/Closeable { public fun ()V public fun close ()V - public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/AppState { @@ -177,7 +177,7 @@ public final class io/sentry/android/core/CurrentActivityIntegration : android/a public fun onActivitySaveInstanceState (Landroid/app/Activity;Landroid/os/Bundle;)V public fun onActivityStarted (Landroid/app/Activity;)V public fun onActivityStopped (Landroid/app/Activity;)V - public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/DeviceInfoUtil { @@ -193,7 +193,7 @@ public abstract class io/sentry/android/core/EnvelopeFileObserverIntegration : i public fun ()V public fun close ()V public static fun getOutboxFileObserver ()Lio/sentry/android/core/EnvelopeFileObserverIntegration; - public final fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public final fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public abstract interface class io/sentry/android/core/IDebugImagesLoader { @@ -219,19 +219,19 @@ public final class io/sentry/android/core/NdkIntegration : io/sentry/Integration public static final field SENTRY_NDK_CLASS_NAME Ljava/lang/String; public fun (Ljava/lang/Class;)V public fun close ()V - public final fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public final fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/NetworkBreadcrumbsIntegration : io/sentry/Integration, java/io/Closeable { public fun (Landroid/content/Context;Lio/sentry/android/core/BuildInfoProvider;Lio/sentry/ILogger;)V public fun close ()V - public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/PhoneStateBreadcrumbsIntegration : io/sentry/Integration, java/io/Closeable { public fun (Landroid/content/Context;)V public fun close ()V - public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/ScreenshotEventProcessor : io/sentry/EventProcessor { @@ -360,7 +360,7 @@ public final class io/sentry/android/core/SystemEventsBreadcrumbsIntegration : i public fun (Landroid/content/Context;)V public fun (Landroid/content/Context;Ljava/util/List;)V public fun close ()V - public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/TempSensorBreadcrumbsIntegration : android/hardware/SensorEventListener, io/sentry/Integration, java/io/Closeable { @@ -368,7 +368,7 @@ public final class io/sentry/android/core/TempSensorBreadcrumbsIntegration : and public fun close ()V public fun onAccuracyChanged (Landroid/hardware/Sensor;I)V public fun onSensorChanged (Landroid/hardware/SensorEvent;)V - public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/UserInteractionIntegration : android/app/Application$ActivityLifecycleCallbacks, io/sentry/Integration, java/io/Closeable { @@ -381,7 +381,7 @@ public final class io/sentry/android/core/UserInteractionIntegration : android/a public fun onActivitySaveInstanceState (Landroid/app/Activity;Landroid/os/Bundle;)V public fun onActivityStarted (Landroid/app/Activity;)V public fun onActivityStopped (Landroid/app/Activity;)V - public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/ViewHierarchyEventProcessor : io/sentry/EventProcessor { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityBreadcrumbsIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityBreadcrumbsIntegration.java index dc03abe808..4a5ca63717 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityBreadcrumbsIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityBreadcrumbsIntegration.java @@ -8,7 +8,7 @@ import android.os.Bundle; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -23,7 +23,7 @@ public final class ActivityBreadcrumbsIntegration implements Integration, Closeable, Application.ActivityLifecycleCallbacks { private final @NotNull Application application; - private @Nullable IHub hub; + private @Nullable IScopes scopes; private boolean enabled; public ActivityBreadcrumbsIntegration(final @NotNull Application application) { @@ -31,13 +31,13 @@ public ActivityBreadcrumbsIntegration(final @NotNull Application application) { } @Override - public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { + public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { final SentryAndroidOptions androidOptions = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, "SentryAndroidOptions is required"); - this.hub = Objects.requireNonNull(hub, "Hub is required"); + this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); this.enabled = androidOptions.isEnableActivityLifecycleBreadcrumbs(); options .getLogger() @@ -54,8 +54,9 @@ public void register(final @NotNull IHub hub, final @NotNull SentryOptions optio public void close() throws IOException { if (enabled) { application.unregisterActivityLifecycleCallbacks(this); - if (hub != null) { - hub.getOptions() + if (scopes != null) { + scopes + .getOptions() .getLogger() .log(SentryLevel.DEBUG, "ActivityBreadcrumbsIntegration removed."); } @@ -100,7 +101,7 @@ public synchronized void onActivityDestroyed(final @NotNull Activity activity) { } private void addBreadcrumb(final @NotNull Activity activity, final @NotNull String state) { - if (hub == null) { + if (scopes == null) { return; } @@ -114,7 +115,7 @@ private void addBreadcrumb(final @NotNull Activity activity, final @NotNull Stri final Hint hint = new Hint(); hint.set(ANDROID_ACTIVITY, activity); - hub.addBreadcrumb(breadcrumb, hint); + scopes.addBreadcrumb(breadcrumb, hint); } private @NotNull String getActivityName(final @NotNull Activity activity) { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java index 1121a6bfe7..76bedae5d0 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java @@ -12,8 +12,8 @@ import android.view.View; import androidx.annotation.NonNull; import io.sentry.FullyDisplayedReporter; -import io.sentry.IHub; import io.sentry.IScope; +import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.ITransaction; import io.sentry.Instrumenter; @@ -60,7 +60,7 @@ public final class ActivityLifecycleIntegration private final @NotNull Application application; private final @NotNull BuildInfoProvider buildInfoProvider; - private @Nullable IHub hub; + private @Nullable IScopes scopes; private @Nullable SentryAndroidOptions options; private boolean performanceEnabled = false; @@ -102,13 +102,13 @@ public ActivityLifecycleIntegration( } @Override - public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { + public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, "SentryAndroidOptions is required"); - this.hub = Objects.requireNonNull(hub, "Hub is required"); + this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); performanceEnabled = isPerformanceEnabled(this.options); fullyDisplayedReporter = this.options.getFullyDisplayedReporter(); @@ -150,10 +150,10 @@ private void stopPreviousTransactions() { private void startTracing(final @NotNull Activity activity) { WeakReference weakActivity = new WeakReference<>(activity); - if (hub != null && !isRunningTransactionOrTrace(activity)) { + if (scopes != null && !isRunningTransactionOrTrace(activity)) { if (!performanceEnabled) { activitiesWithOngoingTransactions.put(activity, NoOpTransaction.getInstance()); - TracingUtils.startNewTrace(hub); + TracingUtils.startNewTrace(scopes); } else { // as we allow a single transaction running on the bound Scope, we finish the previous ones stopPreviousTransactions(); @@ -225,7 +225,7 @@ private void startTracing(final @NotNull Activity activity) { // we can only bind to the scope if there's no running transaction ITransaction transaction = - hub.startTransaction( + scopes.startTransaction( new TransactionContext( activityName, TransactionNameSource.COMPONENT, @@ -278,7 +278,7 @@ private void startTracing(final @NotNull Activity activity) { } // lets bind to the scope so other integrations can pick it up - hub.configureScope( + scopes.configureScope( scope -> { applyScope(scope, transaction); }); @@ -356,10 +356,10 @@ private void finishTransaction( status = SpanStatus.OK; } transaction.finish(status); - if (hub != null) { + if (scopes != null) { // make sure to remove the transaction from scope, as it may contain running children, // therefore `finish` method will not remove it from scope - hub.configureScope( + scopes.configureScope( scope -> { clearScope(scope, transaction); }); @@ -371,9 +371,9 @@ private void finishTransaction( public synchronized void onActivityCreated( final @NotNull Activity activity, final @Nullable Bundle savedInstanceState) { setColdStart(savedInstanceState); - if (hub != null) { + if (scopes != null) { final @Nullable String activityClassName = ClassUtil.getClassName(activity); - hub.configureScope(scope -> scope.setScreen(activityClassName)); + scopes.configureScope(scope -> scope.setScreen(activityClassName)); } startTracing(activity); final @Nullable ISpan ttfdSpan = ttfdSpanMap.get(activity); @@ -429,10 +429,10 @@ public void onActivityPrePaused(@NonNull Activity activity) { // well // this ensures any newly launched activity will not use the app start timestamp as txn start firstActivityCreated = true; - if (hub == null) { + if (scopes == null) { lastPausedTime = AndroidDateUtils.getCurrentSentryDateTime(); } else { - lastPausedTime = hub.getOptions().getDateProvider().now(); + lastPausedTime = scopes.getOptions().getDateProvider().now(); } } } @@ -445,10 +445,10 @@ public synchronized void onActivityPaused(final @NotNull Activity activity) { // well // this ensures any newly launched activity will not use the app start timestamp as txn start firstActivityCreated = true; - if (hub == null) { + if (scopes == null) { lastPausedTime = AndroidDateUtils.getCurrentSentryDateTime(); } else { - lastPausedTime = hub.getOptions().getDateProvider().now(); + lastPausedTime = scopes.getOptions().getDateProvider().now(); } } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransactionProfiler.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransactionProfiler.java index 3abfe1306a..0d232a6ada 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransactionProfiler.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransactionProfiler.java @@ -9,15 +9,15 @@ import android.os.Build; import android.os.Process; import android.os.SystemClock; -import io.sentry.HubAdapter; -import io.sentry.IHub; import io.sentry.ILogger; +import io.sentry.IScopes; import io.sentry.ISentryExecutorService; import io.sentry.ITransaction; import io.sentry.ITransactionProfiler; import io.sentry.PerformanceCollectionData; import io.sentry.ProfilingTraceData; import io.sentry.ProfilingTransactionData; +import io.sentry.ScopesAdapter; import io.sentry.SentryLevel; import io.sentry.SentryOptions; import io.sentry.android.core.internal.util.CpuInfoUtils; @@ -46,8 +46,8 @@ final class AndroidTransactionProfiler implements ITransactionProfiler { private long profileStartCpuMillis; /** - * @deprecated please use a constructor that doesn't takes a {@link IHub} instead, as it would be - * ignored anyway. + * @deprecated please use a constructor that doesn't takes a {@link IScopes} instead, as it would + * be ignored anyway. */ @Deprecated public AndroidTransactionProfiler( @@ -55,7 +55,7 @@ public AndroidTransactionProfiler( final @NotNull SentryAndroidOptions sentryAndroidOptions, final @NotNull BuildInfoProvider buildInfoProvider, final @NotNull SentryFrameMetricsCollector frameMetricsCollector, - final @NotNull IHub hub) { + final @NotNull IScopes scopes) { this(context, sentryAndroidOptions, buildInfoProvider, frameMetricsCollector); } @@ -311,7 +311,7 @@ public void close() { currentProfilingTransactionData.getTraceId(), true, null, - HubAdapter.getInstance().getOptions()); + ScopesAdapter.getInstance().getOptions()); } else if (transactionsCounter != 0) { // in case the app start profiling is running, and it's not bound to a transaction, we still // stop profiling, but we also have to manually update the counter. diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AnrIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/AnrIntegration.java index 0ad2c242da..90d53a3c9b 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AnrIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AnrIntegration.java @@ -5,7 +5,7 @@ import android.annotation.SuppressLint; import android.content.Context; import io.sentry.Hint; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.SentryEvent; import io.sentry.SentryLevel; @@ -48,12 +48,13 @@ public AnrIntegration(final @NotNull Context context) { private static final @NotNull Object watchDogLock = new Object(); @Override - public final void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { + public final void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { this.options = Objects.requireNonNull(options, "SentryOptions is required"); - register(hub, (SentryAndroidOptions) options); + register(scopes, (SentryAndroidOptions) options); } - private void register(final @NotNull IHub hub, final @NotNull SentryAndroidOptions options) { + private void register( + final @NotNull IScopes scopes, final @NotNull SentryAndroidOptions options) { options .getLogger() .log(SentryLevel.DEBUG, "AnrIntegration enabled: %s", options.isAnrEnabled()); @@ -67,7 +68,7 @@ private void register(final @NotNull IHub hub, final @NotNull SentryAndroidOptio () -> { synchronized (startLock) { if (!isClosed) { - startAnrWatchdog(hub, options); + startAnrWatchdog(scopes, options); } } }); @@ -80,7 +81,7 @@ private void register(final @NotNull IHub hub, final @NotNull SentryAndroidOptio } private void startAnrWatchdog( - final @NotNull IHub hub, final @NotNull SentryAndroidOptions options) { + final @NotNull IScopes scopes, final @NotNull SentryAndroidOptions options) { synchronized (watchDogLock) { if (anrWatchDog == null) { options @@ -94,7 +95,7 @@ private void startAnrWatchdog( new ANRWatchDog( options.getAnrTimeoutIntervalMillis(), options.isAnrReportInDebug(), - error -> reportANR(hub, options, error), + error -> reportANR(scopes, options, error), options.getLogger(), context); anrWatchDog.start(); @@ -106,7 +107,7 @@ private void startAnrWatchdog( @TestOnly void reportANR( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull SentryAndroidOptions options, final @NotNull ApplicationNotResponding error) { options.getLogger().log(SentryLevel.INFO, "ANR triggered with message: %s", error.getMessage()); @@ -122,7 +123,7 @@ void reportANR( final AnrHint anrHint = new AnrHint(isAppInBackground); final Hint hint = HintUtils.createWithTypeCheckHint(anrHint); - hub.captureEvent(event, hint); + scopes.captureEvent(event, hint); } private @NotNull Throwable buildAnrThrowable( diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java b/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java index 669233bb09..152ceed322 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java @@ -9,8 +9,8 @@ import io.sentry.Attachment; import io.sentry.DateUtils; import io.sentry.Hint; -import io.sentry.IHub; import io.sentry.ILogger; +import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.SentryEvent; import io.sentry.SentryLevel; @@ -69,7 +69,7 @@ public AnrV2Integration(final @NotNull Context context) { @SuppressLint("NewApi") // we do the check in the AnrIntegrationFactory @Override - public void register(@NotNull IHub hub, @NotNull SentryOptions options) { + public void register(@NotNull IScopes scopes, @NotNull SentryOptions options) { this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -90,7 +90,7 @@ public void register(@NotNull IHub hub, @NotNull SentryOptions options) { try { options .getExecutorService() - .submit(new AnrProcessor(context, hub, this.options, dateProvider)); + .submit(new AnrProcessor(context, scopes, this.options, dateProvider)); } catch (Throwable e) { options.getLogger().log(SentryLevel.DEBUG, "Failed to start AnrProcessor.", e); } @@ -109,17 +109,17 @@ public void close() throws IOException { static class AnrProcessor implements Runnable { private final @NotNull Context context; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @NotNull SentryAndroidOptions options; private final long threshold; AnrProcessor( final @NotNull Context context, - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull SentryAndroidOptions options, final @NotNull ICurrentDateProvider dateProvider) { this.context = context; - this.hub = hub; + this.scopes = scopes; this.options = options; this.threshold = dateProvider.getCurrentTimeMillis() - NINETY_DAYS_THRESHOLD; } @@ -277,7 +277,7 @@ private void reportAsSentryEvent( } } - final @NotNull SentryId sentryId = hub.captureEvent(event, hint); + final @NotNull SentryId sentryId = scopes.captureEvent(event, hint); final boolean isEventDropped = sentryId.equals(SentryId.EMPTY_ID); if (!isEventDropped) { // Block until the event is flushed to disk and the last_reported_anr marker is updated diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegration.java index eef4707683..0d20dc7a90 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegration.java @@ -8,7 +8,7 @@ import android.content.res.Configuration; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -25,7 +25,7 @@ public final class AppComponentsBreadcrumbsIntegration implements Integration, Closeable, ComponentCallbacks2 { private final @NotNull Context context; - private @Nullable IHub hub; + private @Nullable IScopes scopes; private @Nullable SentryAndroidOptions options; public AppComponentsBreadcrumbsIntegration(final @NotNull Context context) { @@ -33,8 +33,8 @@ public AppComponentsBreadcrumbsIntegration(final @NotNull Context context) { } @Override - public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { - this.hub = Objects.requireNonNull(hub, "Hub is required"); + public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -84,7 +84,7 @@ public void close() throws IOException { @SuppressWarnings("deprecation") @Override public void onConfigurationChanged(@NotNull Configuration newConfig) { - if (hub != null) { + if (scopes != null) { final Device.DeviceOrientation deviceOrientation = DeviceOrientations.getOrientation(context.getResources().getConfiguration().orientation); @@ -104,7 +104,7 @@ public void onConfigurationChanged(@NotNull Configuration newConfig) { final Hint hint = new Hint(); hint.set(ANDROID_CONFIGURATION, newConfig); - hub.addBreadcrumb(breadcrumb, hint); + scopes.addBreadcrumb(breadcrumb, hint); } } @@ -119,7 +119,7 @@ public void onTrimMemory(final int level) { } private void createLowMemoryBreadcrumb(final @Nullable Integer level) { - if (hub != null) { + if (scopes != null) { final Breadcrumb breadcrumb = new Breadcrumb(); if (level != null) { // only add breadcrumb if TRIM_MEMORY_BACKGROUND, TRIM_MEMORY_MODERATE or @@ -143,7 +143,7 @@ private void createLowMemoryBreadcrumb(final @Nullable Integer level) { breadcrumb.setMessage("Low memory"); breadcrumb.setData("action", "LOW_MEMORY"); breadcrumb.setLevel(SentryLevel.WARNING); - hub.addBreadcrumb(breadcrumb); + scopes.addBreadcrumb(breadcrumb); } } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AppLifecycleIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/AppLifecycleIntegration.java index 3e8fe6383f..8614a60061 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AppLifecycleIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AppLifecycleIntegration.java @@ -3,7 +3,7 @@ import static io.sentry.util.IntegrationUtils.addIntegrationToSdkVersion; import androidx.lifecycle.ProcessLifecycleOwner; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -32,8 +32,8 @@ public AppLifecycleIntegration() { } @Override - public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { - Objects.requireNonNull(hub, "Hub is required"); + public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + Objects.requireNonNull(scopes, "Scopes are required"); this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -59,11 +59,11 @@ public void register(final @NotNull IHub hub, final @NotNull SentryOptions optio Class.forName("androidx.lifecycle.DefaultLifecycleObserver"); Class.forName("androidx.lifecycle.ProcessLifecycleOwner"); if (AndroidMainThreadChecker.getInstance().isMainThread()) { - addObserver(hub); + addObserver(scopes); } else { // some versions of the androidx lifecycle-process require this to be executed on the main // thread. - handler.post(() -> addObserver(hub)); + handler.post(() -> addObserver(scopes)); } } catch (ClassNotFoundException e) { options @@ -80,7 +80,7 @@ public void register(final @NotNull IHub hub, final @NotNull SentryOptions optio } } - private void addObserver(final @NotNull IHub hub) { + private void addObserver(final @NotNull IScopes scopes) { // this should never happen, check added to avoid warnings from NullAway if (this.options == null) { return; @@ -88,7 +88,7 @@ private void addObserver(final @NotNull IHub hub) { watcher = new LifecycleWatcher( - hub, + scopes, this.options.getSessionTrackingIntervalMillis(), this.options.isEnableAutoSessionTracking(), this.options.isEnableAppLifecycleBreadcrumbs()); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/CurrentActivityIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/CurrentActivityIntegration.java index b4c5f1ed02..0b618636d3 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/CurrentActivityIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/CurrentActivityIntegration.java @@ -4,7 +4,7 @@ import android.app.Application; import android.os.Bundle; import androidx.annotation.NonNull; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.SentryOptions; import io.sentry.util.Objects; @@ -25,7 +25,7 @@ public CurrentActivityIntegration(final @NotNull Application application) { } @Override - public void register(@NotNull IHub hub, @NotNull SentryOptions options) { + public void register(@NotNull IScopes scopes, @NotNull SentryOptions options) { application.registerActivityLifecycleCallbacks(this); } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/EnvelopeFileObserverIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/EnvelopeFileObserverIntegration.java index f99294584b..6e821e5be7 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/EnvelopeFileObserverIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/EnvelopeFileObserverIntegration.java @@ -1,7 +1,7 @@ package io.sentry.android.core; -import io.sentry.IHub; import io.sentry.ILogger; +import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.OutboxSender; import io.sentry.SentryLevel; @@ -24,8 +24,8 @@ public abstract class EnvelopeFileObserverIntegration implements Integration, Cl } @Override - public final void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { - Objects.requireNonNull(hub, "Hub is required"); + public final void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + Objects.requireNonNull(scopes, "Scopes are required"); Objects.requireNonNull(options, "SentryOptions is required"); logger = options.getLogger(); @@ -46,7 +46,7 @@ public final void register(final @NotNull IHub hub, final @NotNull SentryOptions () -> { synchronized (startLock) { if (!isClosed) { - startOutboxSender(hub, options, path); + startOutboxSender(scopes, options, path); } } }); @@ -60,10 +60,12 @@ public final void register(final @NotNull IHub hub, final @NotNull SentryOptions } private void startOutboxSender( - final @NotNull IHub hub, final @NotNull SentryOptions options, final @NotNull String path) { + final @NotNull IScopes scopes, + final @NotNull SentryOptions options, + final @NotNull String path) { final OutboxSender outboxSender = new OutboxSender( - hub, + scopes, options.getEnvelopeReader(), options.getSerializer(), options.getLogger(), diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java index 9bdbe86a77..692d8562f8 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java @@ -4,12 +4,12 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import io.sentry.DateUtils; -import io.sentry.HubAdapter; -import io.sentry.IHub; import io.sentry.ILogger; import io.sentry.IScope; +import io.sentry.IScopes; import io.sentry.ISerializer; import io.sentry.ObjectWriter; +import io.sentry.ScopesAdapter; import io.sentry.SentryEnvelope; import io.sentry.SentryEnvelopeItem; import io.sentry.SentryEvent; @@ -39,12 +39,12 @@ public final class InternalSentrySdk { /** - * @return a copy of the current hub's topmost scope, or null in case the hub is disabled + * @return a copy of the current scopes's topmost scope, or null in case the scopes is disabled */ @Nullable public static IScope getCurrentScope() { final @NotNull AtomicReference scopeRef = new AtomicReference<>(); - HubAdapter.getInstance() + ScopesAdapter.getInstance() .configureScope( scope -> { scopeRef.set(scope.clone()); @@ -134,8 +134,8 @@ public static Map serializeScope( } /** - * Captures the provided envelope. Compared to {@link IHub#captureEvent(SentryEvent)} this method - *
+ * Captures the provided envelope. Compared to {@link IScopes#captureEvent(SentryEvent)} this + * method
* - will not enrich events with additional data (e.g. scope)
* - will not execute beforeSend: it's up to the caller to take care of this
* - will not perform any sampling: it's up to the caller to take care of this
@@ -147,8 +147,8 @@ public static Map serializeScope( */ @Nullable public static SentryId captureEnvelope(final @NotNull byte[] envelopeData) { - final @NotNull IHub hub = HubAdapter.getInstance(); - final @NotNull SentryOptions options = hub.getOptions(); + final @NotNull IScopes scopes = ScopesAdapter.getInstance(); + final @NotNull SentryOptions options = scopes.getOptions(); try (final InputStream envelopeInputStream = new ByteArrayInputStream(envelopeData)) { final @NotNull ISerializer serializer = options.getSerializer(); @@ -178,7 +178,7 @@ public static SentryId captureEnvelope(final @NotNull byte[] envelopeData) { } // update session and add it to envelope if necessary - final @Nullable Session session = updateSession(hub, options, status, crashedOrErrored); + final @Nullable Session session = updateSession(scopes, options, status, crashedOrErrored); if (session != null) { final SentryEnvelopeItem sessionItem = SentryEnvelopeItem.fromSession(serializer, session); envelopeItems.add(sessionItem); @@ -186,7 +186,7 @@ public static SentryId captureEnvelope(final @NotNull byte[] envelopeData) { final SentryEnvelope repackagedEnvelope = new SentryEnvelope(envelope.getHeader(), envelopeItems); - return hub.captureEnvelope(repackagedEnvelope); + return scopes.captureEnvelope(repackagedEnvelope); } catch (Throwable t) { options.getLogger().log(SentryLevel.ERROR, "Failed to capture envelope", t); } @@ -195,12 +195,12 @@ public static SentryId captureEnvelope(final @NotNull byte[] envelopeData) { @Nullable private static Session updateSession( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull SentryOptions options, final @Nullable Session.State status, final boolean crashedOrErrored) { final @NotNull AtomicReference sessionRef = new AtomicReference<>(); - hub.configureScope( + scopes.configureScope( scope -> { final @Nullable Session session = scope.getSession(); if (session != null) { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java b/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java index 7b38bcd9c2..a32fa51d3f 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java @@ -3,7 +3,7 @@ import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.LifecycleOwner; import io.sentry.Breadcrumb; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.SentryLevel; import io.sentry.Session; import io.sentry.android.core.internal.util.BreadcrumbFactory; @@ -25,19 +25,19 @@ final class LifecycleWatcher implements DefaultLifecycleObserver { private @Nullable TimerTask timerTask; private final @Nullable Timer timer; private final @NotNull Object timerLock = new Object(); - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final boolean enableSessionTracking; private final boolean enableAppLifecycleBreadcrumbs; private final @NotNull ICurrentDateProvider currentDateProvider; LifecycleWatcher( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final long sessionIntervalMillis, final boolean enableSessionTracking, final boolean enableAppLifecycleBreadcrumbs) { this( - hub, + scopes, sessionIntervalMillis, enableSessionTracking, enableAppLifecycleBreadcrumbs, @@ -45,7 +45,7 @@ final class LifecycleWatcher implements DefaultLifecycleObserver { } LifecycleWatcher( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final long sessionIntervalMillis, final boolean enableSessionTracking, final boolean enableAppLifecycleBreadcrumbs, @@ -53,7 +53,7 @@ final class LifecycleWatcher implements DefaultLifecycleObserver { this.sessionIntervalMillis = sessionIntervalMillis; this.enableSessionTracking = enableSessionTracking; this.enableAppLifecycleBreadcrumbs = enableAppLifecycleBreadcrumbs; - this.hub = hub; + this.scopes = scopes; this.currentDateProvider = currentDateProvider; if (enableSessionTracking) { timer = new Timer(true); @@ -79,7 +79,7 @@ private void startSession() { final long currentTimeMillis = currentDateProvider.getCurrentTimeMillis(); - hub.configureScope( + scopes.configureScope( scope -> { if (lastUpdatedSession.get() == 0L) { final @Nullable Session currentSession = scope.getSession(); @@ -93,7 +93,7 @@ private void startSession() { if (lastUpdatedSession == 0L || (lastUpdatedSession + sessionIntervalMillis) <= currentTimeMillis) { addSessionBreadcrumb("start"); - hub.startSession(); + scopes.startSession(); } this.lastUpdatedSession.set(currentTimeMillis); } @@ -123,7 +123,7 @@ private void scheduleEndSession() { @Override public void run() { addSessionBreadcrumb("end"); - hub.endSession(); + scopes.endSession(); } }; @@ -148,13 +148,13 @@ private void addAppBreadcrumb(final @NotNull String state) { breadcrumb.setData("state", state); breadcrumb.setCategory("app.lifecycle"); breadcrumb.setLevel(SentryLevel.INFO); - hub.addBreadcrumb(breadcrumb); + scopes.addBreadcrumb(breadcrumb); } } private void addSessionBreadcrumb(final @NotNull String state) { final Breadcrumb breadcrumb = BreadcrumbFactory.forSession(state); - hub.addBreadcrumb(breadcrumb); + scopes.addBreadcrumb(breadcrumb); } @TestOnly diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/NdkIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/NdkIntegration.java index 3a4a91498e..dc464303c6 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/NdkIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/NdkIntegration.java @@ -2,7 +2,7 @@ import static io.sentry.util.IntegrationUtils.addIntegrationToSdkVersion; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -28,8 +28,8 @@ public NdkIntegration(final @Nullable Class sentryNdkClass) { } @Override - public final void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { - Objects.requireNonNull(hub, "Hub is required"); + public final void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + Objects.requireNonNull(scopes, "Scopes are required"); this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -38,7 +38,8 @@ public final void register(final @NotNull IHub hub, final @NotNull SentryOptions final boolean enabled = this.options.isEnableNdk(); this.options.getLogger().log(SentryLevel.DEBUG, "NdkIntegration enabled: %s", enabled); - // Note: `hub` isn't used here because the NDK integration writes files to disk which are picked + // Note: `scopes` isn't used here because the NDK integration writes files to disk which are + // picked // up by another integration (EnvelopeFileObserverIntegration). if (enabled && sentryNdkClass != null) { final String cachedDir = this.options.getCacheDirPath(); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/NetworkBreadcrumbsIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/NetworkBreadcrumbsIntegration.java index 1cd42e9dab..9f1dd3ecf6 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/NetworkBreadcrumbsIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/NetworkBreadcrumbsIntegration.java @@ -13,8 +13,8 @@ import io.sentry.Breadcrumb; import io.sentry.DateUtils; import io.sentry.Hint; -import io.sentry.IHub; import io.sentry.ILogger; +import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.SentryDateProvider; import io.sentry.SentryLevel; @@ -50,8 +50,8 @@ public NetworkBreadcrumbsIntegration( @SuppressLint("NewApi") @Override - public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { - Objects.requireNonNull(hub, "Hub is required"); + public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + Objects.requireNonNull(scopes, "Scopes are required"); SentryAndroidOptions androidOptions = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -72,7 +72,8 @@ public void register(final @NotNull IHub hub, final @NotNull SentryOptions optio } networkCallback = - new NetworkBreadcrumbsNetworkCallback(hub, buildInfoProvider, options.getDateProvider()); + new NetworkBreadcrumbsNetworkCallback( + scopes, buildInfoProvider, options.getDateProvider()); final boolean registered = AndroidConnectionStatusProvider.registerNetworkCallback( context, logger, buildInfoProvider, networkCallback); @@ -101,7 +102,7 @@ public void close() throws IOException { @SuppressLint("ObsoleteSdkInt") @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) static final class NetworkBreadcrumbsNetworkCallback extends ConnectivityManager.NetworkCallback { - final @NotNull IHub hub; + final @NotNull IScopes scopes; final @NotNull BuildInfoProvider buildInfoProvider; @Nullable Network currentNetwork = null; @@ -111,10 +112,10 @@ static final class NetworkBreadcrumbsNetworkCallback extends ConnectivityManager final @NotNull SentryDateProvider dateProvider; NetworkBreadcrumbsNetworkCallback( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull BuildInfoProvider buildInfoProvider, final @NotNull SentryDateProvider dateProvider) { - this.hub = Objects.requireNonNull(hub, "Hub is required"); + this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); this.buildInfoProvider = Objects.requireNonNull(buildInfoProvider, "BuildInfoProvider is required"); this.dateProvider = Objects.requireNonNull(dateProvider, "SentryDateProvider is required"); @@ -126,7 +127,7 @@ public void onAvailable(final @NonNull Network network) { return; } final Breadcrumb breadcrumb = createBreadcrumb("NETWORK_AVAILABLE"); - hub.addBreadcrumb(breadcrumb); + scopes.addBreadcrumb(breadcrumb); currentNetwork = network; lastCapabilities = null; } @@ -156,7 +157,7 @@ public void onCapabilitiesChanged( } Hint hint = new Hint(); hint.set(TypeCheckHint.ANDROID_NETWORK_CAPABILITIES, connectionDetail); - hub.addBreadcrumb(breadcrumb, hint); + scopes.addBreadcrumb(breadcrumb, hint); } @Override @@ -165,7 +166,7 @@ public void onLost(final @NonNull Network network) { return; } final Breadcrumb breadcrumb = createBreadcrumb("NETWORK_LOST"); - hub.addBreadcrumb(breadcrumb); + scopes.addBreadcrumb(breadcrumb); currentNetwork = null; lastCapabilities = null; } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/PhoneStateBreadcrumbsIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/PhoneStateBreadcrumbsIntegration.java index c10d25b057..cae1492d3e 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/PhoneStateBreadcrumbsIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/PhoneStateBreadcrumbsIntegration.java @@ -6,7 +6,7 @@ import android.content.Context; import android.telephony.TelephonyManager; import io.sentry.Breadcrumb; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -32,8 +32,8 @@ public PhoneStateBreadcrumbsIntegration(final @NotNull Context context) { } @Override - public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { - Objects.requireNonNull(hub, "Hub is required"); + public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + Objects.requireNonNull(scopes, "Scopes are required"); this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -55,7 +55,7 @@ public void register(final @NotNull IHub hub, final @NotNull SentryOptions optio () -> { synchronized (startLock) { if (!isClosed) { - startTelephonyListener(hub, options); + startTelephonyListener(scopes, options); } } }); @@ -72,11 +72,11 @@ public void register(final @NotNull IHub hub, final @NotNull SentryOptions optio @SuppressWarnings("deprecation") private void startTelephonyListener( - final @NotNull IHub hub, final @NotNull SentryOptions options) { + final @NotNull IScopes scopes, final @NotNull SentryOptions options) { telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); if (telephonyManager != null) { try { - listener = new PhoneStateChangeListener(hub); + listener = new PhoneStateChangeListener(scopes); telephonyManager.listen(listener, android.telephony.PhoneStateListener.LISTEN_CALL_STATE); options.getLogger().log(SentryLevel.DEBUG, "PhoneStateBreadcrumbsIntegration installed."); @@ -110,10 +110,10 @@ public void close() throws IOException { @SuppressWarnings("deprecation") static final class PhoneStateChangeListener extends android.telephony.PhoneStateListener { - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; - PhoneStateChangeListener(final @NotNull IHub hub) { - this.hub = hub; + PhoneStateChangeListener(final @NotNull IScopes scopes) { + this.scopes = scopes; } @SuppressWarnings("deprecation") @@ -128,7 +128,7 @@ public void onCallStateChanged(int state, String incomingNumber) { breadcrumb.setData("action", "CALL_STATE_RINGING"); breadcrumb.setMessage("Device ringing"); breadcrumb.setLevel(SentryLevel.INFO); - hub.addBreadcrumb(breadcrumb); + scopes.addBreadcrumb(breadcrumb); } } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SendCachedEnvelopeIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/SendCachedEnvelopeIntegration.java index 66e534bb7d..64f1cab362 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SendCachedEnvelopeIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SendCachedEnvelopeIntegration.java @@ -2,7 +2,7 @@ import io.sentry.DataCategory; import io.sentry.IConnectionStatusProvider; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.SendCachedEnvelopeFireAndForgetIntegration; import io.sentry.SentryLevel; @@ -28,7 +28,7 @@ final class SendCachedEnvelopeIntegration private final @NotNull LazyEvaluator startupCrashMarkerEvaluator; private final AtomicBoolean startupCrashHandled = new AtomicBoolean(false); private @Nullable IConnectionStatusProvider connectionStatusProvider; - private @Nullable IHub hub; + private @Nullable IScopes scopes; private @Nullable SentryAndroidOptions options; private @Nullable SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget sender; private final AtomicBoolean isInitialized = new AtomicBoolean(false); @@ -42,8 +42,8 @@ public SendCachedEnvelopeIntegration( } @Override - public void register(@NotNull IHub hub, @NotNull SentryOptions options) { - this.hub = Objects.requireNonNull(hub, "Hub is required"); + public void register(@NotNull IScopes scopes, @NotNull SentryOptions options) { + this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -55,7 +55,7 @@ public void register(@NotNull IHub hub, @NotNull SentryOptions options) { return; } - sendCachedEnvelopes(hub, this.options); + sendCachedEnvelopes(scopes, this.options); } @Override @@ -69,14 +69,14 @@ public void close() throws IOException { @Override public void onConnectionStatusChanged( final @NotNull IConnectionStatusProvider.ConnectionStatus status) { - if (hub != null && options != null) { - sendCachedEnvelopes(hub, options); + if (scopes != null && options != null) { + sendCachedEnvelopes(scopes, options); } } @SuppressWarnings({"NullAway"}) private synchronized void sendCachedEnvelopes( - final @NotNull IHub hub, final @NotNull SentryAndroidOptions options) { + final @NotNull IScopes scopes, final @NotNull SentryAndroidOptions options) { try { final Future future = options @@ -97,7 +97,7 @@ private synchronized void sendCachedEnvelopes( connectionStatusProvider = options.getConnectionStatusProvider(); connectionStatusProvider.addConnectionStatusObserver(this); - sender = factory.create(hub, options); + sender = factory.create(scopes, options); } if (connectionStatusProvider != null @@ -110,7 +110,7 @@ private synchronized void sendCachedEnvelopes( } // in case there's rate limiting active, skip processing - final @Nullable RateLimiter rateLimiter = hub.getRateLimiter(); + final @Nullable RateLimiter rateLimiter = scopes.getRateLimiter(); if (rateLimiter != null && rateLimiter.isActiveForCategory(DataCategory.All)) { options diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java index af68a026fb..424de4d82e 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java @@ -4,8 +4,8 @@ import android.content.Context; import android.os.Process; import android.os.SystemClock; -import io.sentry.IHub; import io.sentry.ILogger; +import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.OptionsContainer; import io.sentry.Sentry; @@ -144,10 +144,11 @@ public static synchronized void init( }, true); - final @NotNull IHub hub = Sentry.getCurrentHub(); - if (hub.getOptions().isEnableAutoSessionTracking() && ContextUtils.isForegroundImportance()) { - hub.addBreadcrumb(BreadcrumbFactory.forSession("session.start")); - hub.startSession(); + final @NotNull IScopes scopes = Sentry.getCurrentScopes(); + if (scopes.getOptions().isEnableAutoSessionTracking() + && ContextUtils.isForegroundImportance()) { + scopes.addBreadcrumb(BreadcrumbFactory.forSession("session.start")); + scopes.startSession(); } } catch (IllegalAccessException e) { logger.log(SentryLevel.FATAL, "Fatal error during SentryAndroid.init(...)", e); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java index 1c22a7dcc8..333ece2148 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java @@ -40,8 +40,8 @@ import android.os.Bundle; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IHub; import io.sentry.ILogger; +import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -80,8 +80,8 @@ public SystemEventsBreadcrumbsIntegration( } @Override - public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { - Objects.requireNonNull(hub, "Hub is required"); + public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + Objects.requireNonNull(scopes, "Scopes are required"); this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -103,7 +103,7 @@ public void register(final @NotNull IHub hub, final @NotNull SentryOptions optio () -> { synchronized (startLock) { if (!isClosed) { - startSystemEventsReceiver(hub, (SentryAndroidOptions) options); + startSystemEventsReceiver(scopes, (SentryAndroidOptions) options); } } }); @@ -119,8 +119,8 @@ public void register(final @NotNull IHub hub, final @NotNull SentryOptions optio } private void startSystemEventsReceiver( - final @NotNull IHub hub, final @NotNull SentryAndroidOptions options) { - receiver = new SystemEventsBroadcastReceiver(hub, options.getLogger()); + final @NotNull IScopes scopes, final @NotNull SentryAndroidOptions options) { + receiver = new SystemEventsBroadcastReceiver(scopes, options.getLogger()); final IntentFilter filter = new IntentFilter(); for (String item : actions) { filter.addAction(item); @@ -204,11 +204,11 @@ public void close() throws IOException { static final class SystemEventsBroadcastReceiver extends BroadcastReceiver { - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @NotNull ILogger logger; - SystemEventsBroadcastReceiver(final @NotNull IHub hub, final @NotNull ILogger logger) { - this.hub = hub; + SystemEventsBroadcastReceiver(final @NotNull IScopes scopes, final @NotNull ILogger logger) { + this.scopes = scopes; this.logger = logger; } @@ -249,7 +249,7 @@ public void onReceive(Context context, Intent intent) { final Hint hint = new Hint(); hint.set(ANDROID_INTENT, intent); - hub.addBreadcrumb(breadcrumb, hint); + scopes.addBreadcrumb(breadcrumb, hint); } } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/TempSensorBreadcrumbsIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/TempSensorBreadcrumbsIntegration.java index eaf5c64991..4d0e9c7e60 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/TempSensorBreadcrumbsIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/TempSensorBreadcrumbsIntegration.java @@ -11,7 +11,7 @@ import android.hardware.SensorManager; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -26,7 +26,7 @@ public final class TempSensorBreadcrumbsIntegration implements Integration, Closeable, SensorEventListener { private final @NotNull Context context; - private @Nullable IHub hub; + private @Nullable IScopes scopes; private @Nullable SentryAndroidOptions options; @TestOnly @Nullable SensorManager sensorManager; @@ -38,8 +38,8 @@ public TempSensorBreadcrumbsIntegration(final @NotNull Context context) { } @Override - public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { - this.hub = Objects.requireNonNull(hub, "Hub is required"); + public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -121,7 +121,7 @@ public void onSensorChanged(final @NotNull SensorEvent event) { return; } - if (hub != null) { + if (scopes != null) { final Breadcrumb breadcrumb = new Breadcrumb(); breadcrumb.setType("system"); breadcrumb.setCategory("device.event"); @@ -134,7 +134,7 @@ public void onSensorChanged(final @NotNull SensorEvent event) { final Hint hint = new Hint(); hint.set(ANDROID_SENSOR_EVENT, event); - hub.addBreadcrumb(breadcrumb, hint); + scopes.addBreadcrumb(breadcrumb, hint); } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/UserInteractionIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/UserInteractionIntegration.java index c361529671..712651b460 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/UserInteractionIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/UserInteractionIntegration.java @@ -6,7 +6,7 @@ import android.app.Application; import android.os.Bundle; import android.view.Window; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -23,7 +23,7 @@ public final class UserInteractionIntegration implements Integration, Closeable, Application.ActivityLifecycleCallbacks { private final @NotNull Application application; - private @Nullable IHub hub; + private @Nullable IScopes scopes; private @Nullable SentryAndroidOptions options; private final boolean isAndroidXAvailable; @@ -44,14 +44,14 @@ private void startTracking(final @NotNull Activity activity) { return; } - if (hub != null && options != null) { + if (scopes != null && options != null) { Window.Callback delegate = window.getCallback(); if (delegate == null) { delegate = new NoOpWindowCallback(); } final SentryGestureListener gestureListener = - new SentryGestureListener(activity, hub, options); + new SentryGestureListener(activity, scopes, options); window.setCallback(new SentryWindowCallback(delegate, activity, gestureListener, options)); } } @@ -102,13 +102,13 @@ public void onActivitySaveInstanceState(@NotNull Activity activity, @NotNull Bun public void onActivityDestroyed(@NotNull Activity activity) {} @Override - public void register(@NotNull IHub hub, @NotNull SentryOptions options) { + public void register(@NotNull IScopes scopes, @NotNull SentryOptions options) { this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, "SentryAndroidOptions is required"); - this.hub = Objects.requireNonNull(hub, "Hub is required"); + this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); final boolean integrationEnabled = this.options.isEnableUserInteractionBreadcrumbs() diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java index 0ec0d83258..9154f3e7c6 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java @@ -10,8 +10,8 @@ import android.view.Window; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IHub; import io.sentry.IScope; +import io.sentry.IScopes; import io.sentry.ITransaction; import io.sentry.SentryLevel; import io.sentry.SpanStatus; @@ -43,7 +43,7 @@ private enum GestureType { private static final String TRACE_ORIGIN = "auto.ui.gesture_listener"; private final @NotNull WeakReference activityRef; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @NotNull SentryAndroidOptions options; private @Nullable UiElement activeUiElement = null; @@ -54,10 +54,10 @@ private enum GestureType { public SentryGestureListener( final @NotNull Activity currentActivity, - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull SentryAndroidOptions options) { this.activityRef = new WeakReference<>(currentActivity); - this.hub = hub; + this.scopes = scopes; this.options = options; } @@ -185,7 +185,7 @@ private void addBreadcrumb( hint.set(ANDROID_MOTION_EVENT, motionEvent); hint.set(ANDROID_VIEW, target.getView()); - hub.addBreadcrumb( + scopes.addBreadcrumb( Breadcrumb.userInteraction( type, target.getResourceName(), target.getClassName(), target.getTag(), additionalData), hint); @@ -202,7 +202,7 @@ private void startTracing(final @NotNull UiElement target, final @NotNull Gestur if (!(options.isTracingEnabled() && options.isEnableUserInteractionTracing())) { if (isNewInteraction) { - TracingUtils.startNewTrace(hub); + TracingUtils.startNewTrace(scopes); activeUiElement = target; activeEventType = eventType; } @@ -253,12 +253,12 @@ private void startTracing(final @NotNull UiElement target, final @NotNull Gestur transactionOptions.setTrimEnd(true); final ITransaction transaction = - hub.startTransaction( + scopes.startTransaction( new TransactionContext(name, TransactionNameSource.COMPONENT, op), transactionOptions); transaction.getSpanContext().setOrigin(TRACE_ORIGIN + "." + target.getOrigin()); - hub.configureScope( + scopes.configureScope( scope -> { applyScope(scope, transaction); }); @@ -278,7 +278,7 @@ void stopTracing(final @NotNull SpanStatus status) { activeTransaction.finish(); } } - hub.configureScope( + scopes.configureScope( scope -> { // avoid method refs on Android due to some issues with older AGP setups // noinspection Convert2MethodRef diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidTransactionProfilerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidTransactionProfilerTest.kt index 405aa6dc98..436c3ee6f9 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidTransactionProfilerTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidTransactionProfilerTest.kt @@ -5,8 +5,8 @@ import android.os.Build import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.CpuCollectionData -import io.sentry.IHub import io.sentry.ILogger +import io.sentry.IScopes import io.sentry.ISentryExecutorService import io.sentry.MemoryCollectionData import io.sentry.PerformanceCollectionData @@ -89,7 +89,7 @@ class AndroidTransactionProfilerTest { executorService = mockExecutorService } - val hub: IHub = mock() + val scopes: IScopes = mock() val frameMetricsCollector: SentryFrameMetricsCollector = mock() lateinit var transaction1: SentryTracer @@ -97,10 +97,10 @@ class AndroidTransactionProfilerTest { lateinit var transaction3: SentryTracer fun getSut(context: Context, buildInfoProvider: BuildInfoProvider = buildInfo): AndroidTransactionProfiler { - whenever(hub.options).thenReturn(options) - transaction1 = SentryTracer(TransactionContext("", ""), hub) - transaction2 = SentryTracer(TransactionContext("", ""), hub) - transaction3 = SentryTracer(TransactionContext("", ""), hub) + whenever(scopes.options).thenReturn(options) + transaction1 = SentryTracer(TransactionContext("", ""), scopes) + transaction2 = SentryTracer(TransactionContext("", ""), scopes) + transaction3 = SentryTracer(TransactionContext("", ""), scopes) return AndroidTransactionProfiler(context, options, buildInfoProvider, frameMetricsCollector) } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AnrIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AnrIntegrationTest.kt index cceabc9774..1a74a47ae1 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AnrIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AnrIntegrationTest.kt @@ -2,7 +2,7 @@ package io.sentry.android.core import android.content.Context import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryLevel import io.sentry.android.core.AnrIntegration.AnrHint import io.sentry.exception.ExceptionMechanismException @@ -24,7 +24,7 @@ class AnrIntegrationTest { private class Fixture { val context = mock() - val hub = mock() + val scopes = mock() var options: SentryAndroidOptions = SentryAndroidOptions().apply { setLogger(mock()) } @@ -49,7 +49,7 @@ class AnrIntegrationTest { fixture.options.executorService = ImmediateExecutorService() val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertNotNull(sut.anrWatchDog) assertTrue((sut.anrWatchDog as ANRWatchDog).isAlive) @@ -60,7 +60,7 @@ class AnrIntegrationTest { fixture.options.executorService = mock() val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertNull(sut.anrWatchDog) } @@ -70,7 +70,7 @@ class AnrIntegrationTest { val sut = fixture.getSut() fixture.options.isAnrEnabled = false - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertNull(sut.anrWatchDog) } @@ -79,9 +79,9 @@ class AnrIntegrationTest { fun `When ANR watch dog is triggered, it should capture an error event with AnrHint`() { val sut = fixture.getSut() - sut.reportANR(fixture.hub, fixture.options, getApplicationNotResponding()) + sut.reportANR(fixture.scopes, fixture.options, getApplicationNotResponding()) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { assertEquals(SentryLevel.ERROR, it.level) }, @@ -97,7 +97,7 @@ class AnrIntegrationTest { val sut = fixture.getSut() fixture.options.executorService = ImmediateExecutorService() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertNotNull(sut.anrWatchDog) @@ -107,11 +107,11 @@ class AnrIntegrationTest { } @Test - fun `when hub is closed right after start, integration is not registered`() { + fun `when scopes is closed right after start, integration is not registered`() { val deferredExecutorService = DeferredExecutorService() val sut = fixture.getSut() fixture.options.executorService = deferredExecutorService - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertNull(sut.anrWatchDog) sut.close() deferredExecutorService.runAll() @@ -122,9 +122,9 @@ class AnrIntegrationTest { fun `When ANR watch dog is triggered, constructs exception with proper mechanism and snapshot flag`() { val sut = fixture.getSut() - sut.reportANR(fixture.hub, fixture.options, getApplicationNotResponding()) + sut.reportANR(fixture.scopes, fixture.options, getApplicationNotResponding()) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { val ex = it.throwableMechanism as ExceptionMechanismException assertTrue(ex.isSnapshot) @@ -139,9 +139,9 @@ class AnrIntegrationTest { val sut = fixture.getSut() AppState.getInstance().setInBackground(true) - sut.reportANR(fixture.hub, fixture.options, getApplicationNotResponding()) + sut.reportANR(fixture.scopes, fixture.options, getApplicationNotResponding()) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { val message = it.throwable?.message assertTrue(message?.startsWith("Background") == true) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2IntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2IntegrationTest.kt index 885ad22c8f..1abcd43719 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2IntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2IntegrationTest.kt @@ -6,8 +6,8 @@ import android.content.Context import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.Hint -import io.sentry.IHub import io.sentry.ILogger +import io.sentry.IScopes import io.sentry.SentryEnvelope import io.sentry.SentryLevel import io.sentry.android.core.AnrV2Integration.AnrV2Hint @@ -59,7 +59,7 @@ class AnrV2IntegrationTest { lateinit var lastReportedAnrFile: File val options = SentryAndroidOptions() - val hub = mock() + val scopes = mock() val logger = mock() fun getSut( @@ -93,7 +93,7 @@ class AnrV2IntegrationTest { lastReportedAnrFile = File(cacheDir, AndroidEnvelopeCache.LAST_ANR_REPORT) lastReportedAnrFile.writeText(lastReportedAnrTimestamp.toString()) } - whenever(hub.captureEvent(any(), anyOrNull())).thenReturn(lastEventId) + whenever(scopes.captureEvent(any(), anyOrNull())).thenReturn(lastEventId) return AnrV2Integration(context) } @@ -170,7 +170,7 @@ class AnrV2IntegrationTest { fun `when cacheDir is not set, does not process historical exits`() { val integration = fixture.getSut(null, useImmediateExecutorService = false) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) verify(fixture.options.executorService, never()).submit(any()) } @@ -180,7 +180,7 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir, isAnrEnabled = false, useImmediateExecutorService = false) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) verify(fixture.options.executorService, never()).submit(any()) } @@ -189,9 +189,9 @@ class AnrV2IntegrationTest { fun `when historical exit list is empty, does not process historical exits`() { val integration = fixture.getSut(tmpDir) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) - verify(fixture.hub, never()).captureEvent(any(), anyOrNull()) + verify(fixture.scopes, never()).captureEvent(any(), anyOrNull()) } @Test @@ -199,9 +199,9 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir) fixture.addAppExitInfo(reason = null) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) - verify(fixture.hub, never()).captureEvent(any(), anyOrNull()) + verify(fixture.scopes, never()).captureEvent(any(), anyOrNull()) } @Test @@ -212,9 +212,9 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir) fixture.addAppExitInfo(timestamp = oldTimestamp) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) - verify(fixture.hub, never()).captureEvent(any(), anyOrNull()) + verify(fixture.scopes, never()).captureEvent(any(), anyOrNull()) } @Test @@ -222,9 +222,9 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir, lastReportedAnrTimestamp = oldTimestamp) fixture.addAppExitInfo(timestamp = oldTimestamp) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) - verify(fixture.hub, never()).captureEvent(any(), anyOrNull()) + verify(fixture.scopes, never()).captureEvent(any(), anyOrNull()) } @Test @@ -232,9 +232,9 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir, lastReportedAnrTimestamp = null) fixture.addAppExitInfo(timestamp = oldTimestamp) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) - verify(fixture.hub).captureEvent(any(), anyOrNull()) + verify(fixture.scopes).captureEvent(any(), anyOrNull()) } @Test @@ -242,9 +242,9 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir, lastReportedAnrTimestamp = oldTimestamp) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { assertEquals(newTimestamp, it.timestamp.time) assertEquals(SentryLevel.FATAL, it.level) @@ -291,9 +291,9 @@ class AnrV2IntegrationTest { importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND ) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( any(), argThat { val hint = HintUtils.getSentrySdkHint(this) @@ -311,7 +311,7 @@ class AnrV2IntegrationTest { ) fixture.addAppExitInfo(timestamp = newTimestamp) - whenever(fixture.hub.captureEvent(any(), any())).thenAnswer { invocation -> + whenever(fixture.scopes.captureEvent(any(), any())).thenAnswer { invocation -> val hint = HintUtils.getSentrySdkHint(invocation.getArgument(1)) as DiskFlushNotification thread { @@ -321,9 +321,9 @@ class AnrV2IntegrationTest { SentryId() } - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) - verify(fixture.hub).captureEvent(any(), anyOrNull()) + verify(fixture.scopes).captureEvent(any(), anyOrNull()) // shouldn't fall into timed out state, because we marked event as flushed on another thread verify(fixture.logger, never()).log( any(), @@ -341,9 +341,9 @@ class AnrV2IntegrationTest { ) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) - verify(fixture.hub).captureEvent(any(), anyOrNull()) + verify(fixture.scopes).captureEvent(any(), anyOrNull()) // we do not call markFlushed, hence it should time out waiting for flush, but because // we drop the event, it should not even come to this if-check verify(fixture.logger, never()).log( @@ -360,9 +360,9 @@ class AnrV2IntegrationTest { fixture.addAppExitInfo(timestamp = newTimestamp - 1 * 60 * 1000) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) - verify(fixture.hub, times(2)).captureEvent( + verify(fixture.scopes, times(2)).captureEvent( any(), argThat { val hint = HintUtils.getSentrySdkHint(this) @@ -382,10 +382,10 @@ class AnrV2IntegrationTest { fixture.addAppExitInfo(timestamp = newTimestamp - 1 * 60 * 1000) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) // only the latest anr is reported which should be enrichable - verify(fixture.hub, atMost(1)).captureEvent( + verify(fixture.scopes, atMost(1)).captureEvent( any(), argThat { val hint = HintUtils.getSentrySdkHint(this) @@ -402,20 +402,20 @@ class AnrV2IntegrationTest { fixture.addAppExitInfo(timestamp = newTimestamp - TimeUnit.DAYS.toMillis(1)) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) // the order is reverse here, so the oldest ANR will be reported first to keep track of // last reported ANR in a marker file - inOrder(fixture.hub) { - verify(fixture.hub).captureEvent( + inOrder(fixture.scopes) { + verify(fixture.scopes).captureEvent( argThat { timestamp.time == newTimestamp - TimeUnit.DAYS.toMillis(2) }, anyOrNull() ) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( argThat { timestamp.time == newTimestamp - TimeUnit.DAYS.toMillis(1) }, anyOrNull() ) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( argThat { timestamp.time == newTimestamp }, anyOrNull() ) @@ -427,9 +427,9 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir, lastReportedAnrTimestamp = oldTimestamp) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( any(), argThat { val hint = HintUtils.getSentrySdkHint(this) @@ -443,9 +443,9 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir, lastReportedAnrTimestamp = oldTimestamp) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( any(), argThat { val hint = HintUtils.getSentrySdkHint(this) @@ -472,7 +472,7 @@ class AnrV2IntegrationTest { ) } - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) // we store envelope with StartSessionHint on different thread after some delay, which // triggers the previous session flush, so no timeout @@ -493,14 +493,14 @@ class AnrV2IntegrationTest { ) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) verify(fixture.logger, never()).log( any(), argThat { startsWith("Timed out waiting to flush previous session to its own file.") }, any() ) - verify(fixture.hub).captureEvent(any(), any()) + verify(fixture.scopes).captureEvent(any(), any()) } @Test @@ -512,7 +512,7 @@ class AnrV2IntegrationTest { ) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) verify(fixture.logger).log( any(), @@ -532,9 +532,9 @@ class AnrV2IntegrationTest { ) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( any(), check { assertNotNull(it.threadDump) @@ -547,8 +547,8 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir, lastReportedAnrTimestamp = oldTimestamp) fixture.addAppExitInfo(timestamp = newTimestamp, addTrace = false) - integration.register(fixture.hub, fixture.options) + integration.register(fixture.scopes, fixture.options) - verify(fixture.hub, never()).captureEvent(any(), anyOrNull()) + verify(fixture.scopes, never()).captureEvent(any(), anyOrNull()) } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegrationTest.kt index 15a6d690e5..5f8792c850 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegrationTest.kt @@ -5,7 +5,7 @@ import android.content.Context import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.Breadcrumb -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryLevel import org.junit.runner.RunWith import org.mockito.kotlin.any @@ -37,8 +37,8 @@ class AppComponentsBreadcrumbsIntegrationTest { fun `When app components breadcrumb is enabled, it registers callback`() { val sut = fixture.getSut() val options = SentryAndroidOptions() - val hub = mock() - sut.register(hub, options) + val scopes = mock() + sut.register(scopes, options) verify(fixture.context).registerComponentCallbacks(any()) } @@ -46,10 +46,10 @@ class AppComponentsBreadcrumbsIntegrationTest { fun `When app components breadcrumb is enabled, but ComponentCallbacks is not ready, do not throw`() { val sut = fixture.getSut() val options = SentryAndroidOptions() - val hub = mock() - sut.register(hub, options) + val scopes = mock() + sut.register(scopes, options) whenever(fixture.context.registerComponentCallbacks(any())).thenThrow(NullPointerException()) - sut.register(hub, options) + sut.register(scopes, options) assertFalse(options.isEnableAppComponentBreadcrumbs) } @@ -59,8 +59,8 @@ class AppComponentsBreadcrumbsIntegrationTest { val options = SentryAndroidOptions().apply { isEnableAppComponentBreadcrumbs = false } - val hub = mock() - sut.register(hub, options) + val scopes = mock() + sut.register(scopes, options) verify(fixture.context, never()).registerComponentCallbacks(any()) } @@ -68,8 +68,8 @@ class AppComponentsBreadcrumbsIntegrationTest { fun `When AppComponentsBreadcrumbsIntegrationTest is closed, it should unregister the callback`() { val sut = fixture.getSut() val options = SentryAndroidOptions() - val hub = mock() - sut.register(hub, options) + val scopes = mock() + sut.register(scopes, options) sut.close() verify(fixture.context).unregisterComponentCallbacks(any()) } @@ -78,10 +78,10 @@ class AppComponentsBreadcrumbsIntegrationTest { fun `When app components breadcrumb is closed, but ComponentCallbacks is not ready, do not throw`() { val sut = fixture.getSut() val options = SentryAndroidOptions() - val hub = mock() + val scopes = mock() whenever(fixture.context.registerComponentCallbacks(any())).thenThrow(NullPointerException()) whenever(fixture.context.unregisterComponentCallbacks(any())).thenThrow(NullPointerException()) - sut.register(hub, options) + sut.register(scopes, options) sut.close() } @@ -89,10 +89,10 @@ class AppComponentsBreadcrumbsIntegrationTest { fun `When low memory event, a breadcrumb with type, category and level should be set`() { val sut = fixture.getSut() val options = SentryAndroidOptions() - val hub = mock() - sut.register(hub, options) + val scopes = mock() + sut.register(scopes, options) sut.onLowMemory() - verify(hub).addBreadcrumb( + verify(scopes).addBreadcrumb( check { assertEquals("device.event", it.category) assertEquals("system", it.type) @@ -105,10 +105,10 @@ class AppComponentsBreadcrumbsIntegrationTest { fun `When trim memory event with level, a breadcrumb with type, category and level should be set`() { val sut = fixture.getSut() val options = SentryAndroidOptions() - val hub = mock() - sut.register(hub, options) + val scopes = mock() + sut.register(scopes, options) sut.onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) - verify(hub).addBreadcrumb( + verify(scopes).addBreadcrumb( check { assertEquals("device.event", it.category) assertEquals("system", it.type) @@ -121,20 +121,20 @@ class AppComponentsBreadcrumbsIntegrationTest { fun `When trim memory event with level not so high, do not add a breadcrumb`() { val sut = fixture.getSut() val options = SentryAndroidOptions() - val hub = mock() - sut.register(hub, options) + val scopes = mock() + sut.register(scopes, options) sut.onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) - verify(hub, never()).addBreadcrumb(any()) + verify(scopes, never()).addBreadcrumb(any()) } @Test fun `When device orientation event, a breadcrumb with type, category and level should be set`() { val sut = AppComponentsBreadcrumbsIntegration(ApplicationProvider.getApplicationContext()) val options = SentryAndroidOptions() - val hub = mock() - sut.register(hub, options) + val scopes = mock() + sut.register(scopes, options) sut.onConfigurationChanged(mock()) - verify(hub).addBreadcrumb( + verify(scopes).addBreadcrumb( check { assertEquals("device.orientation", it.category) assertEquals("navigation", it.type) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AppLifecycleIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AppLifecycleIntegrationTest.kt index ed8d53227c..733aefa8d6 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AppLifecycleIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AppLifecycleIntegrationTest.kt @@ -2,7 +2,7 @@ package io.sentry.android.core import android.os.Looper import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.sentry.IHub +import io.sentry.IScopes import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.mock @@ -17,7 +17,7 @@ import kotlin.test.assertNull class AppLifecycleIntegrationTest { private class Fixture { - val hub = mock() + val scopes = mock() lateinit var handler: MainLooperHandler val options = SentryAndroidOptions() @@ -33,7 +33,7 @@ class AppLifecycleIntegrationTest { fun `When AppLifecycleIntegration is added, lifecycle watcher should be started`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertNotNull(sut.watcher) } @@ -46,7 +46,7 @@ class AppLifecycleIntegrationTest { isEnableAutoSessionTracking = false } - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertNull(sut.watcher) } @@ -55,7 +55,7 @@ class AppLifecycleIntegrationTest { fun `When AppLifecycleIntegration is closed, lifecycle watcher should be closed`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertNotNull(sut.watcher) @@ -70,7 +70,7 @@ class AppLifecycleIntegrationTest { val latch = CountDownLatch(1) Thread { - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) latch.countDown() }.start() @@ -84,7 +84,7 @@ class AppLifecycleIntegrationTest { val sut = fixture.getSut() val latch = CountDownLatch(1) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertNotNull(sut.watcher) @@ -103,7 +103,7 @@ class AppLifecycleIntegrationTest { val sut = fixture.getSut(mockHandler = false) val latch = CountDownLatch(1) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertNotNull(sut.watcher) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/CurrentActivityIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/CurrentActivityIntegrationTest.kt index 6330623121..ecdbff5104 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/CurrentActivityIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/CurrentActivityIntegrationTest.kt @@ -3,7 +3,7 @@ package io.sentry.android.core import android.app.Activity import android.app.Application import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.sentry.IHub +import io.sentry.IScopes import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.mock @@ -19,7 +19,7 @@ class CurrentActivityIntegrationTest { private class Fixture { val application = mock() val activity = mock() - val hub = mock() + val scopes = mock() val options = SentryAndroidOptions().apply { dsn = "https://key@sentry.io/proj" @@ -27,7 +27,7 @@ class CurrentActivityIntegrationTest { fun getSut(): CurrentActivityIntegration { val integration = CurrentActivityIntegration(application) - integration.register(hub, options) + integration.register(scopes, options) return integration } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt index 80954f67a5..c4fef01cc2 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt @@ -7,7 +7,7 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.DiagnosticLogger import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryEvent import io.sentry.SentryLevel import io.sentry.SentryTracer @@ -62,13 +62,13 @@ class DefaultAndroidEventProcessorTest { sdkVersion = SdkVersion("test", "1.2.3") } - val hub: IHub = mock() + val scopes: IScopes = mock() lateinit var sentryTracer: SentryTracer fun getSut(context: Context): DefaultAndroidEventProcessor { - whenever(hub.options).thenReturn(options) - sentryTracer = SentryTracer(TransactionContext("", ""), hub) + whenever(scopes.options).thenReturn(options) + sentryTracer = SentryTracer(TransactionContext("", ""), scopes) return DefaultAndroidEventProcessor(context, buildInfo, options) } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/EnvelopeFileObserverIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/EnvelopeFileObserverIntegrationTest.kt index 699fa2d2f2..192565f101 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/EnvelopeFileObserverIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/EnvelopeFileObserverIntegrationTest.kt @@ -2,8 +2,8 @@ package io.sentry.android.core import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.Hub -import io.sentry.IHub import io.sentry.ILogger +import io.sentry.IScopes import io.sentry.SentryLevel import io.sentry.SentryOptions import io.sentry.test.DeferredExecutorService @@ -24,7 +24,7 @@ import kotlin.test.assertEquals @RunWith(AndroidJUnit4::class) class EnvelopeFileObserverIntegrationTest { inner class Fixture { - val hub: IHub = mock() + val scopes: IScopes = mock() private lateinit var options: SentryAndroidOptions val logger = mock() @@ -33,7 +33,7 @@ class EnvelopeFileObserverIntegrationTest { options.setLogger(logger) options.isDebug = true optionConfiguration(options) - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) return object : EnvelopeFileObserverIntegration() { override fun getPath(options: SentryOptions): String? = file.absolutePath @@ -65,7 +65,7 @@ class EnvelopeFileObserverIntegrationTest { } @Test - fun `when hub is closed, integrations should be closed`() { + fun `when scopes is closed, integrations should be closed`() { val integrationMock = mock() val options = SentryOptions() options.dsn = "https://key@sentry.io/proj" @@ -73,19 +73,19 @@ class EnvelopeFileObserverIntegrationTest { options.addIntegration(integrationMock) options.setSerializer(mock()) // val expected = HubAdapter.getInstance() - val hub = Hub(options) + val scopes = Hub(options) // verify(integrationMock).register(expected, options) - hub.close() + scopes.close() verify(integrationMock).close() } @Test - fun `when hub is closed right after start, integration is not registered`() { + fun `when scopes is closed right after start, integration is not registered`() { val deferredExecutorService = DeferredExecutorService() val integration = fixture.getSut { it.executorService = deferredExecutorService } - integration.register(fixture.hub, fixture.hub.options) + integration.register(fixture.scopes, fixture.scopes.options) integration.close() deferredExecutorService.runAll() verify(fixture.logger, never()).log(eq(SentryLevel.DEBUG), eq("EnvelopeFileObserverIntegration installed.")) @@ -96,7 +96,7 @@ class EnvelopeFileObserverIntegrationTest { val integration = fixture.getSut { it.executorService = mock() } - integration.register(fixture.hub, fixture.hub.options) + integration.register(fixture.scopes, fixture.scopes.options) verify(fixture.logger).log( eq(SentryLevel.DEBUG), eq("Registering EnvelopeFileObserverIntegration for path: %s"), @@ -110,7 +110,7 @@ class EnvelopeFileObserverIntegrationTest { val integration = fixture.getSut { it.executorService = ImmediateExecutorService() } - integration.register(fixture.hub, fixture.hub.options) + integration.register(fixture.scopes, fixture.scopes.options) verify(fixture.logger).log( eq(SentryLevel.DEBUG), eq("Registering EnvelopeFileObserverIntegration for path: %s"), diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/LifecycleWatcherTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/LifecycleWatcherTest.kt index be30993142..73571a5ad4 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/LifecycleWatcherTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/LifecycleWatcherTest.kt @@ -3,8 +3,8 @@ package io.sentry.android.core import androidx.lifecycle.LifecycleOwner import io.sentry.Breadcrumb import io.sentry.DateUtils -import io.sentry.IHub import io.sentry.IScope +import io.sentry.IScopes import io.sentry.ScopeCallback import io.sentry.SentryLevel import io.sentry.Session @@ -32,7 +32,7 @@ class LifecycleWatcherTest { private class Fixture { val ownerMock = mock() - val hub = mock() + val scopes = mock() val dateProvider = mock() fun getSUT( @@ -44,12 +44,12 @@ class LifecycleWatcherTest { val argumentCaptor: ArgumentCaptor = ArgumentCaptor.forClass(ScopeCallback::class.java) val scope = mock() whenever(scope.session).thenReturn(session) - whenever(hub.configureScope(argumentCaptor.capture())).thenAnswer { + whenever(scopes.configureScope(argumentCaptor.capture())).thenAnswer { argumentCaptor.value.run(scope) } return LifecycleWatcher( - hub, + scopes, sessionIntervalMillis, enableAutoSessionTracking, enableAppLifecycleBreadcrumbs, @@ -69,7 +69,7 @@ class LifecycleWatcherTest { fun `if last started session is 0, start new session`() { val watcher = fixture.getSUT(enableAppLifecycleBreadcrumbs = false) watcher.onStart(fixture.ownerMock) - verify(fixture.hub).startSession() + verify(fixture.scopes).startSession() } @Test @@ -78,7 +78,7 @@ class LifecycleWatcherTest { whenever(fixture.dateProvider.currentTimeMillis).thenReturn(1L, 2L) watcher.onStart(fixture.ownerMock) watcher.onStart(fixture.ownerMock) - verify(fixture.hub, times(2)).startSession() + verify(fixture.scopes, times(2)).startSession() } @Test @@ -87,7 +87,7 @@ class LifecycleWatcherTest { whenever(fixture.dateProvider.currentTimeMillis).thenReturn(2L, 1L) watcher.onStart(fixture.ownerMock) watcher.onStart(fixture.ownerMock) - verify(fixture.hub).startSession() + verify(fixture.scopes).startSession() } @Test @@ -95,7 +95,7 @@ class LifecycleWatcherTest { val watcher = fixture.getSUT(enableAppLifecycleBreadcrumbs = false) watcher.onStart(fixture.ownerMock) watcher.onStop(fixture.ownerMock) - verify(fixture.hub, timeout(10000)).endSession() + verify(fixture.scopes, timeout(10000)).endSession() } @Test @@ -109,14 +109,14 @@ class LifecycleWatcherTest { watcher.onStart(fixture.ownerMock) assertNull(watcher.timerTask) - verify(fixture.hub, never()).endSession() + verify(fixture.scopes, never()).endSession() } @Test fun `When session tracking is disabled, do not start session`() { val watcher = fixture.getSUT(enableAutoSessionTracking = false, enableAppLifecycleBreadcrumbs = false) watcher.onStart(fixture.ownerMock) - verify(fixture.hub, never()).startSession() + verify(fixture.scopes, never()).startSession() } @Test @@ -124,14 +124,14 @@ class LifecycleWatcherTest { val watcher = fixture.getSUT(enableAutoSessionTracking = false, enableAppLifecycleBreadcrumbs = false) watcher.onStop(fixture.ownerMock) assertNull(watcher.timerTask) - verify(fixture.hub, never()).endSession() + verify(fixture.scopes, never()).endSession() } @Test fun `When session tracking is enabled, add breadcrumb on start`() { val watcher = fixture.getSUT(enableAppLifecycleBreadcrumbs = false) watcher.onStart(fixture.ownerMock) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("app.lifecycle", it.category) assertEquals("session", it.type) @@ -145,8 +145,8 @@ class LifecycleWatcherTest { fun `When session tracking is enabled, add breadcrumb on stop`() { val watcher = fixture.getSUT(enableAppLifecycleBreadcrumbs = false) watcher.onStop(fixture.ownerMock) - verify(fixture.hub, timeout(10000)).endSession() - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes, timeout(10000)).endSession() + verify(fixture.scopes).addBreadcrumb( check { assertEquals("app.lifecycle", it.category) assertEquals("session", it.type) @@ -160,7 +160,7 @@ class LifecycleWatcherTest { fun `When session tracking is disabled, do not add breadcrumb on start`() { val watcher = fixture.getSUT(enableAutoSessionTracking = false, enableAppLifecycleBreadcrumbs = false) watcher.onStart(fixture.ownerMock) - verify(fixture.hub, never()).addBreadcrumb(any()) + verify(fixture.scopes, never()).addBreadcrumb(any()) } @Test @@ -168,14 +168,14 @@ class LifecycleWatcherTest { val watcher = fixture.getSUT(enableAutoSessionTracking = false, enableAppLifecycleBreadcrumbs = false) watcher.onStop(fixture.ownerMock) assertNull(watcher.timerTask) - verify(fixture.hub, never()).addBreadcrumb(any()) + verify(fixture.scopes, never()).addBreadcrumb(any()) } @Test fun `When app lifecycle breadcrumbs is enabled, add breadcrumb on start`() { val watcher = fixture.getSUT(enableAutoSessionTracking = false) watcher.onStart(fixture.ownerMock) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("app.lifecycle", it.category) assertEquals("navigation", it.type) @@ -189,14 +189,14 @@ class LifecycleWatcherTest { fun `When app lifecycle breadcrumbs is disabled, do not add breadcrumb on start`() { val watcher = fixture.getSUT(enableAutoSessionTracking = false, enableAppLifecycleBreadcrumbs = false) watcher.onStart(fixture.ownerMock) - verify(fixture.hub, never()).addBreadcrumb(any()) + verify(fixture.scopes, never()).addBreadcrumb(any()) } @Test fun `When app lifecycle breadcrumbs is enabled, add breadcrumb on stop`() { val watcher = fixture.getSUT(enableAutoSessionTracking = false) watcher.onStop(fixture.ownerMock) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("app.lifecycle", it.category) assertEquals("navigation", it.type) @@ -210,7 +210,7 @@ class LifecycleWatcherTest { fun `When app lifecycle breadcrumbs is disabled, do not add breadcrumb on stop`() { val watcher = fixture.getSUT(enableAutoSessionTracking = false, enableAppLifecycleBreadcrumbs = false) watcher.onStop(fixture.ownerMock) - verify(fixture.hub, never()).addBreadcrumb(any()) + verify(fixture.scopes, never()).addBreadcrumb(any()) } @Test @@ -226,7 +226,7 @@ class LifecycleWatcherTest { } @Test - fun `if the hub has already a fresh session running, don't start new one`() { + fun `if the scopes has already a fresh session running, don't start new one`() { val watcher = fixture.getSUT( enableAppLifecycleBreadcrumbs = false, session = Session( @@ -248,11 +248,11 @@ class LifecycleWatcherTest { ) watcher.onStart(fixture.ownerMock) - verify(fixture.hub, never()).startSession() + verify(fixture.scopes, never()).startSession() } @Test - fun `if the hub has a long running session, start new one`() { + fun `if the scopes has a long running session, start new one`() { val watcher = fixture.getSUT( enableAppLifecycleBreadcrumbs = false, session = Session( @@ -274,7 +274,7 @@ class LifecycleWatcherTest { ) watcher.onStart(fixture.ownerMock) - verify(fixture.hub).startSession() + verify(fixture.scopes).startSession() } @Test diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/NdkIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/NdkIntegrationTest.kt index e86de06814..e282ad7141 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/NdkIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/NdkIntegrationTest.kt @@ -1,7 +1,7 @@ package io.sentry.android.core -import io.sentry.IHub import io.sentry.ILogger +import io.sentry.IScopes import io.sentry.SentryLevel import org.mockito.kotlin.any import org.mockito.kotlin.eq @@ -15,7 +15,7 @@ import kotlin.test.assertTrue class NdkIntegrationTest { private class Fixture { - val hub = mock() + val scopes = mock() val logger = mock() fun getSut(clazz: Class<*>? = SentryNdk::class.java): NdkIntegration { @@ -31,7 +31,7 @@ class NdkIntegrationTest { val options = getOptions() - integration.register(fixture.hub, options) + integration.register(fixture.scopes, options) verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any()) assertTrue(options.isEnableNdk) @@ -44,7 +44,7 @@ class NdkIntegrationTest { val options = getOptions() - integration.register(fixture.hub, options) + integration.register(fixture.scopes, options) assertTrue(options.isEnableNdk) assertTrue(options.isEnableScopeSync) @@ -62,7 +62,7 @@ class NdkIntegrationTest { val options = getOptions(enableNdk = false) - integration.register(fixture.hub, options) + integration.register(fixture.scopes, options) verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any()) @@ -76,7 +76,7 @@ class NdkIntegrationTest { val options = getOptions() - integration.register(fixture.hub, options) + integration.register(fixture.scopes, options) verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any()) @@ -90,7 +90,7 @@ class NdkIntegrationTest { val options = getOptions() - integration.register(fixture.hub, options) + integration.register(fixture.scopes, options) verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), any()) @@ -104,7 +104,7 @@ class NdkIntegrationTest { val options = getOptions() - integration.register(fixture.hub, options) + integration.register(fixture.scopes, options) assertTrue(options.isEnableNdk) assertTrue(options.isEnableScopeSync) @@ -122,7 +122,7 @@ class NdkIntegrationTest { val options = getOptions() - integration.register(fixture.hub, options) + integration.register(fixture.scopes, options) verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), any()) @@ -136,7 +136,7 @@ class NdkIntegrationTest { val options = getOptions(cacheDir = null) - integration.register(fixture.hub, options) + integration.register(fixture.scopes, options) verify(fixture.logger).log(eq(SentryLevel.ERROR), any()) @@ -150,7 +150,7 @@ class NdkIntegrationTest { val options = getOptions(cacheDir = "") - integration.register(fixture.hub, options) + integration.register(fixture.scopes, options) verify(fixture.logger).log(eq(SentryLevel.ERROR), any()) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/NetworkBreadcrumbsIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/NetworkBreadcrumbsIntegrationTest.kt index 146f229fdf..c28cf64085 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/NetworkBreadcrumbsIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/NetworkBreadcrumbsIntegrationTest.kt @@ -8,7 +8,7 @@ import android.net.NetworkCapabilities import android.os.Build import io.sentry.Breadcrumb import io.sentry.DateUtils -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryDateProvider import io.sentry.SentryLevel import io.sentry.SentryNanotimeDate @@ -39,7 +39,7 @@ class NetworkBreadcrumbsIntegrationTest { private class Fixture { val context = mock() var options = SentryAndroidOptions() - val hub = mock() + val scopes = mock() val mockBuildInfoProvider = mock() val connectivityManager = mock() var nowMs: Long = 0 @@ -68,7 +68,7 @@ class NetworkBreadcrumbsIntegrationTest { fun `When network events breadcrumb is enabled, it registers callback`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.connectivityManager).registerDefaultNetworkCallback(any()) assertNotNull(sut.networkCallback) @@ -78,7 +78,7 @@ class NetworkBreadcrumbsIntegrationTest { fun `When system events breadcrumb is disabled, it doesn't register callback`() { val sut = fixture.getSut(enableNetworkEventBreadcrumbs = false) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.connectivityManager, never()).registerDefaultNetworkCallback(any()) assertNull(sut.networkCallback) @@ -90,7 +90,7 @@ class NetworkBreadcrumbsIntegrationTest { whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.M) val sut = fixture.getSut(buildInfo = buildInfo) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.connectivityManager, never()).registerDefaultNetworkCallback(any()) assertNull(sut.networkCallback) @@ -100,7 +100,7 @@ class NetworkBreadcrumbsIntegrationTest { fun `When NetworkBreadcrumbsIntegration is closed, it should unregister the callback`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.close() verify(fixture.connectivityManager).unregisterNetworkCallback(any()) @@ -114,7 +114,7 @@ class NetworkBreadcrumbsIntegrationTest { val sut = fixture.getSut(buildInfo = buildInfo) assertNull(sut.networkCallback) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.close() verify(fixture.connectivityManager, never()).unregisterNetworkCallback(any()) @@ -124,12 +124,12 @@ class NetworkBreadcrumbsIntegrationTest { @Test fun `When connected to a new network, a breadcrumb is captured`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(mock()) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("system", it.type) assertEquals("network.event", it.category) @@ -142,27 +142,27 @@ class NetworkBreadcrumbsIntegrationTest { @Test fun `When connected to the same network without disconnecting from the previous one, only one breadcrumb is captured`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) callback.onAvailable(fixture.network) - verify(fixture.hub, times(1)).addBreadcrumb(any()) + verify(fixture.scopes, times(1)).addBreadcrumb(any()) } @Test fun `When disconnected from a network, a breadcrumb is captured`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) - verify(fixture.hub).addBreadcrumb(any()) + verify(fixture.scopes).addBreadcrumb(any()) callback.onLost(fixture.network) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("system", it.type) assertEquals("network.event", it.category) @@ -175,12 +175,12 @@ class NetworkBreadcrumbsIntegrationTest { @Test fun `When disconnected from a network, a breadcrumb is captured only if previously connected to that network`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) // callback.onAvailable(network) was not called, so no breadcrumb should be captured callback.onLost(mock()) - verify(fixture.hub, never()).addBreadcrumb(any()) + verify(fixture.scopes, never()).addBreadcrumb(any()) } @Test @@ -188,7 +188,7 @@ class NetworkBreadcrumbsIntegrationTest { val buildInfo = mock() whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.Q) val sut = fixture.getSut(buildInfo = buildInfo) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -204,7 +204,7 @@ class NetworkBreadcrumbsIntegrationTest { isCellular = false ) ) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("system", it.type) assertEquals("network.event", it.category) @@ -223,18 +223,18 @@ class NetworkBreadcrumbsIntegrationTest { @Test fun `When a network connection detail changes, a breadcrumb is captured only if previously connected to that network`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) // callback.onAvailable(network) was not called, so no breadcrumb should be captured onCapabilitiesChanged(callback, mock()) - verify(fixture.hub, never()).addBreadcrumb(any(), anyOrNull()) + verify(fixture.scopes, never()).addBreadcrumb(any(), anyOrNull()) } @Test fun `When a network connection detail changes, a new breadcrumb is captured if vpn flag changes`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -245,17 +245,17 @@ class NetworkBreadcrumbsIntegrationTest { onCapabilitiesChanged(callback, details1) onCapabilitiesChanged(callback, details2) onCapabilitiesChanged(callback, details3) - inOrder(fixture.hub) { + inOrder(fixture.scopes) { verifyBreadcrumbInOrder { assertFalse(it.isVpn) } verifyBreadcrumbInOrder { assertTrue(it.isVpn) } - verify(fixture.hub, never()).addBreadcrumb(any(), any()) + verify(fixture.scopes, never()).addBreadcrumb(any(), any()) } } @Test fun `When a network connection detail changes, a new breadcrumb is captured if type changes`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -266,10 +266,10 @@ class NetworkBreadcrumbsIntegrationTest { onCapabilitiesChanged(callback, details1) onCapabilitiesChanged(callback, details2) onCapabilitiesChanged(callback, details3) - inOrder(fixture.hub) { + inOrder(fixture.scopes) { verifyBreadcrumbInOrder { assertEquals("wifi", it.type) } verifyBreadcrumbInOrder { assertEquals("cellular", it.type) } - verify(fixture.hub, never()).addBreadcrumb(any(), any()) + verify(fixture.scopes, never()).addBreadcrumb(any(), any()) } } @@ -278,7 +278,7 @@ class NetworkBreadcrumbsIntegrationTest { val buildInfo = mock() whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.Q) val sut = fixture.getSut(buildInfo = buildInfo) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -289,10 +289,10 @@ class NetworkBreadcrumbsIntegrationTest { // A change of signal strength of 5 doesn't trigger a new breadcrumb onCapabilitiesChanged(callback, details2) onCapabilitiesChanged(callback, details3) - inOrder(fixture.hub) { + inOrder(fixture.scopes) { verifyBreadcrumbInOrder { assertEquals(50, it.signalStrength) } verifyBreadcrumbInOrder { assertEquals(56, it.signalStrength) } - verify(fixture.hub, never()).addBreadcrumb(any(), any()) + verify(fixture.scopes, never()).addBreadcrumb(any(), any()) } } @@ -301,7 +301,7 @@ class NetworkBreadcrumbsIntegrationTest { val buildInfo = mock() whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.Q) val sut = fixture.getSut(buildInfo = buildInfo) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -317,11 +317,11 @@ class NetworkBreadcrumbsIntegrationTest { // A change of download bandwidth of 10% (more than 1000) doesn't trigger a new breadcrumb onCapabilitiesChanged(callback, details4) onCapabilitiesChanged(callback, details5) - inOrder(fixture.hub) { + inOrder(fixture.scopes) { verifyBreadcrumbInOrder { assertEquals(1000, it.downBandwidth) } verifyBreadcrumbInOrder { assertEquals(20000, it.downBandwidth) } verifyBreadcrumbInOrder { assertEquals(22001, it.downBandwidth) } - verify(fixture.hub, never()).addBreadcrumb(any(), any()) + verify(fixture.scopes, never()).addBreadcrumb(any(), any()) } } @@ -330,7 +330,7 @@ class NetworkBreadcrumbsIntegrationTest { val buildInfo = mock() whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.Q) val sut = fixture.getSut(buildInfo = buildInfo) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -346,18 +346,18 @@ class NetworkBreadcrumbsIntegrationTest { // A change of upload bandwidth of 10% (more than 1000) doesn't trigger a new breadcrumb onCapabilitiesChanged(callback, details4) onCapabilitiesChanged(callback, details5) - inOrder(fixture.hub) { + inOrder(fixture.scopes) { verifyBreadcrumbInOrder { assertEquals(1000, it.upBandwidth) } verifyBreadcrumbInOrder { assertEquals(20000, it.upBandwidth) } verifyBreadcrumbInOrder { assertEquals(22001, it.upBandwidth) } - verify(fixture.hub, never()).addBreadcrumb(any(), any()) + verify(fixture.scopes, never()).addBreadcrumb(any(), any()) } } @Test fun `signal strength is 0 if not on Android Q+`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -371,7 +371,7 @@ class NetworkBreadcrumbsIntegrationTest { val buildInfo = mock() whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.Q) val sut = fixture.getSut(buildInfo = buildInfo) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -384,7 +384,7 @@ class NetworkBreadcrumbsIntegrationTest { @Test fun `A breadcrumb is captured when vpn status changes, regardless of the timestamp`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -392,17 +392,17 @@ class NetworkBreadcrumbsIntegrationTest { val details2 = createConnectionDetail(isVpn = true) onCapabilitiesChanged(callback, details1, 0) onCapabilitiesChanged(callback, details2, 0) - inOrder(fixture.hub) { + inOrder(fixture.scopes) { verifyBreadcrumbInOrder { assertFalse(it.isVpn) } verifyBreadcrumbInOrder { assertTrue(it.isVpn) } - verify(fixture.hub, never()).addBreadcrumb(any(), any()) + verify(fixture.scopes, never()).addBreadcrumb(any(), any()) } } @Test fun `A breadcrumb is captured when connection type changes, regardless of the timestamp`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -412,11 +412,11 @@ class NetworkBreadcrumbsIntegrationTest { onCapabilitiesChanged(callback, details1, 0) onCapabilitiesChanged(callback, details2, 0) onCapabilitiesChanged(callback, details3, 0) - inOrder(fixture.hub) { + inOrder(fixture.scopes) { verifyBreadcrumbInOrder { assertEquals("wifi", it.type) } verifyBreadcrumbInOrder { assertEquals("cellular", it.type) } verifyBreadcrumbInOrder { assertEquals("ethernet", it.type) } - verify(fixture.hub, never()).addBreadcrumb(any(), any()) + verify(fixture.scopes, never()).addBreadcrumb(any(), any()) } } @@ -425,7 +425,7 @@ class NetworkBreadcrumbsIntegrationTest { val buildInfo = mock() whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.Q) val sut = fixture.getSut(buildInfo = buildInfo) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -435,17 +435,17 @@ class NetworkBreadcrumbsIntegrationTest { onCapabilitiesChanged(callback, details1, 0) onCapabilitiesChanged(callback, details2, 0) onCapabilitiesChanged(callback, details3, 5000) - inOrder(fixture.hub) { + inOrder(fixture.scopes) { verifyBreadcrumbInOrder { assertEquals(1, it.signalStrength) } verifyBreadcrumbInOrder { assertEquals(51, it.signalStrength) } - verify(fixture.hub, never()).addBreadcrumb(any(), any()) + verify(fixture.scopes, never()).addBreadcrumb(any(), any()) } } @Test fun `A breadcrumb is captured when downBandwidth changes at most once every 5 seconds`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -455,17 +455,17 @@ class NetworkBreadcrumbsIntegrationTest { onCapabilitiesChanged(callback, details1, 0) onCapabilitiesChanged(callback, details2, 0) onCapabilitiesChanged(callback, details3, 5000) - inOrder(fixture.hub) { + inOrder(fixture.scopes) { verifyBreadcrumbInOrder { assertEquals(1, it.downBandwidth) } verifyBreadcrumbInOrder { assertEquals(2001, it.downBandwidth) } - verify(fixture.hub, never()).addBreadcrumb(any(), any()) + verify(fixture.scopes, never()).addBreadcrumb(any(), any()) } } @Test fun `A breadcrumb is captured when upBandwidth changes at most once every 5 seconds`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -475,15 +475,15 @@ class NetworkBreadcrumbsIntegrationTest { onCapabilitiesChanged(callback, details1, 0) onCapabilitiesChanged(callback, details2, 0) onCapabilitiesChanged(callback, details3, 5000) - inOrder(fixture.hub) { + inOrder(fixture.scopes) { verifyBreadcrumbInOrder { assertEquals(1, it.upBandwidth) } verifyBreadcrumbInOrder { assertEquals(2001, it.upBandwidth) } - verify(fixture.hub, never()).addBreadcrumb(any(), any()) + verify(fixture.scopes, never()).addBreadcrumb(any(), any()) } } private fun KInOrder.verifyBreadcrumbInOrder(check: (detail: NetworkBreadcrumbConnectionDetail) -> Unit) { - verify(fixture.hub, times(1)).addBreadcrumb( + verify(fixture.scopes, times(1)).addBreadcrumb( any(), check { val connectionDetail = it[TypeCheckHint.ANDROID_NETWORK_CAPABILITIES] as NetworkBreadcrumbConnectionDetail @@ -493,7 +493,7 @@ class NetworkBreadcrumbsIntegrationTest { } private fun verifyBreadcrumb(check: (detail: NetworkBreadcrumbConnectionDetail) -> Unit) { - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( any(), check { val connectionDetail = it[TypeCheckHint.ANDROID_NETWORK_CAPABILITIES] as NetworkBreadcrumbConnectionDetail diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt index 4c23691e63..e1593e600f 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt @@ -3,7 +3,7 @@ package io.sentry.android.core import android.content.ContentProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.MeasurementUnit import io.sentry.SentryTracer import io.sentry.SpanContext @@ -35,7 +35,7 @@ class PerformanceAndroidEventProcessorTest { private class Fixture { val options = SentryAndroidOptions() - val hub = mock() + val scopes = mock() val context = TransactionContext("name", "op", TracesSamplingDecision(true)) lateinit var tracer: SentryTracer val activityFramesTracker = mock() @@ -46,8 +46,8 @@ class PerformanceAndroidEventProcessorTest { ): PerformanceAndroidEventProcessor { options.tracesSampleRate = tracesSampleRate options.isEnablePerformanceV2 = enablePerformanceV2 - whenever(hub.options).thenReturn(options) - tracer = SentryTracer(context, hub) + whenever(scopes.options).thenReturn(options) + tracer = SentryTracer(context, scopes) return PerformanceAndroidEventProcessor(options, activityFramesTracker) } } @@ -181,7 +181,7 @@ class PerformanceAndroidEventProcessorTest { fun `add slow and frozen frames if auto transaction`() { val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.hub) + val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) val metrics = mapOf( @@ -227,7 +227,7 @@ class PerformanceAndroidEventProcessorTest { // when an activity transaction is created val sut = fixture.getSut(enablePerformanceV2 = true) val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.hub) + val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) // and it contains an app.start.cold span @@ -297,7 +297,7 @@ class PerformanceAndroidEventProcessorTest { // when an activity transaction is created val sut = fixture.getSut(enablePerformanceV2 = true) val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.hub) + val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) // then the app start metrics should not be attached @@ -326,7 +326,7 @@ class PerformanceAndroidEventProcessorTest { // when the first activity transaction is created val sut = fixture.getSut(enablePerformanceV2 = true) val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.hub) + val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) val appStartSpan = SentrySpan( 0.0, @@ -377,7 +377,7 @@ class PerformanceAndroidEventProcessorTest { val sut = fixture.getSut(enablePerformanceV2 = true) val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.hub) + val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) val appStartSpan = SentrySpan( 0.0, @@ -424,7 +424,7 @@ class PerformanceAndroidEventProcessorTest { val sut = fixture.getSut(enablePerformanceV2 = true) val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.hub) + val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) val appStartSpan = SentrySpan( 0.0, diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/PhoneStateBreadcrumbsIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/PhoneStateBreadcrumbsIntegrationTest.kt index 2b6ca801da..c764d11c2d 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/PhoneStateBreadcrumbsIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/PhoneStateBreadcrumbsIntegrationTest.kt @@ -4,7 +4,7 @@ import android.content.Context import android.telephony.PhoneStateListener import android.telephony.TelephonyManager import io.sentry.Breadcrumb -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ISentryExecutorService import io.sentry.SentryLevel import io.sentry.test.DeferredExecutorService @@ -41,8 +41,8 @@ class PhoneStateBreadcrumbsIntegrationTest { @Test fun `When system events breadcrumb is enabled, it registers callback`() { val sut = fixture.getSut() - val hub = mock() - sut.register(hub, fixture.options) + val scopes = mock() + sut.register(scopes, fixture.options) verify(fixture.manager).listen(any(), eq(PhoneStateListener.LISTEN_CALL_STATE)) assertNotNull(sut.listener) } @@ -50,8 +50,8 @@ class PhoneStateBreadcrumbsIntegrationTest { @Test fun `Phone state callback is registered in the executorService`() { val sut = fixture.getSut(mock()) - val hub = mock() - sut.register(hub, fixture.options) + val scopes = mock() + sut.register(scopes, fixture.options) assertNull(sut.listener) } @@ -59,9 +59,9 @@ class PhoneStateBreadcrumbsIntegrationTest { @Test fun `When system events breadcrumb is disabled, it doesn't register callback`() { val sut = fixture.getSut() - val hub = mock() + val scopes = mock() sut.register( - hub, + scopes, fixture.options.apply { isEnableSystemEventBreadcrumbs = false } @@ -73,15 +73,15 @@ class PhoneStateBreadcrumbsIntegrationTest { @Test fun `When ActivityBreadcrumbsIntegration is closed, it should unregister the callback`() { val sut = fixture.getSut() - val hub = mock() - sut.register(hub, fixture.options) + val scopes = mock() + sut.register(scopes, fixture.options) sut.close() verify(fixture.manager).listen(any(), eq(PhoneStateListener.LISTEN_NONE)) assertNull(sut.listener) } @Test - fun `when hub is closed right after start, integration is not registered`() { + fun `when scopes is closed right after start, integration is not registered`() { val deferredExecutorService = DeferredExecutorService() val sut = fixture.getSut(executorService = deferredExecutorService) sut.register(mock(), fixture.options) @@ -94,11 +94,11 @@ class PhoneStateBreadcrumbsIntegrationTest { @Test fun `When on call state received, added breadcrumb with type and category`() { val sut = fixture.getSut() - val hub = mock() - sut.register(hub, fixture.options) + val scopes = mock() + sut.register(scopes, fixture.options) sut.listener!!.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING, null) - verify(hub).addBreadcrumb( + verify(scopes).addBreadcrumb( check { assertEquals("device.event", it.category) assertEquals("system", it.type) @@ -111,18 +111,18 @@ class PhoneStateBreadcrumbsIntegrationTest { @Test fun `When on idle state received, added breadcrumb with type and category`() { val sut = fixture.getSut() - val hub = mock() - sut.register(hub, fixture.options) + val scopes = mock() + sut.register(scopes, fixture.options) sut.listener!!.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE, null) - verify(hub, never()).addBreadcrumb(any()) + verify(scopes, never()).addBreadcrumb(any()) } @Test fun `When on offhook state received, added breadcrumb with type and category`() { val sut = fixture.getSut() - val hub = mock() - sut.register(hub, fixture.options) + val scopes = mock() + sut.register(scopes, fixture.options) sut.listener!!.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK, null) - verify(hub, never()).addBreadcrumb(any()) + verify(scopes, never()).addBreadcrumb(any()) } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SendCachedEnvelopeIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SendCachedEnvelopeIntegrationTest.kt index 403f40ee70..f1e345eefc 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SendCachedEnvelopeIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SendCachedEnvelopeIntegrationTest.kt @@ -2,8 +2,8 @@ package io.sentry.android.core import io.sentry.IConnectionStatusProvider import io.sentry.IConnectionStatusProvider.ConnectionStatus -import io.sentry.IHub import io.sentry.ILogger +import io.sentry.IScopes import io.sentry.ISentryExecutorService import io.sentry.SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget import io.sentry.SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForgetFactory @@ -28,7 +28,7 @@ import kotlin.test.Test class SendCachedEnvelopeIntegrationTest { private class Fixture { - val hub: IHub = mock() + val scopes: IScopes = mock() val options = SentryAndroidOptions() val logger = mock() val factory = mock() @@ -74,7 +74,7 @@ class SendCachedEnvelopeIntegrationTest { fun `when cacheDirPath is not set, does nothing`() { val sut = fixture.getSut(cacheDirPath = null) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.factory, never()).create(any(), any()) } @@ -83,7 +83,7 @@ class SendCachedEnvelopeIntegrationTest { fun `when factory returns null, does nothing`() { val sut = fixture.getSut(hasSender = false, mockExecutorService = ImmediateExecutorService()) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.factory).create(any(), any()) verify(fixture.sender, never()).send() @@ -93,7 +93,7 @@ class SendCachedEnvelopeIntegrationTest { fun `when has factory and cacheDirPath set, submits task into queue`() { val sut = fixture.getSut(mockExecutorService = ImmediateExecutorService()) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) await.untilFalse(fixture.flag) verify(fixture.sender).send() @@ -102,7 +102,7 @@ class SendCachedEnvelopeIntegrationTest { @Test fun `when executorService is fake, does nothing`() { val sut = fixture.getSut(mockExecutorService = mock()) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.factory, never()).create(any(), any()) verify(fixture.sender, never()).send() @@ -112,7 +112,7 @@ class SendCachedEnvelopeIntegrationTest { fun `when has startup crash marker, awaits the task on the calling thread`() { val sut = fixture.getSut(hasStartupCrashMarker = true) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) // we do not need to await here, because it's executed synchronously verify(fixture.sender).send() @@ -123,7 +123,7 @@ class SendCachedEnvelopeIntegrationTest { val sut = fixture.getSut(hasStartupCrashMarker = true, delaySend = 1000) fixture.options.startupCrashFlushTimeoutMillis = 100 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) // first wait until synchronous send times out and check that the logger was hit in the catch block await.atLeast(500, MILLISECONDS) @@ -144,7 +144,7 @@ class SendCachedEnvelopeIntegrationTest { val connectionStatusProvider = mock() fixture.options.connectionStatusProvider = connectionStatusProvider - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(connectionStatusProvider).addConnectionStatusObserver(any()) } @@ -159,7 +159,7 @@ class SendCachedEnvelopeIntegrationTest { ConnectionStatus.DISCONNECTED ) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.sender, never()).send() } @@ -174,7 +174,7 @@ class SendCachedEnvelopeIntegrationTest { ConnectionStatus.UNKNOWN ) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.factory).create(any(), any()) } @@ -187,7 +187,7 @@ class SendCachedEnvelopeIntegrationTest { whenever(connectionStatusProvider.connectionStatus).thenReturn( ConnectionStatus.DISCONNECTED ) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) // when there's no connection no factory create call should be done verify(fixture.sender, never()).send() @@ -215,9 +215,9 @@ class SendCachedEnvelopeIntegrationTest { val rateLimiter = mock { whenever(mock.isActiveForCategory(any())).thenReturn(true) } - whenever(fixture.hub.rateLimiter).thenReturn(rateLimiter) + whenever(fixture.scopes.rateLimiter).thenReturn(rateLimiter) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) // no factory call should be done if there's rate limiting active verify(fixture.sender, never()).send() @@ -228,7 +228,7 @@ class SendCachedEnvelopeIntegrationTest { val deferredExecutorService = DeferredExecutorService() val sut = fixture.getSut(mockExecutorService = deferredExecutorService) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.sender, never()).send() sut.close() diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegrationTest.kt index f8293f9b87..146abb617e 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegrationTest.kt @@ -3,7 +3,7 @@ package io.sentry.android.core import android.content.Context import android.content.Intent import io.sentry.Breadcrumb -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ISentryExecutorService import io.sentry.SentryLevel import io.sentry.test.DeferredExecutorService @@ -26,7 +26,7 @@ class SystemEventsBreadcrumbsIntegrationTest { private class Fixture { val context = mock() var options = SentryAndroidOptions() - val hub = mock() + val scopes = mock() fun getSut(enableSystemEventBreadcrumbs: Boolean = true, executorService: ISentryExecutorService = ImmediateExecutorService()): SystemEventsBreadcrumbsIntegration { options = SentryAndroidOptions().apply { @@ -43,7 +43,7 @@ class SystemEventsBreadcrumbsIntegrationTest { fun `When system events breadcrumb is enabled, it registers callback`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.context).registerReceiver(any(), any()) assertNotNull(sut.receiver) @@ -52,8 +52,8 @@ class SystemEventsBreadcrumbsIntegrationTest { @Test fun `system events callback is registered in the executorService`() { val sut = fixture.getSut(executorService = mock()) - val hub = mock() - sut.register(hub, fixture.options) + val scopes = mock() + sut.register(scopes, fixture.options) assertNull(sut.receiver) } @@ -62,7 +62,7 @@ class SystemEventsBreadcrumbsIntegrationTest { fun `When system events breadcrumb is disabled, it doesn't register callback`() { val sut = fixture.getSut(enableSystemEventBreadcrumbs = false) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.context, never()).registerReceiver(any(), any()) assertNull(sut.receiver) @@ -72,7 +72,7 @@ class SystemEventsBreadcrumbsIntegrationTest { fun `When ActivityBreadcrumbsIntegration is closed, it should unregister the callback`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.close() verify(fixture.context).unregisterReceiver(any()) @@ -80,10 +80,10 @@ class SystemEventsBreadcrumbsIntegrationTest { } @Test - fun `when hub is closed right after start, integration is not registered`() { + fun `when scopes is closed right after start, integration is not registered`() { val deferredExecutorService = DeferredExecutorService() val sut = fixture.getSut(executorService = deferredExecutorService) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertNull(sut.receiver) sut.close() deferredExecutorService.runAll() @@ -94,13 +94,13 @@ class SystemEventsBreadcrumbsIntegrationTest { fun `When broadcast received, added breadcrumb with type and category`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val intent = Intent().apply { action = Intent.ACTION_TIME_CHANGED } sut.receiver!!.onReceive(fixture.context, intent) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("device.event", it.category) assertEquals("system", it.type) @@ -116,7 +116,7 @@ class SystemEventsBreadcrumbsIntegrationTest { val sut = fixture.getSut() whenever(fixture.context.registerReceiver(any(), any())).thenThrow(SecurityException()) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertFalse(fixture.options.isEnableSystemEventBreadcrumbs) } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/TempSensorBreadcrumbsIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/TempSensorBreadcrumbsIntegrationTest.kt index d443b1e345..5d049e3dad 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/TempSensorBreadcrumbsIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/TempSensorBreadcrumbsIntegrationTest.kt @@ -7,7 +7,7 @@ import android.hardware.SensorEventListener import android.hardware.SensorManager import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ISentryExecutorService import io.sentry.SentryLevel import io.sentry.TypeCheckHint @@ -47,8 +47,8 @@ class TempSensorBreadcrumbsIntegrationTest { @Test fun `When system events breadcrumb is enabled, it registers callback`() { val sut = fixture.getSut() - val hub = mock() - sut.register(hub, fixture.options) + val scopes = mock() + sut.register(scopes, fixture.options) verify(fixture.manager).registerListener(any(), any(), eq(SensorManager.SENSOR_DELAY_NORMAL)) assertNotNull(sut.sensorManager) } @@ -56,8 +56,8 @@ class TempSensorBreadcrumbsIntegrationTest { @Test fun `temp sensor listener is registered in the executorService`() { val sut = fixture.getSut(executorService = mock()) - val hub = mock() - sut.register(hub, fixture.options) + val scopes = mock() + sut.register(scopes, fixture.options) assertNull(sut.sensorManager) } @@ -65,9 +65,9 @@ class TempSensorBreadcrumbsIntegrationTest { @Test fun `When system events breadcrumb is disabled, it should not register a callback`() { val sut = fixture.getSut() - val hub = mock() + val scopes = mock() sut.register( - hub, + scopes, fixture.options.apply { isEnableSystemEventBreadcrumbs = false } @@ -79,15 +79,15 @@ class TempSensorBreadcrumbsIntegrationTest { @Test fun `When TempSensorBreadcrumbsIntegration is closed, it should unregister the callback`() { val sut = fixture.getSut() - val hub = mock() - sut.register(hub, fixture.options) + val scopes = mock() + sut.register(scopes, fixture.options) sut.close() verify(fixture.manager).unregisterListener(any()) assertNull(sut.sensorManager) } @Test - fun `when hub is closed right after start, integration is not registered`() { + fun `when scopes is closed right after start, integration is not registered`() { val deferredExecutorService = DeferredExecutorService() val sut = fixture.getSut(executorService = deferredExecutorService) sut.register(mock(), fixture.options) @@ -100,14 +100,14 @@ class TempSensorBreadcrumbsIntegrationTest { @Test fun `When onSensorChanged received, add a breadcrumb with type and category`() { val sut = fixture.getSut() - val hub = mock() - sut.register(hub, fixture.options) + val scopes = mock() + sut.register(scopes, fixture.options) val sensorCtor = "android.hardware.SensorEvent".getDeclaredCtor(emptyArray()) val sensorEvent: SensorEvent = sensorCtor.newInstance() as SensorEvent sensorEvent.injectForField("values", FloatArray(2) { 1F }) sut.onSensorChanged(sensorEvent) - verify(hub).addBreadcrumb( + verify(scopes).addBreadcrumb( check { assertEquals("device.event", it.category) assertEquals("system", it.type) @@ -122,12 +122,12 @@ class TempSensorBreadcrumbsIntegrationTest { @Test fun `When onSensorChanged received and null values, do not add a breadcrumb`() { val sut = fixture.getSut() - val hub = mock() - sut.register(hub, fixture.options) + val scopes = mock() + sut.register(scopes, fixture.options) val event = mock() assertNull(event.values) sut.onSensorChanged(event) - verify(hub, never()).addBreadcrumb(any()) + verify(scopes, never()).addBreadcrumb(any()) } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerClickTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerClickTest.kt index 1e6652276a..74edfb4302 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerClickTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerClickTest.kt @@ -10,8 +10,8 @@ import android.view.Window import android.widget.CheckBox import android.widget.RadioButton import io.sentry.Breadcrumb -import io.sentry.IHub import io.sentry.IScope +import io.sentry.IScopes import io.sentry.PropagationContext import io.sentry.Scope.IWithPropagationContext import io.sentry.ScopeCallback @@ -40,7 +40,7 @@ class SentryGestureListenerClickTest { gestureTargetLocators = listOf(AndroidViewGestureTargetLocator(true)) dsn = "https://key@sentry.io/proj" } - val hub = mock() + val scopes = mock() val scope = mock() val propagationContext = PropagationContext() lateinit var target: View @@ -86,11 +86,11 @@ class SentryGestureListenerClickTest { whenever(context.resources).thenReturn(resources) whenever(this.target.context).thenReturn(context) whenever(activity.window).thenReturn(window) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) doAnswer { (it.arguments[0] as IWithPropagationContext).accept(propagationContext); propagationContext; }.whenever(scope).withPropagationContext(any()) return SentryGestureListener( activity, - hub, + scopes, options ) } @@ -123,7 +123,7 @@ class SentryGestureListenerClickTest { sut.onSingleTapUp(event) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("ui.click", it.category) assertEquals("user", it.type) @@ -146,7 +146,7 @@ class SentryGestureListenerClickTest { sut.onSingleTapUp(event) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("radio_button", it.data["view.id"]) assertEquals("android.widget.RadioButton", it.data["view.class"]) @@ -166,7 +166,7 @@ class SentryGestureListenerClickTest { sut.onSingleTapUp(event) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("check_box", it.data["view.id"]) assertEquals("android.widget.CheckBox", it.data["view.class"]) @@ -185,7 +185,7 @@ class SentryGestureListenerClickTest { sut.onSingleTapUp(event) - verify(fixture.hub, never()).addBreadcrumb(any()) + verify(fixture.scopes, never()).addBreadcrumb(any()) } @Test @@ -198,7 +198,7 @@ class SentryGestureListenerClickTest { val sut = fixture.getSut(event, "decor_view", targetOverride = decorView) sut.onSingleTapUp(event) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals(decorView.javaClass.canonicalName, it.data["view.class"]) assertEquals("decor_view", it.data["view.id"]) @@ -214,7 +214,7 @@ class SentryGestureListenerClickTest { sut.onSingleTapUp(event) - verify(fixture.hub, never()).addBreadcrumb(any()) + verify(fixture.scopes, never()).addBreadcrumb(any()) } @Test @@ -230,7 +230,7 @@ class SentryGestureListenerClickTest { sut.onSingleTapUp(event) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals(fixture.target.javaClass.simpleName, it.data["view.class"]) }, diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerScrollTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerScrollTest.kt index 5d39b64753..e5a9623c4d 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerScrollTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerScrollTest.kt @@ -11,8 +11,8 @@ import android.widget.AbsListView import android.widget.ListAdapter import androidx.core.view.ScrollingView import io.sentry.Breadcrumb -import io.sentry.IHub import io.sentry.IScope +import io.sentry.IScopes import io.sentry.PropagationContext import io.sentry.Scope import io.sentry.ScopeCallback @@ -44,7 +44,7 @@ class SentryGestureListenerScrollTest { isEnableUserInteractionTracing = true gestureTargetLocators = listOf(AndroidViewGestureTargetLocator(true)) } - val hub = mock() + val scopes = mock() val scope = mock() val propagationContext = PropagationContext() @@ -77,11 +77,11 @@ class SentryGestureListenerScrollTest { endEvent.mockDirection(firstEvent, direction) } whenever(activity.window).thenReturn(window) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) doAnswer { (it.arguments[0] as Scope.IWithPropagationContext).accept(propagationContext); propagationContext }.whenever(scope).withPropagationContext(any()) return SentryGestureListener( activity, - hub, + scopes, options ) } @@ -99,7 +99,7 @@ class SentryGestureListenerScrollTest { } sut.onUp(fixture.endEvent) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("ui.scroll", it.category) assertEquals("user", it.type) @@ -122,7 +122,7 @@ class SentryGestureListenerScrollTest { } sut.onUp(fixture.endEvent) - verify(fixture.hub, never()).addBreadcrumb(any()) + verify(fixture.scopes, never()).addBreadcrumb(any()) } @Test @@ -143,8 +143,8 @@ class SentryGestureListenerScrollTest { sut.onFling(fixture.firstEvent, fixture.endEvent, 1.0f, 1.0f) sut.onUp(fixture.endEvent) - inOrder(fixture.hub) { - verify(fixture.hub).addBreadcrumb( + inOrder(fixture.scopes) { + verify(fixture.scopes).addBreadcrumb( check { assertEquals("ui.swipe", it.category) assertEquals("user", it.type) @@ -155,8 +155,8 @@ class SentryGestureListenerScrollTest { }, anyOrNull() ) - verify(fixture.hub).configureScope(anyOrNull()) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).configureScope(anyOrNull()) + verify(fixture.scopes).addBreadcrumb( check { assertEquals("ui.swipe", it.category) assertEquals("user", it.type) @@ -168,7 +168,7 @@ class SentryGestureListenerScrollTest { anyOrNull() ) } - verifyNoMoreInteractions(fixture.hub) + verifyNoMoreInteractions(fixture.scopes) } @Test @@ -177,7 +177,7 @@ class SentryGestureListenerScrollTest { sut.onUp(fixture.firstEvent) sut.onDown(fixture.endEvent) - verify(fixture.hub, never()).addBreadcrumb(any()) + verify(fixture.scopes, never()).addBreadcrumb(any()) } @Test @@ -190,7 +190,7 @@ class SentryGestureListenerScrollTest { } sut.onUp(fixture.endEvent) - verify(fixture.hub, never()).addBreadcrumb(any()) + verify(fixture.scopes, never()).addBreadcrumb(any()) } @Test diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerTracingTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerTracingTest.kt index c7ada69c88..d3f6647c2a 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerTracingTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerTracingTest.kt @@ -9,8 +9,8 @@ import android.view.ViewGroup import android.view.Window import android.widget.AbsListView import android.widget.ListAdapter -import io.sentry.IHub import io.sentry.IScope +import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryTracer @@ -46,7 +46,7 @@ class SentryGestureListenerTracingTest { val options = SentryAndroidOptions().apply { dsn = "https://key@sentry.io/proj" } - val hub = mock() + val scopes = mock() val event = mock() val scope = mock() lateinit var target: View @@ -64,9 +64,9 @@ class SentryGestureListenerTracingTest { options.isEnableUserInteractionBreadcrumbs = true options.gestureTargetLocators = listOf(AndroidViewGestureTargetLocator(true)) - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) - this.transaction = transaction ?: SentryTracer(TransactionContext("name", "op"), hub) + this.transaction = transaction ?: SentryTracer(TransactionContext("name", "op"), scopes) target = mockView(event = event, clickable = true, context = context) window.mockDecorView(event = event, context = context) { @@ -86,13 +86,13 @@ class SentryGestureListenerTracingTest { whenever(activity.window).thenReturn(window) - whenever(hub.startTransaction(any(), any())) + whenever(scopes.startTransaction(any(), any())) .thenReturn(this.transaction) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) return SentryGestureListener( activity, - hub, + scopes, options ) } @@ -106,7 +106,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) - verify(fixture.hub, never()).startTransaction( + verify(fixture.scopes, never()).startTransaction( any(), any() ) @@ -118,7 +118,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) - verify(fixture.hub, never()).startTransaction( + verify(fixture.scopes, never()).startTransaction( any(), any() ) @@ -130,7 +130,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) - verify(fixture.hub, never()).startTransaction( + verify(fixture.scopes, never()).startTransaction( any(), any() ) @@ -140,7 +140,7 @@ class SentryGestureListenerTracingTest { fun `when transaction is created, set transaction to the bound Scope`() { val sut = fixture.getSut() - whenever(fixture.hub.configureScope(any())).thenAnswer { + whenever(fixture.scopes.configureScope(any())).thenAnswer { val scope = Scope(fixture.options) sut.applyScope(scope, fixture.transaction) @@ -155,9 +155,9 @@ class SentryGestureListenerTracingTest { fun `when transaction is created, do not overwrite transaction already bound to the Scope`() { val sut = fixture.getSut() - whenever(fixture.hub.configureScope(any())).thenAnswer { + whenever(fixture.scopes.configureScope(any())).thenAnswer { val scope = Scope(fixture.options) - val previousTransaction = SentryTracer(TransactionContext("name", "op"), fixture.hub) + val previousTransaction = SentryTracer(TransactionContext("name", "op"), fixture.scopes) scope.transaction = previousTransaction sut.applyScope(scope, fixture.transaction) @@ -173,14 +173,14 @@ class SentryGestureListenerTracingTest { val sut = fixture.getSut() val expectedStatus = SpanStatus.CANCELLED - whenever(fixture.hub.configureScope(any())).thenAnswer { + whenever(fixture.scopes.configureScope(any())).thenAnswer { val scope = Scope(fixture.options) sut.applyScope(scope, fixture.transaction) } sut.onSingleTapUp(fixture.event) - whenever(fixture.hub.configureScope(any())).thenAnswer { + whenever(fixture.scopes.configureScope(any())).thenAnswer { val scope = Scope(fixture.options) scope.transaction = fixture.transaction @@ -199,7 +199,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("Activity.test_button", it.name) assertEquals(TransactionNameSource.COMPONENT, it.transactionNameSource) @@ -214,7 +214,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( any(), check { transactionOptions -> assertEquals(fixture.options.idleTimeout, transactionOptions.idleTimeout) @@ -232,7 +232,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("ui.action.click", it.operation) assertEquals(TransactionNameSource.COMPONENT, it.transactionNameSource) @@ -248,7 +248,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("Activity.test_button", it.name) assertEquals(TransactionNameSource.COMPONENT, it.transactionNameSource) @@ -256,7 +256,7 @@ class SentryGestureListenerTracingTest { any() ) - clearInvocations(fixture.hub) + clearInvocations(fixture.scopes) // second view interaction with another view val newTarget = mockView(event = fixture.event, clickable = true, context = fixture.context) val newContext = mock() @@ -269,16 +269,16 @@ class SentryGestureListenerTracingTest { whenever(it.getChildAt(0)).thenReturn(newTarget) } - whenever(fixture.hub.startTransaction(any(), any())) + whenever(fixture.scopes.startTransaction(any(), any())) .thenAnswer { // verify that the active transaction gets finished when a new one appears assertEquals(true, fixture.transaction.isFinished) - SentryTracer(TransactionContext("name", "op"), fixture.hub) + SentryTracer(TransactionContext("name", "op"), fixture.scopes) } sut.onSingleTapUp(fixture.event) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("Activity.test_checkbox", it.name) assertEquals(TransactionNameSource.COMPONENT, it.transactionNameSource) @@ -293,7 +293,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("Activity.test_scroll_view", it.name) assertEquals("ui.action.click", it.operation) @@ -302,20 +302,20 @@ class SentryGestureListenerTracingTest { any() ) - clearInvocations(fixture.hub) + clearInvocations(fixture.scopes) // second view interaction with a different interaction type (scroll) - whenever(fixture.hub.startTransaction(any(), any())) + whenever(fixture.scopes.startTransaction(any(), any())) .thenAnswer { // verify that the active transaction gets finished when a new one appears assertEquals(true, fixture.transaction.isFinished) - SentryTracer(TransactionContext("name", "op"), fixture.hub) + SentryTracer(TransactionContext("name", "op"), fixture.scopes) } sut.onScroll(fixture.event, mock(), 10.0f, 0f) sut.onUp(mock()) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("Activity.test_scroll_view", it.name) assertEquals("ui.action.scroll", it.operation) @@ -340,7 +340,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) // then two transaction should be captured - verify(fixture.hub, times(2)).startTransaction( + verify(fixture.scopes, times(2)).startTransaction( check { assertEquals("Activity.test_button", it.name) assertEquals(TransactionNameSource.COMPONENT, it.transactionNameSource) From baa35e1cdd85900fb588df66637a0e910f71ad33 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 16 Apr 2024 16:38:37 +0200 Subject: [PATCH 04/91] Hubs/Scopes Merge 4 - Replace `IHub` with `IScopes` in Android integrations (#3300) * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations --- .../api/sentry-android-fragment.api | 8 +-- .../fragment/FragmentLifecycleIntegration.kt | 10 ++-- .../SentryFragmentLifecycleCallbacks.kt | 18 +++---- .../FragmentLifecycleIntegrationTest.kt | 16 +++--- .../SentryFragmentLifecycleCallbacksTest.kt | 14 ++--- .../android/mockservers/RelayAsserter.kt | 2 +- .../api/sentry-android-navigation.api | 10 ++-- .../navigation/SentryNavigationListener.kt | 24 ++++----- .../SentryNavigationListenerTest.kt | 46 ++++++++-------- .../api/sentry-android-okhttp.api | 18 +++---- .../okhttp/SentryOkHttpEventListener.kt | 22 ++++---- .../android/okhttp/SentryOkHttpInterceptor.kt | 16 +++--- .../android/sqlite/SQLiteSpanManager.kt | 12 ++--- .../android/sqlite/SQLiteSpanManagerTest.kt | 12 ++--- .../sqlite/SentrySupportSQLiteDatabaseTest.kt | 12 ++--- .../SentrySupportSQLiteStatementTest.kt | 12 ++--- .../api/sentry-android-timber.api | 4 +- .../android/timber/SentryTimberIntegration.kt | 6 +-- .../sentry/android/timber/SentryTimberTree.kt | 8 +-- .../timber/SentryTimberIntegrationTest.kt | 18 +++---- .../android/timber/SentryTimberTreeTest.kt | 52 +++++++++---------- 21 files changed, 170 insertions(+), 170 deletions(-) diff --git a/sentry-android-fragment/api/sentry-android-fragment.api b/sentry-android-fragment/api/sentry-android-fragment.api index 4b3487c36e..f2f3334280 100644 --- a/sentry-android-fragment/api/sentry-android-fragment.api +++ b/sentry-android-fragment/api/sentry-android-fragment.api @@ -18,7 +18,7 @@ public final class io/sentry/android/fragment/FragmentLifecycleIntegration : and public fun onActivitySaveInstanceState (Landroid/app/Activity;Landroid/os/Bundle;)V public fun onActivityStarted (Landroid/app/Activity;)V public fun onActivityStopped (Landroid/app/Activity;)V - public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/fragment/FragmentLifecycleState : java/lang/Enum { @@ -40,9 +40,9 @@ public final class io/sentry/android/fragment/FragmentLifecycleState : java/lang public final class io/sentry/android/fragment/SentryFragmentLifecycleCallbacks : androidx/fragment/app/FragmentManager$FragmentLifecycleCallbacks { public static final field Companion Lio/sentry/android/fragment/SentryFragmentLifecycleCallbacks$Companion; public static final field FRAGMENT_LOAD_OP Ljava/lang/String; - public fun (Lio/sentry/IHub;Ljava/util/Set;Z)V - public synthetic fun (Lio/sentry/IHub;Ljava/util/Set;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Lio/sentry/IHub;ZZ)V + public fun (Lio/sentry/IScopes;Ljava/util/Set;Z)V + public synthetic fun (Lio/sentry/IScopes;Ljava/util/Set;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IScopes;ZZ)V public fun (ZZ)V public synthetic fun (ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getEnableAutoFragmentLifecycleTracing ()Z diff --git a/sentry-android-fragment/src/main/java/io/sentry/android/fragment/FragmentLifecycleIntegration.kt b/sentry-android-fragment/src/main/java/io/sentry/android/fragment/FragmentLifecycleIntegration.kt index 4129ea4356..d2fd393200 100644 --- a/sentry-android-fragment/src/main/java/io/sentry/android/fragment/FragmentLifecycleIntegration.kt +++ b/sentry-android-fragment/src/main/java/io/sentry/android/fragment/FragmentLifecycleIntegration.kt @@ -5,7 +5,7 @@ import android.app.Application import android.app.Application.ActivityLifecycleCallbacks import android.os.Bundle import androidx.fragment.app.FragmentActivity -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.Integration import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryLevel.DEBUG @@ -40,11 +40,11 @@ class FragmentLifecycleIntegration( enableAutoFragmentLifecycleTracing = enableAutoFragmentLifecycleTracing ) - private lateinit var hub: IHub + private lateinit var scopes: IScopes private lateinit var options: SentryOptions - override fun register(hub: IHub, options: SentryOptions) { - this.hub = hub + override fun register(scopes: IScopes, options: SentryOptions) { + this.scopes = scopes this.options = options application.registerActivityLifecycleCallbacks(this) @@ -66,7 +66,7 @@ class FragmentLifecycleIntegration( ?.supportFragmentManager ?.registerFragmentLifecycleCallbacks( SentryFragmentLifecycleCallbacks( - hub = hub, + scopes = scopes, filterFragmentLifecycleBreadcrumbs = filterFragmentLifecycleBreadcrumbs, enableAutoFragmentLifecycleTracing = enableAutoFragmentLifecycleTracing ), diff --git a/sentry-android-fragment/src/main/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacks.kt b/sentry-android-fragment/src/main/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacks.kt index 78f45474e2..64ecb21d41 100644 --- a/sentry-android-fragment/src/main/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacks.kt +++ b/sentry-android-fragment/src/main/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacks.kt @@ -8,9 +8,9 @@ import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.HubAdapter -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ISpan +import io.sentry.ScopesAdapter import io.sentry.SentryLevel.INFO import io.sentry.SpanStatus import io.sentry.TypeCheckHint.ANDROID_FRAGMENT @@ -20,17 +20,17 @@ private const val TRACE_ORIGIN = "auto.ui.fragment" @Suppress("TooManyFunctions") class SentryFragmentLifecycleCallbacks( - private val hub: IHub = HubAdapter.getInstance(), + private val scopes: IScopes = ScopesAdapter.getInstance(), val filterFragmentLifecycleBreadcrumbs: Set, val enableAutoFragmentLifecycleTracing: Boolean ) : FragmentLifecycleCallbacks() { constructor( - hub: IHub, + scopes: IScopes, enableFragmentLifecycleBreadcrumbs: Boolean, enableAutoFragmentLifecycleTracing: Boolean ) : this( - hub = hub, + scopes = scopes, filterFragmentLifecycleBreadcrumbs = FragmentLifecycleState.values().toSet() .takeIf { enableFragmentLifecycleBreadcrumbs } .orEmpty(), @@ -41,14 +41,14 @@ class SentryFragmentLifecycleCallbacks( enableFragmentLifecycleBreadcrumbs: Boolean = true, enableAutoFragmentLifecycleTracing: Boolean = false ) : this( - hub = HubAdapter.getInstance(), + scopes = ScopesAdapter.getInstance(), filterFragmentLifecycleBreadcrumbs = FragmentLifecycleState.values().toSet() .takeIf { enableFragmentLifecycleBreadcrumbs } .orEmpty(), enableAutoFragmentLifecycleTracing = enableAutoFragmentLifecycleTracing ) - private val isPerformanceEnabled get() = hub.options.isTracingEnabled && enableAutoFragmentLifecycleTracing + private val isPerformanceEnabled get() = scopes.options.isTracingEnabled && enableAutoFragmentLifecycleTracing private val fragmentsWithOngoingTransactions = WeakHashMap() @@ -141,7 +141,7 @@ class SentryFragmentLifecycleCallbacks( val hint = Hint() .also { it.set(ANDROID_FRAGMENT, fragment) } - hub.addBreadcrumb(breadcrumb, hint) + scopes.addBreadcrumb(breadcrumb, hint) } private fun getFragmentName(fragment: Fragment): String { @@ -157,7 +157,7 @@ class SentryFragmentLifecycleCallbacks( } var transaction: ISpan? = null - hub.configureScope { + scopes.configureScope { transaction = it.transaction } diff --git a/sentry-android-fragment/src/test/java/io/sentry/android/fragment/FragmentLifecycleIntegrationTest.kt b/sentry-android-fragment/src/test/java/io/sentry/android/fragment/FragmentLifecycleIntegrationTest.kt index 032aef58e1..84286503b9 100644 --- a/sentry-android-fragment/src/test/java/io/sentry/android/fragment/FragmentLifecycleIntegrationTest.kt +++ b/sentry-android-fragment/src/test/java/io/sentry/android/fragment/FragmentLifecycleIntegrationTest.kt @@ -4,7 +4,7 @@ import android.app.Activity import android.app.Application import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentManager -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryOptions import org.mockito.kotlin.check import org.mockito.kotlin.doReturn @@ -24,14 +24,14 @@ class FragmentLifecycleIntegrationTest { val fragmentActivity = mock { on { supportFragmentManager } doReturn fragmentManager } - val hub = mock() + val scopes = mock() val options = SentryOptions() fun getSut( enableFragmentLifecycleBreadcrumbs: Boolean = true, enableAutoFragmentLifecycleTracing: Boolean = false ): FragmentLifecycleIntegration { - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) return FragmentLifecycleIntegration( application = application, enableFragmentLifecycleBreadcrumbs = enableFragmentLifecycleBreadcrumbs, @@ -46,7 +46,7 @@ class FragmentLifecycleIntegrationTest { fun `When register, it should register activity lifecycle callbacks`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.application).registerActivityLifecycleCallbacks(sut) } @@ -55,7 +55,7 @@ class FragmentLifecycleIntegrationTest { fun `When close, it should unregister lifecycle callbacks`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.close() verify(fixture.application).unregisterActivityLifecycleCallbacks(sut) @@ -69,7 +69,7 @@ class FragmentLifecycleIntegrationTest { on { supportFragmentManager } doReturn fragmentManager } - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.onActivityCreated(fragmentActivity, savedInstanceState = null) verify(fragmentManager).registerFragmentLifecycleCallbacks( @@ -84,7 +84,7 @@ class FragmentLifecycleIntegrationTest { fun `When FragmentActivity is created, it should register fragment lifecycle callbacks with passed config`() { val sut = fixture.getSut(enableFragmentLifecycleBreadcrumbs = false, enableAutoFragmentLifecycleTracing = true) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.onActivityCreated(fixture.fragmentActivity, savedInstanceState = null) verify(fixture.fragmentManager).registerFragmentLifecycleCallbacks( @@ -102,7 +102,7 @@ class FragmentLifecycleIntegrationTest { val sut = fixture.getSut() val activity = mock() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.onActivityCreated(activity, savedInstanceState = null) } diff --git a/sentry-android-fragment/src/test/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacksTest.kt b/sentry-android-fragment/src/test/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacksTest.kt index 9b7fd5c5f2..26cb5b211a 100644 --- a/sentry-android-fragment/src/test/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacksTest.kt +++ b/sentry-android-fragment/src/test/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacksTest.kt @@ -5,8 +5,8 @@ import android.os.Bundle import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import io.sentry.Breadcrumb -import io.sentry.Hub import io.sentry.IScope +import io.sentry.IScopes import io.sentry.ISpan import io.sentry.ITransaction import io.sentry.ScopeCallback @@ -32,7 +32,7 @@ class SentryFragmentLifecycleCallbacksTest { private class Fixture { val fragmentManager = mock() - val hub = mock() + val scopes = mock() val fragment = mock() val context = mock() val scope = mock() @@ -45,7 +45,7 @@ class SentryFragmentLifecycleCallbacksTest { tracesSampleRate: Double? = 1.0, isAdded: Boolean = true ): SentryFragmentLifecycleCallbacks { - whenever(hub.options).thenReturn( + whenever(scopes.options).thenReturn( SentryOptions().apply { setTracesSampleRate(tracesSampleRate) } @@ -55,12 +55,12 @@ class SentryFragmentLifecycleCallbacksTest { ) whenever(transaction.startChild(any(), any())).thenReturn(span) whenever(scope.transaction).thenReturn(transaction) - whenever(hub.configureScope(any())).thenAnswer { + whenever(scopes.configureScope(any())).thenAnswer { (it.arguments[0] as ScopeCallback).run(scope) } whenever(fragment.isAdded).thenReturn(isAdded) return SentryFragmentLifecycleCallbacks( - hub = hub, + scopes = scopes, filterFragmentLifecycleBreadcrumbs = loggedFragmentLifecycleStates, enableAutoFragmentLifecycleTracing = enableAutoFragmentLifecycleTracing ) @@ -272,7 +272,7 @@ class SentryFragmentLifecycleCallbacksTest { } private fun verifyBreadcrumbAdded(expectedState: String) { - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { breadcrumb: Breadcrumb -> assertEquals("ui.fragment.lifecycle", breadcrumb.category) assertEquals("navigation", breadcrumb.type) @@ -285,6 +285,6 @@ class SentryFragmentLifecycleCallbacksTest { } private fun verifyBreadcrumbAddedCount(count: Int) { - verify(fixture.hub, times(count)).addBreadcrumb(any(), anyOrNull()) + verify(fixture.scopes, times(count)).addBreadcrumb(any(), anyOrNull()) } } diff --git a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/mockservers/RelayAsserter.kt b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/mockservers/RelayAsserter.kt index e956978086..f685e84874 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/mockservers/RelayAsserter.kt +++ b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/mockservers/RelayAsserter.kt @@ -93,7 +93,7 @@ class RelayAsserter( /** Request parsed as envelope. */ val envelope: SentryEnvelope? by lazy { try { - EnvelopeReader(Sentry.getCurrentHub().options.serializer) + EnvelopeReader(Sentry.getCurrentScopes().options.serializer) .read(GZIPInputStream(request.body.inputStream())) } catch (e: IOException) { null diff --git a/sentry-android-navigation/api/sentry-android-navigation.api b/sentry-android-navigation/api/sentry-android-navigation.api index 79151bb3fb..03a46d8b87 100644 --- a/sentry-android-navigation/api/sentry-android-navigation.api +++ b/sentry-android-navigation/api/sentry-android-navigation.api @@ -10,11 +10,11 @@ public final class io/sentry/android/navigation/SentryNavigationListener : andro public static final field Companion Lio/sentry/android/navigation/SentryNavigationListener$Companion; public static final field NAVIGATION_OP Ljava/lang/String; public fun ()V - public fun (Lio/sentry/IHub;)V - public fun (Lio/sentry/IHub;Z)V - public fun (Lio/sentry/IHub;ZZ)V - public fun (Lio/sentry/IHub;ZZLjava/lang/String;)V - public synthetic fun (Lio/sentry/IHub;ZZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IScopes;Z)V + public fun (Lio/sentry/IScopes;ZZ)V + public fun (Lio/sentry/IScopes;ZZLjava/lang/String;)V + public synthetic fun (Lio/sentry/IScopes;ZZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun onDestinationChanged (Landroidx/navigation/NavController;Landroidx/navigation/NavDestination;Landroid/os/Bundle;)V } diff --git a/sentry-android-navigation/src/main/java/io/sentry/android/navigation/SentryNavigationListener.kt b/sentry-android-navigation/src/main/java/io/sentry/android/navigation/SentryNavigationListener.kt index 8fdf8b0df8..bb06d66b3c 100644 --- a/sentry-android-navigation/src/main/java/io/sentry/android/navigation/SentryNavigationListener.kt +++ b/sentry-android-navigation/src/main/java/io/sentry/android/navigation/SentryNavigationListener.kt @@ -6,9 +6,9 @@ import androidx.navigation.NavController import androidx.navigation.NavDestination import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.HubAdapter -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ITransaction +import io.sentry.ScopesAdapter import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryLevel.DEBUG import io.sentry.SentryLevel.INFO @@ -34,7 +34,7 @@ private const val TRACE_ORIGIN = "auto.navigation" * with [SentryOptions.idleTimeout] for navigation events. */ class SentryNavigationListener @JvmOverloads constructor( - private val hub: IHub = HubAdapter.getInstance(), + private val scopes: IScopes = ScopesAdapter.getInstance(), private val enableNavigationBreadcrumbs: Boolean = true, private val enableNavigationTracing: Boolean = true, private val traceOriginAppendix: String? = null @@ -43,7 +43,7 @@ class SentryNavigationListener @JvmOverloads constructor( private var previousDestinationRef: WeakReference? = null private var previousArgs: Bundle? = null - private val isPerformanceEnabled get() = hub.options.isTracingEnabled && enableNavigationTracing + private val isPerformanceEnabled get() = scopes.options.isTracingEnabled && enableNavigationTracing private var activeTransaction: ITransaction? = null @@ -91,7 +91,7 @@ class SentryNavigationListener @JvmOverloads constructor( } val hint = Hint() hint.set(TypeCheckHint.ANDROID_NAV_DESTINATION, destination) - hub.addBreadcrumb(breadcrumb, hint) + scopes.addBreadcrumb(breadcrumb, hint) } private fun startTracing( @@ -100,7 +100,7 @@ class SentryNavigationListener @JvmOverloads constructor( arguments: Map ) { if (!isPerformanceEnabled) { - TracingUtils.startNewTrace(hub) + TracingUtils.startNewTrace(scopes) return } @@ -111,7 +111,7 @@ class SentryNavigationListener @JvmOverloads constructor( if (destination.navigatorName == "activity") { // we do not trace navigation between activities to avoid clashing with activity lifecycle tracing - hub.options.logger.log( + scopes.options.logger.log( DEBUG, "Navigating to activity destination, no transaction captured." ) @@ -122,7 +122,7 @@ class SentryNavigationListener @JvmOverloads constructor( var name = destination.route ?: try { controller.context.resources.getResourceEntryName(destination.id) } catch (e: NotFoundException) { - hub.options.logger.log( + scopes.options.logger.log( DEBUG, "Destination id cannot be retrieved from Resources, no transaction captured." ) @@ -134,12 +134,12 @@ class SentryNavigationListener @JvmOverloads constructor( val transactionOptions = TransactionOptions().also { it.isWaitForChildren = true - it.idleTimeout = hub.options.idleTimeout + it.idleTimeout = scopes.options.idleTimeout it.deadlineTimeout = TransactionOptions.DEFAULT_DEADLINE_TIMEOUT_AUTO_TRANSACTION it.isTrimEnd = true } - val transaction = hub.startTransaction( + val transaction = scopes.startTransaction( TransactionContext(name, TransactionNameSource.ROUTE, NAVIGATION_OP), transactionOptions ) @@ -151,7 +151,7 @@ class SentryNavigationListener @JvmOverloads constructor( if (arguments.isNotEmpty()) { transaction.setData("arguments", arguments) } - hub.configureScope { scope -> + scopes.configureScope { scope -> scope.withTransaction { tx -> if (tx == null) { scope.transaction = transaction @@ -166,7 +166,7 @@ class SentryNavigationListener @JvmOverloads constructor( activeTransaction?.finish(status) // clear transaction from scope so others can bind to it - hub.configureScope { scope -> + scopes.configureScope { scope -> scope.withTransaction { tx -> if (tx == activeTransaction) { scope.clearTransaction() diff --git a/sentry-android-navigation/src/test/java/io/sentry/android/navigation/SentryNavigationListenerTest.kt b/sentry-android-navigation/src/test/java/io/sentry/android/navigation/SentryNavigationListenerTest.kt index 76c57159c3..b37133410f 100644 --- a/sentry-android-navigation/src/test/java/io/sentry/android/navigation/SentryNavigationListenerTest.kt +++ b/sentry-android-navigation/src/test/java/io/sentry/android/navigation/SentryNavigationListenerTest.kt @@ -7,8 +7,8 @@ import androidx.navigation.NavController import androidx.navigation.NavDestination import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.Breadcrumb -import io.sentry.IHub import io.sentry.IScope +import io.sentry.IScopes import io.sentry.Scope import io.sentry.Scope.IWithTransaction import io.sentry.ScopeCallback @@ -39,7 +39,7 @@ import kotlin.test.assertNull class SentryNavigationListenerTest { class Fixture { - val hub = mock() + val scopes = mock() val destination = mock() val navController = mock() @@ -67,20 +67,20 @@ class SentryNavigationListenerTest { tracesSampleRate ) } - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) this.transaction = transaction ?: SentryTracer( TransactionContext( "/$toRoute", SentryNavigationListener.NAVIGATION_OP ), - hub + scopes ) - whenever(hub.startTransaction(any(), any())) + whenever(scopes.startTransaction(any(), any())) .thenReturn(this.transaction) - whenever(hub.configureScope(any())).thenAnswer { + whenever(scopes.configureScope(any())).thenAnswer { (it.arguments[0] as ScopeCallback).run(scope) } @@ -96,7 +96,7 @@ class SentryNavigationListenerTest { whenever(navController.context).thenReturn(context) whenever(destination.route).thenReturn(toRoute) return SentryNavigationListener( - hub, + scopes, enableBreadcrumbs, enableTracing, traceOriginAppendix @@ -112,7 +112,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("navigation", it.type) assertEquals("navigation", it.category) @@ -133,7 +133,7 @@ class SentryNavigationListenerTest { bundleOf("arg1" to "foo", "arg2" to "bar") ) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("/route", it.data["to"]) assertEquals(mapOf("arg1" to "foo", "arg2" to "bar"), it.data["to_arguments"]) @@ -152,7 +152,7 @@ class SentryNavigationListenerTest { bundleOf() ) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("/route", it.data["to"]) assertNull(it.data["to_arguments"]) @@ -180,7 +180,7 @@ class SentryNavigationListenerTest { bundleOf("to_arg1" to "to_foo") ) val captor = argumentCaptor() - verify(fixture.hub, times(2)).addBreadcrumb(captor.capture(), any()) + verify(fixture.scopes, times(2)).addBreadcrumb(captor.capture(), any()) captor.secondValue.let { assertEquals("/route_from", it.data["from"]) assertEquals(mapOf("from_arg1" to "from_foo"), it.data["from_arguments"]) @@ -196,7 +196,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.hub, never()).addBreadcrumb(any()) + verify(fixture.scopes, never()).addBreadcrumb(any()) } @Test @@ -205,7 +205,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.hub, never()).startTransaction( + verify(fixture.scopes, never()).startTransaction( any(), any() ) @@ -217,7 +217,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.hub, never()).startTransaction( + verify(fixture.scopes, never()).startTransaction( any(), any() ) @@ -230,7 +230,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.hub, never()).startTransaction( + verify(fixture.scopes, never()).startTransaction( any(), any() ) @@ -242,7 +242,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.hub, never()).startTransaction( + verify(fixture.scopes, never()).startTransaction( any(), any() ) @@ -254,7 +254,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("/route", it.name) assertEquals(SentryNavigationListener.NAVIGATION_OP, it.operation) @@ -270,7 +270,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("/github", it.name) assertEquals(TransactionNameSource.ROUTE, it.transactionNameSource) @@ -285,7 +285,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("/destination-id-1", it.name) assertEquals(TransactionNameSource.ROUTE, it.transactionNameSource) @@ -304,7 +304,7 @@ class SentryNavigationListenerTest { bundleOf("user_id" to 123, "per_page" to 10) ) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("/github", it.name) assertEquals(TransactionNameSource.ROUTE, it.transactionNameSource) @@ -365,13 +365,13 @@ class SentryNavigationListenerTest { ArgumentCaptor.forClass(ScopeCallback::class.java) val scope = Scope(fixture.options) val propagationContextAtStart = scope.propagationContext - whenever(fixture.hub.configureScope(argumentCaptor.capture())).thenAnswer { + whenever(fixture.scopes.configureScope(argumentCaptor.capture())).thenAnswer { argumentCaptor.value.run(scope) } sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.hub).configureScope(any()) + verify(fixture.scopes).configureScope(any()) assertNotSame(propagationContextAtStart, scope.propagationContext) } @@ -399,7 +399,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( any(), check { options -> assertEquals(TransactionOptions.DEFAULT_DEADLINE_TIMEOUT_AUTO_TRANSACTION, options.deadlineTimeout) diff --git a/sentry-android-okhttp/api/sentry-android-okhttp.api b/sentry-android-okhttp/api/sentry-android-okhttp.api index a1ad9114a2..35f42842dd 100644 --- a/sentry-android-okhttp/api/sentry-android-okhttp.api +++ b/sentry-android-okhttp/api/sentry-android-okhttp.api @@ -8,12 +8,12 @@ public final class io/sentry/android/okhttp/BuildConfig { public final class io/sentry/android/okhttp/SentryOkHttpEventListener : okhttp3/EventListener { public fun ()V - public fun (Lio/sentry/IHub;Lkotlin/jvm/functions/Function1;)V - public synthetic fun (Lio/sentry/IHub;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Lio/sentry/IHub;Lokhttp3/EventListener$Factory;)V - public synthetic fun (Lio/sentry/IHub;Lokhttp3/EventListener$Factory;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Lio/sentry/IHub;Lokhttp3/EventListener;)V - public synthetic fun (Lio/sentry/IHub;Lokhttp3/EventListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IScopes;Lkotlin/jvm/functions/Function1;)V + public synthetic fun (Lio/sentry/IScopes;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IScopes;Lokhttp3/EventListener$Factory;)V + public synthetic fun (Lio/sentry/IScopes;Lokhttp3/EventListener$Factory;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IScopes;Lokhttp3/EventListener;)V + public synthetic fun (Lio/sentry/IScopes;Lokhttp3/EventListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun (Lokhttp3/EventListener$Factory;)V public fun (Lokhttp3/EventListener;)V public fun cacheConditionalHit (Lokhttp3/Call;Lokhttp3/Response;)V @@ -49,9 +49,9 @@ public final class io/sentry/android/okhttp/SentryOkHttpEventListener : okhttp3/ public final class io/sentry/android/okhttp/SentryOkHttpInterceptor : okhttp3/Interceptor { public fun ()V - public fun (Lio/sentry/IHub;)V - public fun (Lio/sentry/IHub;Lio/sentry/android/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;ZLjava/util/List;Ljava/util/List;)V - public synthetic fun (Lio/sentry/IHub;Lio/sentry/android/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;ZLjava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IScopes;Lio/sentry/android/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;ZLjava/util/List;Ljava/util/List;)V + public synthetic fun (Lio/sentry/IScopes;Lio/sentry/android/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;ZLjava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun (Lio/sentry/android/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;)V public fun intercept (Lokhttp3/Interceptor$Chain;)Lokhttp3/Response; } diff --git a/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpEventListener.kt b/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpEventListener.kt index 7ca5313d8f..f99106e8d9 100644 --- a/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpEventListener.kt +++ b/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpEventListener.kt @@ -1,7 +1,7 @@ package io.sentry.android.okhttp -import io.sentry.HubAdapter -import io.sentry.IHub +import io.sentry.IScopes +import io.sentry.ScopesAdapter import okhttp3.Call import okhttp3.Connection import okhttp3.EventListener @@ -42,35 +42,35 @@ import java.net.Proxy ) @Suppress("TooManyFunctions") class SentryOkHttpEventListener( - hub: IHub = HubAdapter.getInstance(), + scopes: IScopes = ScopesAdapter.getInstance(), originalEventListenerCreator: ((call: Call) -> EventListener)? = null ) : EventListener() { constructor() : this( - HubAdapter.getInstance(), + ScopesAdapter.getInstance(), originalEventListenerCreator = null ) constructor(originalEventListener: EventListener) : this( - HubAdapter.getInstance(), + ScopesAdapter.getInstance(), originalEventListenerCreator = { originalEventListener } ) constructor(originalEventListenerFactory: Factory) : this( - HubAdapter.getInstance(), + ScopesAdapter.getInstance(), originalEventListenerCreator = { originalEventListenerFactory.create(it) } ) - constructor(hub: IHub = HubAdapter.getInstance(), originalEventListener: EventListener) : this( - hub, + constructor(scopes: IScopes = ScopesAdapter.getInstance(), originalEventListener: EventListener) : this( + scopes, originalEventListenerCreator = { originalEventListener } ) - constructor(hub: IHub = HubAdapter.getInstance(), originalEventListenerFactory: Factory) : this( - hub, + constructor(scopes: IScopes = ScopesAdapter.getInstance(), originalEventListenerFactory: Factory) : this( + scopes, originalEventListenerCreator = { originalEventListenerFactory.create(it) } ) - private val delegate = io.sentry.okhttp.SentryOkHttpEventListener(hub, originalEventListenerCreator) + private val delegate = io.sentry.okhttp.SentryOkHttpEventListener(scopes, originalEventListenerCreator) override fun cacheConditionalHit(call: Call, cachedResponse: Response) { delegate.cacheConditionalHit(call, cachedResponse) diff --git a/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpInterceptor.kt b/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpInterceptor.kt index 971af925ff..3c7e590fe1 100644 --- a/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpInterceptor.kt +++ b/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpInterceptor.kt @@ -1,9 +1,9 @@ package io.sentry.android.okhttp import io.sentry.HttpStatusCodeRange -import io.sentry.HubAdapter -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ISpan +import io.sentry.ScopesAdapter import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryOptions.DEFAULT_PROPAGATION_TARGETS import io.sentry.android.okhttp.SentryOkHttpInterceptor.BeforeSpanCallback @@ -17,7 +17,7 @@ import okhttp3.Response * out of the active span bound to the scope for each HTTP Request. * If [captureFailedRequests] is enabled, the SDK will capture HTTP Client errors as well. * - * @param hub The [IHub], internal and only used for testing. + * @param scopes The [IScopes], internal and only used for testing. * @param beforeSpan The [ISpan] can be customized or dropped with the [BeforeSpanCallback]. * @param captureFailedRequests The SDK will only capture HTTP Client errors if it is enabled, * Defaults to false. @@ -31,7 +31,7 @@ import okhttp3.Response ReplaceWith("SentryOkHttpInterceptor", "io.sentry.okhttp.SentryOkHttpInterceptor") ) class SentryOkHttpInterceptor( - private val hub: IHub = HubAdapter.getInstance(), + private val scopes: IScopes = ScopesAdapter.getInstance(), private val beforeSpan: BeforeSpanCallback? = null, private val captureFailedRequests: Boolean = true, private val failedRequestStatusCodes: List = listOf( @@ -39,7 +39,7 @@ class SentryOkHttpInterceptor( ), private val failedRequestTargets: List = listOf(DEFAULT_PROPAGATION_TARGETS) ) : Interceptor by io.sentry.okhttp.SentryOkHttpInterceptor( - hub, + scopes, { span, request, response -> beforeSpan ?: return@SentryOkHttpInterceptor span beforeSpan.execute(span, request, response) @@ -49,9 +49,9 @@ class SentryOkHttpInterceptor( failedRequestTargets ) { - constructor() : this(HubAdapter.getInstance()) - constructor(hub: IHub) : this(hub, null) - constructor(beforeSpan: BeforeSpanCallback) : this(HubAdapter.getInstance(), beforeSpan) + constructor() : this(ScopesAdapter.getInstance()) + constructor(scopes: IScopes) : this(scopes, null) + constructor(beforeSpan: BeforeSpanCallback) : this(ScopesAdapter.getInstance(), beforeSpan) init { addIntegrationToSdkVersion(javaClass) diff --git a/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SQLiteSpanManager.kt b/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SQLiteSpanManager.kt index 3bfa855d53..2e39bf76ec 100644 --- a/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SQLiteSpanManager.kt +++ b/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SQLiteSpanManager.kt @@ -1,8 +1,8 @@ package io.sentry.android.sqlite import android.database.SQLException -import io.sentry.HubAdapter -import io.sentry.IHub +import io.sentry.IScopes +import io.sentry.ScopesAdapter import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryStackTraceFactory import io.sentry.SpanDataConvention @@ -11,10 +11,10 @@ import io.sentry.SpanStatus private const val TRACE_ORIGIN = "auto.db.sqlite" internal class SQLiteSpanManager( - private val hub: IHub = HubAdapter.getInstance(), + private val scopes: IScopes = ScopesAdapter.getInstance(), private val databaseName: String? = null ) { - private val stackTraceFactory = SentryStackTraceFactory(hub.options) + private val stackTraceFactory = SentryStackTraceFactory(scopes.options) init { SentryIntegrationPackageStorage.getInstance().addIntegration("SQLite") @@ -30,7 +30,7 @@ internal class SQLiteSpanManager( @Suppress("TooGenericExceptionCaught") @Throws(SQLException::class) fun performSql(sql: String, operation: () -> T): T { - val span = hub.span?.startChild("db.sql.query", sql) + val span = scopes.span?.startChild("db.sql.query", sql) span?.spanContext?.origin = TRACE_ORIGIN return try { val result = operation() @@ -42,7 +42,7 @@ internal class SQLiteSpanManager( throw e } finally { span?.apply { - val isMainThread: Boolean = hub.options.mainThreadChecker.isMainThread + val isMainThread: Boolean = scopes.options.mainThreadChecker.isMainThread setData(SpanDataConvention.BLOCKED_MAIN_THREAD_KEY, isMainThread) if (isMainThread) { setData(SpanDataConvention.CALL_STACK_KEY, stackTraceFactory.inAppCallStack) diff --git a/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SQLiteSpanManagerTest.kt b/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SQLiteSpanManagerTest.kt index e2fa0c2e4d..9265cd260a 100644 --- a/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SQLiteSpanManagerTest.kt +++ b/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SQLiteSpanManagerTest.kt @@ -1,7 +1,7 @@ package io.sentry.android.sqlite import android.database.SQLException -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -22,7 +22,7 @@ import kotlin.test.assertTrue class SQLiteSpanManagerTest { private class Fixture { - private val hub = mock() + private val scopes = mock() lateinit var sentryTracer: SentryTracer lateinit var options: SentryOptions @@ -30,13 +30,13 @@ class SQLiteSpanManagerTest { options = SentryOptions().apply { dsn = "https://key@sentry.io/proj" } - whenever(hub.options).thenReturn(options) - sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) + whenever(scopes.options).thenReturn(options) + sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) if (isSpanActive) { - whenever(hub.span).thenReturn(sentryTracer) + whenever(scopes.span).thenReturn(sentryTracer) } - return SQLiteSpanManager(hub, databaseName) + return SQLiteSpanManager(scopes, databaseName) } } diff --git a/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentrySupportSQLiteDatabaseTest.kt b/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentrySupportSQLiteDatabaseTest.kt index cf22c3b0ec..99e1d5f4a0 100644 --- a/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentrySupportSQLiteDatabaseTest.kt +++ b/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentrySupportSQLiteDatabaseTest.kt @@ -3,7 +3,7 @@ package io.sentry.android.sqlite import android.database.Cursor import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteQuery -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ISpan import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -23,8 +23,8 @@ import kotlin.test.assertTrue class SentrySupportSQLiteDatabaseTest { private class Fixture { - private val hub = mock() - private val spanManager = SQLiteSpanManager(hub) + private val scopes = mock() + private val spanManager = SQLiteSpanManager(scopes) val mockDatabase = mock() lateinit var sentryTracer: SentryTracer lateinit var options: SentryOptions @@ -37,11 +37,11 @@ class SentrySupportSQLiteDatabaseTest { options = SentryOptions().apply { dsn = "https://key@sentry.io/proj" } - whenever(hub.options).thenReturn(options) - sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) + whenever(scopes.options).thenReturn(options) + sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) if (isSpanActive) { - whenever(hub.span).thenReturn(sentryTracer) + whenever(scopes.span).thenReturn(sentryTracer) } return SentrySupportSQLiteDatabase(mockDatabase, spanManager) diff --git a/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentrySupportSQLiteStatementTest.kt b/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentrySupportSQLiteStatementTest.kt index 9078ba8b08..4b6292bd27 100644 --- a/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentrySupportSQLiteStatementTest.kt +++ b/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentrySupportSQLiteStatementTest.kt @@ -1,7 +1,7 @@ package io.sentry.android.sqlite import androidx.sqlite.db.SupportSQLiteStatement -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ISpan import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -18,8 +18,8 @@ import kotlin.test.assertTrue class SentrySupportSQLiteStatementTest { private class Fixture { - private val hub = mock() - private val spanManager = SQLiteSpanManager(hub) + private val scopes = mock() + private val spanManager = SQLiteSpanManager(scopes) val mockStatement = mock() lateinit var sentryTracer: SentryTracer lateinit var options: SentryOptions @@ -28,11 +28,11 @@ class SentrySupportSQLiteStatementTest { options = SentryOptions().apply { dsn = "https://key@sentry.io/proj" } - whenever(hub.options).thenReturn(options) - sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) + whenever(scopes.options).thenReturn(options) + sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) if (isSpanActive) { - whenever(hub.span).thenReturn(sentryTracer) + whenever(scopes.span).thenReturn(sentryTracer) } return SentrySupportSQLiteStatement(mockStatement, spanManager, sql) } diff --git a/sentry-android-timber/api/sentry-android-timber.api b/sentry-android-timber/api/sentry-android-timber.api index 808e91bf10..2d71f67570 100644 --- a/sentry-android-timber/api/sentry-android-timber.api +++ b/sentry-android-timber/api/sentry-android-timber.api @@ -14,11 +14,11 @@ public final class io/sentry/android/timber/SentryTimberIntegration : io/sentry/ public fun close ()V public final fun getMinBreadcrumbLevel ()Lio/sentry/SentryLevel; public final fun getMinEventLevel ()Lio/sentry/SentryLevel; - public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/timber/SentryTimberTree : timber/log/Timber$Tree { - public fun (Lio/sentry/IHub;Lio/sentry/SentryLevel;Lio/sentry/SentryLevel;)V + public fun (Lio/sentry/IScopes;Lio/sentry/SentryLevel;Lio/sentry/SentryLevel;)V public fun d (Ljava/lang/String;[Ljava/lang/Object;)V public fun d (Ljava/lang/Throwable;)V public fun d (Ljava/lang/Throwable;Ljava/lang/String;[Ljava/lang/Object;)V diff --git a/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberIntegration.kt b/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberIntegration.kt index d043faa5f6..334146a218 100644 --- a/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberIntegration.kt +++ b/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberIntegration.kt @@ -1,7 +1,7 @@ package io.sentry.android.timber -import io.sentry.IHub import io.sentry.ILogger +import io.sentry.IScopes import io.sentry.Integration import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryLevel @@ -21,10 +21,10 @@ class SentryTimberIntegration( private lateinit var tree: SentryTimberTree private lateinit var logger: ILogger - override fun register(hub: IHub, options: SentryOptions) { + override fun register(scopes: IScopes, options: SentryOptions) { logger = options.logger - tree = SentryTimberTree(hub, minEventLevel, minBreadcrumbLevel) + tree = SentryTimberTree(scopes, minEventLevel, minBreadcrumbLevel) Timber.plant(tree) logger.log(SentryLevel.DEBUG, "SentryTimberIntegration installed.") diff --git a/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberTree.kt b/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberTree.kt index f3a0f599a9..dddab75133 100644 --- a/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberTree.kt +++ b/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberTree.kt @@ -2,7 +2,7 @@ package io.sentry.android.timber import android.util.Log import io.sentry.Breadcrumb -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryEvent import io.sentry.SentryLevel import io.sentry.protocol.Message @@ -13,7 +13,7 @@ import timber.log.Timber */ @Suppress("TooManyFunctions") // we have to override all methods to be able to tweak logging class SentryTimberTree( - private val hub: IHub, + private val scopes: IScopes, private val minEventLevel: SentryLevel, private val minBreadcrumbLevel: SentryLevel ) : Timber.Tree() { @@ -269,7 +269,7 @@ class SentryTimberTree( logger = "Timber" } - hub.captureEvent(sentryEvent) + scopes.captureEvent(sentryEvent) } } @@ -296,7 +296,7 @@ class SentryTimberTree( else -> null } - breadCrumb?.let { hub.addBreadcrumb(it) } + breadCrumb?.let { scopes.addBreadcrumb(it) } } } diff --git a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberIntegrationTest.kt b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberIntegrationTest.kt index a57853e059..8bb85aa085 100644 --- a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberIntegrationTest.kt +++ b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberIntegrationTest.kt @@ -1,6 +1,6 @@ package io.sentry.android.timber -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryLevel import io.sentry.SentryOptions import io.sentry.protocol.SdkVersion @@ -16,7 +16,7 @@ import kotlin.test.assertTrue class SentryTimberIntegrationTest { private class Fixture { - val hub = mock() + val scopes = mock() val options = SentryOptions().apply { sdkVersion = SdkVersion("test", "1.2.3") } @@ -41,7 +41,7 @@ class SentryTimberIntegrationTest { @Test fun `Integrations plants a tree into Timber on register`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertEquals(1, Timber.treeCount()) @@ -53,16 +53,16 @@ class SentryTimberIntegrationTest { @Test fun `Integrations plants the SentryTimberTree tree`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) Timber.e(Throwable()) - verify(fixture.hub).captureEvent(any()) + verify(fixture.scopes).captureEvent(any()) } @Test fun `Integrations removes a tree from Timber on close integration`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertEquals(1, Timber.treeCount()) @@ -84,7 +84,7 @@ class SentryTimberIntegrationTest { minEventLevel = SentryLevel.INFO, minBreadcrumbLevel = SentryLevel.DEBUG ) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertEquals(sut.minEventLevel, SentryLevel.INFO) assertEquals(sut.minBreadcrumbLevel, SentryLevel.DEBUG) @@ -93,7 +93,7 @@ class SentryTimberIntegrationTest { @Test fun `Integration adds itself to the package list`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertTrue( fixture.options.sdkVersion!!.packageSet.any { @@ -106,7 +106,7 @@ class SentryTimberIntegrationTest { @Test fun `Integration adds itself to the integration list`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) assertTrue( fixture.options.sdkVersion!!.integrationSet.contains("Timber") diff --git a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt index 3d82b139ec..2ab7ff64db 100644 --- a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt +++ b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt @@ -1,7 +1,7 @@ package io.sentry.android.timber import io.sentry.Breadcrumb -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryLevel import io.sentry.getExc import org.mockito.kotlin.any @@ -19,13 +19,13 @@ import kotlin.test.assertNull class SentryTimberTreeTest { private class Fixture { - val hub = mock() + val scopes = mock() fun getSut( minEventLevel: SentryLevel = SentryLevel.ERROR, minBreadcrumbLevel: SentryLevel = SentryLevel.INFO ): SentryTimberTree { - return SentryTimberTree(hub, minEventLevel, minBreadcrumbLevel) + return SentryTimberTree(scopes, minEventLevel, minBreadcrumbLevel) } } @@ -40,28 +40,28 @@ class SentryTimberTreeTest { fun `Tree captures an event if min level is equal`() { val sut = fixture.getSut() sut.e(Throwable()) - verify(fixture.hub).captureEvent(any()) + verify(fixture.scopes).captureEvent(any()) } @Test fun `Tree captures an event if min level is higher`() { val sut = fixture.getSut() sut.wtf(Throwable()) - verify(fixture.hub).captureEvent(any()) + verify(fixture.scopes).captureEvent(any()) } @Test fun `Tree won't capture an event if min level is lower`() { val sut = fixture.getSut() sut.d(Throwable()) - verify(fixture.hub, never()).captureEvent(any()) + verify(fixture.scopes, never()).captureEvent(any()) } @Test fun `Tree captures debug level event`() { val sut = fixture.getSut(SentryLevel.DEBUG) sut.d(Throwable()) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { assertEquals(SentryLevel.DEBUG, it.level) } @@ -72,7 +72,7 @@ class SentryTimberTreeTest { fun `Tree captures info level event`() { val sut = fixture.getSut(SentryLevel.DEBUG) sut.i(Throwable()) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { assertEquals(SentryLevel.INFO, it.level) } @@ -83,7 +83,7 @@ class SentryTimberTreeTest { fun `Tree captures warning level event`() { val sut = fixture.getSut(SentryLevel.DEBUG) sut.w(Throwable()) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { assertEquals(SentryLevel.WARNING, it.level) } @@ -94,7 +94,7 @@ class SentryTimberTreeTest { fun `Tree captures error level event`() { val sut = fixture.getSut(SentryLevel.DEBUG) sut.e(Throwable()) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { assertEquals(SentryLevel.ERROR, it.level) } @@ -105,7 +105,7 @@ class SentryTimberTreeTest { fun `Tree captures fatal level event`() { val sut = fixture.getSut(SentryLevel.DEBUG) sut.wtf(Throwable()) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { assertEquals(SentryLevel.FATAL, it.level) } @@ -116,7 +116,7 @@ class SentryTimberTreeTest { fun `Tree captures unknown as debug level event`() { val sut = fixture.getSut(SentryLevel.DEBUG) sut.log(15, Throwable()) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { assertEquals(SentryLevel.DEBUG, it.level) } @@ -128,7 +128,7 @@ class SentryTimberTreeTest { val sut = fixture.getSut() val throwable = Throwable() sut.e(throwable) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { assertEquals(throwable, it.getExc()) } @@ -139,7 +139,7 @@ class SentryTimberTreeTest { fun `Tree captures an event without an exception`() { val sut = fixture.getSut() sut.e("message") - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { assertNull(it.getExc()) } @@ -150,7 +150,7 @@ class SentryTimberTreeTest { fun `Tree captures an event and sets Timber as a logger`() { val sut = fixture.getSut() sut.e("message") - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { assertEquals("Timber", it.logger) } @@ -164,7 +164,7 @@ class SentryTimberTreeTest { // only available thru static class Timber.tag("tag") Timber.e("message") - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { assertEquals("tag", it.getTag("TimberTag")) } @@ -176,7 +176,7 @@ class SentryTimberTreeTest { val sut = fixture.getSut() Timber.plant(sut) Timber.e("message") - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { assertNull(it.getTag("TimberTag")) } @@ -187,7 +187,7 @@ class SentryTimberTreeTest { fun `Tree captures an event with given message`() { val sut = fixture.getSut() sut.e("message") - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { assertNotNull(it.message) { message -> assertEquals("message", message.message) @@ -200,7 +200,7 @@ class SentryTimberTreeTest { fun `Tree captures an event with formatted message and arguments, when provided`() { val sut = fixture.getSut() sut.e("test count: %d", 32) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { assertNotNull(it.message) { message -> assertEquals("test count: %d", message.message) @@ -216,7 +216,7 @@ class SentryTimberTreeTest { val sut = fixture.getSut() sut.e("test count: %d", 32) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("test count: 32", it.message) } @@ -227,28 +227,28 @@ class SentryTimberTreeTest { fun `Tree adds a breadcrumb if min level is equal`() { val sut = fixture.getSut() sut.i(Throwable("test")) - verify(fixture.hub).addBreadcrumb(any()) + verify(fixture.scopes).addBreadcrumb(any()) } @Test fun `Tree adds a breadcrumb if min level is higher`() { val sut = fixture.getSut() sut.e(Throwable("test")) - verify(fixture.hub).addBreadcrumb(any()) + verify(fixture.scopes).addBreadcrumb(any()) } @Test fun `Tree won't add a breadcrumb if min level is lower`() { val sut = fixture.getSut(minBreadcrumbLevel = SentryLevel.ERROR) sut.i(Throwable("test")) - verify(fixture.hub, never()).addBreadcrumb(any()) + verify(fixture.scopes, never()).addBreadcrumb(any()) } @Test fun `Tree adds an info breadcrumb`() { val sut = fixture.getSut() sut.i("message") - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("Timber", it.category) assertEquals(SentryLevel.INFO, it.level) @@ -261,7 +261,7 @@ class SentryTimberTreeTest { fun `Tree adds an error breadcrumb`() { val sut = fixture.getSut() sut.e(Throwable("test")) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("exception", it.category) assertEquals(SentryLevel.ERROR, it.level) @@ -274,7 +274,7 @@ class SentryTimberTreeTest { fun `Tree does not add a breadcrumb, if no message provided`() { val sut = fixture.getSut() sut.e(Throwable()) - verify(fixture.hub, never()).addBreadcrumb(any()) + verify(fixture.scopes, never()).addBreadcrumb(any()) } @Test From 00c25352631d7147e3b8778b61beb5ee5052a33f Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 16 Apr 2024 16:38:57 +0200 Subject: [PATCH 05/91] Hubs/Scopes Merge 5 - Replace `IHub` with `IScopes` in apollo integrations (#3301) * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations --- sentry-apollo-3/api/sentry-apollo-3.api | 20 +++++----- .../apollo3/SentryApollo3HttpInterceptor.kt | 34 ++++++++-------- .../apollo3/SentryApolloBuilderExtensions.kt | 10 ++--- .../SentryApollo3InterceptorClientErrors.kt | 40 +++++++++---------- .../apollo3/SentryApollo3InterceptorTest.kt | 40 +++++++++---------- ...ntryApollo3InterceptorWithVariablesTest.kt | 22 +++++----- sentry-apollo/api/sentry-apollo.api | 6 +-- .../sentry/apollo/SentryApolloInterceptor.kt | 20 +++++----- .../apollo/SentryApolloInterceptorTest.kt | 38 +++++++++--------- 9 files changed, 115 insertions(+), 115 deletions(-) diff --git a/sentry-apollo-3/api/sentry-apollo-3.api b/sentry-apollo-3/api/sentry-apollo-3.api index 1c80e1950b..e106585156 100644 --- a/sentry-apollo-3/api/sentry-apollo-3.api +++ b/sentry-apollo-3/api/sentry-apollo-3.api @@ -17,11 +17,11 @@ public final class io/sentry/apollo3/SentryApollo3HttpInterceptor : com/apollogr public static final field SENTRY_APOLLO_3_OPERATION_TYPE Ljava/lang/String; public static final field SENTRY_APOLLO_3_VARIABLES Ljava/lang/String; public fun ()V - public fun (Lio/sentry/IHub;)V - public fun (Lio/sentry/IHub;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;)V - public fun (Lio/sentry/IHub;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;Z)V - public fun (Lio/sentry/IHub;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;ZLjava/util/List;)V - public synthetic fun (Lio/sentry/IHub;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;ZLjava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IScopes;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;)V + public fun (Lio/sentry/IScopes;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;Z)V + public fun (Lio/sentry/IScopes;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;ZLjava/util/List;)V + public synthetic fun (Lio/sentry/IScopes;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;ZLjava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun dispose ()V public fun intercept (Lcom/apollographql/apollo3/api/http/HttpRequest;Lcom/apollographql/apollo3/network/http/HttpInterceptorChain;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } @@ -40,12 +40,12 @@ public final class io/sentry/apollo3/SentryApollo3Interceptor : com/apollographq public final class io/sentry/apollo3/SentryApolloBuilderExtensionsKt { public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;)Lcom/apollographql/apollo3/ApolloClient$Builder; - public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IHub;)Lcom/apollographql/apollo3/ApolloClient$Builder; - public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IHub;Z)Lcom/apollographql/apollo3/ApolloClient$Builder; - public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IHub;ZLjava/util/List;)Lcom/apollographql/apollo3/ApolloClient$Builder; - public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IHub;ZLjava/util/List;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;)Lcom/apollographql/apollo3/ApolloClient$Builder; + public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IScopes;)Lcom/apollographql/apollo3/ApolloClient$Builder; + public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IScopes;Z)Lcom/apollographql/apollo3/ApolloClient$Builder; + public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IScopes;ZLjava/util/List;)Lcom/apollographql/apollo3/ApolloClient$Builder; + public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IScopes;ZLjava/util/List;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;)Lcom/apollographql/apollo3/ApolloClient$Builder; public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;ZLjava/util/List;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;)Lcom/apollographql/apollo3/ApolloClient$Builder; - public static synthetic fun sentryTracing$default (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IHub;ZLjava/util/List;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;ILjava/lang/Object;)Lcom/apollographql/apollo3/ApolloClient$Builder; + public static synthetic fun sentryTracing$default (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IScopes;ZLjava/util/List;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;ILjava/lang/Object;)Lcom/apollographql/apollo3/ApolloClient$Builder; public static synthetic fun sentryTracing$default (Lcom/apollographql/apollo3/ApolloClient$Builder;ZLjava/util/List;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;ILjava/lang/Object;)Lcom/apollographql/apollo3/ApolloClient$Builder; } diff --git a/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApollo3HttpInterceptor.kt b/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApollo3HttpInterceptor.kt index 08cab179a5..52219cb8e1 100644 --- a/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApollo3HttpInterceptor.kt +++ b/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApollo3HttpInterceptor.kt @@ -11,9 +11,9 @@ import com.apollographql.apollo3.network.http.HttpInterceptorChain import io.sentry.BaggageHeader import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.HubAdapter -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ISpan +import io.sentry.ScopesAdapter import io.sentry.SentryEvent import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryLevel @@ -41,7 +41,7 @@ import java.util.Locale private const val TRACE_ORIGIN = "auto.graphql.apollo3" class SentryApollo3HttpInterceptor @JvmOverloads constructor( - @ApiStatus.Internal private val hub: IHub = HubAdapter.getInstance(), + @ApiStatus.Internal private val scopes: IScopes = ScopesAdapter.getInstance(), private val beforeSpan: BeforeSpanCallback? = null, private val captureFailedRequests: Boolean = DEFAULT_CAPTURE_FAILED_REQUESTS, private val failedRequestTargets: List = listOf(DEFAULT_PROPAGATION_TARGETS) @@ -65,7 +65,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( request: HttpRequest, chain: HttpInterceptorChain ): HttpResponse { - val activeSpan = if (Platform.isAndroid()) hub.transaction else hub.span + val activeSpan = if (Platform.isAndroid()) scopes.transaction else scopes.span val operationName = getHeader(HEADER_APOLLO_OPERATION_NAME, request.headers) val operationType = decodeHeaderValue(request, SENTRY_APOLLO_3_OPERATION_TYPE) @@ -77,7 +77,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( span = startChild(request, activeSpan, operationName, operationType, operationId) } - val modifiedRequest = maybeAddTracingHeaders(hub, request, span) + val modifiedRequest = maybeAddTracingHeaders(scopes, request, span) var httpResponse: HttpResponse? = null var statusCode: Int? = null @@ -117,10 +117,10 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( } } - private fun maybeAddTracingHeaders(hub: IHub, request: HttpRequest, span: ISpan?): HttpRequest { + private fun maybeAddTracingHeaders(scopes: IScopes, request: HttpRequest, span: ISpan?): HttpRequest { var cleanedHeaders = removeSentryInternalHeaders(request.headers).toMutableList() - TracingUtils.traceIfAllowed(hub, request.url, request.headers.filter { it.name == BaggageHeader.BAGGAGE_HEADER }.map { it.value }, span)?.let { + TracingUtils.traceIfAllowed(scopes, request.url, request.headers.filter { it.name == BaggageHeader.BAGGAGE_HEADER }.map { it.value }, span)?.let { cleanedHeaders.add(HttpHeader(it.sentryTraceHeader.name, it.sentryTraceHeader.value)) it.baggageHeader?.let { baggageHeader -> cleanedHeaders = cleanedHeaders.filterNot { it.name == BaggageHeader.BAGGAGE_HEADER }.toMutableList().apply { @@ -179,7 +179,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( try { String(Base64.decode(it, Base64.NO_WRAP)) } catch (e: Throwable) { - hub.options.logger.log( + scopes.options.logger.log( SentryLevel.ERROR, "Error decoding internal apolloHeader $headerName", e @@ -218,7 +218,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( span.spanContext.sampled = false } } catch (e: Throwable) { - hub.options.logger.log( + scopes.options.logger.log( SentryLevel.ERROR, "An error occurred while executing beforeSpan on ApolloInterceptor", e @@ -256,7 +256,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( hint.set(APOLLO_RESPONSE, httpResponse) } - hub.addBreadcrumb(breadcrumb, hint) + scopes.addBreadcrumb(breadcrumb, hint) } // Extensions @@ -273,7 +273,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( private fun getHeaders(headers: List): MutableMap? { // Headers are only sent if isSendDefaultPii is enabled due to PII - if (!hub.options.isSendDefaultPii) { + if (!scopes.options.isSendDefaultPii) { return null } @@ -311,7 +311,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( val body = try { response.body?.peek()?.readUtf8() ?: "" } catch (e: Throwable) { - hub.options.logger.log( + scopes.options.logger.log( SentryLevel.ERROR, "Error reading the response body.", e @@ -368,7 +368,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( urlDetails.applyToRequest(this) // Cookie is only sent if isSendDefaultPii is enabled cookies = - if (hub.options.isSendDefaultPii) getHeader("Cookie", request.headers) else null + if (scopes.options.isSendDefaultPii) getHeader("Cookie", request.headers) else null method = request.method.name headers = getHeaders(request.headers) apiTarget = "graphql" @@ -382,7 +382,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( it.writeTo(buffer) data = buffer.readUtf8() } catch (e: Throwable) { - hub.options.logger.log( + scopes.options.logger.log( SentryLevel.ERROR, "Error reading the request body.", e @@ -396,7 +396,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( val sentryResponse = Response().apply { // Set-Cookie is only sent if isSendDefaultPii is enabled due to PII - cookies = if (hub.options.isSendDefaultPii) { + cookies = if (scopes.options.isSendDefaultPii) { getHeader( "Set-Cookie", response.headers @@ -419,9 +419,9 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( event.contexts.setResponse(sentryResponse) event.fingerprints = fingerprints - hub.captureEvent(event, hint) + scopes.captureEvent(event, hint) } catch (e: Throwable) { - hub.options.logger.log( + scopes.options.logger.log( SentryLevel.ERROR, "Error capturing the GraphQL error.", e diff --git a/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApolloBuilderExtensions.kt b/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApolloBuilderExtensions.kt index 2cdbc148fb..b40b1c183d 100644 --- a/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApolloBuilderExtensions.kt +++ b/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApolloBuilderExtensions.kt @@ -1,14 +1,14 @@ package io.sentry.apollo3 import com.apollographql.apollo3.ApolloClient -import io.sentry.HubAdapter -import io.sentry.IHub +import io.sentry.IScopes +import io.sentry.ScopesAdapter import io.sentry.SentryOptions.DEFAULT_PROPAGATION_TARGETS import io.sentry.apollo3.SentryApollo3HttpInterceptor.Companion.DEFAULT_CAPTURE_FAILED_REQUESTS @JvmOverloads fun ApolloClient.Builder.sentryTracing( - hub: IHub = HubAdapter.getInstance(), + scopes: IScopes = ScopesAdapter.getInstance(), captureFailedRequests: Boolean = DEFAULT_CAPTURE_FAILED_REQUESTS, failedRequestTargets: List = listOf(DEFAULT_PROPAGATION_TARGETS), beforeSpan: SentryApollo3HttpInterceptor.BeforeSpanCallback? = null @@ -16,7 +16,7 @@ fun ApolloClient.Builder.sentryTracing( addInterceptor(SentryApollo3Interceptor()) addHttpInterceptor( SentryApollo3HttpInterceptor( - hub = hub, + scopes = scopes, captureFailedRequests = captureFailedRequests, failedRequestTargets = failedRequestTargets, beforeSpan = beforeSpan @@ -31,7 +31,7 @@ fun ApolloClient.Builder.sentryTracing( beforeSpan: SentryApollo3HttpInterceptor.BeforeSpanCallback? = null ): ApolloClient.Builder { return sentryTracing( - hub = HubAdapter.getInstance(), + scopes = ScopesAdapter.getInstance(), captureFailedRequests = captureFailedRequests, failedRequestTargets = failedRequestTargets, beforeSpan = beforeSpan diff --git a/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorClientErrors.kt b/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorClientErrors.kt index 40406b77b5..b3f8b6d57e 100644 --- a/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorClientErrors.kt +++ b/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorClientErrors.kt @@ -5,7 +5,7 @@ import com.apollographql.apollo3.api.http.HttpRequest import com.apollographql.apollo3.api.http.HttpResponse import com.apollographql.apollo3.exception.ApolloException import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryOptions import io.sentry.SentryOptions.DEFAULT_PROPAGATION_TARGETS @@ -35,7 +35,7 @@ import kotlin.test.assertTrue class SentryApollo3InterceptorClientErrors { class Fixture { val server = MockWebServer() - lateinit var hub: IHub + lateinit var scopes: IScopes private val responseBodyOk = """{ @@ -75,7 +75,7 @@ class SentryApollo3InterceptorClientErrors { ): ApolloClient { SentryIntegrationPackageStorage.getInstance().clearStorage() - hub = mock().apply { + scopes = mock().apply { whenever(options).thenReturn( SentryOptions().apply { dsn = "https://key@sentry.io/proj" @@ -84,7 +84,7 @@ class SentryApollo3InterceptorClientErrors { } ) } - whenever(hub.captureEvent(any(), any())).thenReturn(SentryId.EMPTY_ID) + whenever(scopes.captureEvent(any(), any())).thenReturn(SentryId.EMPTY_ID) val response = MockResponse() .setBody(responseBody) @@ -102,7 +102,7 @@ class SentryApollo3InterceptorClientErrors { val builder = ApolloClient.Builder() .serverUrl(server.url("?myQuery=query#myFragment").toString()) .sentryTracing( - hub = hub, + scopes = scopes, captureFailedRequests = captureFailedRequests, failedRequestTargets = failedRequestTargets ) @@ -123,7 +123,7 @@ class SentryApollo3InterceptorClientErrors { val sut = fixture.getSut(captureFailedRequests = false, responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.hub, never()).captureEvent(any(), any()) + verify(fixture.scopes, never()).captureEvent(any(), any()) } @Test @@ -132,7 +132,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.hub).captureEvent(any(), any()) + verify(fixture.scopes).captureEvent(any(), any()) } // endregion @@ -165,7 +165,7 @@ class SentryApollo3InterceptorClientErrors { ) executeQuery(sut) - verify(fixture.hub, never()).captureEvent(any(), any()) + verify(fixture.scopes, never()).captureEvent(any(), any()) } @Test @@ -174,7 +174,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.hub).captureEvent(any(), any()) + verify(fixture.scopes).captureEvent(any(), any()) } // endregion @@ -187,7 +187,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { val throwable = (it.throwableMechanism as ExceptionMechanismException) assertEquals("SentryApollo3Interceptor", throwable.exceptionMechanism.type) @@ -202,7 +202,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { val throwable = (it.throwableMechanism as ExceptionMechanismException) assertEquals("GraphQL Request failed, name: LaunchDetails, type: query", throwable.throwable.message) @@ -217,7 +217,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { val throwable = (it.throwableMechanism as ExceptionMechanismException) assertTrue(throwable.isSnapshot) @@ -238,7 +238,7 @@ class SentryApollo3InterceptorClientErrors { {"operationName":"LaunchDetails","variables":{"id":"83"},"query":"query LaunchDetails($escapeDolar: ID!) { launch(id: $escapeDolar) { id site mission { name missionPatch(size: LARGE) } rocket { name type } } }"} """.trimIndent() - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { val request = it.request!! @@ -262,7 +262,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk, sendDefaultPii = true) executeQuery(sut) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { val request = it.request!! @@ -280,7 +280,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { val response = it.contexts.response!! @@ -300,7 +300,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk, sendDefaultPii = true) executeQuery(sut) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { val response = it.contexts.response!! @@ -318,7 +318,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { assertEquals(listOf("LaunchDetails", "query", "200"), it.fingerprints) }, @@ -337,7 +337,7 @@ class SentryApollo3InterceptorClientErrors { executeQuery(sut) // HttpInterceptor does not throw for >= 400 - verify(fixture.hub).captureEvent(any(), any()) + verify(fixture.scopes).captureEvent(any(), any()) } @Test @@ -345,7 +345,7 @@ class SentryApollo3InterceptorClientErrors { val sut = fixture.getSut(responseBody = fixture.responseBodyNotOk) - whenever(fixture.hub.captureEvent(any(), any())).thenThrow(RuntimeException()) + whenever(fixture.scopes.captureEvent(any(), any())).thenThrow(RuntimeException()) executeQuery(sut) } @@ -360,7 +360,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( any(), check { val request = it.get(TypeCheckHint.APOLLO_REQUEST) diff --git a/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorTest.kt b/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorTest.kt index 44d8bfd624..3b836f45b9 100644 --- a/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorTest.kt +++ b/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorTest.kt @@ -9,7 +9,7 @@ import com.apollographql.apollo3.network.http.HttpInterceptor import com.apollographql.apollo3.network.http.HttpInterceptorChain import io.sentry.BaggageHeader import io.sentry.Breadcrumb -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ITransaction import io.sentry.Scope import io.sentry.ScopeCallback @@ -57,11 +57,11 @@ class SentryApollo3InterceptorTest { sdkVersion = SdkVersion("test", "1.2.3") } val scope = Scope(options) - val hub = mock().also { + val scopes = mock().also { whenever(it.options).thenReturn(options) doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(it).configureScope(any()) } - private var httpInterceptor = SentryApollo3HttpInterceptor(hub, captureFailedRequests = false) + private var httpInterceptor = SentryApollo3HttpInterceptor(scopes, captureFailedRequests = false) @SuppressWarnings("LongParameterList") fun getSut( @@ -93,7 +93,7 @@ class SentryApollo3InterceptorTest { ) if (beforeSpan != null) { - httpInterceptor = SentryApollo3HttpInterceptor(hub, beforeSpan, captureFailedRequests = false) + httpInterceptor = SentryApollo3HttpInterceptor(scopes, beforeSpan, captureFailedRequests = false) } val builder = ApolloClient.Builder() @@ -124,7 +124,7 @@ class SentryApollo3InterceptorTest { fun `creates a span around the successful request`() { executeQuery() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertTransactionDetails(it, httpStatusCode = 200) assertEquals(SpanStatus.OK, it.spans.first().status) @@ -139,7 +139,7 @@ class SentryApollo3InterceptorTest { fun `creates a span around the failed request`() { executeQuery(fixture.getSut(httpStatusCode = 403)) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertTransactionDetails(it, httpStatusCode = 403) assertEquals(SpanStatus.PERMISSION_DENIED, it.spans.first().status) @@ -159,7 +159,7 @@ class SentryApollo3InterceptorTest { } executeQuery(fixture.getSut(interceptor = failingInterceptor)) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertTransactionDetails(it, httpStatusCode = 404, contentLength = null) assertEquals("POST", it.spans.first().data?.get(SpanDataConvention.HTTP_METHOD_KEY)) @@ -176,7 +176,7 @@ class SentryApollo3InterceptorTest { fun `creates a span around the request failing with network error`() { executeQuery(fixture.getSut(socketPolicy = SocketPolicy.DISCONNECT_DURING_REQUEST_BODY)) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertTransactionDetails(it, httpStatusCode = null, contentLength = null) assertEquals(SpanStatus.INTERNAL_ERROR, it.spans.first().status) @@ -241,7 +241,7 @@ class SentryApollo3InterceptorTest { ) ) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(1, it.spans.size) val httpClientSpan = it.spans.first() @@ -261,7 +261,7 @@ class SentryApollo3InterceptorTest { ) ) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(0, it.spans.size) }, @@ -281,7 +281,7 @@ class SentryApollo3InterceptorTest { ) ) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(1, it.spans.size) }, @@ -294,7 +294,7 @@ class SentryApollo3InterceptorTest { @Test fun `adds breadcrumb when http calls succeeds`() { executeQuery(fixture.getSut()) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) // response_body_size is added but mock webserver returns 0 always @@ -309,9 +309,9 @@ class SentryApollo3InterceptorTest { @Test fun `sets SDKVersion Info`() { - assertNotNull(fixture.hub.options.sdkVersion) - assert(fixture.hub.options.sdkVersion!!.integrationSet.contains("Apollo3")) - val packageInfo = fixture.hub.options.sdkVersion!!.packageSet.firstOrNull { pkg -> pkg.name == "maven:io.sentry:sentry-apollo-3" } + assertNotNull(fixture.scopes.options.sdkVersion) + assert(fixture.scopes.options.sdkVersion!!.integrationSet.contains("Apollo3")) + val packageInfo = fixture.scopes.options.sdkVersion!!.packageSet.firstOrNull { pkg -> pkg.name == "maven:io.sentry:sentry-apollo-3" } assertNotNull(packageInfo) assert(packageInfo.version == BuildConfig.VERSION_NAME) } @@ -320,14 +320,14 @@ class SentryApollo3InterceptorTest { fun `attaches to root transaction on Android`() { Apollo3PlatformTestManipulator.pretendIsAndroid(true) executeQuery(fixture.getSut()) - verify(fixture.hub).transaction + verify(fixture.scopes).transaction } @Test fun `attaches to child span on non-Android`() { Apollo3PlatformTestManipulator.pretendIsAndroid(false) executeQuery(fixture.getSut()) - verify(fixture.hub).span + verify(fixture.scopes).span } private fun assertTransactionDetails(it: SentryTransaction, httpStatusCode: Int? = 200, contentLength: Long? = 0L) { @@ -350,9 +350,9 @@ class SentryApollo3InterceptorTest { private fun executeQuery(sut: ApolloClient = fixture.getSut(), isSpanActive: Boolean = true, id: String = "83") = runBlocking { var tx: ITransaction? = null if (isSpanActive) { - tx = SentryTracer(TransactionContext("op", "desc", TracesSamplingDecision(true)), fixture.hub) - whenever(fixture.hub.transaction).thenReturn(tx) - whenever(fixture.hub.span).thenReturn(tx) + tx = SentryTracer(TransactionContext("op", "desc", TracesSamplingDecision(true)), fixture.scopes) + whenever(fixture.scopes.transaction).thenReturn(tx) + whenever(fixture.scopes.span).thenReturn(tx) } val coroutine = launch { diff --git a/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorWithVariablesTest.kt b/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorWithVariablesTest.kt index 81775efc18..3ac3d80d7d 100644 --- a/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorWithVariablesTest.kt +++ b/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorWithVariablesTest.kt @@ -3,7 +3,7 @@ package io.sentry.apollo3 import com.apollographql.apollo3.ApolloClient import com.apollographql.apollo3.exception.ApolloException import io.sentry.Breadcrumb -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ITransaction import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -32,7 +32,7 @@ class SentryApollo3InterceptorWithVariablesTest { class Fixture { val server = MockWebServer() - val hub = mock() + val scopes = mock() @SuppressWarnings("LongParameterList") fun getSut( @@ -54,7 +54,7 @@ class SentryApollo3InterceptorWithVariablesTest { socketPolicy: SocketPolicy = SocketPolicy.KEEP_OPEN, beforeSpan: BeforeSpanCallback? = null ): ApolloClient { - whenever(hub.options).thenReturn( + whenever(scopes.options).thenReturn( SentryOptions().apply { dsn = "http://key@localhost/proj" } @@ -68,7 +68,7 @@ class SentryApollo3InterceptorWithVariablesTest { ) return ApolloClient.Builder().serverUrl(server.url("/").toString()) - .sentryTracing(hub = hub, beforeSpan = beforeSpan, captureFailedRequests = false) + .sentryTracing(scopes = scopes, beforeSpan = beforeSpan, captureFailedRequests = false) .build() } } @@ -79,7 +79,7 @@ class SentryApollo3InterceptorWithVariablesTest { fun `creates a span around the successful request`() { executeQuery() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertTransactionDetails(it) assertEquals(SpanStatus.OK, it.spans.first().status) @@ -94,7 +94,7 @@ class SentryApollo3InterceptorWithVariablesTest { fun `creates a span around the failed request`() { executeQuery(fixture.getSut(httpStatusCode = 403)) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertTransactionDetails(it) assertEquals(SpanStatus.PERMISSION_DENIED, it.spans.first().status) @@ -109,7 +109,7 @@ class SentryApollo3InterceptorWithVariablesTest { fun `creates a span around the request failing with network error`() { executeQuery(fixture.getSut(socketPolicy = SocketPolicy.DISCONNECT_DURING_REQUEST_BODY)) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertTransactionDetails(it) assertEquals(SpanStatus.INTERNAL_ERROR, it.spans.first().status) @@ -124,7 +124,7 @@ class SentryApollo3InterceptorWithVariablesTest { fun `handles non-ascii header values correctly`() { executeQuery(id = "á") - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertTransactionDetails(it) assertEquals(SpanStatus.OK, it.spans.first().status) @@ -138,7 +138,7 @@ class SentryApollo3InterceptorWithVariablesTest { @Test fun `adds breadcrumb when http calls succeeds`() { executeQuery(fixture.getSut()) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) // response_body_size is added but mock webserver returns 0 always @@ -173,8 +173,8 @@ class SentryApollo3InterceptorWithVariablesTest { private fun executeQuery(sut: ApolloClient = fixture.getSut(), isSpanActive: Boolean = true, id: String = "83") = runBlocking { var tx: ITransaction? = null if (isSpanActive) { - tx = SentryTracer(TransactionContext("op", "desc", TracesSamplingDecision(true)), fixture.hub) - whenever(fixture.hub.span).thenReturn(tx) + tx = SentryTracer(TransactionContext("op", "desc", TracesSamplingDecision(true)), fixture.scopes) + whenever(fixture.scopes.span).thenReturn(tx) } val coroutine = launch { diff --git a/sentry-apollo/api/sentry-apollo.api b/sentry-apollo/api/sentry-apollo.api index 8c18bce06e..63eac6a193 100644 --- a/sentry-apollo/api/sentry-apollo.api +++ b/sentry-apollo/api/sentry-apollo.api @@ -5,9 +5,9 @@ public final class io/sentry/apollo/BuildConfig { public final class io/sentry/apollo/SentryApolloInterceptor : com/apollographql/apollo/interceptor/ApolloInterceptor { public fun ()V - public fun (Lio/sentry/IHub;)V - public fun (Lio/sentry/IHub;Lio/sentry/apollo/SentryApolloInterceptor$BeforeSpanCallback;)V - public synthetic fun (Lio/sentry/IHub;Lio/sentry/apollo/SentryApolloInterceptor$BeforeSpanCallback;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IScopes;Lio/sentry/apollo/SentryApolloInterceptor$BeforeSpanCallback;)V + public synthetic fun (Lio/sentry/IScopes;Lio/sentry/apollo/SentryApolloInterceptor$BeforeSpanCallback;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun (Lio/sentry/apollo/SentryApolloInterceptor$BeforeSpanCallback;)V public fun dispose ()V public fun interceptAsync (Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InterceptorRequest;Lcom/apollographql/apollo/interceptor/ApolloInterceptorChain;Ljava/util/concurrent/Executor;Lcom/apollographql/apollo/interceptor/ApolloInterceptor$CallBack;)V diff --git a/sentry-apollo/src/main/java/io/sentry/apollo/SentryApolloInterceptor.kt b/sentry-apollo/src/main/java/io/sentry/apollo/SentryApolloInterceptor.kt index faa8a549a9..fe5a6a4762 100644 --- a/sentry-apollo/src/main/java/io/sentry/apollo/SentryApolloInterceptor.kt +++ b/sentry-apollo/src/main/java/io/sentry/apollo/SentryApolloInterceptor.kt @@ -15,9 +15,9 @@ import com.apollographql.apollo.request.RequestHeaders import io.sentry.BaggageHeader import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.HubAdapter -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ISpan +import io.sentry.ScopesAdapter import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryLevel import io.sentry.SpanDataConvention @@ -32,12 +32,12 @@ import java.util.concurrent.Executor private const val TRACE_ORIGIN = "auto.graphql.apollo" class SentryApolloInterceptor( - private val hub: IHub = HubAdapter.getInstance(), + private val scopes: IScopes = ScopesAdapter.getInstance(), private val beforeSpan: BeforeSpanCallback? = null ) : ApolloInterceptor { - constructor(hub: IHub) : this(hub, null) - constructor(beforeSpan: BeforeSpanCallback) : this(HubAdapter.getInstance(), beforeSpan) + constructor(scopes: IScopes) : this(scopes, null) + constructor(beforeSpan: BeforeSpanCallback) : this(ScopesAdapter.getInstance(), beforeSpan) init { addIntegrationToSdkVersion(javaClass) @@ -45,7 +45,7 @@ class SentryApolloInterceptor( } override fun interceptAsync(request: InterceptorRequest, chain: ApolloInterceptorChain, dispatcher: Executor, callBack: CallBack) { - val activeSpan = if (io.sentry.util.Platform.isAndroid()) hub.transaction else hub.span + val activeSpan = if (io.sentry.util.Platform.isAndroid()) scopes.transaction else scopes.span if (activeSpan == null) { val headers = addTracingHeaders(request, null) val modifiedRequest = request.toBuilder().requestHeaders(headers).build() @@ -115,10 +115,10 @@ class SentryApolloInterceptor( private fun addTracingHeaders(request: InterceptorRequest, span: ISpan?): RequestHeaders { val requestHeaderBuilder = request.requestHeaders.toBuilder() - if (hub.options.isTraceSampling) { + if (scopes.options.isTraceSampling) { // we have no access to URI, no way to verify tracing origins TracingUtils.trace( - hub, + scopes, listOf(request.requestHeaders.headerValue(BaggageHeader.BAGGAGE_HEADER)), span )?.let { tracingHeaders -> @@ -154,7 +154,7 @@ class SentryApolloInterceptor( try { newSpan = beforeSpan.execute(span, request, response) } catch (e: Exception) { - hub.options.logger.log(SentryLevel.ERROR, "An error occurred while executing beforeSpan on ApolloInterceptor", e) + scopes.options.logger.log(SentryLevel.ERROR, "An error occurred while executing beforeSpan on ApolloInterceptor", e) } } if (newSpan == null) { @@ -182,7 +182,7 @@ class SentryApolloInterceptor( set(APOLLO_REQUEST, httpRequest) set(APOLLO_RESPONSE, httpResponse) } - hub.addBreadcrumb(breadcrumb, hint) + scopes.addBreadcrumb(breadcrumb, hint) } } } diff --git a/sentry-apollo/src/test/java/io/sentry/apollo/SentryApolloInterceptorTest.kt b/sentry-apollo/src/test/java/io/sentry/apollo/SentryApolloInterceptorTest.kt index d22c2fd3e5..b1b118c334 100644 --- a/sentry-apollo/src/test/java/io/sentry/apollo/SentryApolloInterceptorTest.kt +++ b/sentry-apollo/src/test/java/io/sentry/apollo/SentryApolloInterceptorTest.kt @@ -5,7 +5,7 @@ import com.apollographql.apollo.coroutines.await import com.apollographql.apollo.exception.ApolloException import io.sentry.BaggageHeader import io.sentry.Breadcrumb -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ITransaction import io.sentry.Scope import io.sentry.ScopeCallback @@ -48,13 +48,13 @@ class SentryApolloInterceptorTest { sdkVersion = SdkVersion("test", "1.2.3") } val scope = Scope(options) - val hub = mock().also { + val scopes = mock().also { whenever(it.options).thenReturn(options) doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(it).configureScope( any() ) } - private var interceptor = SentryApolloInterceptor(hub) + private var interceptor = SentryApolloInterceptor(scopes) @SuppressWarnings("LongParameterList") fun getSut( @@ -84,7 +84,7 @@ class SentryApolloInterceptorTest { ) if (beforeSpan != null) { - interceptor = SentryApolloInterceptor(hub, beforeSpan) + interceptor = SentryApolloInterceptor(scopes, beforeSpan) } return ApolloClient.builder() .serverUrl(server.url("/")) @@ -104,7 +104,7 @@ class SentryApolloInterceptorTest { fun `creates a span around the successful request`() { executeQuery() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertTransactionDetails(it) assertEquals(SpanStatus.OK, it.spans.first().status) @@ -120,7 +120,7 @@ class SentryApolloInterceptorTest { fun `creates a span around the failed request`() { executeQuery(fixture.getSut(httpStatusCode = 403)) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertTransactionDetails(it) assertEquals(SpanStatus.PERMISSION_DENIED, it.spans.first().status) @@ -138,7 +138,7 @@ class SentryApolloInterceptorTest { fun `creates a span around the request failing with network error`() { executeQuery(fixture.getSut(socketPolicy = SocketPolicy.DISCONNECT_DURING_REQUEST_BODY)) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertTransactionDetails(it) assertEquals(SpanStatus.INTERNAL_ERROR, it.spans.first().status) @@ -176,7 +176,7 @@ class SentryApolloInterceptorTest { } ) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(1, it.spans.size) val httpClientSpan = it.spans.first() @@ -196,7 +196,7 @@ class SentryApolloInterceptorTest { } ) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertTrue(it.spans.isEmpty()) }, @@ -212,7 +212,7 @@ class SentryApolloInterceptorTest { fixture.getSut { _, _, _ -> throw RuntimeException() } ) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(1, it.spans.size) }, @@ -225,7 +225,7 @@ class SentryApolloInterceptorTest { @Test fun `adds breadcrumb when http calls succeeds`() { executeQuery() - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(280L, it.data["response_body_size"]) @@ -237,9 +237,9 @@ class SentryApolloInterceptorTest { @Test fun `sets SDKVersion Info`() { - assertNotNull(fixture.hub.options.sdkVersion) - assert(fixture.hub.options.sdkVersion!!.integrationSet.contains("Apollo")) - val packageInfo = fixture.hub.options.sdkVersion!!.packageSet.firstOrNull { pkg -> pkg.name == "maven:io.sentry:sentry-apollo" } + assertNotNull(fixture.scopes.options.sdkVersion) + assert(fixture.scopes.options.sdkVersion!!.integrationSet.contains("Apollo")) + val packageInfo = fixture.scopes.options.sdkVersion!!.packageSet.firstOrNull { pkg -> pkg.name == "maven:io.sentry:sentry-apollo" } assertNotNull(packageInfo) assert(packageInfo.version == BuildConfig.VERSION_NAME) } @@ -248,14 +248,14 @@ class SentryApolloInterceptorTest { fun `attaches to root transaction on Android`() { ApolloPlatformTestManipulator.pretendIsAndroid(true) executeQuery(fixture.getSut()) - verify(fixture.hub).transaction + verify(fixture.scopes).transaction } @Test fun `attaches to child span on non-Android`() { ApolloPlatformTestManipulator.pretendIsAndroid(false) executeQuery(fixture.getSut()) - verify(fixture.hub).span + verify(fixture.scopes).span } private fun assertTransactionDetails(it: SentryTransaction) { @@ -273,9 +273,9 @@ class SentryApolloInterceptorTest { private fun executeQuery(sut: ApolloClient = fixture.getSut(), isSpanActive: Boolean = true) = runBlocking { var tx: ITransaction? = null if (isSpanActive) { - tx = SentryTracer(TransactionContext("op", "desc", TracesSamplingDecision(true)), fixture.hub) - whenever(fixture.hub.transaction).thenReturn(tx) - whenever(fixture.hub.span).thenReturn(tx) + tx = SentryTracer(TransactionContext("op", "desc", TracesSamplingDecision(true)), fixture.scopes) + whenever(fixture.scopes.transaction).thenReturn(tx) + whenever(fixture.scopes.span).thenReturn(tx) } val coroutine = launch { From c1840cf5a334b141eee74d2667b0fb0722603bb3 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 16 Apr 2024 16:40:23 +0200 Subject: [PATCH 06/91] Hubs/Scopes Merge 6 - Replace `IHub` with `IScopes` in OkHttp integration (#3302) * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration --- sentry-okhttp/api/sentry-okhttp.api | 18 +++---- .../io/sentry/okhttp/SentryOkHttpEvent.kt | 16 +++--- .../okhttp/SentryOkHttpEventListener.kt | 24 ++++----- .../sentry/okhttp/SentryOkHttpInterceptor.kt | 22 ++++---- .../io/sentry/okhttp/SentryOkHttpUtils.kt | 18 +++---- .../okhttp/SentryOkHttpEventListenerTest.kt | 28 +++++----- .../io/sentry/okhttp/SentryOkHttpEventTest.kt | 52 +++++++++---------- .../okhttp/SentryOkHttpInterceptorTest.kt | 50 +++++++++--------- .../io/sentry/okhttp/SentryOkHttpUtilsTest.kt | 22 ++++---- 9 files changed, 125 insertions(+), 125 deletions(-) diff --git a/sentry-okhttp/api/sentry-okhttp.api b/sentry-okhttp/api/sentry-okhttp.api index 3095659c88..9cb875ff34 100644 --- a/sentry-okhttp/api/sentry-okhttp.api +++ b/sentry-okhttp/api/sentry-okhttp.api @@ -6,12 +6,12 @@ public final class io/sentry/okhttp/BuildConfig { public class io/sentry/okhttp/SentryOkHttpEventListener : okhttp3/EventListener { public static final field Companion Lio/sentry/okhttp/SentryOkHttpEventListener$Companion; public fun ()V - public fun (Lio/sentry/IHub;Lkotlin/jvm/functions/Function1;)V - public synthetic fun (Lio/sentry/IHub;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Lio/sentry/IHub;Lokhttp3/EventListener$Factory;)V - public synthetic fun (Lio/sentry/IHub;Lokhttp3/EventListener$Factory;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Lio/sentry/IHub;Lokhttp3/EventListener;)V - public synthetic fun (Lio/sentry/IHub;Lokhttp3/EventListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IScopes;Lkotlin/jvm/functions/Function1;)V + public synthetic fun (Lio/sentry/IScopes;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IScopes;Lokhttp3/EventListener$Factory;)V + public synthetic fun (Lio/sentry/IScopes;Lokhttp3/EventListener$Factory;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IScopes;Lokhttp3/EventListener;)V + public synthetic fun (Lio/sentry/IScopes;Lokhttp3/EventListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun (Lokhttp3/EventListener$Factory;)V public fun (Lokhttp3/EventListener;)V public fun cacheConditionalHit (Lokhttp3/Call;Lokhttp3/Response;)V @@ -50,9 +50,9 @@ public final class io/sentry/okhttp/SentryOkHttpEventListener$Companion { public class io/sentry/okhttp/SentryOkHttpInterceptor : okhttp3/Interceptor { public fun ()V - public fun (Lio/sentry/IHub;)V - public fun (Lio/sentry/IHub;Lio/sentry/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;ZLjava/util/List;Ljava/util/List;)V - public synthetic fun (Lio/sentry/IHub;Lio/sentry/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;ZLjava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IScopes;Lio/sentry/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;ZLjava/util/List;Ljava/util/List;)V + public synthetic fun (Lio/sentry/IScopes;Lio/sentry/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;ZLjava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun (Lio/sentry/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;)V public fun intercept (Lokhttp3/Interceptor$Chain;)Lokhttp3/Response; } diff --git a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt index 00cc26e754..153499210c 100644 --- a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt +++ b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt @@ -2,7 +2,7 @@ package io.sentry.okhttp import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ISpan import io.sentry.SentryDate import io.sentry.SentryLevel @@ -30,7 +30,7 @@ private const val RESPONSE_BODY_TIMEOUT_MILLIS = 800L internal const val TRACE_ORIGIN = "auto.http.okhttp" @Suppress("TooManyFunctions") -internal class SentryOkHttpEvent(private val hub: IHub, private val request: Request) { +internal class SentryOkHttpEvent(private val scopes: IScopes, private val request: Request) { private val eventSpans: MutableMap = ConcurrentHashMap() private val breadcrumb: Breadcrumb internal val callRootSpan: ISpan? @@ -47,7 +47,7 @@ internal class SentryOkHttpEvent(private val hub: IHub, private val request: Req val method: String = request.method // We start the call span that will contain all the others - val parentSpan = if (Platform.isAndroid()) hub.transaction else hub.span + val parentSpan = if (Platform.isAndroid()) scopes.transaction else scopes.span callRootSpan = parentSpan?.startChild("http.client", "$method $url") callRootSpan?.spanContext?.origin = TRACE_ORIGIN urlDetails.applyToSpan(callRootSpan) @@ -149,13 +149,13 @@ internal class SentryOkHttpEvent(private val hub: IHub, private val request: Req response?.let { hint.set(TypeCheckHint.OKHTTP_RESPONSE, it) } // We send the breadcrumb even without spans. - hub.addBreadcrumb(breadcrumb, hint) + scopes.addBreadcrumb(breadcrumb, hint) // No span is created (e.g. no transaction is running) if (callRootSpan == null) { // We report the client error even without spans. clientErrorResponse?.let { - SentryOkHttpUtils.captureClientError(hub, it.request, it) + SentryOkHttpUtils.captureClientError(scopes, it.request, it) } return } @@ -173,7 +173,7 @@ internal class SentryOkHttpEvent(private val hub: IHub, private val request: Req // We report the client error here, after all sub-spans finished, so that it will be bound // to the root call span. clientErrorResponse?.let { - SentryOkHttpUtils.captureClientError(hub, it.request, it) + SentryOkHttpUtils.captureClientError(scopes, it.request, it) } if (finishDate != null) { callRootSpan.finish(callRootSpan.status, finishDate) @@ -204,7 +204,7 @@ internal class SentryOkHttpEvent(private val hub: IHub, private val request: Req fun scheduleFinish(timestamp: SentryDate) { try { - hub.options.executorService.schedule({ + scopes.options.executorService.schedule({ if (!isReadingResponseBody.get() && (eventSpans.values.all { it.isFinished } || callRootSpan?.isFinished != true) ) { @@ -212,7 +212,7 @@ internal class SentryOkHttpEvent(private val hub: IHub, private val request: Req } }, RESPONSE_BODY_TIMEOUT_MILLIS) } catch (e: RejectedExecutionException) { - hub.options + scopes.options .logger .log( SentryLevel.ERROR, diff --git a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEventListener.kt b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEventListener.kt index 67a8cd8b56..f20e2ef0cb 100644 --- a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEventListener.kt +++ b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEventListener.kt @@ -1,7 +1,7 @@ package io.sentry.okhttp -import io.sentry.HubAdapter -import io.sentry.IHub +import io.sentry.IScopes +import io.sentry.ScopesAdapter import io.sentry.SpanDataConvention import io.sentry.SpanStatus import okhttp3.Call @@ -41,7 +41,7 @@ import java.util.concurrent.ConcurrentHashMap */ @Suppress("TooManyFunctions") public open class SentryOkHttpEventListener( - private val hub: IHub = HubAdapter.getInstance(), + private val scopes: IScopes = ScopesAdapter.getInstance(), private val originalEventListenerCreator: ((call: Call) -> EventListener)? = null ) : EventListener() { @@ -62,27 +62,27 @@ public open class SentryOkHttpEventListener( } public constructor() : this( - HubAdapter.getInstance(), + ScopesAdapter.getInstance(), originalEventListenerCreator = null ) public constructor(originalEventListener: EventListener) : this( - HubAdapter.getInstance(), + ScopesAdapter.getInstance(), originalEventListenerCreator = { originalEventListener } ) public constructor(originalEventListenerFactory: Factory) : this( - HubAdapter.getInstance(), + ScopesAdapter.getInstance(), originalEventListenerCreator = { originalEventListenerFactory.create(it) } ) - public constructor(hub: IHub = HubAdapter.getInstance(), originalEventListener: EventListener) : this( - hub, + public constructor(scopes: IScopes = ScopesAdapter.getInstance(), originalEventListener: EventListener) : this( + scopes, originalEventListenerCreator = { originalEventListener } ) - public constructor(hub: IHub = HubAdapter.getInstance(), originalEventListenerFactory: Factory) : this( - hub, + public constructor(scopes: IScopes = ScopesAdapter.getInstance(), originalEventListenerFactory: Factory) : this( + scopes, originalEventListenerCreator = { originalEventListenerFactory.create(it) } ) @@ -92,7 +92,7 @@ public open class SentryOkHttpEventListener( // If the wrapped EventListener is ours, we can just delegate the calls, // without creating other events that would create duplicates if (canCreateEventSpan()) { - eventMap[call] = SentryOkHttpEvent(hub, call.request()) + eventMap[call] = SentryOkHttpEvent(scopes, call.request()) } } @@ -318,7 +318,7 @@ public open class SentryOkHttpEventListener( it.status = SpanStatus.fromHttpStatusCode(response.code) } } - okHttpEvent.scheduleFinish(responseHeadersSpan?.finishDate ?: hub.options.dateProvider.now()) + okHttpEvent.scheduleFinish(responseHeadersSpan?.finishDate ?: scopes.options.dateProvider.now()) } override fun responseBodyStart(call: Call) { diff --git a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpInterceptor.kt b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpInterceptor.kt index efa472963d..a079864612 100644 --- a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpInterceptor.kt +++ b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpInterceptor.kt @@ -4,9 +4,9 @@ import io.sentry.BaggageHeader import io.sentry.Breadcrumb import io.sentry.Hint import io.sentry.HttpStatusCodeRange -import io.sentry.HubAdapter -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ISpan +import io.sentry.ScopesAdapter import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryOptions.DEFAULT_PROPAGATION_TARGETS import io.sentry.SpanDataConvention @@ -29,7 +29,7 @@ import java.io.IOException * out of the active span bound to the scope for each HTTP Request. * If [captureFailedRequests] is enabled, the SDK will capture HTTP Client errors as well. * - * @param hub The [IHub], internal and only used for testing. + * @param scopes The [IScopes], internal and only used for testing. * @param beforeSpan The [ISpan] can be customized or dropped with the [BeforeSpanCallback]. * @param captureFailedRequests The SDK will only capture HTTP Client errors if it is enabled, * Defaults to false. @@ -39,7 +39,7 @@ import java.io.IOException * is a match for any of the defined targets. */ public open class SentryOkHttpInterceptor( - private val hub: IHub = HubAdapter.getInstance(), + private val scopes: IScopes = ScopesAdapter.getInstance(), private val beforeSpan: BeforeSpanCallback? = null, private val captureFailedRequests: Boolean = true, private val failedRequestStatusCodes: List = listOf( @@ -48,9 +48,9 @@ public open class SentryOkHttpInterceptor( private val failedRequestTargets: List = listOf(DEFAULT_PROPAGATION_TARGETS) ) : Interceptor { - public constructor() : this(HubAdapter.getInstance()) - public constructor(hub: IHub) : this(hub, null) - public constructor(beforeSpan: BeforeSpanCallback) : this(HubAdapter.getInstance(), beforeSpan) + public constructor() : this(ScopesAdapter.getInstance()) + public constructor(scopes: IScopes) : this(scopes, null) + public constructor(beforeSpan: BeforeSpanCallback) : this(ScopesAdapter.getInstance(), beforeSpan) init { addIntegrationToSdkVersion(javaClass) @@ -76,7 +76,7 @@ public open class SentryOkHttpInterceptor( } else { // read the span from the bound scope okHttpEvent = null - val parentSpan = if (Platform.isAndroid()) hub.transaction else hub.span + val parentSpan = if (Platform.isAndroid()) scopes.transaction else scopes.span span = parentSpan?.startChild("http.client", "$method $url") } @@ -92,7 +92,7 @@ public open class SentryOkHttpInterceptor( val requestBuilder = request.newBuilder() TracingUtils.traceIfAllowed( - hub, + scopes, request.url.toString(), request.headers(BaggageHeader.BAGGAGE_HEADER), span @@ -121,7 +121,7 @@ public open class SentryOkHttpInterceptor( if (isFromEventListener && okHttpEvent != null) { okHttpEvent.setClientErrorResponse(response) } else { - SentryOkHttpUtils.captureClientError(hub, request, response) + SentryOkHttpUtils.captureClientError(scopes, request, response) } } @@ -157,7 +157,7 @@ public open class SentryOkHttpInterceptor( hint[OKHTTP_RESPONSE] = it } - hub.addBreadcrumb(breadcrumb, hint) + scopes.addBreadcrumb(breadcrumb, hint) } private fun finishSpan(span: ISpan?, request: Request, response: Response?, isFromEventListener: Boolean) { diff --git a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpUtils.kt b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpUtils.kt index 0cfc1c5a75..eea35ca22e 100644 --- a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpUtils.kt +++ b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpUtils.kt @@ -1,7 +1,7 @@ package io.sentry.okhttp import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryEvent import io.sentry.TypeCheckHint import io.sentry.exception.ExceptionMechanismException @@ -15,7 +15,7 @@ import okhttp3.Response internal object SentryOkHttpUtils { - internal fun captureClientError(hub: IHub, request: Request, response: Response) { + internal fun captureClientError(scopes: IScopes, request: Request, response: Response) { // not possible to get a parameterized url, but we remove at least the // query string and the fragment. // url example: https://api.github.com/users/getsentry/repos/#fragment?query=query @@ -40,9 +40,9 @@ internal object SentryOkHttpUtils { val sentryRequest = io.sentry.protocol.Request().apply { urlDetails.applyToRequest(this) // Cookie is only sent if isSendDefaultPii is enabled - cookies = if (hub.options.isSendDefaultPii) request.headers["Cookie"] else null + cookies = if (scopes.options.isSendDefaultPii) request.headers["Cookie"] else null method = request.method - headers = getHeaders(hub, request.headers) + headers = getHeaders(scopes, request.headers) request.body?.contentLength().ifHasValidLength { bodySize = it @@ -51,8 +51,8 @@ internal object SentryOkHttpUtils { val sentryResponse = io.sentry.protocol.Response().apply { // Set-Cookie is only sent if isSendDefaultPii is enabled due to PII - cookies = if (hub.options.isSendDefaultPii) response.headers["Set-Cookie"] else null - headers = getHeaders(hub, response.headers) + cookies = if (scopes.options.isSendDefaultPii) response.headers["Set-Cookie"] else null + headers = getHeaders(scopes, response.headers) statusCode = response.code response.body?.contentLength().ifHasValidLength { @@ -63,7 +63,7 @@ internal object SentryOkHttpUtils { event.request = sentryRequest event.contexts.setResponse(sentryResponse) - hub.captureEvent(event, hint) + scopes.captureEvent(event, hint) } private fun Long?.ifHasValidLength(fn: (Long) -> Unit) { @@ -72,9 +72,9 @@ internal object SentryOkHttpUtils { } } - private fun getHeaders(hub: IHub, requestHeaders: Headers): MutableMap? { + private fun getHeaders(scopes: IScopes, requestHeaders: Headers): MutableMap? { // Headers are only sent if isSendDefaultPii is enabled due to PII - if (!hub.options.isSendDefaultPii) { + if (!scopes.options.isSendDefaultPii) { return null } diff --git a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventListenerTest.kt b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventListenerTest.kt index 1d90da70fe..3a90a4c05c 100644 --- a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventListenerTest.kt +++ b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventListenerTest.kt @@ -1,7 +1,7 @@ package io.sentry.okhttp import io.sentry.BaggageHeader -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryOptions import io.sentry.SentryTraceHeader import io.sentry.SentryTracer @@ -36,7 +36,7 @@ import kotlin.test.assertTrue class SentryOkHttpEventListenerTest { class Fixture { - val hub = mock() + val scopes = mock() val server = MockWebServer() val mockEventListener = mock() val mockEventListenerFactory = mock() @@ -63,12 +63,12 @@ class SentryOkHttpEventListenerTest { isSendDefaultPii = sendDefaultPii configureOptions(this) } - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) - sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) + sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) if (isSpanActive) { - whenever(hub.span).thenReturn(sentryTracer) + whenever(scopes.span).thenReturn(sentryTracer) } server.enqueue( MockResponse() @@ -80,12 +80,12 @@ class SentryOkHttpEventListenerTest { val builder = OkHttpClient.Builder() if (useInterceptor) { - builder.addInterceptor(SentryOkHttpInterceptor(hub)) + builder.addInterceptor(SentryOkHttpInterceptor(scopes)) } sentryOkHttpEventListener = when { - eventListenerFactory != null -> SentryOkHttpEventListener(hub, eventListenerFactory) - eventListener != null -> SentryOkHttpEventListener(hub, eventListener) - else -> SentryOkHttpEventListener(hub) + eventListenerFactory != null -> SentryOkHttpEventListener(scopes, eventListenerFactory) + eventListener != null -> SentryOkHttpEventListener(scopes, eventListener) + else -> SentryOkHttpEventListener(scopes) } return builder.eventListener(sentryOkHttpEventListener).build() } @@ -276,7 +276,7 @@ class SentryOkHttpEventListenerTest { @Test fun `propagate all calls to the SentryOkHttpEventListener passed in the ctor`() { - val originalListener = spy(SentryOkHttpEventListener(fixture.hub, fixture.mockEventListener)) + val originalListener = spy(SentryOkHttpEventListener(fixture.scopes, fixture.mockEventListener)) val sut = fixture.getSut(eventListener = originalListener) val listener = fixture.sentryOkHttpEventListener val request = postRequest(body = "requestBody") @@ -288,7 +288,7 @@ class SentryOkHttpEventListenerTest { @Test fun `propagate all calls to the SentryOkHttpEventListener factory passed in the ctor`() { - val originalListener = spy(SentryOkHttpEventListener(fixture.hub, fixture.mockEventListener)) + val originalListener = spy(SentryOkHttpEventListener(fixture.scopes, fixture.mockEventListener)) val sut = fixture.getSut(eventListenerFactory = { originalListener }) val listener = fixture.sentryOkHttpEventListener val request = postRequest(body = "requestBody") @@ -300,7 +300,7 @@ class SentryOkHttpEventListenerTest { @Test fun `does not duplicated spans if an SentryOkHttpEventListener is passed in the ctor`() { - val originalListener = spy(SentryOkHttpEventListener(fixture.hub, fixture.mockEventListener)) + val originalListener = spy(SentryOkHttpEventListener(fixture.scopes, fixture.mockEventListener)) val sut = fixture.getSut(eventListener = originalListener) val request = postRequest(body = "requestBody") val call = sut.newCall(request) @@ -363,8 +363,8 @@ class SentryOkHttpEventListenerTest { @Test fun `responseHeadersEnd schedules event finish`() { - val listener = SentryOkHttpEventListener(fixture.hub, fixture.mockEventListener) - whenever(fixture.hub.options).thenReturn(SentryOptions()) + val listener = SentryOkHttpEventListener(fixture.scopes, fixture.mockEventListener) + whenever(fixture.scopes.options).thenReturn(SentryOptions()) val call = mock() whenever(call.request()).thenReturn(getRequest()) val okHttpEvent = mock() diff --git a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventTest.kt b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventTest.kt index 337f722848..4d9f005143 100644 --- a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventTest.kt +++ b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventTest.kt @@ -2,7 +2,7 @@ package io.sentry.okhttp import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ISentryExecutorService import io.sentry.ISpan import io.sentry.SentryDate @@ -50,14 +50,14 @@ import kotlin.test.assertTrue class SentryOkHttpEventTest { private class Fixture { - val hub = mock() + val scopes = mock() val server = MockWebServer() val span: ISpan val mockRequest: Request val response: Response init { - whenever(hub.options).thenReturn( + whenever(scopes.options).thenReturn( SentryOptions().apply { dsn = "https://key@sentry.io/proj" } @@ -65,8 +65,8 @@ class SentryOkHttpEventTest { span = Span( TransactionContext("name", "op", TracesSamplingDecision(true)), - SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), hub), - hub, + SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), scopes), + scopes, null, SpanOptions() ) @@ -86,7 +86,7 @@ class SentryOkHttpEventTest { } fun getSut(currentSpan: ISpan? = span, requestUrl: String ? = null): SentryOkHttpEvent { - whenever(hub.span).thenReturn(currentSpan) + whenever(scopes.span).thenReturn(currentSpan) val request = if (requestUrl == null) { mockRequest } else { @@ -96,7 +96,7 @@ class SentryOkHttpEventTest { .url(server.url(requestUrl)) .build() } - return SentryOkHttpEvent(hub, request) + return SentryOkHttpEvent(scopes, request) } } @@ -126,7 +126,7 @@ class SentryOkHttpEventTest { val sut = fixture.getSut(currentSpan = null) assertNull(sut.callRootSpan) sut.finishEvent() - verify(fixture.hub).addBreadcrumb(any(), anyOrNull()) + verify(fixture.scopes).addBreadcrumb(any(), anyOrNull()) } @Test @@ -240,7 +240,7 @@ class SentryOkHttpEventTest { fun `when finishEvent, a breadcrumb is captured with request in the hint`() { val sut = fixture.getSut() sut.finishEvent() - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals(fixture.mockRequest.url.toString(), it.data["url"]) assertEquals(fixture.mockRequest.url.host, it.data["host"]) @@ -258,7 +258,7 @@ class SentryOkHttpEventTest { val sut = fixture.getSut() sut.finishEvent() sut.finishEvent() - verify(fixture.hub, times(1)).addBreadcrumb(any(), any()) + verify(fixture.scopes, times(1)).addBreadcrumb(any(), any()) } @Test @@ -283,7 +283,7 @@ class SentryOkHttpEventTest { assertEquals(fixture.response.code, sut.callRootSpan?.getData(SpanDataConvention.HTTP_STATUS_CODE_KEY)) sut.finishEvent() - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals(fixture.response.protocol.name, it.data["protocol"]) assertEquals(fixture.response.code, it.data["status_code"]) @@ -300,7 +300,7 @@ class SentryOkHttpEventTest { sut.setProtocol("protocol") assertEquals("protocol", sut.callRootSpan?.getData("protocol")) sut.finishEvent() - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("protocol", it.data["protocol"]) }, @@ -314,7 +314,7 @@ class SentryOkHttpEventTest { sut.setProtocol(null) assertNull(sut.callRootSpan?.getData("protocol")) sut.finishEvent() - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertNull(it.data["protocol"]) }, @@ -328,7 +328,7 @@ class SentryOkHttpEventTest { sut.setRequestBodySize(10) assertEquals(10L, sut.callRootSpan?.getData("http.request_content_length")) sut.finishEvent() - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals(10L, it.data["request_content_length"]) }, @@ -342,7 +342,7 @@ class SentryOkHttpEventTest { sut.setRequestBodySize(-1) assertNull(sut.callRootSpan?.getData("http.request_content_length")) sut.finishEvent() - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertNull(it.data["request_content_length"]) }, @@ -356,7 +356,7 @@ class SentryOkHttpEventTest { sut.setResponseBodySize(10) assertEquals(10L, sut.callRootSpan?.getData(SpanDataConvention.HTTP_RESPONSE_CONTENT_LENGTH_KEY)) sut.finishEvent() - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals(10L, it.data["response_content_length"]) }, @@ -370,7 +370,7 @@ class SentryOkHttpEventTest { sut.setResponseBodySize(-1) assertNull(sut.callRootSpan?.getData(SpanDataConvention.HTTP_RESPONSE_CONTENT_LENGTH_KEY)) sut.finishEvent() - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertNull(it.data["response_content_length"]) }, @@ -384,7 +384,7 @@ class SentryOkHttpEventTest { sut.setError("errorMessage") assertEquals("errorMessage", sut.callRootSpan?.getData("error_message")) sut.finishEvent() - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("errorMessage", it.data["error_message"]) }, @@ -399,7 +399,7 @@ class SentryOkHttpEventTest { assertNotNull(sut.callRootSpan) assertNull(sut.callRootSpan.getData("error_message")) sut.finishEvent() - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertNull(it.data["error_message"]) }, @@ -532,7 +532,7 @@ class SentryOkHttpEventTest { @Test fun `scheduleFinish schedules finishEvent and finish running spans to specific timestamp`() { - fixture.hub.options.executorService = ImmediateExecutorService() + fixture.scopes.options.executorService = ImmediateExecutorService() val sut = spy(fixture.getSut()) val timestamp = mock() sut.startSpan(CONNECTION_EVENT) @@ -554,7 +554,7 @@ class SentryOkHttpEventTest { fun `scheduleFinish does not throw if executor is shut down`() { val executorService = mock() whenever(executorService.schedule(any(), any())).thenThrow(RejectedExecutionException()) - whenever(fixture.hub.options).thenReturn(SentryOptions().apply { this.executorService = executorService }) + whenever(fixture.scopes.options).thenReturn(SentryOptions().apply { this.executorService = executorService }) val sut = fixture.getSut() sut.scheduleFinish(mock()) } @@ -565,10 +565,10 @@ class SentryOkHttpEventTest { val clientErrorResponse = mock() whenever(clientErrorResponse.request).thenReturn(fixture.mockRequest) sut.setClientErrorResponse(clientErrorResponse) - verify(fixture.hub, never()).captureEvent(any(), any()) + verify(fixture.scopes, never()).captureEvent(any(), any()) sut.finishEvent() assertNotNull(sut.callRootSpan) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( argThat { throwable is SentryHttpClientException && throwable!!.message!!.startsWith("HTTP Client Error with status code: ") @@ -586,10 +586,10 @@ class SentryOkHttpEventTest { val clientErrorResponse = mock() whenever(clientErrorResponse.request).thenReturn(fixture.mockRequest) sut.setClientErrorResponse(clientErrorResponse) - verify(fixture.hub, never()).captureEvent(any(), any()) + verify(fixture.scopes, never()).captureEvent(any(), any()) sut.finishEvent() assertNull(sut.callRootSpan) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( argThat { throwable is SentryHttpClientException && throwable!!.message!!.startsWith("HTTP Client Error with status code: ") @@ -605,7 +605,7 @@ class SentryOkHttpEventTest { fun `when setClientErrorResponse is not called, no client error is captured`() { val sut = fixture.getSut() sut.finishEvent() - verify(fixture.hub, never()).captureEvent(any(), any()) + verify(fixture.scopes, never()).captureEvent(any(), any()) } /** Retrieve all the spans started in the event using reflection. */ diff --git a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt index fce16d9220..f40b2c4cb5 100644 --- a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt +++ b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt @@ -6,8 +6,8 @@ import io.sentry.BaggageHeader import io.sentry.Breadcrumb import io.sentry.Hint import io.sentry.HttpStatusCodeRange -import io.sentry.IHub import io.sentry.IScope +import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -47,7 +47,7 @@ import kotlin.test.fail class SentryOkHttpInterceptorTest { class Fixture { - val hub = mock() + val scopes = mock() val server = MockWebServer() lateinit var sentryTracer: SentryTracer lateinit var options: SentryOptions @@ -82,13 +82,13 @@ class SentryOkHttpInterceptorTest { isSendDefaultPii = sendDefaultPii } scope = Scope(options) - whenever(hub.options).thenReturn(options) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) + whenever(scopes.options).thenReturn(options) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) - sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) + sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) if (isSpanActive) { - whenever(hub.span).thenReturn(sentryTracer) + whenever(scopes.span).thenReturn(sentryTracer) } server.enqueue( MockResponse() @@ -100,14 +100,14 @@ class SentryOkHttpInterceptorTest { val interceptor = when (captureFailedRequests) { null -> SentryOkHttpInterceptor( - hub, + scopes, beforeSpan, failedRequestTargets = failedRequestTargets, failedRequestStatusCodes = failedRequestStatusCodes ) else -> SentryOkHttpInterceptor( - hub, + scopes, beforeSpan, captureFailedRequests = captureFailedRequests, failedRequestTargets = failedRequestTargets, @@ -281,7 +281,7 @@ class SentryOkHttpInterceptorTest { fun `adds breadcrumb when http calls succeeds`() { val sut = fixture.getSut(responseBody = "response body") sut.newCall(postRequest()).execute() - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(13L, it.data[SpanDataConvention.HTTP_RESPONSE_CONTENT_LENGTH_KEY]) @@ -296,7 +296,7 @@ class SentryOkHttpInterceptorTest { fun `adds breadcrumb when http calls results in exception`() { // to setup mocks fixture.getSut() - val interceptor = SentryOkHttpInterceptor(fixture.hub) + val interceptor = SentryOkHttpInterceptor(fixture.scopes) val chain = mock() whenever(chain.call()).thenReturn(mock()) whenever(chain.proceed(any())).thenThrow(IOException()) @@ -308,7 +308,7 @@ class SentryOkHttpInterceptorTest { } catch (e: IOException) { // ignore me } - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) }, @@ -385,7 +385,7 @@ class SentryOkHttpInterceptorTest { ) sut.newCall(getRequest()).execute() - verify(fixture.hub).captureEvent(any(), any()) + verify(fixture.scopes).captureEvent(any(), any()) } @Test @@ -396,7 +396,7 @@ class SentryOkHttpInterceptorTest { ) sut.newCall(getRequest()).execute() - verify(fixture.hub).captureEvent(any(), any()) + verify(fixture.scopes).captureEvent(any(), any()) } @Test @@ -406,7 +406,7 @@ class SentryOkHttpInterceptorTest { ) sut.newCall(getRequest()).execute() - verify(fixture.hub, never()).captureEvent(any(), any()) + verify(fixture.scopes, never()).captureEvent(any(), any()) } @Test @@ -417,7 +417,7 @@ class SentryOkHttpInterceptorTest { ) sut.newCall(getRequest()).execute() - verify(fixture.hub, never()).captureEvent(any(), any()) + verify(fixture.scopes, never()).captureEvent(any(), any()) } @Test @@ -429,7 +429,7 @@ class SentryOkHttpInterceptorTest { ) sut.newCall(getRequest()).execute() - verify(fixture.hub, never()).captureEvent(any(), any()) + verify(fixture.scopes, never()).captureEvent(any(), any()) } @Test @@ -440,7 +440,7 @@ class SentryOkHttpInterceptorTest { ) sut.newCall(getRequest()).execute() - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( any(), check { assertNotNull(it.get(TypeCheckHint.OKHTTP_REQUEST)) @@ -462,7 +462,7 @@ class SentryOkHttpInterceptorTest { val request = getRequest(url = "/hello?myQuery=myValue#myFragment") val response = sut.newCall(request).execute() - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { val sentryRequest = it.request!! assertEquals("http://localhost:${fixture.server.port}/hello", sentryRequest.url) @@ -503,7 +503,7 @@ class SentryOkHttpInterceptorTest { sut.newCall(postRequest(body = body)).execute() - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { val sentryRequest = it.request!! assertEquals(body.contentLength(), sentryRequest.bodySize) @@ -522,7 +522,7 @@ class SentryOkHttpInterceptorTest { sut.newCall(getRequest()).execute() - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( check { val sentryRequest = it.request!! assertEquals("myValue", sentryRequest.headers!!["myHeader"]) @@ -540,7 +540,7 @@ class SentryOkHttpInterceptorTest { // to setup mocks fixture.getSut() val interceptor = SentryOkHttpInterceptor( - fixture.hub, + fixture.scopes, captureFailedRequests = true ) val chain = mock() @@ -554,7 +554,7 @@ class SentryOkHttpInterceptorTest { } catch (e: IOException) { // ignore me } - verify(fixture.hub, never()).captureEvent(any(), any()) + verify(fixture.scopes, never()).captureEvent(any(), any()) } @Test @@ -565,7 +565,7 @@ class SentryOkHttpInterceptorTest { call.execute() val httpClientSpan = fixture.sentryTracer.children.firstOrNull() assertNull(httpClientSpan) - verify(fixture.hub, never()).addBreadcrumb(any(), anyOrNull()) + verify(fixture.scopes, never()).addBreadcrumb(any(), anyOrNull()) } @Test @@ -573,7 +573,7 @@ class SentryOkHttpInterceptorTest { val sut = fixture.getSut(captureFailedRequests = true, httpStatusCode = 500) val call = sut.newCall(getRequest()) call.execute() - verify(fixture.hub).captureEvent(any(), any()) + verify(fixture.scopes).captureEvent(any(), any()) } @Test @@ -582,6 +582,6 @@ class SentryOkHttpInterceptorTest { val call = sut.newCall(getRequest()) SentryOkHttpEventListener.eventMap[call] = mock() call.execute() - verify(fixture.hub, never()).captureEvent(any(), any()) + verify(fixture.scopes, never()).captureEvent(any(), any()) } } diff --git a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpUtilsTest.kt b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpUtilsTest.kt index ec19454327..c7194e5994 100644 --- a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpUtilsTest.kt +++ b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpUtilsTest.kt @@ -1,7 +1,7 @@ package io.sentry.okhttp import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryOptions import io.sentry.SentryTracer import io.sentry.TransactionContext @@ -29,7 +29,7 @@ import kotlin.test.assertTrue class SentryOkHttpUtilsTest { class Fixture { - val hub = mock() + val scopes = mock() val server = MockWebServer() fun getSut( @@ -43,11 +43,11 @@ class SentryOkHttpUtilsTest { setTracePropagationTargets(listOf(server.hostName)) isSendDefaultPii = sendDefaultPii } - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) - val sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) + val sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) - whenever(hub.span).thenReturn(sentryTracer) + whenever(scopes.span).thenReturn(sentryTracer) server.enqueue( MockResponse() @@ -78,8 +78,8 @@ class SentryOkHttpUtilsTest { val request = getRequest() val response = sut.newCall(request).execute() - SentryOkHttpUtils.captureClientError(fixture.hub, request, response) - verify(fixture.hub).captureEvent( + SentryOkHttpUtils.captureClientError(fixture.scopes, request, response) + verify(fixture.scopes).captureEvent( check { val req = it.request val resp = it.contexts.response @@ -103,8 +103,8 @@ class SentryOkHttpUtilsTest { val request = getRequest() val response = sut.newCall(request).execute() - SentryOkHttpUtils.captureClientError(fixture.hub, request, response) - verify(fixture.hub).captureEvent( + SentryOkHttpUtils.captureClientError(fixture.scopes, request, response) + verify(fixture.scopes).captureEvent( check { val req = it.request val resp = it.contexts.response @@ -127,8 +127,8 @@ class SentryOkHttpUtilsTest { val request = getRequest() val response = sut.newCall(request).execute() - SentryOkHttpUtils.captureClientError(fixture.hub, request, response) - verify(fixture.hub).captureEvent( + SentryOkHttpUtils.captureClientError(fixture.scopes, request, response) + verify(fixture.scopes).captureEvent( check { val req = it.request val resp = it.contexts.response From 784341ec379a93b75eeec1dc4d73841990ffd487 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 16 Apr 2024 16:40:47 +0200 Subject: [PATCH 07/91] Hubs/Scopes Merge 7 - Replace `IHub` with `IScopes` in GraphQL integration (#3303) * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration --- sentry-graphql/api/sentry-graphql.api | 20 +++++---- .../io/sentry/graphql/ExceptionReporter.java | 43 +++++++++++-------- .../graphql/NoOpSubscriptionHandler.java | 4 +- .../SentryDataFetcherExceptionHandler.java | 14 +++--- ...tryGenericDataFetcherExceptionHandler.java | 4 +- .../sentry/graphql/SentryInstrumentation.java | 38 ++++++++-------- .../graphql/SentrySubscriptionHandler.java | 4 +- .../sentry/graphql/ExceptionReporterTest.kt | 32 +++++++------- .../SentryDataFetcherExceptionHandlerTest.kt | 8 ++-- ...yGenericDataFetcherExceptionHandlerTest.kt | 6 +-- .../SentryInstrumentationAnotherTest.kt | 40 +++++++++-------- .../graphql/SentryInstrumentationTest.kt | 36 ++++++++-------- 12 files changed, 132 insertions(+), 117 deletions(-) diff --git a/sentry-graphql/api/sentry-graphql.api b/sentry-graphql/api/sentry-graphql.api index 57c253e23c..d119256010 100644 --- a/sentry-graphql/api/sentry-graphql.api +++ b/sentry-graphql/api/sentry-graphql.api @@ -9,10 +9,11 @@ public final class io/sentry/graphql/ExceptionReporter { } public final class io/sentry/graphql/ExceptionReporter$ExceptionDetails { - public fun (Lio/sentry/IHub;Lgraphql/execution/instrumentation/parameters/InstrumentationExecutionParameters;Z)V - public fun (Lio/sentry/IHub;Lgraphql/schema/DataFetchingEnvironment;Z)V - public fun getHub ()Lio/sentry/IHub; + public fun (Lio/sentry/IScopes;Lgraphql/execution/instrumentation/parameters/InstrumentationExecutionParameters;Z)V + public fun (Lio/sentry/IScopes;Lgraphql/schema/DataFetchingEnvironment;Z)V + public fun getHub ()Lio/sentry/IScopes; public fun getQuery ()Ljava/lang/String; + public fun getScopes ()Lio/sentry/IScopes; public fun getVariables ()Ljava/util/Map; public fun isSubscription ()Z } @@ -26,19 +27,19 @@ public final class io/sentry/graphql/GraphqlStringUtils { public final class io/sentry/graphql/NoOpSubscriptionHandler : io/sentry/graphql/SentrySubscriptionHandler { public static fun getInstance ()Lio/sentry/graphql/NoOpSubscriptionHandler; - public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IHub;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; + public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IScopes;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; } public final class io/sentry/graphql/SentryDataFetcherExceptionHandler : graphql/execution/DataFetcherExceptionHandler { public fun (Lgraphql/execution/DataFetcherExceptionHandler;)V - public fun (Lio/sentry/IHub;Lgraphql/execution/DataFetcherExceptionHandler;)V + public fun (Lio/sentry/IScopes;Lgraphql/execution/DataFetcherExceptionHandler;)V public fun handleException (Lgraphql/execution/DataFetcherExceptionHandlerParameters;)Ljava/util/concurrent/CompletableFuture; public fun onException (Lgraphql/execution/DataFetcherExceptionHandlerParameters;)Lgraphql/execution/DataFetcherExceptionHandlerResult; } public final class io/sentry/graphql/SentryGenericDataFetcherExceptionHandler : graphql/execution/DataFetcherExceptionHandler { public fun (Lgraphql/execution/DataFetcherExceptionHandler;)V - public fun (Lio/sentry/IHub;Lgraphql/execution/DataFetcherExceptionHandler;)V + public fun (Lio/sentry/IScopes;Lgraphql/execution/DataFetcherExceptionHandler;)V public fun handleException (Lgraphql/execution/DataFetcherExceptionHandlerParameters;)Ljava/util/concurrent/CompletableFuture; public fun onException (Lgraphql/execution/DataFetcherExceptionHandlerParameters;)Lgraphql/execution/DataFetcherExceptionHandlerResult; } @@ -51,9 +52,10 @@ public final class io/sentry/graphql/SentryGraphqlExceptionHandler { public final class io/sentry/graphql/SentryInstrumentation : graphql/execution/instrumentation/SimpleInstrumentation { public static final field SENTRY_EXCEPTIONS_CONTEXT_KEY Ljava/lang/String; public static final field SENTRY_HUB_CONTEXT_KEY Ljava/lang/String; + public static final field SENTRY_SCOPES_CONTEXT_KEY Ljava/lang/String; public fun ()V - public fun (Lio/sentry/IHub;)V - public fun (Lio/sentry/IHub;Lio/sentry/graphql/SentryInstrumentation$BeforeSpanCallback;)V + public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IScopes;Lio/sentry/graphql/SentryInstrumentation$BeforeSpanCallback;)V public fun (Lio/sentry/graphql/SentryInstrumentation$BeforeSpanCallback;)V public fun (Lio/sentry/graphql/SentryInstrumentation$BeforeSpanCallback;Lio/sentry/graphql/SentrySubscriptionHandler;Lio/sentry/graphql/ExceptionReporter;Ljava/util/List;)V public fun (Lio/sentry/graphql/SentryInstrumentation$BeforeSpanCallback;Lio/sentry/graphql/SentrySubscriptionHandler;Z)V @@ -71,6 +73,6 @@ public abstract interface class io/sentry/graphql/SentryInstrumentation$BeforeSp } public abstract interface class io/sentry/graphql/SentrySubscriptionHandler { - public abstract fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IHub;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; + public abstract fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IScopes;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; } diff --git a/sentry-graphql/src/main/java/io/sentry/graphql/ExceptionReporter.java b/sentry-graphql/src/main/java/io/sentry/graphql/ExceptionReporter.java index 30ccb21425..843ca77494 100644 --- a/sentry-graphql/src/main/java/io/sentry/graphql/ExceptionReporter.java +++ b/sentry-graphql/src/main/java/io/sentry/graphql/ExceptionReporter.java @@ -5,7 +5,7 @@ import graphql.language.AstPrinter; import graphql.schema.DataFetchingEnvironment; import io.sentry.Hint; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.SentryEvent; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -33,7 +33,7 @@ public void captureThrowable( final @NotNull Throwable throwable, final @NotNull ExceptionDetails exceptionDetails, final @Nullable ExecutionResult result) { - final @NotNull IHub hub = exceptionDetails.getHub(); + final @NotNull IScopes scopes = exceptionDetails.getScopes(); final @NotNull Mechanism mechanism = new Mechanism(); mechanism.setType(MECHANISM_TYPE); mechanism.setHandled(false); @@ -43,44 +43,44 @@ public void captureThrowable( event.setLevel(SentryLevel.FATAL); final @NotNull Hint hint = new Hint(); - setRequestDetailsOnEvent(hub, exceptionDetails, event); + setRequestDetailsOnEvent(scopes, exceptionDetails, event); - if (result != null && isAllowedToAttachBody(hub)) { + if (result != null && isAllowedToAttachBody(scopes)) { final @NotNull Response response = new Response(); final @NotNull Map responseBody = result.toSpecification(); response.setData(responseBody); event.getContexts().setResponse(response); } - hub.captureEvent(event, hint); + scopes.captureEvent(event, hint); } - private boolean isAllowedToAttachBody(final @NotNull IHub hub) { - final @NotNull SentryOptions options = hub.getOptions(); + private boolean isAllowedToAttachBody(final @NotNull IScopes scopes) { + final @NotNull SentryOptions options = scopes.getOptions(); return options.isSendDefaultPii() && !SentryOptions.RequestSize.NONE.equals(options.getMaxRequestBodySize()); } private void setRequestDetailsOnEvent( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull ExceptionDetails exceptionDetails, final @NotNull SentryEvent event) { - hub.configureScope( + scopes.configureScope( (scope) -> { final @Nullable Request scopeRequest = scope.getRequest(); final @NotNull Request request = scopeRequest == null ? new Request() : scopeRequest; - setDetailsOnRequest(hub, exceptionDetails, request); + setDetailsOnRequest(scopes, exceptionDetails, request); event.setRequest(request); }); } private void setDetailsOnRequest( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull ExceptionDetails exceptionDetails, final @NotNull Request request) { request.setApiTarget("graphql"); - if (isAllowedToAttachBody(hub) + if (isAllowedToAttachBody(scopes) && (exceptionDetails.isSubscription() || captureRequestBodyForNonSubscriptions)) { final @NotNull Map data = new HashMap<>(); @@ -99,27 +99,27 @@ private void setDetailsOnRequest( public static final class ExceptionDetails { - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @Nullable InstrumentationExecutionParameters instrumentationExecutionParameters; private final @Nullable DataFetchingEnvironment dataFetchingEnvironment; private final boolean isSubscription; public ExceptionDetails( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @Nullable InstrumentationExecutionParameters instrumentationExecutionParameters, final boolean isSubscription) { - this.hub = hub; + this.scopes = scopes; this.instrumentationExecutionParameters = instrumentationExecutionParameters; dataFetchingEnvironment = null; this.isSubscription = isSubscription; } public ExceptionDetails( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @Nullable DataFetchingEnvironment dataFetchingEnvironment, final boolean isSubscription) { - this.hub = hub; + this.scopes = scopes; this.dataFetchingEnvironment = dataFetchingEnvironment; instrumentationExecutionParameters = null; this.isSubscription = isSubscription; @@ -149,8 +149,13 @@ public boolean isSubscription() { return isSubscription; } - public @NotNull IHub getHub() { - return hub; + @Deprecated + public @NotNull IScopes getHub() { + return scopes; + } + + public @NotNull IScopes getScopes() { + return scopes; } } } diff --git a/sentry-graphql/src/main/java/io/sentry/graphql/NoOpSubscriptionHandler.java b/sentry-graphql/src/main/java/io/sentry/graphql/NoOpSubscriptionHandler.java index df241ce35b..839f413719 100644 --- a/sentry-graphql/src/main/java/io/sentry/graphql/NoOpSubscriptionHandler.java +++ b/sentry-graphql/src/main/java/io/sentry/graphql/NoOpSubscriptionHandler.java @@ -1,7 +1,7 @@ package io.sentry.graphql; import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; -import io.sentry.IHub; +import io.sentry.IScopes; import org.jetbrains.annotations.NotNull; public final class NoOpSubscriptionHandler implements SentrySubscriptionHandler { @@ -17,7 +17,7 @@ private NoOpSubscriptionHandler() {} @Override public @NotNull Object onSubscriptionResult( @NotNull Object result, - @NotNull IHub hub, + @NotNull IScopes scopes, @NotNull ExceptionReporter exceptionReporter, @NotNull InstrumentationFieldFetchParameters parameters) { return result; diff --git a/sentry-graphql/src/main/java/io/sentry/graphql/SentryDataFetcherExceptionHandler.java b/sentry-graphql/src/main/java/io/sentry/graphql/SentryDataFetcherExceptionHandler.java index c0467c0089..0813aab851 100644 --- a/sentry-graphql/src/main/java/io/sentry/graphql/SentryDataFetcherExceptionHandler.java +++ b/sentry-graphql/src/main/java/io/sentry/graphql/SentryDataFetcherExceptionHandler.java @@ -6,8 +6,8 @@ import graphql.execution.DataFetcherExceptionHandlerParameters; import graphql.execution.DataFetcherExceptionHandlerResult; import io.sentry.Hint; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; +import io.sentry.ScopesAdapter; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.util.Objects; import java.util.concurrent.CompletableFuture; @@ -24,18 +24,18 @@ */ @Deprecated public final class SentryDataFetcherExceptionHandler implements DataFetcherExceptionHandler { - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @NotNull DataFetcherExceptionHandler delegate; public SentryDataFetcherExceptionHandler( - final @NotNull IHub hub, final @NotNull DataFetcherExceptionHandler delegate) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + final @NotNull IScopes scopes, final @NotNull DataFetcherExceptionHandler delegate) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); this.delegate = Objects.requireNonNull(delegate, "delegate is required"); SentryIntegrationPackageStorage.getInstance().addIntegration("GrahQLLegacyExceptionHandler"); } public SentryDataFetcherExceptionHandler(final @NotNull DataFetcherExceptionHandler delegate) { - this(HubAdapter.getInstance(), delegate); + this(ScopesAdapter.getInstance(), delegate); } @Override @@ -44,7 +44,7 @@ public CompletableFuture handleException( final Hint hint = new Hint(); hint.set(GRAPHQL_HANDLER_PARAMETERS, handlerParameters); - hub.captureException(handlerParameters.getException(), hint); + scopes.captureException(handlerParameters.getException(), hint); return delegate.handleException(handlerParameters); } diff --git a/sentry-graphql/src/main/java/io/sentry/graphql/SentryGenericDataFetcherExceptionHandler.java b/sentry-graphql/src/main/java/io/sentry/graphql/SentryGenericDataFetcherExceptionHandler.java index 6251d00779..1287d38caa 100644 --- a/sentry-graphql/src/main/java/io/sentry/graphql/SentryGenericDataFetcherExceptionHandler.java +++ b/sentry-graphql/src/main/java/io/sentry/graphql/SentryGenericDataFetcherExceptionHandler.java @@ -3,7 +3,7 @@ import graphql.execution.DataFetcherExceptionHandler; import graphql.execution.DataFetcherExceptionHandlerParameters; import graphql.execution.DataFetcherExceptionHandlerResult; -import io.sentry.IHub; +import io.sentry.IScopes; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.jetbrains.annotations.NotNull; @@ -17,7 +17,7 @@ public final class SentryGenericDataFetcherExceptionHandler implements DataFetch private final @NotNull SentryGraphqlExceptionHandler handler; public SentryGenericDataFetcherExceptionHandler( - final @Nullable IHub hub, final @NotNull DataFetcherExceptionHandler delegate) { + final @Nullable IScopes scopes, final @NotNull DataFetcherExceptionHandler delegate) { this.handler = new SentryGraphqlExceptionHandler(delegate); } diff --git a/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java b/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java index d2d62c99d8..e4f85d12a2 100644 --- a/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java +++ b/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java @@ -18,9 +18,9 @@ import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLOutputType; import io.sentry.Breadcrumb; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ISpan; -import io.sentry.NoOpHub; +import io.sentry.NoOpScopes; import io.sentry.Sentry; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SpanStatus; @@ -46,7 +46,11 @@ public final class SentryInstrumentation "INTERNAL", // Netflix DGS "DataFetchingException" // raw graphql-java ); - public static final @NotNull String SENTRY_HUB_CONTEXT_KEY = "sentry.hub"; + public static final @NotNull String SENTRY_SCOPES_CONTEXT_KEY = "sentry.scopes"; + + @Deprecated + public static final @NotNull String SENTRY_HUB_CONTEXT_KEY = SENTRY_SCOPES_CONTEXT_KEY; + public static final @NotNull String SENTRY_EXCEPTIONS_CONTEXT_KEY = "sentry.exceptions"; private static final String TRACE_ORIGIN = "auto.graphql.graphql"; private final @Nullable BeforeSpanCallback beforeSpan; @@ -70,7 +74,7 @@ public SentryInstrumentation() { */ @Deprecated @SuppressWarnings("InlineMeSuggester") - public SentryInstrumentation(final @Nullable IHub hub) { + public SentryInstrumentation(final @Nullable IScopes scopes) { this(null, NoOpSubscriptionHandler.getInstance(), true); } @@ -89,7 +93,7 @@ public SentryInstrumentation(final @Nullable BeforeSpanCallback beforeSpan) { @Deprecated @SuppressWarnings("InlineMeSuggester") public SentryInstrumentation( - final @Nullable IHub hub, final @Nullable BeforeSpanCallback beforeSpan) { + final @Nullable IScopes scopes, final @Nullable BeforeSpanCallback beforeSpan) { this(beforeSpan, NoOpSubscriptionHandler.getInstance(), true); } @@ -172,9 +176,9 @@ public SentryInstrumentation( public @NotNull InstrumentationContext beginExecution( final @NotNull InstrumentationExecutionParameters parameters) { final TracingState tracingState = parameters.getInstrumentationState(); - final @NotNull IHub currentHub = Sentry.getCurrentHub(); - tracingState.setTransaction(currentHub.getSpan()); - parameters.getGraphQLContext().put(SENTRY_HUB_CONTEXT_KEY, currentHub); + final @NotNull IScopes currentScopes = Sentry.getCurrentScopes(); + tracingState.setTransaction(currentScopes.getSpan()); + parameters.getGraphQLContext().put(SENTRY_SCOPES_CONTEXT_KEY, currentScopes); return super.beginExecution(parameters); } @@ -195,7 +199,7 @@ public CompletableFuture instrumentExecutionResult( exceptionReporter.captureThrowable( throwable, new ExceptionReporter.ExceptionDetails( - hubFromContext(graphQLContext), parameters, false), + scopesFromContext(graphQLContext), parameters, false), result); } } @@ -207,7 +211,7 @@ public CompletableFuture instrumentExecutionResult( exceptionReporter.captureThrowable( new RuntimeException(error.getMessage()), new ExceptionReporter.ExceptionDetails( - hubFromContext(graphQLContext), parameters, false), + scopesFromContext(graphQLContext), parameters, false), result); } } @@ -217,7 +221,7 @@ public CompletableFuture instrumentExecutionResult( exceptionReporter.captureThrowable( exception, new ExceptionReporter.ExceptionDetails( - hubFromContext(parameters.getGraphQLContext()), parameters, false), + scopesFromContext(parameters.getGraphQLContext()), parameters, false), null); } }); @@ -262,7 +266,7 @@ private boolean isIgnored(final @Nullable String errorType) { operationDefinition.getOperation(); final @Nullable String operationType = operation == null ? null : operation.name().toLowerCase(Locale.ROOT); - hubFromContext(parameters.getExecutionContext().getGraphQLContext()) + scopesFromContext(parameters.getExecutionContext().getGraphQLContext()) .addBreadcrumb( Breadcrumb.graphqlOperation( operationDefinition.getName(), @@ -273,11 +277,11 @@ private boolean isIgnored(final @Nullable String errorType) { return super.beginExecuteOperation(parameters); } - private @NotNull IHub hubFromContext(final @Nullable GraphQLContext context) { + private @NotNull IScopes scopesFromContext(final @Nullable GraphQLContext context) { if (context == null) { - return NoOpHub.getInstance(); + return NoOpScopes.getInstance(); } - return context.getOrDefault(SENTRY_HUB_CONTEXT_KEY, NoOpHub.getInstance()); + return context.getOrDefault(SENTRY_SCOPES_CONTEXT_KEY, NoOpScopes.getInstance()); } @Override @@ -293,7 +297,7 @@ private boolean isIgnored(final @Nullable String errorType) { return environment -> { final @Nullable ExecutionStepInfo executionStepInfo = environment.getExecutionStepInfo(); if (executionStepInfo != null) { - hubFromContext(parameters.getExecutionContext().getGraphQLContext()) + scopesFromContext(parameters.getExecutionContext().getGraphQLContext()) .addBreadcrumb( Breadcrumb.graphqlDataFetcher( StringUtils.toString(executionStepInfo.getPath()), @@ -351,7 +355,7 @@ private boolean isIgnored(final @Nullable String errorType) { environment.getOperationDefinition().getOperation())) { return subscriptionHandler.onSubscriptionResult( tmpResult, - hubFromContext(environment.getGraphQlContext()), + scopesFromContext(environment.getGraphQlContext()), exceptionReporter, parameters); } diff --git a/sentry-graphql/src/main/java/io/sentry/graphql/SentrySubscriptionHandler.java b/sentry-graphql/src/main/java/io/sentry/graphql/SentrySubscriptionHandler.java index bfc962b501..0a5538ce22 100644 --- a/sentry-graphql/src/main/java/io/sentry/graphql/SentrySubscriptionHandler.java +++ b/sentry-graphql/src/main/java/io/sentry/graphql/SentrySubscriptionHandler.java @@ -1,14 +1,14 @@ package io.sentry.graphql; import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; -import io.sentry.IHub; +import io.sentry.IScopes; import org.jetbrains.annotations.NotNull; public interface SentrySubscriptionHandler { @NotNull Object onSubscriptionResult( @NotNull Object result, - @NotNull IHub hub, + @NotNull IScopes scopes, @NotNull ExceptionReporter exceptionReporter, @NotNull InstrumentationFieldFetchParameters parameters); } diff --git a/sentry-graphql/src/test/kotlin/io/sentry/graphql/ExceptionReporterTest.kt b/sentry-graphql/src/test/kotlin/io/sentry/graphql/ExceptionReporterTest.kt index a2b2b0f101..3a798a2f86 100644 --- a/sentry-graphql/src/test/kotlin/io/sentry/graphql/ExceptionReporterTest.kt +++ b/sentry-graphql/src/test/kotlin/io/sentry/graphql/ExceptionReporterTest.kt @@ -12,8 +12,8 @@ import graphql.schema.GraphQLObjectType import graphql.schema.GraphQLScalarType import graphql.schema.GraphQLSchema import io.sentry.Hint -import io.sentry.IHub import io.sentry.IScope +import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -39,7 +39,7 @@ class ExceptionReporterTest { it.maxRequestBodySize = SentryOptions.RequestSize.ALWAYS } val exception = IllegalStateException("some exception") - val hub = mock() + val scopes = mock() lateinit var instrumentationExecutionParameters: InstrumentationExecutionParameters lateinit var executionResult: ExecutionResult lateinit var scope: IScope @@ -47,7 +47,7 @@ class ExceptionReporterTest { val variables = mapOf("variableA" to "value a") fun getSut(options: SentryOptions = defaultOptions, captureRequestBodyForNonSubscriptions: Boolean = true): ExceptionReporter { - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) scope = Scope(options) val exceptionReporter = ExceptionReporter(captureRequestBodyForNonSubscriptions) executionResult = ExecutionResultImpl.newExecutionResult() @@ -77,7 +77,7 @@ class ExceptionReporterTest { ).build() val instrumentationState = SentryInstrumentation.TracingState() instrumentationExecutionParameters = InstrumentationExecutionParameters(executionInput, schema, instrumentationState) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) return exceptionReporter } @@ -88,9 +88,9 @@ class ExceptionReporterTest { @Test fun `captures throwable`() { val exceptionReporter = fixture.getSut() - exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.hub, fixture.instrumentationExecutionParameters, false), fixture.executionResult) + exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.scopes, fixture.instrumentationExecutionParameters, false), fixture.executionResult) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( org.mockito.kotlin.check { val ex = it.throwableMechanism as ExceptionMechanismException assertFalse(ex.exceptionMechanism.isHandled!!) @@ -112,9 +112,9 @@ class ExceptionReporterTest { val exceptionReporter = fixture.getSut() val headers = mapOf("some-header" to "some-header-value") fixture.scope.request = Request().also { it.headers = headers } - exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.hub, fixture.instrumentationExecutionParameters, false), fixture.executionResult) + exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.scopes, fixture.instrumentationExecutionParameters, false), fixture.executionResult) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( org.mockito.kotlin.check { val ex = it.throwableMechanism as ExceptionMechanismException assertFalse(ex.exceptionMechanism.isHandled!!) @@ -136,9 +136,9 @@ class ExceptionReporterTest { @Test fun `does not attach query or variables if spring`() { val exceptionReporter = fixture.getSut(captureRequestBodyForNonSubscriptions = false) - exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.hub, fixture.instrumentationExecutionParameters, false), fixture.executionResult) + exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.scopes, fixture.instrumentationExecutionParameters, false), fixture.executionResult) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( org.mockito.kotlin.check { val ex = it.throwableMechanism as ExceptionMechanismException assertFalse(ex.exceptionMechanism.isHandled!!) @@ -156,9 +156,9 @@ class ExceptionReporterTest { @Test fun `does not attach query or variables if no max body size is set`() { val exceptionReporter = fixture.getSut(SentryOptions().also { it.isSendDefaultPii = true }, false) - exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.hub, fixture.instrumentationExecutionParameters, false), fixture.executionResult) + exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.scopes, fixture.instrumentationExecutionParameters, false), fixture.executionResult) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( org.mockito.kotlin.check { val ex = it.throwableMechanism as ExceptionMechanismException assertFalse(ex.exceptionMechanism.isHandled!!) @@ -176,9 +176,9 @@ class ExceptionReporterTest { @Test fun `does not attach query or variables if sendDefaultPii is false`() { val exceptionReporter = fixture.getSut(SentryOptions().also { it.maxRequestBodySize = SentryOptions.RequestSize.ALWAYS }, false) - exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.hub, fixture.instrumentationExecutionParameters, false), fixture.executionResult) + exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.scopes, fixture.instrumentationExecutionParameters, false), fixture.executionResult) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( org.mockito.kotlin.check { val ex = it.throwableMechanism as ExceptionMechanismException assertFalse(ex.exceptionMechanism.isHandled!!) @@ -196,9 +196,9 @@ class ExceptionReporterTest { @Test fun `attaches query and variables if spring and subscription`() { val exceptionReporter = fixture.getSut(captureRequestBodyForNonSubscriptions = false) - exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.hub, fixture.instrumentationExecutionParameters, true), fixture.executionResult) + exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.scopes, fixture.instrumentationExecutionParameters, true), fixture.executionResult) - verify(fixture.hub).captureEvent( + verify(fixture.scopes).captureEvent( org.mockito.kotlin.check { val ex = it.throwableMechanism as ExceptionMechanismException assertFalse(ex.exceptionMechanism.isHandled!!) diff --git a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryDataFetcherExceptionHandlerTest.kt b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryDataFetcherExceptionHandlerTest.kt index b571fa8218..de51abac21 100644 --- a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryDataFetcherExceptionHandlerTest.kt +++ b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryDataFetcherExceptionHandlerTest.kt @@ -3,7 +3,7 @@ package io.sentry.graphql import graphql.execution.DataFetcherExceptionHandler import graphql.execution.DataFetcherExceptionHandlerParameters import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.eq import org.mockito.kotlin.mock @@ -14,15 +14,15 @@ class SentryDataFetcherExceptionHandlerTest { @Test fun `passes exception to Sentry and invokes delegate`() { - val hub = mock() + val scopes = mock() val delegate = mock() - val handler = SentryDataFetcherExceptionHandler(hub, delegate) + val handler = SentryDataFetcherExceptionHandler(scopes, delegate) val exception = RuntimeException() val parameters = DataFetcherExceptionHandlerParameters.newExceptionParameters().exception(exception).build() handler.onException(parameters) - verify(hub).captureException(eq(exception), anyOrNull()) + verify(scopes).captureException(eq(exception), anyOrNull()) verify(delegate).handleException(parameters) } } diff --git a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryGenericDataFetcherExceptionHandlerTest.kt b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryGenericDataFetcherExceptionHandlerTest.kt index 6d643baf01..88e2f5df55 100644 --- a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryGenericDataFetcherExceptionHandlerTest.kt +++ b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryGenericDataFetcherExceptionHandlerTest.kt @@ -4,7 +4,7 @@ import graphql.GraphQLContext import graphql.execution.DataFetcherExceptionHandler import graphql.execution.DataFetcherExceptionHandlerParameters import graphql.schema.DataFetchingEnvironmentImpl -import io.sentry.IHub +import io.sentry.IScopes import org.mockito.kotlin.mock import org.mockito.kotlin.verify import kotlin.test.Test @@ -15,10 +15,10 @@ class SentryGenericDataFetcherExceptionHandlerTest { @Test fun `collects exception into GraphQLContext and invokes delegate`() { - val hub = mock() + val scopes = mock() val delegate = mock() val handler = SentryGenericDataFetcherExceptionHandler( - hub, + scopes, delegate ) diff --git a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationAnotherTest.kt b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationAnotherTest.kt index e30bbc9415..087a1dfa72 100644 --- a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationAnotherTest.kt +++ b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationAnotherTest.kt @@ -28,7 +28,8 @@ import graphql.schema.GraphQLObjectType import graphql.schema.GraphQLScalarType import graphql.schema.GraphQLSchema import io.sentry.Breadcrumb -import io.sentry.IHub +import io.sentry.HubScopesWrapper +import io.sentry.IScopes import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -52,7 +53,7 @@ import kotlin.test.assertSame class SentryInstrumentationAnotherTest { class Fixture { - val hub = mock() + val scopes = mock() lateinit var activeSpan: SentryTracer lateinit var dataFetcher: DataFetcher lateinit var fieldFetchParameters: InstrumentationFieldFetchParameters @@ -70,17 +71,17 @@ class SentryInstrumentationAnotherTest { val variables = mapOf("variableA" to "value a") fun getSut(isTransactionActive: Boolean = true, operation: OperationDefinition.Operation = OperationDefinition.Operation.QUERY, graphQLContextParam: Map? = null, addTransactionToTracingState: Boolean = true, ignoredErrors: List = emptyList()): SentryInstrumentation { - whenever(hub.options).thenReturn(SentryOptions()) - activeSpan = SentryTracer(TransactionContext("name", "op"), hub) + whenever(scopes.options).thenReturn(SentryOptions()) + activeSpan = SentryTracer(TransactionContext("name", "op"), scopes) if (isTransactionActive) { - whenever(hub.span).thenReturn(activeSpan) + whenever(scopes.span).thenReturn(activeSpan) } else { - whenever(hub.span).thenReturn(null) + whenever(scopes.span).thenReturn(null) } val defaultGraphQLContext = mapOf( - SentryInstrumentation.SENTRY_HUB_CONTEXT_KEY to hub + SentryInstrumentation.SENTRY_SCOPES_CONTEXT_KEY to scopes ) val mergedField = MergedField.newMergedField().addField(Field.newField("myFieldName").build()).build() @@ -165,7 +166,7 @@ class SentryInstrumentationAnotherTest { val result = instrumentedDataFetcher.get(fixture.environment) assertEquals("result modified by subscription handler", result) - verify(fixture.subscriptionHandler).onSubscriptionResult(eq("raw result"), same(fixture.hub), same(fixture.exceptionReporter), same(fixture.fieldFetchParameters)) + verify(fixture.subscriptionHandler).onSubscriptionResult(eq("raw result"), same(fixture.scopes), same(fixture.exceptionReporter), same(fixture.fieldFetchParameters)) } @Test @@ -175,7 +176,7 @@ class SentryInstrumentationAnotherTest { val result = instrumentedDataFetcher.get(fixture.environment) assertEquals("result modified by subscription handler", result) - verify(fixture.subscriptionHandler).onSubscriptionResult(eq("raw result"), same(fixture.hub), same(fixture.exceptionReporter), same(fixture.fieldFetchParameters)) + verify(fixture.subscriptionHandler).onSubscriptionResult(eq("raw result"), same(fixture.scopes), same(fixture.exceptionReporter), same(fixture.fieldFetchParameters)) } @Test @@ -222,7 +223,7 @@ class SentryInstrumentationAnotherTest { fun `adds a breadcrumb for operation`() { val instrumentation = fixture.getSut() instrumentation.beginExecuteOperation(fixture.instrumentationExecuteOperationParameters) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( org.mockito.kotlin.check { breadcrumb -> assertEquals("graphql", breadcrumb.type) assertEquals("query", breadcrumb.category) @@ -237,7 +238,7 @@ class SentryInstrumentationAnotherTest { fun `adds a breadcrumb for data fetcher`() { val instrumentation = fixture.getSut() instrumentation.instrumentDataFetcher(fixture.dataFetcher, fixture.fieldFetchParameters).get(fixture.environment) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( org.mockito.kotlin.check { breadcrumb -> assertEquals("graphql", breadcrumb.type) assertEquals("graphql.fetcher", breadcrumb.category) @@ -250,11 +251,11 @@ class SentryInstrumentationAnotherTest { } @Test - fun `stores hub in context and adds transaction to state`() { + fun `stores scopes in context and adds transaction to state`() { val instrumentation = fixture.getSut(isTransactionActive = true, operation = OperationDefinition.Operation.MUTATION, graphQLContextParam = emptyMap(), addTransactionToTracingState = false) - withMockHub { + withMockScopes { instrumentation.beginExecution(fixture.instrumentationExecutionParameters) - assertSame(fixture.hub, fixture.instrumentationExecutionParameters.graphQLContext.get(SentryInstrumentation.SENTRY_HUB_CONTEXT_KEY)) + assertSame(fixture.scopes, fixture.instrumentationExecutionParameters.graphQLContext.get(SentryInstrumentation.SENTRY_SCOPES_CONTEXT_KEY)) assertNotNull(fixture.instrumentationState.transaction) } } @@ -276,7 +277,7 @@ class SentryInstrumentationAnotherTest { assertEquals("exception message", it.message) }, org.mockito.kotlin.check { - assertSame(fixture.hub, it.hub) + assertSame(fixture.scopes, it.scopes) assertSame(fixture.query, it.query) assertEquals(false, it.isSubscription) assertEquals(fixture.variables, it.variables) @@ -293,7 +294,7 @@ class SentryInstrumentationAnotherTest { val instrumentation = fixture.getSut( graphQLContextParam = mapOf( SENTRY_EXCEPTIONS_CONTEXT_KEY to listOf(exception), - SentryInstrumentation.SENTRY_HUB_CONTEXT_KEY to fixture.hub + SentryInstrumentation.SENTRY_SCOPES_CONTEXT_KEY to fixture.scopes ) ) val executionResult = ExecutionResultImpl.newExecutionResult() @@ -305,7 +306,7 @@ class SentryInstrumentationAnotherTest { assertSame(exception, it) }, org.mockito.kotlin.check { - assertSame(fixture.hub, it.hub) + assertSame(fixture.scopes, it.scopes) assertSame(fixture.query, it.query) assertEquals(false, it.isSubscription) assertEquals(fixture.variables, it.variables) @@ -356,8 +357,9 @@ class SentryInstrumentationAnotherTest { assertSame(executionResult, result) } - fun withMockHub(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { - it.`when` { Sentry.getCurrentHub() }.thenReturn(fixture.hub) + fun withMockScopes(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { + it.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(fixture.scopes)) + it.`when` { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes) closure.invoke() } diff --git a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationTest.kt b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationTest.kt index 8a579e2687..2bb46f79fc 100644 --- a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationTest.kt +++ b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationTest.kt @@ -18,7 +18,8 @@ import graphql.schema.GraphQLScalarType import graphql.schema.idl.RuntimeWiring import graphql.schema.idl.SchemaGenerator import graphql.schema.idl.SchemaParser -import io.sentry.IHub +import io.sentry.HubScopesWrapper +import io.sentry.IScopes import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -39,12 +40,12 @@ import kotlin.test.assertTrue class SentryInstrumentationTest { class Fixture { - val hub = mock() + val scopes = mock() lateinit var activeSpan: SentryTracer fun getSut(isTransactionActive: Boolean = true, dataFetcherThrows: Boolean = false, beforeSpan: SentryInstrumentation.BeforeSpanCallback? = null): GraphQL { - whenever(hub.options).thenReturn(SentryOptions()) - activeSpan = SentryTracer(TransactionContext("name", "op"), hub) + whenever(scopes.options).thenReturn(SentryOptions()) + activeSpan = SentryTracer(TransactionContext("name", "op"), scopes) val schema = """ type Query { shows: [Show] @@ -61,9 +62,9 @@ class SentryInstrumentationTest { .build() if (isTransactionActive) { - whenever(hub.span).thenReturn(activeSpan) + whenever(scopes.span).thenReturn(activeSpan) } else { - whenever(hub.span).thenReturn(null) + whenever(scopes.span).thenReturn(null) } return graphQL @@ -87,7 +88,7 @@ class SentryInstrumentationTest { fun `when transaction is active, creates inner spans`() { val sut = fixture.getSut() - withMockHub { + withMockScopes { val result = sut.execute("{ shows { id } }") assertTrue(result.errors.isEmpty()) @@ -105,7 +106,7 @@ class SentryInstrumentationTest { fun `when transaction is active, and data fetcher throws, creates inner spans`() { val sut = fixture.getSut(dataFetcherThrows = true) - withMockHub { + withMockScopes { val result = sut.execute("{ shows { id } }") assertTrue(result.errors.isNotEmpty()) @@ -122,7 +123,7 @@ class SentryInstrumentationTest { fun `when transaction is not active, does not create spans`() { val sut = fixture.getSut(isTransactionActive = false) - withMockHub { + withMockScopes { val result = sut.execute("{ shows { id } }") assertTrue(result.errors.isEmpty()) @@ -134,7 +135,7 @@ class SentryInstrumentationTest { fun `beforeSpan can drop spans`() { val sut = fixture.getSut(beforeSpan = SentryInstrumentation.BeforeSpanCallback { _, _, _ -> null }) - withMockHub { + withMockScopes { val result = sut.execute("{ shows { id } }") assertTrue(result.errors.isEmpty()) @@ -152,7 +153,7 @@ class SentryInstrumentationTest { fun `beforeSpan can modify spans`() { val sut = fixture.getSut(beforeSpan = SentryInstrumentation.BeforeSpanCallback { span, _, _ -> span.apply { description = "changed" } }) - withMockHub { + withMockScopes { val result = sut.execute("{ shows { id } }") assertTrue(result.errors.isEmpty()) @@ -208,19 +209,20 @@ class SentryInstrumentationTest { @Test fun `Integration adds itself to integration and package list`() { - withMockHub { + withMockScopes { val sut = fixture.getSut() - assertNotNull(fixture.hub.options.sdkVersion) - assert(fixture.hub.options.sdkVersion!!.integrationSet.contains("GraphQL")) + assertNotNull(fixture.scopes.options.sdkVersion) + assert(fixture.scopes.options.sdkVersion!!.integrationSet.contains("GraphQL")) val packageInfo = - fixture.hub.options.sdkVersion!!.packageSet.firstOrNull { pkg -> pkg.name == "maven:io.sentry:sentry-graphql" } + fixture.scopes.options.sdkVersion!!.packageSet.firstOrNull { pkg -> pkg.name == "maven:io.sentry:sentry-graphql" } assertNotNull(packageInfo) assert(packageInfo.version == BuildConfig.VERSION_NAME) } } - fun withMockHub(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { - it.`when` { Sentry.getCurrentHub() }.thenReturn(fixture.hub) + fun withMockScopes(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { + it.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(fixture.scopes)) + it.`when` { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes) closure.invoke() } From c0be8eaf29769064a2af22310ce3cd20454ddf53 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 16 Apr 2024 16:41:09 +0200 Subject: [PATCH 08/91] Hubs/Scopes Merge 8 - Replace `IHub` with `IScopes` in logging integrations (#3304) * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations --- .../java/io/sentry/jul/SentryHandler.java | 6 +++--- sentry-log4j2/api/sentry-log4j2.api | 2 +- .../java/io/sentry/log4j2/SentryAppender.java | 20 +++++++++---------- .../io/sentry/log4j2/SentryAppenderTest.kt | 6 +++--- .../io/sentry/logback/SentryAppender.java | 6 +++--- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java b/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java index 2ca775572b..c52b4706f2 100644 --- a/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java +++ b/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java @@ -6,7 +6,7 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.HubAdapter; +import io.sentry.ScopesAdapter; import io.sentry.Sentry; import io.sentry.SentryEvent; import io.sentry.SentryIntegrationPackageStorage; @@ -210,9 +210,9 @@ SentryEvent createEvent(final @NotNull LogRecord record) { mdcProperties = CollectionUtils.filterMapEntries(mdcProperties, entry -> entry.getValue() != null); if (!mdcProperties.isEmpty()) { - // get tags from HubAdapter options to allow getting the correct tags if Sentry has been + // get tags from ScopesAdapter options to allow getting the correct tags if Sentry has been // initialized somewhere else - final List contextTags = HubAdapter.getInstance().getOptions().getContextTags(); + final List contextTags = ScopesAdapter.getInstance().getOptions().getContextTags(); if (!contextTags.isEmpty()) { for (final String contextTag : contextTags) { // if mdc tag is listed in SentryOptions, apply as event tag diff --git a/sentry-log4j2/api/sentry-log4j2.api b/sentry-log4j2/api/sentry-log4j2.api index 76aa3e823e..b7fe8b3273 100644 --- a/sentry-log4j2/api/sentry-log4j2.api +++ b/sentry-log4j2/api/sentry-log4j2.api @@ -5,7 +5,7 @@ public final class io/sentry/log4j2/BuildConfig { public class io/sentry/log4j2/SentryAppender : org/apache/logging/log4j/core/appender/AbstractAppender { public static final field MECHANISM_TYPE Ljava/lang/String; - public fun (Ljava/lang/String;Lorg/apache/logging/log4j/core/Filter;Ljava/lang/String;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Ljava/lang/Boolean;Lio/sentry/ITransportFactory;Lio/sentry/IHub;[Ljava/lang/String;)V + public fun (Ljava/lang/String;Lorg/apache/logging/log4j/core/Filter;Ljava/lang/String;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Ljava/lang/Boolean;Lio/sentry/ITransportFactory;Lio/sentry/IScopes;[Ljava/lang/String;)V public fun append (Lorg/apache/logging/log4j/core/LogEvent;)V public static fun createAppender (Ljava/lang/String;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Ljava/lang/String;Ljava/lang/Boolean;Lorg/apache/logging/log4j/core/Filter;Ljava/lang/String;)Lio/sentry/log4j2/SentryAppender; protected fun createBreadcrumb (Lorg/apache/logging/log4j/core/LogEvent;)Lio/sentry/Breadcrumb; diff --git a/sentry-log4j2/src/main/java/io/sentry/log4j2/SentryAppender.java b/sentry-log4j2/src/main/java/io/sentry/log4j2/SentryAppender.java index 4cf4ad4a86..4ee07ab7b9 100644 --- a/sentry-log4j2/src/main/java/io/sentry/log4j2/SentryAppender.java +++ b/sentry-log4j2/src/main/java/io/sentry/log4j2/SentryAppender.java @@ -7,9 +7,9 @@ import io.sentry.Breadcrumb; import io.sentry.DateUtils; import io.sentry.Hint; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ITransportFactory; +import io.sentry.ScopesAdapter; import io.sentry.Sentry; import io.sentry.SentryEvent; import io.sentry.SentryIntegrationPackageStorage; @@ -50,7 +50,7 @@ public class SentryAppender extends AbstractAppender { private @NotNull Level minimumBreadcrumbLevel = Level.INFO; private @NotNull Level minimumEventLevel = Level.ERROR; private final @Nullable Boolean debug; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @Nullable List contextTags; public SentryAppender( @@ -61,7 +61,7 @@ public SentryAppender( final @Nullable Level minimumEventLevel, final @Nullable Boolean debug, final @Nullable ITransportFactory transportFactory, - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @Nullable String[] contextTags) { super(name, filter, null, true, null); this.dsn = dsn; @@ -73,7 +73,7 @@ public SentryAppender( } this.debug = debug; this.transportFactory = transportFactory; - this.hub = hub; + this.scopes = scopes; this.contextTags = contextTags != null ? Arrays.asList(contextTags) : null; } @@ -110,7 +110,7 @@ public SentryAppender( minimumEventLevel, debug, null, - HubAdapter.getInstance(), + ScopesAdapter.getInstance(), contextTags != null ? contextTags.split(",") : null); } @@ -149,13 +149,13 @@ public void append(final @NotNull LogEvent eventObject) { final Hint hint = new Hint(); hint.set(SENTRY_SYNTHETIC_EXCEPTION, eventObject); - hub.captureEvent(createEvent(eventObject), hint); + scopes.captureEvent(createEvent(eventObject), hint); } if (eventObject.getLevel().isMoreSpecificThan(minimumBreadcrumbLevel)) { final Hint hint = new Hint(); hint.set(LOG4J_LOG_EVENT, eventObject); - hub.addBreadcrumb(createBreadcrumb(eventObject), hint); + scopes.addBreadcrumb(createBreadcrumb(eventObject), hint); } } @@ -199,9 +199,9 @@ public void append(final @NotNull LogEvent eventObject) { CollectionUtils.filterMapEntries( loggingEvent.getContextData().toMap(), entry -> entry.getValue() != null); if (!contextData.isEmpty()) { - // get tags from HubAdapter options to allow getting the correct tags if Sentry has been + // get tags from ScopesAdapter options to allow getting the correct tags if Sentry has been // initialized somewhere else - final List contextTags = hub.getOptions().getContextTags(); + final List contextTags = scopes.getOptions().getContextTags(); if (contextTags != null && !contextTags.isEmpty()) { for (final String contextTag : contextTags) { // if mdc tag is listed in SentryOptions, apply as event tag diff --git a/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt b/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt index b6f004232c..3786555a61 100644 --- a/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt +++ b/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt @@ -1,7 +1,7 @@ package io.sentry.log4j2 -import io.sentry.HubAdapter import io.sentry.ITransportFactory +import io.sentry.ScopesAdapter import io.sentry.Sentry import io.sentry.SentryLevel import io.sentry.checkEvent @@ -49,7 +49,7 @@ class SentryAppenderTest { } loggerContext.start() val config: Configuration = loggerContext.configuration - val appender = SentryAppender("sentry", null, "http://key@localhost/proj", minimumBreadcrumbLevel, minimumEventLevel, debug, this.transportFactory, HubAdapter.getInstance(), contextTags?.toTypedArray()) + val appender = SentryAppender("sentry", null, "http://key@localhost/proj", minimumBreadcrumbLevel, minimumEventLevel, debug, this.transportFactory, ScopesAdapter.getInstance(), contextTags?.toTypedArray()) config.addAppender(appender) val ref = AppenderRef.createAppenderRef("sentry", null, null) @@ -445,6 +445,6 @@ class SentryAppenderTest { @Test fun `sets the debug mode`() { fixture.getSut(debug = true) - assertTrue(HubAdapter.getInstance().options.isDebug) + assertTrue(ScopesAdapter.getInstance().options.isDebug) } } diff --git a/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java b/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java index d0be108149..56db0b4dbc 100644 --- a/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java +++ b/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java @@ -12,8 +12,8 @@ import io.sentry.Breadcrumb; import io.sentry.DateUtils; import io.sentry.Hint; -import io.sentry.HubAdapter; import io.sentry.ITransportFactory; +import io.sentry.ScopesAdapter; import io.sentry.Sentry; import io.sentry.SentryEvent; import io.sentry.SentryIntegrationPackageStorage; @@ -134,9 +134,9 @@ protected void append(@NotNull ILoggingEvent eventObject) { CollectionUtils.filterMapEntries( loggingEvent.getMDCPropertyMap(), entry -> entry.getValue() != null); if (!mdcProperties.isEmpty()) { - // get tags from HubAdapter options to allow getting the correct tags if Sentry has been + // get tags from ScopesAdapter options to allow getting the correct tags if Sentry has been // initialized somewhere else - final List contextTags = HubAdapter.getInstance().getOptions().getContextTags(); + final List contextTags = ScopesAdapter.getInstance().getOptions().getContextTags(); if (!contextTags.isEmpty()) { for (final String contextTag : contextTags) { // if mdc tag is listed in SentryOptions, apply as event tag From 76ec6b0804de8ceac56243244d538833f165de75 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 16 Apr 2024 16:41:37 +0200 Subject: [PATCH 09/91] Hubs/Scopes Merge 9 - Replace `IHub` with `IScopes` in more integrations (#3305) * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations --- sentry-jdbc/api/sentry-jdbc.api | 2 +- .../sentry/jdbc/SentryJdbcEventListener.java | 14 +++++----- .../jdbc/SentryJdbcEventListenerTest.kt | 16 +++++------ .../api/sentry-kotlin-extensions.api | 8 +++--- .../java/io/sentry/kotlin/SentryContext.kt | 28 +++++++++++-------- .../io/sentry/kotlin/SentryContextTest.kt | 6 ++-- sentry-openfeign/api/sentry-openfeign.api | 4 +-- .../io/sentry/openfeign/SentryCapability.java | 17 +++++------ .../sentry/openfeign/SentryFeignClient.java | 14 +++++----- .../sentry/openfeign/SentryFeignClientTest.kt | 22 +++++++-------- sentry-quartz/api/sentry-quartz.api | 2 +- .../io/sentry/quartz/SentryJobListener.java | 28 ++++++++++--------- .../api/sentry-servlet-jakarta.api | 2 +- .../jakarta/SentryServletRequestListener.java | 20 ++++++------- .../SentryServletRequestListenerTest.kt | 12 ++++---- sentry-servlet/api/sentry-servlet.api | 2 +- .../servlet/SentryServletRequestListener.java | 20 ++++++------- .../SentryServletRequestListenerTest.kt | 12 ++++---- .../api/sentry-test-support.api | 1 + .../main/kotlin/io/sentry/test/Reflection.kt | 11 ++++++-- 20 files changed, 129 insertions(+), 112 deletions(-) diff --git a/sentry-jdbc/api/sentry-jdbc.api b/sentry-jdbc/api/sentry-jdbc.api index cff0f37fd2..700cbb2d69 100644 --- a/sentry-jdbc/api/sentry-jdbc.api +++ b/sentry-jdbc/api/sentry-jdbc.api @@ -16,7 +16,7 @@ public final class io/sentry/jdbc/DatabaseUtils$DatabaseDetails { public class io/sentry/jdbc/SentryJdbcEventListener : com/p6spy/engine/event/SimpleJdbcEventListener { public fun ()V - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun onAfterAnyExecute (Lcom/p6spy/engine/common/StatementInformation;JLjava/sql/SQLException;)V public fun onBeforeAnyExecute (Lcom/p6spy/engine/common/StatementInformation;)V } diff --git a/sentry-jdbc/src/main/java/io/sentry/jdbc/SentryJdbcEventListener.java b/sentry-jdbc/src/main/java/io/sentry/jdbc/SentryJdbcEventListener.java index 0346d2d0b9..4cb21188e4 100644 --- a/sentry-jdbc/src/main/java/io/sentry/jdbc/SentryJdbcEventListener.java +++ b/sentry-jdbc/src/main/java/io/sentry/jdbc/SentryJdbcEventListener.java @@ -6,9 +6,9 @@ import com.jakewharton.nopen.annotation.Open; import com.p6spy.engine.common.StatementInformation; import com.p6spy.engine.event.SimpleJdbcEventListener; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ISpan; +import io.sentry.ScopesAdapter; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.Span; import io.sentry.SpanStatus; @@ -21,24 +21,24 @@ @Open public class SentryJdbcEventListener extends SimpleJdbcEventListener { private static final String TRACE_ORIGIN = "auto.db.jdbc"; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private static final @NotNull ThreadLocal CURRENT_SPAN = new ThreadLocal<>(); private volatile @Nullable DatabaseUtils.DatabaseDetails cachedDatabaseDetails = null; private final @NotNull Object databaseDetailsLock = new Object(); - public SentryJdbcEventListener(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + public SentryJdbcEventListener(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); addPackageAndIntegrationInfo(); } public SentryJdbcEventListener() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } @Override public void onBeforeAnyExecute(final @NotNull StatementInformation statementInformation) { - final ISpan parent = hub.getSpan(); + final ISpan parent = scopes.getSpan(); if (parent != null && !parent.isNoOp()) { final ISpan span = parent.startChild("db.query", statementInformation.getSql()); CURRENT_SPAN.set(span); diff --git a/sentry-jdbc/src/test/kotlin/io/sentry/jdbc/SentryJdbcEventListenerTest.kt b/sentry-jdbc/src/test/kotlin/io/sentry/jdbc/SentryJdbcEventListenerTest.kt index 78c5d4cf12..00ce03de41 100644 --- a/sentry-jdbc/src/test/kotlin/io/sentry/jdbc/SentryJdbcEventListenerTest.kt +++ b/sentry-jdbc/src/test/kotlin/io/sentry/jdbc/SentryJdbcEventListenerTest.kt @@ -2,7 +2,7 @@ package io.sentry.jdbc import com.p6spy.engine.common.StatementInformation import com.p6spy.engine.spy.P6DataSource -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryOptions import io.sentry.SentryTracer import io.sentry.SpanDataConvention.DB_NAME_KEY @@ -26,7 +26,7 @@ import kotlin.test.assertTrue class SentryJdbcEventListenerTest { class Fixture { - val hub = mock().apply { + val scopes = mock().apply { whenever(options).thenReturn( SentryOptions().apply { sdkVersion = SdkVersion("test", "1.2.3") @@ -37,9 +37,9 @@ class SentryJdbcEventListenerTest { val actualDataSource = JDBCDataSource() fun getSut(withRunningTransaction: Boolean = true, existingRow: Int? = null): DataSource { - tx = SentryTracer(TransactionContext("name", "op"), hub) + tx = SentryTracer(TransactionContext("name", "op"), scopes) if (withRunningTransaction) { - whenever(hub.span).thenReturn(tx) + whenever(scopes.span).thenReturn(tx) } actualDataSource.setURL("jdbc:hsqldb:mem:testdb") @@ -54,7 +54,7 @@ class SentryJdbcEventListenerTest { } } - val sentryQueryExecutionListener = SentryJdbcEventListener(hub) + val sentryQueryExecutionListener = SentryJdbcEventListener(scopes) val p6spyDataSource = P6DataSource(actualDataSource) p6spyDataSource.setJdbcEventListenerFactory { sentryQueryExecutionListener } return p6spyDataSource @@ -131,9 +131,9 @@ class SentryJdbcEventListenerTest { @Test fun `sets SDKVersion Info`() { val sut = fixture.getSut() - assertNotNull(fixture.hub.options.sdkVersion) - assert(fixture.hub.options.sdkVersion!!.integrationSet.contains("JDBC")) - val packageInfo = fixture.hub.options.sdkVersion!!.packageSet.firstOrNull { pkg -> pkg.name == "maven:io.sentry:sentry-jdbc" } + assertNotNull(fixture.scopes.options.sdkVersion) + assert(fixture.scopes.options.sdkVersion!!.integrationSet.contains("JDBC")) + val packageInfo = fixture.scopes.options.sdkVersion!!.packageSet.firstOrNull { pkg -> pkg.name == "maven:io.sentry:sentry-jdbc" } assertNotNull(packageInfo) assert(packageInfo.version == BuildConfig.VERSION_NAME) } diff --git a/sentry-kotlin-extensions/api/sentry-kotlin-extensions.api b/sentry-kotlin-extensions/api/sentry-kotlin-extensions.api index d501240a3a..7e3be67279 100644 --- a/sentry-kotlin-extensions/api/sentry-kotlin-extensions.api +++ b/sentry-kotlin-extensions/api/sentry-kotlin-extensions.api @@ -1,16 +1,16 @@ public final class io/sentry/kotlin/SentryContext : kotlin/coroutines/AbstractCoroutineContextElement, kotlinx/coroutines/CopyableThreadContextElement { public fun ()V - public fun (Lio/sentry/IHub;)V - public synthetic fun (Lio/sentry/IHub;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IScopes;)V + public synthetic fun (Lio/sentry/IScopes;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun copyForChild ()Lkotlinx/coroutines/CopyableThreadContextElement; public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; public fun mergeForChild (Lkotlin/coroutines/CoroutineContext$Element;)Lkotlin/coroutines/CoroutineContext; public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; - public fun restoreThreadContext (Lkotlin/coroutines/CoroutineContext;Lio/sentry/IHub;)V + public fun restoreThreadContext (Lkotlin/coroutines/CoroutineContext;Lio/sentry/IScopes;)V public synthetic fun restoreThreadContext (Lkotlin/coroutines/CoroutineContext;Ljava/lang/Object;)V - public fun updateThreadContext (Lkotlin/coroutines/CoroutineContext;)Lio/sentry/IHub; + public fun updateThreadContext (Lkotlin/coroutines/CoroutineContext;)Lio/sentry/IScopes; public synthetic fun updateThreadContext (Lkotlin/coroutines/CoroutineContext;)Ljava/lang/Object; } diff --git a/sentry-kotlin-extensions/src/main/java/io/sentry/kotlin/SentryContext.kt b/sentry-kotlin-extensions/src/main/java/io/sentry/kotlin/SentryContext.kt index 3cf22a20da..4c814f2805 100644 --- a/sentry-kotlin-extensions/src/main/java/io/sentry/kotlin/SentryContext.kt +++ b/sentry-kotlin-extensions/src/main/java/io/sentry/kotlin/SentryContext.kt @@ -1,6 +1,6 @@ package io.sentry.kotlin -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.Sentry import kotlinx.coroutines.CopyableThreadContextElement import kotlin.coroutines.AbstractCoroutineContextElement @@ -9,26 +9,32 @@ import kotlin.coroutines.CoroutineContext /** * Sentry context element for [CoroutineContext]. */ -public class SentryContext(private val hub: IHub = Sentry.getCurrentHub().clone()) : - CopyableThreadContextElement, AbstractCoroutineContextElement(Key) { +@SuppressWarnings("deprecation") +// TODO fork instead +public class SentryContext(private val scopes: IScopes = Sentry.getCurrentScopes().clone()) : + CopyableThreadContextElement, AbstractCoroutineContextElement(Key) { private companion object Key : CoroutineContext.Key - override fun copyForChild(): CopyableThreadContextElement { - return SentryContext(hub.clone()) + @SuppressWarnings("deprecation") + override fun copyForChild(): CopyableThreadContextElement { + // TODO fork instead + return SentryContext(scopes.clone()) } + @SuppressWarnings("deprecation") override fun mergeForChild(overwritingElement: CoroutineContext.Element): CoroutineContext { - return overwritingElement[Key] ?: SentryContext(hub.clone()) + // TODO fork instead? + return overwritingElement[Key] ?: SentryContext(scopes.clone()) } - override fun updateThreadContext(context: CoroutineContext): IHub { - val oldState = Sentry.getCurrentHub() - Sentry.setCurrentHub(hub) + override fun updateThreadContext(context: CoroutineContext): IScopes { + val oldState = Sentry.getCurrentScopes() + Sentry.setCurrentScopes(scopes) return oldState } - override fun restoreThreadContext(context: CoroutineContext, oldState: IHub) { - Sentry.setCurrentHub(oldState) + override fun restoreThreadContext(context: CoroutineContext, oldState: IScopes) { + Sentry.setCurrentScopes(oldState) } } diff --git a/sentry-kotlin-extensions/src/test/java/io/sentry/kotlin/SentryContextTest.kt b/sentry-kotlin-extensions/src/test/java/io/sentry/kotlin/SentryContextTest.kt index b54ceabc51..578b610267 100644 --- a/sentry-kotlin-extensions/src/test/java/io/sentry/kotlin/SentryContextTest.kt +++ b/sentry-kotlin-extensions/src/test/java/io/sentry/kotlin/SentryContextTest.kt @@ -119,7 +119,7 @@ class SentryContextTest { val c2 = launch( SentryContext( - Sentry.getCurrentHub().clone().also { + Sentry.getCurrentScopes().clone().also { it.setTag("cloned", "clonedValue") } ) @@ -145,7 +145,7 @@ class SentryContextTest { @Test fun `mergeForChild returns copy of initial context if Key not present`() { val initialContextElement = SentryContext( - Sentry.getCurrentHub().clone().also { + Sentry.getCurrentScopes().clone().also { it.setTag("cloned", "clonedValue") } ) @@ -158,7 +158,7 @@ class SentryContextTest { @Test fun `mergeForChild returns passed context`() { val initialContextElement = SentryContext( - Sentry.getCurrentHub().clone().also { + Sentry.getCurrentScopes().clone().also { it.setTag("cloned", "clonedValue") } ) diff --git a/sentry-openfeign/api/sentry-openfeign.api b/sentry-openfeign/api/sentry-openfeign.api index beb15c9e02..4ab65a5ca4 100644 --- a/sentry-openfeign/api/sentry-openfeign.api +++ b/sentry-openfeign/api/sentry-openfeign.api @@ -1,12 +1,12 @@ public final class io/sentry/openfeign/SentryCapability : feign/Capability { public fun ()V - public fun (Lio/sentry/IHub;Lio/sentry/openfeign/SentryFeignClient$BeforeSpanCallback;)V + public fun (Lio/sentry/IScopes;Lio/sentry/openfeign/SentryFeignClient$BeforeSpanCallback;)V public fun (Lio/sentry/openfeign/SentryFeignClient$BeforeSpanCallback;)V public fun enrich (Lfeign/Client;)Lfeign/Client; } public final class io/sentry/openfeign/SentryFeignClient : feign/Client { - public fun (Lfeign/Client;Lio/sentry/IHub;Lio/sentry/openfeign/SentryFeignClient$BeforeSpanCallback;)V + public fun (Lfeign/Client;Lio/sentry/IScopes;Lio/sentry/openfeign/SentryFeignClient$BeforeSpanCallback;)V public fun execute (Lfeign/Request;Lfeign/Request$Options;)Lfeign/Response; } diff --git a/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryCapability.java b/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryCapability.java index b65685c3fd..1ad6b1f274 100644 --- a/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryCapability.java +++ b/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryCapability.java @@ -2,33 +2,34 @@ import feign.Capability; import feign.Client; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; +import io.sentry.ScopesAdapter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** Adds Sentry tracing capability to Feign clients. */ public final class SentryCapability implements Capability { - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @Nullable SentryFeignClient.BeforeSpanCallback beforeSpan; public SentryCapability( - final @NotNull IHub hub, final @Nullable SentryFeignClient.BeforeSpanCallback beforeSpan) { - this.hub = hub; + final @NotNull IScopes scopes, + final @Nullable SentryFeignClient.BeforeSpanCallback beforeSpan) { + this.scopes = scopes; this.beforeSpan = beforeSpan; } public SentryCapability(final @Nullable SentryFeignClient.BeforeSpanCallback beforeSpan) { - this(HubAdapter.getInstance(), beforeSpan); + this(ScopesAdapter.getInstance(), beforeSpan); } public SentryCapability() { - this(HubAdapter.getInstance(), null); + this(ScopesAdapter.getInstance(), null); } @Override public @NotNull Client enrich(final @NotNull Client client) { - return new SentryFeignClient(client, hub, beforeSpan); + return new SentryFeignClient(client, scopes, beforeSpan); } } diff --git a/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java b/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java index cb8aa3d9e0..037768c7ad 100644 --- a/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java +++ b/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java @@ -9,7 +9,7 @@ import io.sentry.BaggageHeader; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.SpanDataConvention; import io.sentry.SpanStatus; @@ -30,15 +30,15 @@ public final class SentryFeignClient implements Client { private static final String TRACE_ORIGIN = "auto.http.openfeign"; private final @NotNull Client delegate; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @Nullable BeforeSpanCallback beforeSpan; public SentryFeignClient( final @NotNull Client delegate, - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @Nullable BeforeSpanCallback beforeSpan) { this.delegate = Objects.requireNonNull(delegate, "delegate is required"); - this.hub = Objects.requireNonNull(hub, "hub is required"); + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); this.beforeSpan = beforeSpan; } @@ -47,7 +47,7 @@ public Response execute(final @NotNull Request request, final @NotNull Request.O throws IOException { Response response = null; try { - final ISpan activeSpan = hub.getSpan(); + final ISpan activeSpan = scopes.getSpan(); if (activeSpan == null) { final @NotNull Request modifiedRequest = maybeAddTracingHeaders(request, null); @@ -102,7 +102,7 @@ public Response execute(final @NotNull Request request, final @NotNull Request.O final @Nullable TracingUtils.TracingHeaders tracingHeaders = TracingUtils.traceIfAllowed( - hub, + scopes, request.url(), (requestBaggageHeaders != null ? new ArrayList<>(requestBaggageHeaders) : null), span); @@ -139,7 +139,7 @@ private void addBreadcrumb(final @NotNull Request request, final @Nullable Respo hint.set(OPEN_FEIGN_RESPONSE, response); } - hub.addBreadcrumb(breadcrumb, hint); + scopes.addBreadcrumb(breadcrumb, hint); } static final class RequestWrapper { diff --git a/sentry-openfeign/src/test/kotlin/io/sentry/openfeign/SentryFeignClientTest.kt b/sentry-openfeign/src/test/kotlin/io/sentry/openfeign/SentryFeignClientTest.kt index 65e56ab02b..959b890d46 100644 --- a/sentry-openfeign/src/test/kotlin/io/sentry/openfeign/SentryFeignClientTest.kt +++ b/sentry-openfeign/src/test/kotlin/io/sentry/openfeign/SentryFeignClientTest.kt @@ -7,7 +7,7 @@ import feign.HeaderMap import feign.RequestLine import io.sentry.BaggageHeader import io.sentry.Breadcrumb -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -37,7 +37,7 @@ import kotlin.test.fail class SentryFeignClientTest { class Fixture { - val hub = mock() + val scopes = mock() val server = MockWebServer() val sentryTracer: SentryTracer val sentryOptions = SentryOptions().apply { @@ -46,9 +46,9 @@ class SentryFeignClientTest { val scope = Scope(sentryOptions) init { - whenever(hub.options).thenReturn(sentryOptions) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) - sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) + whenever(scopes.options).thenReturn(sentryOptions) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) + sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) } fun getSut( @@ -59,7 +59,7 @@ class SentryFeignClientTest { beforeSpan: SentryFeignClient.BeforeSpanCallback? = null ): MockApi { if (isSpanActive) { - whenever(hub.span).thenReturn(sentryTracer) + whenever(scopes.span).thenReturn(sentryTracer) } server.enqueue( MockResponse() @@ -70,12 +70,12 @@ class SentryFeignClientTest { return if (!networkError) { Feign.builder() - .addCapability(SentryCapability(hub, beforeSpan)) + .addCapability(SentryCapability(scopes, beforeSpan)) } else { val mockClient = mock() whenever(mockClient.execute(any(), any())).thenThrow(RuntimeException::class.java) Feign.builder() - .client(SentryFeignClient(mockClient, hub, beforeSpan)) + .client(SentryFeignClient(mockClient, scopes, beforeSpan)) }.target(MockApi::class.java, server.url("/").toUrl().toString()) } } @@ -201,7 +201,7 @@ class SentryFeignClientTest { fun `adds breadcrumb when http calls succeeds`() { val sut = fixture.getSut(responseBody = "response body") sut.postWithBody("request-body") - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(13, it.data["response_body_size"]) @@ -215,7 +215,7 @@ class SentryFeignClientTest { fun `adds breadcrumb when http calls succeeds even though response body is null`() { val sut = fixture.getSut(responseBody = "") sut.postWithBody("request-body") - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(0, it.data["response_body_size"]) @@ -236,7 +236,7 @@ class SentryFeignClientTest { } catch (e: Exception) { // ignore me } - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) }, diff --git a/sentry-quartz/api/sentry-quartz.api b/sentry-quartz/api/sentry-quartz.api index ff32280dc5..23fce49e7d 100644 --- a/sentry-quartz/api/sentry-quartz.api +++ b/sentry-quartz/api/sentry-quartz.api @@ -7,7 +7,7 @@ public final class io/sentry/quartz/SentryJobListener : org/quartz/JobListener { public static final field SENTRY_CHECK_IN_ID_KEY Ljava/lang/String; public static final field SENTRY_SLUG_KEY Ljava/lang/String; public fun ()V - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun getName ()Ljava/lang/String; public fun jobExecutionVetoed (Lorg/quartz/JobExecutionContext;)V public fun jobToBeExecuted (Lorg/quartz/JobExecutionContext;)V diff --git a/sentry-quartz/src/main/java/io/sentry/quartz/SentryJobListener.java b/sentry-quartz/src/main/java/io/sentry/quartz/SentryJobListener.java index 28a0e51200..f9c22022cc 100644 --- a/sentry-quartz/src/main/java/io/sentry/quartz/SentryJobListener.java +++ b/sentry-quartz/src/main/java/io/sentry/quartz/SentryJobListener.java @@ -3,8 +3,8 @@ import io.sentry.BuildConfig; import io.sentry.CheckIn; import io.sentry.CheckInStatus; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; +import io.sentry.ScopesAdapter; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryLevel; import io.sentry.protocol.SentryId; @@ -24,14 +24,14 @@ public final class SentryJobListener implements JobListener { public static final String SENTRY_CHECK_IN_ID_KEY = "sentry-checkin-id"; public static final String SENTRY_SLUG_KEY = "sentry-slug"; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; public SentryJobListener() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } - public SentryJobListener(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + public SentryJobListener(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); SentryIntegrationPackageStorage.getInstance().addIntegration("Quartz"); SentryIntegrationPackageStorage.getInstance() .addPackage("maven:io.sentry:sentry-quartz", BuildConfig.VERSION_NAME); @@ -49,15 +49,16 @@ public void jobToBeExecuted(final @NotNull JobExecutionContext context) { if (maybeSlug == null) { return; } - hub.pushScope(); - TracingUtils.startNewTrace(hub); + scopes.pushScope(); + TracingUtils.startNewTrace(scopes); final @NotNull String slug = maybeSlug; final @NotNull CheckIn checkIn = new CheckIn(slug, CheckInStatus.IN_PROGRESS); - final @NotNull SentryId checkInId = hub.captureCheckIn(checkIn); + final @NotNull SentryId checkInId = scopes.captureCheckIn(checkIn); context.put(SENTRY_CHECK_IN_ID_KEY, checkInId); context.put(SENTRY_SLUG_KEY, slug); } catch (Throwable t) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log(SentryLevel.ERROR, "Unable to capture check-in in jobToBeExecuted.", t); } @@ -94,14 +95,15 @@ public void jobWasExecuted(JobExecutionContext context, JobExecutionException jo if (slug != null) { final boolean isFailed = jobException != null; final @NotNull CheckInStatus status = isFailed ? CheckInStatus.ERROR : CheckInStatus.OK; - hub.captureCheckIn(new CheckIn(checkInId, slug, status)); + scopes.captureCheckIn(new CheckIn(checkInId, slug, status)); } } catch (Throwable t) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log(SentryLevel.ERROR, "Unable to capture check-in in jobWasExecuted.", t); } finally { - hub.popScope(); + scopes.popScope(); } } } diff --git a/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api b/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api index d0367e5195..a5421e7453 100644 --- a/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api +++ b/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api @@ -10,7 +10,7 @@ public class io/sentry/servlet/jakarta/SentryServletContainerInitializer : jakar public class io/sentry/servlet/jakarta/SentryServletRequestListener : jakarta/servlet/ServletRequestListener { public fun ()V - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun requestDestroyed (Ljakarta/servlet/ServletRequestEvent;)V public fun requestInitialized (Ljakarta/servlet/ServletRequestEvent;)V } diff --git a/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java b/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java index e3811157f8..54775386fd 100644 --- a/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java +++ b/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java @@ -5,8 +5,8 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; +import io.sentry.ScopesAdapter; import io.sentry.util.Objects; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletRequestEvent; @@ -21,24 +21,24 @@ @Open public class SentryServletRequestListener implements ServletRequestListener { - private final IHub hub; + private final IScopes scopes; - public SentryServletRequestListener(@NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + public SentryServletRequestListener(@NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); } public SentryServletRequestListener() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } @Override public void requestDestroyed(@NotNull ServletRequestEvent servletRequestEvent) { - hub.popScope(); + scopes.popScope(); } @Override public void requestInitialized(@NotNull ServletRequestEvent servletRequestEvent) { - hub.pushScope(); + scopes.pushScope(); final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); if (servletRequest instanceof HttpServletRequest) { @@ -47,10 +47,10 @@ public void requestInitialized(@NotNull ServletRequestEvent servletRequestEvent) final Hint hint = new Hint(); hint.set(SERVLET_REQUEST, httpRequest); - hub.addBreadcrumb( + scopes.addBreadcrumb( Breadcrumb.http(httpRequest.getRequestURI(), httpRequest.getMethod()), hint); - hub.configureScope( + scopes.configureScope( scope -> { scope.addEventProcessor(new SentryRequestHttpServletRequestProcessor(httpRequest)); }); diff --git a/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt b/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt index b87ea218a2..3be76d1cd2 100644 --- a/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt +++ b/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt @@ -1,7 +1,7 @@ package io.sentry.servlet.jakarta import io.sentry.Breadcrumb -import io.sentry.IHub +import io.sentry.IScopes import jakarta.servlet.ServletRequestEvent import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.check @@ -13,9 +13,9 @@ import kotlin.test.assertEquals class SentryServletRequestListenerTest { private class Fixture { - val hub = mock() + val scopes = mock() val listener = - SentryServletRequestListener(hub) + SentryServletRequestListener(scopes) val request = mockRequest( url = "http://localhost:8080/some-uri", method = "POST" @@ -33,14 +33,14 @@ class SentryServletRequestListenerTest { fun `pushes scope when request gets initialized`() { fixture.listener.requestInitialized(fixture.event) - verify(fixture.hub).pushScope() + verify(fixture.scopes).pushScope() } @Test fun `adds breadcrumb when request gets initialized`() { fixture.listener.requestInitialized(fixture.event) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { it: Breadcrumb -> assertEquals("/some-uri", it.getData("url")) assertEquals("POST", it.getData("method")) @@ -54,6 +54,6 @@ class SentryServletRequestListenerTest { fun `pops scope when request gets destroyed`() { fixture.listener.requestDestroyed(fixture.event) - verify(fixture.hub).popScope() + verify(fixture.scopes).popScope() } } diff --git a/sentry-servlet/api/sentry-servlet.api b/sentry-servlet/api/sentry-servlet.api index a0a2a1e0d2..fd7aee819b 100644 --- a/sentry-servlet/api/sentry-servlet.api +++ b/sentry-servlet/api/sentry-servlet.api @@ -10,7 +10,7 @@ public class io/sentry/servlet/SentryServletContainerInitializer : javax/servlet public class io/sentry/servlet/SentryServletRequestListener : javax/servlet/ServletRequestListener { public fun ()V - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun requestDestroyed (Ljavax/servlet/ServletRequestEvent;)V public fun requestInitialized (Ljavax/servlet/ServletRequestEvent;)V } diff --git a/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java b/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java index 9b981676c4..97c37e1133 100644 --- a/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java +++ b/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java @@ -5,8 +5,8 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; +import io.sentry.ScopesAdapter; import io.sentry.util.Objects; import javax.servlet.ServletRequest; import javax.servlet.ServletRequestEvent; @@ -21,24 +21,24 @@ @Open public class SentryServletRequestListener implements ServletRequestListener { - private final IHub hub; + private final IScopes scopes; - public SentryServletRequestListener(@NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + public SentryServletRequestListener(@NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); } public SentryServletRequestListener() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } @Override public void requestDestroyed(@NotNull ServletRequestEvent servletRequestEvent) { - hub.popScope(); + scopes.popScope(); } @Override public void requestInitialized(@NotNull ServletRequestEvent servletRequestEvent) { - hub.pushScope(); + scopes.pushScope(); final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); if (servletRequest instanceof HttpServletRequest) { @@ -47,10 +47,10 @@ public void requestInitialized(@NotNull ServletRequestEvent servletRequestEvent) final Hint hint = new Hint(); hint.set(SERVLET_REQUEST, httpRequest); - hub.addBreadcrumb( + scopes.addBreadcrumb( Breadcrumb.http(httpRequest.getRequestURI(), httpRequest.getMethod()), hint); - hub.configureScope( + scopes.configureScope( scope -> { scope.addEventProcessor(new SentryRequestHttpServletRequestProcessor(httpRequest)); }); diff --git a/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt b/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt index b94e73f2ef..bfa216f738 100644 --- a/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt +++ b/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt @@ -1,7 +1,7 @@ package io.sentry.servlet import io.sentry.Breadcrumb -import io.sentry.IHub +import io.sentry.IScopes import org.assertj.core.api.Assertions.assertThat import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.check @@ -14,8 +14,8 @@ import kotlin.test.Test class SentryServletRequestListenerTest { private class Fixture { - val hub = mock() - val listener = SentryServletRequestListener(hub) + val scopes = mock() + val listener = SentryServletRequestListener(scopes) val request = MockHttpServletRequest() val event = mock() @@ -32,14 +32,14 @@ class SentryServletRequestListenerTest { fun `pushes scope when request gets initialized`() { fixture.listener.requestInitialized(fixture.event) - verify(fixture.hub).pushScope() + verify(fixture.scopes).pushScope() } @Test fun `adds breadcrumb when request gets initialized`() { fixture.listener.requestInitialized(fixture.event) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { it: Breadcrumb -> assertThat(it.getData("url")).isEqualTo("http://localhost:8080/some-uri") assertThat(it.getData("method")).isEqualTo("POST") @@ -53,6 +53,6 @@ class SentryServletRequestListenerTest { fun `pops scope when request gets destroyed`() { fixture.listener.requestDestroyed(fixture.event) - verify(fixture.hub).popScope() + verify(fixture.scopes).popScope() } } diff --git a/sentry-test-support/api/sentry-test-support.api b/sentry-test-support/api/sentry-test-support.api index ffce23a516..dd1a4b69d3 100644 --- a/sentry-test-support/api/sentry-test-support.api +++ b/sentry-test-support/api/sentry-test-support.api @@ -32,6 +32,7 @@ public final class io/sentry/test/ImmediateExecutorService : io/sentry/ISentryEx } public final class io/sentry/test/ReflectionKt { + public static final fun collectInterfaceHierarchy (Ljava/lang/Class;)Ljava/util/List; public static final fun containsMethod (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Z public static final fun containsMethod (Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Z public static final fun getCtor (Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor; diff --git a/sentry-test-support/src/main/kotlin/io/sentry/test/Reflection.kt b/sentry-test-support/src/main/kotlin/io/sentry/test/Reflection.kt index 690dcc8725..19d11676e1 100644 --- a/sentry-test-support/src/main/kotlin/io/sentry/test/Reflection.kt +++ b/sentry-test-support/src/main/kotlin/io/sentry/test/Reflection.kt @@ -12,16 +12,23 @@ inline fun T.callMethod(name: String, parameterTypes: Class<*> val declaredMethod = try { T::class.java.getDeclaredMethod(name, parameterTypes) } catch (e: NoSuchMethodException) { - T::class.java.interfaces.first { it.containsMethod(name, parameterTypes) }.getDeclaredMethod(name, parameterTypes) + collectInterfaceHierarchy(T::class.java).first { it.containsMethod(name, parameterTypes) }.getDeclaredMethod(name, parameterTypes) } return declaredMethod.invoke(this, value) } +fun collectInterfaceHierarchy(clazz: Class<*>): List> { + if (clazz.interfaces.isEmpty()) { + return listOf(clazz) + } + return clazz.interfaces.flatMap { iface -> collectInterfaceHierarchy(iface) }.also { it.toMutableList().add(clazz) } +} + inline fun T.callMethod(name: String, parameterTypes: Array>, vararg value: Any?): Any? { val declaredMethod = try { T::class.java.getDeclaredMethod(name, *parameterTypes) } catch (e: NoSuchMethodException) { - T::class.java.interfaces.first { it.containsMethod(name, parameterTypes) }.getDeclaredMethod(name, *parameterTypes) + collectInterfaceHierarchy(T::class.java).first { it.containsMethod(name, parameterTypes) }.getDeclaredMethod(name, *parameterTypes) } return declaredMethod.invoke(this, *value) } From c552a2ca1228bb80ffe93389d034a4b01c8f1148 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 16 Apr 2024 16:42:06 +0200 Subject: [PATCH 10/91] Hubs/Scopes Merge 10 - Replace `IHub` with `IScopes` in OTel integration (#3306) * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration --- .../OpenTelemetryLinkErrorEventProcessor.java | 26 ++++++---- .../opentelemetry/SentryPropagator.java | 24 +++++---- .../opentelemetry/SentrySpanProcessor.java | 50 +++++++++++-------- .../test/kotlin/SentrySpanProcessorTest.kt | 46 ++++++++--------- 4 files changed, 82 insertions(+), 64 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java index 1e373ece9c..bfc4cd05f1 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java @@ -5,10 +5,10 @@ import io.opentelemetry.api.trace.TraceId; import io.sentry.EventProcessor; import io.sentry.Hint; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.Instrumenter; +import io.sentry.ScopesAdapter; import io.sentry.SentryEvent; import io.sentry.SentryLevel; import io.sentry.SentrySpanStorage; @@ -20,21 +20,21 @@ public final class OpenTelemetryLinkErrorEventProcessor implements EventProcessor { - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @NotNull SentrySpanStorage spanStorage = SentrySpanStorage.getInstance(); public OpenTelemetryLinkErrorEventProcessor() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } @TestOnly - OpenTelemetryLinkErrorEventProcessor(final @NotNull IHub hub) { - this.hub = hub; + OpenTelemetryLinkErrorEventProcessor(final @NotNull IScopes scopes) { + this.scopes = scopes; } @Override public @Nullable SentryEvent process(final @NotNull SentryEvent event, final @NotNull Hint hint) { - final @NotNull Instrumenter instrumenter = hub.getOptions().getInstrumenter(); + final @NotNull Instrumenter instrumenter = scopes.getOptions().getInstrumenter(); if (Instrumenter.OTEL.equals(instrumenter)) { @NotNull final Span otelSpan = Span.current(); @NotNull final String traceId = otelSpan.getSpanContext().getTraceId(); @@ -55,7 +55,8 @@ public OpenTelemetryLinkErrorEventProcessor() { null); event.getContexts().setTrace(spanContext); - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -64,7 +65,8 @@ public OpenTelemetryLinkErrorEventProcessor() { spanId, traceId); } else { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -74,7 +76,8 @@ public OpenTelemetryLinkErrorEventProcessor() { traceId); } } else { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -84,7 +87,8 @@ public OpenTelemetryLinkErrorEventProcessor() { spanId); } } else { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java index 14ac12323b..ed3e243f4d 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java @@ -10,9 +10,9 @@ import io.opentelemetry.context.propagation.TextMapSetter; import io.sentry.Baggage; import io.sentry.BaggageHeader; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ISpan; +import io.sentry.ScopesAdapter; import io.sentry.SentryLevel; import io.sentry.SentrySpanStorage; import io.sentry.SentryTraceHeader; @@ -29,14 +29,14 @@ public final class SentryPropagator implements TextMapPropagator { private static final @NotNull List FIELDS = Arrays.asList(SentryTraceHeader.SENTRY_TRACE_HEADER, BaggageHeader.BAGGAGE_HEADER); private final @NotNull SentrySpanStorage spanStorage = SentrySpanStorage.getInstance(); - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; public SentryPropagator() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } - SentryPropagator(final @NotNull IHub hub) { - this.hub = hub; + SentryPropagator(final @NotNull IScopes scopes) { + this.scopes = scopes; } @Override @@ -49,7 +49,8 @@ public void inject(final Context context, final C carrier, final TextMapSett final @NotNull Span otelSpan = Span.fromContext(context); final @NotNull SpanContext otelSpanContext = otelSpan.getSpanContext(); if (!otelSpanContext.isValid()) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -58,7 +59,8 @@ public void inject(final Context context, final C carrier, final TextMapSett } final @Nullable ISpan sentrySpan = spanStorage.get(otelSpanContext.getSpanId()); if (sentrySpan == null || sentrySpan.isNoOp()) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -106,13 +108,15 @@ public Context extract( Span wrappedSpan = Span.wrap(otelSpanContext); modifiedContext = modifiedContext.with(wrappedSpan); - hub.getOptions() + scopes + .getOptions() .getLogger() .log(SentryLevel.DEBUG, "Continuing Sentry trace %s", sentryTraceHeader.getTraceId()); return modifiedContext; } catch (InvalidSentryTraceHeaderException e) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.ERROR, diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java index a9e70f66a0..6b7797153b 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java @@ -13,12 +13,12 @@ import io.opentelemetry.semconv.SemanticAttributes; import io.sentry.Baggage; import io.sentry.DsnUtil; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.ITransaction; import io.sentry.Instrumenter; import io.sentry.PropagationContext; +import io.sentry.ScopesAdapter; import io.sentry.SentryDate; import io.sentry.SentryLevel; import io.sentry.SentryLongDate; @@ -46,14 +46,14 @@ public final class SentrySpanProcessor implements SpanProcessor { private final @NotNull SpanDescriptionExtractor spanDescriptionExtractor = new SpanDescriptionExtractor(); private final @NotNull SentrySpanStorage spanStorage = SentrySpanStorage.getInstance(); - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; public SentrySpanProcessor() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } - SentrySpanProcessor(final @NotNull IHub hub) { - this.hub = hub; + SentrySpanProcessor(final @NotNull IScopes scopes) { + this.scopes = scopes; } @Override @@ -65,7 +65,8 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri final @NotNull TraceData traceData = getTraceData(otelSpan, parentContext); if (isSentryRequest(otelSpan)) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -78,7 +79,8 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri traceData.getParentSpanId() == null ? null : spanStorage.get(traceData.getParentSpanId()); if (sentryParentSpan != null) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -94,7 +96,8 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri sentryChildSpan.getSpanContext().setOrigin(TRACE_ORIGN); spanStorage.store(traceData.getSpanId(), sentryChildSpan); } else { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -123,7 +126,7 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri transactionOptions.setStartTimestamp( new SentryLongDate(otelSpan.toSpanData().getStartEpochNanos())); - ISpan sentryTransaction = hub.startTransaction(transactionContext, transactionOptions); + ISpan sentryTransaction = scopes.startTransaction(transactionContext, transactionOptions); sentryTransaction.getSpanContext().setOrigin(TRACE_ORIGN); spanStorage.store(traceData.getSpanId(), sentryTransaction); } @@ -144,7 +147,8 @@ public void onEnd(final @NotNull ReadableSpan otelSpan) { final @Nullable ISpan sentrySpan = spanStorage.removeAndGet(traceData.getSpanId()); if (sentrySpan == null) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -155,7 +159,8 @@ public void onEnd(final @NotNull ReadableSpan otelSpan) { } if (isSentryRequest(otelSpan)) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -168,7 +173,8 @@ public void onEnd(final @NotNull ReadableSpan otelSpan) { if (sentrySpan instanceof ITransaction) { final @NotNull ITransaction sentryTransaction = (ITransaction) sentrySpan; updateTransactionWithOtelData(sentryTransaction, otelSpan); - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -178,7 +184,8 @@ public void onEnd(final @NotNull ReadableSpan otelSpan) { traceData.getTraceId()); } else { updateSpanWithOtelData(sentrySpan, otelSpan); - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -201,7 +208,8 @@ public boolean isEndRequired() { private boolean ensurePrerequisites(final @NotNull ReadableSpan otelSpan) { if (!hasSentryBeenInitialized()) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -209,9 +217,10 @@ private boolean ensurePrerequisites(final @NotNull ReadableSpan otelSpan) { return false; } - final @NotNull Instrumenter instrumenter = hub.getOptions().getInstrumenter(); + final @NotNull Instrumenter instrumenter = scopes.getOptions().getInstrumenter(); if (!Instrumenter.OTEL.equals(instrumenter)) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -222,7 +231,8 @@ private boolean ensurePrerequisites(final @NotNull ReadableSpan otelSpan) { final @NotNull SpanContext otelSpanContext = otelSpan.getSpanContext(); if (!otelSpanContext.isValid()) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -241,7 +251,7 @@ private boolean isSentryRequest(final @NotNull ReadableSpan otelSpan) { } final @Nullable String httpUrl = otelSpan.getAttribute(SemanticAttributes.HTTP_URL); - return DsnUtil.urlContainsDsnHost(hub.getOptions(), httpUrl); + return DsnUtil.urlContainsDsnHost(scopes.getOptions(), httpUrl); } private @NotNull TraceData getTraceData( @@ -334,7 +344,7 @@ private SpanStatus mapOtelStatus(final @NotNull ReadableSpan otelSpan) { } private boolean hasSentryBeenInitialized() { - return hub.isEnabled(); + return scopes.isEnabled(); } private @NotNull Map toMapWithStringKeys(final @Nullable Attributes attributes) { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SentrySpanProcessorTest.kt b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SentrySpanProcessorTest.kt index 5ed757ba16..50d70f34f5 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SentrySpanProcessorTest.kt +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SentrySpanProcessorTest.kt @@ -21,7 +21,7 @@ import io.opentelemetry.semconv.SemanticAttributes import io.sentry.Baggage import io.sentry.BaggageHeader import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ISpan import io.sentry.ITransaction import io.sentry.Instrumenter @@ -65,7 +65,7 @@ class SentrySpanProcessorTest { it.dsn = "https://key@sentry.io/proj" it.instrumenter = Instrumenter.OTEL } - val hub = mock() + val scopes = mock() val transaction = mock() val span = mock() val spanContext = mock() @@ -75,9 +75,9 @@ class SentrySpanProcessorTest { val baggage = Baggage.fromHeader(BAGGAGE_HEADER_STRING) fun setup() { - whenever(hub.isEnabled).thenReturn(true) - whenever(hub.options).thenReturn(options) - whenever(hub.startTransaction(any(), any())).thenReturn(transaction) + whenever(scopes.isEnabled).thenReturn(true) + whenever(scopes.options).thenReturn(options) + whenever(scopes.startTransaction(any(), any())).thenReturn(transaction) whenever(spanContext.operation).thenReturn("spanContextOp") whenever(spanContext.parentSpanId).thenReturn(io.sentry.SpanId("cedf5b7571cb4972")) @@ -94,7 +94,7 @@ class SentrySpanProcessorTest { whenever(transaction.startChild(any(), anyOrNull(), anyOrNull(), eq(Instrumenter.OTEL))).thenReturn(span) val sdkTracerProvider = SdkTracerProvider.builder() - .addSpanProcessor(SentrySpanProcessor(hub)) + .addSpanProcessor(SentrySpanProcessor(scopes)) .build() openTelemetry = OpenTelemetrySdk.builder() @@ -146,13 +146,13 @@ class SentrySpanProcessorTest { val context = mock() val span = mock() - whenever(fixture.hub.isEnabled).thenReturn(false) + whenever(fixture.scopes.isEnabled).thenReturn(false) - SentrySpanProcessor(fixture.hub).onStart(context, span) + SentrySpanProcessor(fixture.scopes).onStart(context, span) - verify(fixture.hub).isEnabled - verify(fixture.hub).options - verifyNoMoreInteractions(fixture.hub) + verify(fixture.scopes).isEnabled + verify(fixture.scopes).options + verifyNoMoreInteractions(fixture.scopes) verifyNoInteractions(context, span) } @@ -161,13 +161,13 @@ class SentrySpanProcessorTest { fixture.setup() val span = mock() - whenever(fixture.hub.isEnabled).thenReturn(false) + whenever(fixture.scopes.isEnabled).thenReturn(false) - SentrySpanProcessor(fixture.hub).onEnd(span) + SentrySpanProcessor(fixture.scopes).onEnd(span) - verify(fixture.hub).isEnabled - verify(fixture.hub).options - verifyNoMoreInteractions(fixture.hub) + verify(fixture.scopes).isEnabled + verify(fixture.scopes).options + verifyNoMoreInteractions(fixture.scopes) verifyNoInteractions(span) } @@ -178,7 +178,7 @@ class SentrySpanProcessorTest { val mockSpanContext = mock() whenever(mockSpanContext.spanId).thenReturn(SpanId.getInvalid()) whenever(mockSpan.spanContext).thenReturn(mockSpanContext) - SentrySpanProcessor(fixture.hub).onStart(Context.current(), mockSpan) + SentrySpanProcessor(fixture.scopes).onStart(Context.current(), mockSpan) thenNoTransactionIsStarted() } @@ -190,7 +190,7 @@ class SentrySpanProcessorTest { whenever(mockSpanContext.spanId).thenReturn(SpanId.fromBytes("seed".toByteArray())) whenever(mockSpanContext.traceId).thenReturn(TraceId.getInvalid()) whenever(mockSpan.spanContext).thenReturn(mockSpanContext) - SentrySpanProcessor(fixture.hub).onStart(Context.current(), mockSpan) + SentrySpanProcessor(fixture.scopes).onStart(Context.current(), mockSpan) thenNoTransactionIsStarted() } @@ -342,7 +342,7 @@ class SentrySpanProcessorTest { thenTransactionIsStarted(otelSpan, isContinued = true) otelSpan.makeCurrent().use { _ -> - val processedEvent = OpenTelemetryLinkErrorEventProcessor(fixture.hub).process(SentryEvent(), Hint()) + val processedEvent = OpenTelemetryLinkErrorEventProcessor(fixture.scopes).process(SentryEvent(), Hint()) val traceContext = processedEvent!!.contexts.trace!! assertEquals("2722d9f6ec019ade60c776169d9a8904", traceContext.traceId.toString()) @@ -361,7 +361,7 @@ class SentrySpanProcessorTest { fixture.options.instrumenter = Instrumenter.SENTRY fixture.setup() - val processedEvent = OpenTelemetryLinkErrorEventProcessor(fixture.hub).process(SentryEvent(), Hint()) + val processedEvent = OpenTelemetryLinkErrorEventProcessor(fixture.scopes).process(SentryEvent(), Hint()) thenNoTraceContextHasBeenAddedToEvent(processedEvent) } @@ -393,7 +393,7 @@ class SentrySpanProcessorTest { private fun thenTransactionIsStarted(otelSpan: Span, isContinued: Boolean = false, continuesWithFilledBaggage: Boolean = true) { if (isContinued) { - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("testspan", it.name) assertEquals(TransactionNameSource.CUSTOM, it.transactionNameSource) @@ -423,7 +423,7 @@ class SentrySpanProcessorTest { } ) } else { - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("testspan", it.name) assertEquals(TransactionNameSource.CUSTOM, it.transactionNameSource) @@ -451,7 +451,7 @@ class SentrySpanProcessorTest { } private fun thenNoTransactionIsStarted() { - verify(fixture.hub, never()).startTransaction( + verify(fixture.scopes, never()).startTransaction( any(), any() ) From 495ed9921146bdd42969d973c68fd992d72e8e23 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 16 Apr 2024 16:43:52 +0200 Subject: [PATCH 11/91] Hubs/Scopes Merge 11 - Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations (#3308) * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations --- sentry-spring-boot/api/sentry-spring-boot.api | 4 +- .../spring/boot/SentryAutoConfiguration.java | 39 ++++---- .../SentrySpanRestTemplateCustomizer.java | 6 +- .../boot/SentrySpanWebClientCustomizer.java | 6 +- .../boot/SentryWebfluxAutoConfiguration.java | 15 +-- .../boot/SentryAutoConfigurationTest.kt | 12 +-- .../SentrySpanRestTemplateCustomizerTest.kt | 22 ++--- .../boot/SentrySpanWebClientCustomizerTest.kt | 22 ++--- .../boot/it/SentrySpringIntegrationTest.kt | 6 +- sentry-spring/api/sentry-spring.api | 37 ++++---- .../spring/SentryExceptionResolver.java | 10 +- .../io/sentry/spring/SentryHubRegistrar.java | 4 +- .../spring/SentryInitBeanPostProcessor.java | 16 ++-- .../sentry/spring/SentryRequestResolver.java | 16 ++-- .../io/sentry/spring/SentrySpringFilter.java | 38 ++++---- .../io/sentry/spring/SentryTaskDecorator.java | 14 +-- .../io/sentry/spring/SentryUserFilter.java | 12 +-- .../spring/checkin/SentryCheckInAdvice.java | 28 +++--- ...SentryCaptureExceptionParameterAdvice.java | 14 +-- .../graphql/SentryBatchLoaderRegistry.java | 16 ++-- .../graphql/SentryDgsSubscriptionHandler.java | 6 +- .../SentrySpringSubscriptionHandler.java | 6 +- .../spring/tracing/SentrySpanAdvice.java | 14 +-- ...entrySpanClientHttpRequestInterceptor.java | 14 +-- .../SentrySpanClientWebRequestFilter.java | 14 +-- .../spring/tracing/SentryTracingFilter.java | 31 +++--- .../tracing/SentryTransactionAdvice.java | 20 ++-- .../spring/webflux/SentryRequestResolver.java | 13 +-- .../spring/webflux/SentryScheduleHook.java | 12 ++- .../webflux/SentryWebExceptionHandler.java | 10 +- .../spring/webflux/SentryWebFilter.java | 33 ++++--- .../io/sentry/spring/EnableSentryTest.kt | 6 +- .../sentry/spring/SentryCheckInAdviceTest.kt | 94 +++++++++---------- .../spring/SentryExceptionResolverTest.kt | 28 +++--- .../spring/SentryInitBeanPostProcessorTest.kt | 10 +- ...yRequestHttpServletRequestProcessorTest.kt | 6 +- .../sentry/spring/SentrySpringFilterTest.kt | 20 ++-- .../sentry/spring/SentryTaskDecoratorTest.kt | 16 ++-- .../io/sentry/spring/SentryUserFilterTest.kt | 20 ++-- ...ntryCaptureExceptionParameterAdviceTest.kt | 16 ++-- .../SentrySpringSubscriptionHandlerTest.kt | 14 +-- .../spring/mvc/SentrySpringIntegrationTest.kt | 24 ++--- .../spring/tracing/SentrySpanAdviceTest.kt | 40 ++++---- .../spring/tracing/SentryTracingFilterTest.kt | 52 +++++----- .../tracing/SentryTransactionAdviceTest.kt | 38 ++++---- .../spring/webflux/SentryScheduleHookTest.kt | 14 +-- .../webflux/SentryWebFluxTracingFilterTest.kt | 93 +++++++++--------- .../webflux/SentryWebfluxIntegrationTest.kt | 10 +- 48 files changed, 516 insertions(+), 495 deletions(-) diff --git a/sentry-spring-boot/api/sentry-spring-boot.api b/sentry-spring-boot/api/sentry-spring-boot.api index 32d7cc8c60..d97e2e1111 100644 --- a/sentry-spring-boot/api/sentry-spring-boot.api +++ b/sentry-spring-boot/api/sentry-spring-boot.api @@ -53,8 +53,8 @@ public class io/sentry/spring/boot/SentryProperties$Logging { public class io/sentry/spring/boot/SentryWebfluxAutoConfiguration { public fun ()V public fun sentryScheduleHookApplicationRunner ()Lorg/springframework/boot/ApplicationRunner; - public fun sentryWebExceptionHandler (Lio/sentry/IHub;)Lio/sentry/spring/webflux/SentryWebExceptionHandler; - public fun sentryWebFilter (Lio/sentry/IHub;)Lio/sentry/spring/webflux/SentryWebFilter; + public fun sentryWebExceptionHandler (Lio/sentry/IScopes;)Lio/sentry/spring/webflux/SentryWebExceptionHandler; + public fun sentryWebFilter (Lio/sentry/IScopes;)Lio/sentry/spring/webflux/SentryWebFilter; } public class io/sentry/spring/boot/graphql/SentryGraphqlAutoConfiguration { diff --git a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java index edbfee271d..7d6a6a6fc4 100644 --- a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java +++ b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java @@ -3,10 +3,10 @@ import com.jakewharton.nopen.annotation.Open; import graphql.GraphQLError; import io.sentry.EventProcessor; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ITransportFactory; import io.sentry.Integration; +import io.sentry.ScopesAdapter; import io.sentry.Sentry; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryOptions; @@ -114,7 +114,7 @@ static class HubConfiguration { } @Bean - public @NotNull IHub sentryHub( + public @NotNull IScopes sentryHub( final @NotNull List> optionsConfigurations, final @NotNull SentryProperties options, final @NotNull ObjectProvider gitProperties) { @@ -137,7 +137,7 @@ static class HubConfiguration { // here we make sure that only classes that extend throwable are set on this field options.getIgnoredExceptionsForType().removeIf(it -> !Throwable.class.isAssignableFrom(it)); Sentry.init(options); - return HubAdapter.getInstance(); + return ScopesAdapter.getInstance(); } @Configuration(proxyBeanMethods = false) @@ -237,7 +237,7 @@ static class SentrySecurityConfiguration { * HttpServletRequest#getUserPrincipal()}. If Spring Security is auto-configured, its order is * set to run after Spring Security. * - * @param hub the Sentry hub + * @param scopes the Sentry scopes * @param sentryProperties the Sentry properties * @param sentryUserProvider the user provider * @return {@link SentryUserFilter} registration bean @@ -245,11 +245,11 @@ static class SentrySecurityConfiguration { @Bean @ConditionalOnBean(SentryUserProvider.class) public @NotNull FilterRegistrationBean sentryUserFilter( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull SentryProperties sentryProperties, final @NotNull List sentryUserProvider) { final FilterRegistrationBean filter = new FilterRegistrationBean<>(); - filter.setFilter(new SentryUserFilter(hub, sentryUserProvider)); + filter.setFilter(new SentryUserFilter(scopes, sentryUserProvider)); filter.setOrder(resolveUserFilterOrder(sentryProperties)); return filter; } @@ -261,8 +261,8 @@ static class SentrySecurityConfiguration { } @Bean - public @NotNull SentryRequestResolver sentryRequestResolver(final @NotNull IHub hub) { - return new SentryRequestResolver(hub); + public @NotNull SentryRequestResolver sentryRequestResolver(final @NotNull IScopes scopes) { + return new SentryRequestResolver(scopes); } @Bean @@ -274,12 +274,12 @@ static class SentrySecurityConfiguration { @Bean @ConditionalOnMissingBean(name = "sentrySpringFilter") public @NotNull FilterRegistrationBean sentrySpringFilter( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull SentryRequestResolver requestResolver, final @NotNull TransactionNameProvider transactionNameProvider) { FilterRegistrationBean filter = new FilterRegistrationBean<>( - new SentrySpringFilter(hub, requestResolver, transactionNameProvider)); + new SentrySpringFilter(scopes, requestResolver, transactionNameProvider)); filter.setOrder(SENTRY_SPRING_FILTER_PRECEDENCE); return filter; } @@ -287,9 +287,10 @@ static class SentrySecurityConfiguration { @Bean @ConditionalOnMissingBean(name = "sentryTracingFilter") public FilterRegistrationBean sentryTracingFilter( - final @NotNull IHub hub, final @NotNull TransactionNameProvider transactionNameProvider) { + final @NotNull IScopes scopes, + final @NotNull TransactionNameProvider transactionNameProvider) { FilterRegistrationBean filter = - new FilterRegistrationBean<>(new SentryTracingFilter(hub, transactionNameProvider)); + new FilterRegistrationBean<>(new SentryTracingFilter(scopes, transactionNameProvider)); filter.setOrder(SENTRY_SPRING_FILTER_PRECEDENCE + 1); // must run after SentrySpringFilter return filter; } @@ -298,11 +299,11 @@ public FilterRegistrationBean sentryTracingFilter( @ConditionalOnMissingBean @ConditionalOnClass(HandlerExceptionResolver.class) public @NotNull SentryExceptionResolver sentryExceptionResolver( - final @NotNull IHub sentryHub, + final @NotNull IScopes scopes, final @NotNull TransactionNameProvider transactionNameProvider, final @NotNull SentryProperties options) { return new SentryExceptionResolver( - sentryHub, transactionNameProvider, options.getExceptionResolverOrder()); + scopes, transactionNameProvider, options.getExceptionResolverOrder()); } } @@ -348,8 +349,8 @@ static class SentrySpanPointcutAutoConfiguration {} @Open static class SentryPerformanceRestTemplateConfiguration { @Bean - public SentrySpanRestTemplateCustomizer sentrySpanRestTemplateCustomizer(IHub hub) { - return new SentrySpanRestTemplateCustomizer(hub); + public SentrySpanRestTemplateCustomizer sentrySpanRestTemplateCustomizer(IScopes scopes) { + return new SentrySpanRestTemplateCustomizer(scopes); } } @@ -359,8 +360,8 @@ public SentrySpanRestTemplateCustomizer sentrySpanRestTemplateCustomizer(IHub hu @Open static class SentryPerformanceWebClientConfiguration { @Bean - public SentrySpanWebClientCustomizer sentrySpanWebClientCustomizer(IHub hub) { - return new SentrySpanWebClientCustomizer(hub); + public SentrySpanWebClientCustomizer sentrySpanWebClientCustomizer(IScopes scopes) { + return new SentrySpanWebClientCustomizer(scopes); } } diff --git a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentrySpanRestTemplateCustomizer.java b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentrySpanRestTemplateCustomizer.java index bd311c55f1..2a5e4f1be5 100644 --- a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentrySpanRestTemplateCustomizer.java +++ b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentrySpanRestTemplateCustomizer.java @@ -1,7 +1,7 @@ package io.sentry.spring.boot; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.spring.tracing.SentrySpanClientHttpRequestInterceptor; import java.util.ArrayList; import java.util.List; @@ -14,8 +14,8 @@ class SentrySpanRestTemplateCustomizer implements RestTemplateCustomizer { private final @NotNull SentrySpanClientHttpRequestInterceptor interceptor; - public SentrySpanRestTemplateCustomizer(final @NotNull IHub hub) { - this.interceptor = new SentrySpanClientHttpRequestInterceptor(hub); + public SentrySpanRestTemplateCustomizer(final @NotNull IScopes scopes) { + this.interceptor = new SentrySpanClientHttpRequestInterceptor(scopes); } @Override diff --git a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentrySpanWebClientCustomizer.java b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentrySpanWebClientCustomizer.java index 0b8aa4055c..79e59f1cf0 100644 --- a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentrySpanWebClientCustomizer.java +++ b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentrySpanWebClientCustomizer.java @@ -1,7 +1,7 @@ package io.sentry.spring.boot; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.spring.tracing.SentrySpanClientWebRequestFilter; import org.jetbrains.annotations.NotNull; import org.springframework.boot.web.reactive.function.client.WebClientCustomizer; @@ -11,8 +11,8 @@ class SentrySpanWebClientCustomizer implements WebClientCustomizer { private final @NotNull SentrySpanClientWebRequestFilter filter; - public SentrySpanWebClientCustomizer(final @NotNull IHub hub) { - this.filter = new SentrySpanClientWebRequestFilter(hub); + public SentrySpanWebClientCustomizer(final @NotNull IScopes scopes) { + this.filter = new SentrySpanClientWebRequestFilter(scopes); } @Override diff --git a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryWebfluxAutoConfiguration.java b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryWebfluxAutoConfiguration.java index 3d507f1b6b..e7f6a444b8 100644 --- a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryWebfluxAutoConfiguration.java +++ b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryWebfluxAutoConfiguration.java @@ -1,8 +1,8 @@ package io.sentry.spring.boot; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IHub; import io.sentry.IScope; +import io.sentry.IScopes; import io.sentry.spring.webflux.SentryScheduleHook; import io.sentry.spring.webflux.SentryWebExceptionHandler; import io.sentry.spring.webflux.SentryWebFilter; @@ -21,14 +21,14 @@ /** Configures Sentry integration for Spring Webflux and Project Reactor. */ @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) -@ConditionalOnBean(IHub.class) +@ConditionalOnBean(IScopes.class) @ConditionalOnClass(Schedulers.class) @Open @ApiStatus.Experimental public class SentryWebfluxAutoConfiguration { private static final int SENTRY_SPRING_FILTER_PRECEDENCE = Ordered.HIGHEST_PRECEDENCE; - /** Configures hook that sets correct hub on the executing thread. */ + /** Configures hook that sets correct scopes on the executing thread. */ @Bean public @NotNull ApplicationRunner sentryScheduleHookApplicationRunner() { return args -> { @@ -39,13 +39,14 @@ public class SentryWebfluxAutoConfiguration { /** Configures a filter that sets up Sentry {@link IScope} for each request. */ @Bean @Order(SENTRY_SPRING_FILTER_PRECEDENCE) - public @NotNull SentryWebFilter sentryWebFilter(final @NotNull IHub hub) { - return new SentryWebFilter(hub); + public @NotNull SentryWebFilter sentryWebFilter(final @NotNull IScopes scopes) { + return new SentryWebFilter(scopes); } /** Configures exception handler that handles unhandled exceptions and sends them to Sentry. */ @Bean - public @NotNull SentryWebExceptionHandler sentryWebExceptionHandler(final @NotNull IHub hub) { - return new SentryWebExceptionHandler(hub); + public @NotNull SentryWebExceptionHandler sentryWebExceptionHandler( + final @NotNull IScopes scopes) { + return new SentryWebExceptionHandler(scopes); } } diff --git a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt index c7fd177ec0..e1fe220aea 100644 --- a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt +++ b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt @@ -5,7 +5,7 @@ import io.sentry.AsyncHttpTransportFactory import io.sentry.Breadcrumb import io.sentry.EventProcessor import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ITransportFactory import io.sentry.Integration import io.sentry.NoOpTransportFactory @@ -76,18 +76,18 @@ class SentryAutoConfigurationTest { .withConfiguration(AutoConfigurations.of(SentryAutoConfiguration::class.java, WebMvcAutoConfiguration::class.java)) @Test - fun `hub is not created when auto-configuration dsn is not set`() { + fun `scopes is not created when auto-configuration dsn is not set`() { contextRunner .run { - assertThat(it).doesNotHaveBean(IHub::class.java) + assertThat(it).doesNotHaveBean(IScopes::class.java) } } @Test - fun `hub is created when dsn is provided`() { + fun `scopes is created when dsn is provided`() { contextRunner.withPropertyValues("sentry.dsn=http://key@localhost/proj") .run { - assertThat(it).hasSingleBean(IHub::class.java) + assertThat(it).hasSingleBean(IScopes::class.java) } } @@ -943,7 +943,7 @@ class SentryAutoConfigurationTest { } class CustomIntegration : Integration { - override fun register(hub: IHub, options: SentryOptions) {} + override fun register(scopes: IScopes, options: SentryOptions) {} } @Configuration(proxyBeanMethods = false) diff --git a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanRestTemplateCustomizerTest.kt b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanRestTemplateCustomizerTest.kt index 0d675b6841..33d7974d8a 100644 --- a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanRestTemplateCustomizerTest.kt +++ b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanRestTemplateCustomizerTest.kt @@ -2,7 +2,7 @@ package io.sentry.spring.boot import io.sentry.BaggageHeader import io.sentry.Breadcrumb -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -37,23 +37,23 @@ import kotlin.test.assertTrue class SentrySpanRestTemplateCustomizerTest { class Fixture { val sentryOptions = SentryOptions() - val hub = mock() + val scopes = mock() val restTemplate = RestTemplateBuilder() .setConnectTimeout(Duration.ofSeconds(2)) .setReadTimeout(Duration.ofSeconds(2)) .build() var mockServer = MockWebServer() val transaction: SentryTracer - internal val customizer = SentrySpanRestTemplateCustomizer(hub) + internal val customizer = SentrySpanRestTemplateCustomizer(scopes) val url = mockServer.url("/test/123").toString() val scope = Scope(sentryOptions) init { - whenever(hub.options).thenReturn(sentryOptions) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope( + whenever(scopes.options).thenReturn(sentryOptions) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope( any() ) - transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), hub) + transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), scopes) } fun getSut(isTransactionActive: Boolean, status: HttpStatus = HttpStatus.OK, socketPolicy: SocketPolicy = SocketPolicy.KEEP_OPEN, includeMockServerInTracingOrigins: Boolean = true): RestTemplate { @@ -76,7 +76,7 @@ class SentrySpanRestTemplateCustomizerTest { ) if (isTransactionActive) { - whenever(hub.span).thenReturn(transaction) + whenever(scopes.span).thenReturn(transaction) } return restTemplate @@ -211,7 +211,7 @@ class SentrySpanRestTemplateCustomizerTest { @Test fun `when transaction is active adds breadcrumb when http calls succeeds`() { fixture.getSut(isTransactionActive = true).postForObject(fixture.url, "content", String::class.java) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -229,7 +229,7 @@ class SentrySpanRestTemplateCustomizerTest { fixture.getSut(isTransactionActive = true, status = HttpStatus.INTERNAL_SERVER_ERROR).getForObject(fixture.url, String::class.java) } catch (e: Throwable) { } - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -242,7 +242,7 @@ class SentrySpanRestTemplateCustomizerTest { @Test fun `when transaction is not active adds breadcrumb when http calls succeeds`() { fixture.getSut(isTransactionActive = false).postForObject(fixture.url, "content", String::class.java) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -260,7 +260,7 @@ class SentrySpanRestTemplateCustomizerTest { fixture.getSut(isTransactionActive = false, status = HttpStatus.INTERNAL_SERVER_ERROR).getForObject(fixture.url, String::class.java) } catch (e: Throwable) { } - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) diff --git a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanWebClientCustomizerTest.kt b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanWebClientCustomizerTest.kt index f925435fd3..4f1f70d75e 100644 --- a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanWebClientCustomizerTest.kt +++ b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanWebClientCustomizerTest.kt @@ -2,8 +2,8 @@ package io.sentry.spring.boot import io.sentry.BaggageHeader import io.sentry.Breadcrumb -import io.sentry.IHub import io.sentry.IScope +import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -39,10 +39,10 @@ class SentrySpanWebClientCustomizerTest { class Fixture { lateinit var sentryOptions: SentryOptions lateinit var scope: IScope - val hub = mock() + val scopes = mock() var mockServer = MockWebServer() lateinit var transaction: SentryTracer - private val customizer = SentrySpanWebClientCustomizer(hub) + private val customizer = SentrySpanWebClientCustomizer(scopes) fun getSut(isTransactionActive: Boolean, status: HttpStatus = HttpStatus.OK, throwIOException: Boolean = false, includeMockServerInTracingOrigins: Boolean = true): WebClient { sentryOptions = SentryOptions().apply { @@ -54,11 +54,11 @@ class SentrySpanWebClientCustomizerTest { dsn = "http://key@localhost/proj" } scope = Scope(sentryOptions) - whenever(hub.options).thenReturn(sentryOptions) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope( + whenever(scopes.options).thenReturn(sentryOptions) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope( any() ) - transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), hub) + transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), scopes) val webClientBuilder = WebClient.builder() customizer.customize(webClientBuilder) val webClient = webClientBuilder.build() @@ -66,7 +66,7 @@ class SentrySpanWebClientCustomizerTest { if (isTransactionActive) { val scope = Scope(sentryOptions) scope.transaction = transaction - whenever(hub.span).thenReturn(transaction) + whenever(scopes.span).thenReturn(transaction) } val dispatcher: Dispatcher = object : Dispatcher() { @@ -238,7 +238,7 @@ class SentrySpanWebClientCustomizerTest { .retrieve() .bodyToMono(String::class.java) .block() - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(uri.toString(), it.data["url"]) @@ -261,7 +261,7 @@ class SentrySpanWebClientCustomizerTest { .block() } catch (e: Throwable) { } - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(uri.toString(), it.data["url"]) @@ -281,7 +281,7 @@ class SentrySpanWebClientCustomizerTest { .retrieve() .bodyToMono(String::class.java) .block() - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(uri.toString(), it.data["url"]) @@ -304,7 +304,7 @@ class SentrySpanWebClientCustomizerTest { .block() } catch (e: Throwable) { } - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(uri.toString(), it.data["url"]) diff --git a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt index eb6d159a7c..3bbcb2c3e7 100644 --- a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt +++ b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.boot.it -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ITransportFactory import io.sentry.Sentry import io.sentry.checkEvent @@ -60,7 +60,7 @@ class SentrySpringIntegrationTest { lateinit var transport: ITransport @SpyBean - lateinit var hub: IHub + lateinit var scopes: IScopes @LocalServerPort var port: Int? = null @@ -188,7 +188,7 @@ class SentrySpringIntegrationTest { restTemplate.getForEntity("http://localhost:$port/throws-handled", String::class.java) - verify(hub, never()).captureEvent(any()) + verify(scopes, never()).captureEvent(any()) } @Test diff --git a/sentry-spring/api/sentry-spring.api b/sentry-spring/api/sentry-spring.api index 9ef6b5bb3a..58de26098f 100644 --- a/sentry-spring/api/sentry-spring.api +++ b/sentry-spring/api/sentry-spring.api @@ -22,7 +22,7 @@ public final class io/sentry/spring/HttpServletRequestSentryUserProvider : io/se public class io/sentry/spring/SentryExceptionResolver : org/springframework/core/Ordered, org/springframework/web/servlet/HandlerExceptionResolver { public static final field MECHANISM_TYPE Ljava/lang/String; - public fun (Lio/sentry/IHub;Lio/sentry/spring/tracing/TransactionNameProvider;I)V + public fun (Lio/sentry/IScopes;Lio/sentry/spring/tracing/TransactionNameProvider;I)V protected fun createEvent (Ljavax/servlet/http/HttpServletRequest;Ljava/lang/Exception;)Lio/sentry/SentryEvent; protected fun createHint (Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)Lio/sentry/Hint; public fun getOrder ()I @@ -47,14 +47,14 @@ public class io/sentry/spring/SentryRequestHttpServletRequestProcessor : io/sent } public class io/sentry/spring/SentryRequestResolver { - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun resolveSentryRequest (Ljavax/servlet/http/HttpServletRequest;)Lio/sentry/protocol/Request; } public class io/sentry/spring/SentrySpringFilter : org/springframework/web/filter/OncePerRequestFilter { public fun ()V - public fun (Lio/sentry/IHub;)V - public fun (Lio/sentry/IHub;Lio/sentry/spring/SentryRequestResolver;Lio/sentry/spring/tracing/TransactionNameProvider;)V + public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IScopes;Lio/sentry/spring/SentryRequestResolver;Lio/sentry/spring/tracing/TransactionNameProvider;)V protected fun doFilterInternal (Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;Ljavax/servlet/FilterChain;)V } @@ -69,7 +69,7 @@ public final class io/sentry/spring/SentryTaskDecorator : org/springframework/co } public class io/sentry/spring/SentryUserFilter : org/springframework/web/filter/OncePerRequestFilter { - public fun (Lio/sentry/IHub;Ljava/util/List;)V + public fun (Lio/sentry/IScopes;Ljava/util/List;)V protected fun doFilterInternal (Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;Ljavax/servlet/FilterChain;)V public fun getSentryUserProviders ()Ljava/util/List; } @@ -96,7 +96,7 @@ public abstract interface annotation class io/sentry/spring/checkin/SentryCheckI public class io/sentry/spring/checkin/SentryCheckInAdvice : org/aopalliance/intercept/MethodInterceptor, org/springframework/context/EmbeddedValueResolverAware { public fun ()V - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun invoke (Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object; public fun setEmbeddedValueResolver (Lorg/springframework/util/StringValueResolver;)V } @@ -127,7 +127,7 @@ public abstract interface annotation class io/sentry/spring/exception/SentryCapt public class io/sentry/spring/exception/SentryCaptureExceptionParameterAdvice : org/aopalliance/intercept/MethodInterceptor { public fun ()V - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun invoke (Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object; } @@ -169,7 +169,7 @@ public final class io/sentry/spring/graphql/SentryDataFetcherExceptionResolverAd public final class io/sentry/spring/graphql/SentryDgsSubscriptionHandler : io/sentry/graphql/SentrySubscriptionHandler { public fun ()V - public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IHub;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; + public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IScopes;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; } public final class io/sentry/spring/graphql/SentryGraphqlBeanPostProcessor : org/springframework/beans/factory/config/BeanPostProcessor, org/springframework/core/PriorityOrdered { @@ -188,7 +188,7 @@ public class io/sentry/spring/graphql/SentryGraphqlConfiguration { public final class io/sentry/spring/graphql/SentrySpringSubscriptionHandler : io/sentry/graphql/SentrySubscriptionHandler { public fun ()V - public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IHub;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; + public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IScopes;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; } public class io/sentry/spring/tracing/SentryAdviceConfiguration { @@ -207,17 +207,17 @@ public abstract interface annotation class io/sentry/spring/tracing/SentrySpan : public class io/sentry/spring/tracing/SentrySpanAdvice : org/aopalliance/intercept/MethodInterceptor { public fun ()V - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun invoke (Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object; } public class io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor : org/springframework/http/client/ClientHttpRequestInterceptor { - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun intercept (Lorg/springframework/http/HttpRequest;[BLorg/springframework/http/client/ClientHttpRequestExecution;)Lorg/springframework/http/client/ClientHttpResponse; } public class io/sentry/spring/tracing/SentrySpanClientWebRequestFilter : org/springframework/web/reactive/function/client/ExchangeFilterFunction { - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun filter (Lorg/springframework/web/reactive/function/client/ClientRequest;Lorg/springframework/web/reactive/function/client/ExchangeFunction;)Lreactor/core/publisher/Mono; } @@ -232,8 +232,8 @@ public class io/sentry/spring/tracing/SentryTracingConfiguration { public class io/sentry/spring/tracing/SentryTracingFilter : org/springframework/web/filter/OncePerRequestFilter { public fun ()V - public fun (Lio/sentry/IHub;)V - public fun (Lio/sentry/IHub;Lio/sentry/spring/tracing/TransactionNameProvider;)V + public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IScopes;Lio/sentry/spring/tracing/TransactionNameProvider;)V protected fun doFilterInternal (Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;Ljavax/servlet/FilterChain;)V } @@ -245,7 +245,7 @@ public abstract interface annotation class io/sentry/spring/tracing/SentryTransa public class io/sentry/spring/tracing/SentryTransactionAdvice : org/aopalliance/intercept/MethodInterceptor { public fun ()V - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun invoke (Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object; } @@ -266,7 +266,7 @@ public abstract interface class io/sentry/spring/tracing/TransactionNameProvider } public class io/sentry/spring/webflux/SentryRequestResolver { - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun resolveSentryRequest (Lorg/springframework/http/server/reactive/ServerHttpRequest;)Lio/sentry/protocol/Request; } @@ -278,13 +278,14 @@ public final class io/sentry/spring/webflux/SentryScheduleHook : java/util/funct public final class io/sentry/spring/webflux/SentryWebExceptionHandler : org/springframework/web/server/WebExceptionHandler { public static final field MECHANISM_TYPE Ljava/lang/String; - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun handle (Lorg/springframework/web/server/ServerWebExchange;Ljava/lang/Throwable;)Lreactor/core/publisher/Mono; } public final class io/sentry/spring/webflux/SentryWebFilter : org/springframework/web/server/WebFilter { public static final field SENTRY_HUB_KEY Ljava/lang/String; - public fun (Lio/sentry/IHub;)V + public static final field SENTRY_SCOPES_KEY Ljava/lang/String; + public fun (Lio/sentry/IScopes;)V public fun filter (Lorg/springframework/web/server/ServerWebExchange;Lorg/springframework/web/server/WebFilterChain;)Lreactor/core/publisher/Mono; } diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryExceptionResolver.java b/sentry-spring/src/main/java/io/sentry/spring/SentryExceptionResolver.java index fc9da87933..ba0586eade 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryExceptionResolver.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryExceptionResolver.java @@ -5,7 +5,7 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.Hint; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.SentryEvent; import io.sentry.SentryLevel; import io.sentry.exception.ExceptionMechanismException; @@ -29,15 +29,15 @@ public class SentryExceptionResolver implements HandlerExceptionResolver, Ordered { public static final String MECHANISM_TYPE = "Spring5ExceptionResolver"; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @NotNull TransactionNameProvider transactionNameProvider; private final int order; public SentryExceptionResolver( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull TransactionNameProvider transactionNameProvider, final int order) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); this.transactionNameProvider = Objects.requireNonNull(transactionNameProvider, "transactionNameProvider is required"); this.order = order; @@ -53,7 +53,7 @@ public SentryExceptionResolver( final SentryEvent event = createEvent(request, ex); final Hint hint = createHint(request, response); - hub.captureEvent(event, hint); + scopes.captureEvent(event, hint); // null = run other HandlerExceptionResolvers to actually handle the exception return null; diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryHubRegistrar.java b/sentry-spring/src/main/java/io/sentry/spring/SentryHubRegistrar.java index e597d175b6..195a88e277 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryHubRegistrar.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryHubRegistrar.java @@ -1,7 +1,7 @@ package io.sentry.spring; import com.jakewharton.nopen.annotation.Open; -import io.sentry.HubAdapter; +import io.sentry.ScopesAdapter; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryOptions; import io.sentry.protocol.SdkVersion; @@ -60,7 +60,7 @@ private void registerSentryOptions( private void registerSentryHubBean(final @NotNull BeanDefinitionRegistry registry) { final BeanDefinitionBuilder builder = - BeanDefinitionBuilder.genericBeanDefinition(HubAdapter.class); + BeanDefinitionBuilder.genericBeanDefinition(ScopesAdapter.class); builder.setInitMethodName("getInstance"); registry.registerBeanDefinition("sentryHub", builder.getBeanDefinition()); diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryInitBeanPostProcessor.java b/sentry-spring/src/main/java/io/sentry/spring/SentryInitBeanPostProcessor.java index 9e455dfb04..ca431aae14 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryInitBeanPostProcessor.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryInitBeanPostProcessor.java @@ -2,10 +2,10 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.EventProcessor; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ITransportFactory; import io.sentry.Integration; +import io.sentry.ScopesAdapter; import io.sentry.Sentry; import io.sentry.SentryOptions; import io.sentry.SentryOptions.TracesSamplerCallback; @@ -27,15 +27,15 @@ public class SentryInitBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware, DisposableBean { private @Nullable ApplicationContext applicationContext; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; public SentryInitBeanPostProcessor() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } - SentryInitBeanPostProcessor(final @NotNull IHub hub) { - Objects.requireNonNull(hub, "hub is required"); - this.hub = hub; + SentryInitBeanPostProcessor(final @NotNull IScopes scopes) { + Objects.requireNonNull(scopes, "scopes are required"); + this.scopes = scopes; } @Override @@ -86,6 +86,6 @@ public void setApplicationContext(final @NotNull ApplicationContext applicationC @Override public void destroy() { - hub.close(); + scopes.close(); } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryRequestResolver.java b/sentry-spring/src/main/java/io/sentry/spring/SentryRequestResolver.java index 442644fcac..2d9e2996f7 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryRequestResolver.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryRequestResolver.java @@ -1,7 +1,7 @@ package io.sentry.spring; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.SentryLevel; import io.sentry.protocol.Request; import io.sentry.util.HttpUtils; @@ -20,11 +20,11 @@ @Open public class SentryRequestResolver { - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private volatile @Nullable List extraSecurityCookies; - public SentryRequestResolver(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "options is required"); + public SentryRequestResolver(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); } // httpRequest.getRequestURL() returns StringBuffer which is considered an obsolete class. @@ -40,7 +40,7 @@ public SentryRequestResolver(final @NotNull IHub hub) { extractSecurityCookieNamesOrUseCached(httpRequest); sentryRequest.setHeaders(resolveHeadersMap(httpRequest, additionalSecurityCookieNames)); - if (hub.getOptions().isSendDefaultPii()) { + if (scopes.getOptions().isSendDefaultPii()) { String cookieName = HttpUtils.COOKIE_HEADER_NAME; final @Nullable List filteredHeaders = HttpUtils.filterOutSecurityCookiesFromHeader( @@ -57,7 +57,8 @@ Map resolveHeadersMap( final Map headersMap = new HashMap<>(); for (String headerName : Collections.list(request.getHeaderNames())) { // do not copy personal information identifiable headers - if (hub.getOptions().isSendDefaultPii() || !HttpUtils.containsSensitiveHeader(headerName)) { + if (scopes.getOptions().isSendDefaultPii() + || !HttpUtils.containsSensitiveHeader(headerName)) { final @Nullable List filteredHeaders = HttpUtils.filterOutSecurityCookiesFromHeader( request.getHeaders(headerName), headerName, additionalSecurityCookieNames); @@ -94,7 +95,8 @@ private List extractSecurityCookieNames(final @NotNull HttpServletReques } } } catch (Throwable t) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log(SentryLevel.WARNING, "Failed to extract session cookie name from request.", t); } diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java b/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java index 8fd8180941..7695545f04 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java @@ -8,8 +8,8 @@ import io.sentry.Breadcrumb; import io.sentry.EventProcessor; import io.sentry.Hint; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; +import io.sentry.ScopesAdapter; import io.sentry.SentryEvent; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -29,26 +29,26 @@ @Open public class SentrySpringFilter extends OncePerRequestFilter { - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @NotNull SentryRequestResolver requestResolver; private final @NotNull TransactionNameProvider transactionNameProvider; public SentrySpringFilter( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull SentryRequestResolver requestResolver, final @NotNull TransactionNameProvider transactionNameProvider) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); this.requestResolver = Objects.requireNonNull(requestResolver, "requestResolver is required"); this.transactionNameProvider = Objects.requireNonNull(transactionNameProvider, "transactionNameProvider is required"); } - public SentrySpringFilter(final @NotNull IHub hub) { - this(hub, new SentryRequestResolver(hub), new SpringMvcTransactionNameProvider()); + public SentrySpringFilter(final @NotNull IScopes scopes) { + this(scopes, new SentryRequestResolver(scopes), new SpringMvcTransactionNameProvider()); } public SentrySpringFilter() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } @Override @@ -57,20 +57,20 @@ protected void doFilterInternal( final @NotNull HttpServletResponse response, final @NotNull FilterChain filterChain) throws ServletException, IOException { - if (hub.isEnabled()) { + if (scopes.isEnabled()) { // request may qualify for caching request body, if so resolve cached request final HttpServletRequest request = resolveHttpServletRequest(servletRequest); - hub.pushScope(); + scopes.pushScope(); try { final Hint hint = new Hint(); hint.set(SPRING_REQUEST_FILTER_REQUEST, servletRequest); hint.set(SPRING_REQUEST_FILTER_RESPONSE, response); - hub.addBreadcrumb(Breadcrumb.http(request.getRequestURI(), request.getMethod()), hint); + scopes.addBreadcrumb(Breadcrumb.http(request.getRequestURI(), request.getMethod()), hint); configureScope(request); filterChain.doFilter(request, response); } finally { - hub.popScope(); + scopes.popScope(); } } else { filterChain.doFilter(servletRequest, response); @@ -79,7 +79,7 @@ protected void doFilterInternal( private void configureScope(HttpServletRequest request) { try { - hub.configureScope( + scopes.configureScope( scope -> { // set basic request information on the scope scope.setRequest(requestResolver.resolveSentryRequest(request)); @@ -92,11 +92,12 @@ private void configureScope(HttpServletRequest request) { // request processing if (request instanceof CachedBodyHttpServletRequest) { scope.addEventProcessor( - new RequestBodyExtractingEventProcessor(request, hub.getOptions())); + new RequestBodyExtractingEventProcessor(request, scopes.getOptions())); } }); } catch (Throwable e) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log(SentryLevel.ERROR, "Failed to set scope for HTTP request", e); } @@ -104,12 +105,13 @@ private void configureScope(HttpServletRequest request) { private @NotNull HttpServletRequest resolveHttpServletRequest( final @NotNull HttpServletRequest request) { - if (hub.getOptions().isSendDefaultPii() - && qualifiesForCaching(request, hub.getOptions().getMaxRequestBodySize())) { + if (scopes.getOptions().isSendDefaultPii() + && qualifiesForCaching(request, scopes.getOptions().getMaxRequestBodySize())) { try { return new CachedBodyHttpServletRequest(request); } catch (IOException e) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.WARNING, diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java b/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java index cb4a700696..88d205a57e 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java @@ -1,6 +1,6 @@ package io.sentry.spring; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.Sentry; import java.util.concurrent.Callable; import org.jetbrains.annotations.NotNull; @@ -9,21 +9,23 @@ /** * Sets a current hub on a thread running a {@link Runnable} given by parameter. Used to propagate - * the current {@link IHub} on the thread executing async task - like MVC controller methods + * the current {@link IScopes} on the thread executing async task - like MVC controller methods * returning a {@link Callable} or Spring beans methods annotated with {@link Async}. */ public final class SentryTaskDecorator implements TaskDecorator { @Override + @SuppressWarnings("deprecation") public @NotNull Runnable decorate(final @NotNull Runnable runnable) { - final IHub newHub = Sentry.getCurrentHub().clone(); + // TODO fork instead + final IScopes newHub = Sentry.getCurrentScopes().clone(); return () -> { - final IHub oldState = Sentry.getCurrentHub(); - Sentry.setCurrentHub(newHub); + final IScopes oldState = Sentry.getCurrentScopes(); + Sentry.setCurrentScopes(newHub); try { runnable.run(); } finally { - Sentry.setCurrentHub(oldState); + Sentry.setCurrentScopes(oldState); } }; } diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryUserFilter.java b/sentry-spring/src/main/java/io/sentry/spring/SentryUserFilter.java index 55c5826d40..e0b4e9c1ba 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryUserFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryUserFilter.java @@ -1,8 +1,8 @@ package io.sentry.spring; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IHub; import io.sentry.IScope; +import io.sentry.IScopes; import io.sentry.IpAddressUtils; import io.sentry.protocol.User; import io.sentry.util.Objects; @@ -26,12 +26,12 @@ */ @Open public class SentryUserFilter extends OncePerRequestFilter { - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @NotNull List sentryUserProviders; public SentryUserFilter( - final @NotNull IHub hub, final @NotNull List sentryUserProviders) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + final @NotNull IScopes scopes, final @NotNull List sentryUserProviders) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); this.sentryUserProviders = Objects.requireNonNull(sentryUserProviders, "sentryUserProviders list is required"); } @@ -46,13 +46,13 @@ protected void doFilterInternal( for (final SentryUserProvider provider : sentryUserProviders) { apply(user, provider.provideUser()); } - if (hub.getOptions().isSendDefaultPii()) { + if (scopes.getOptions().isSendDefaultPii()) { if (IpAddressUtils.isDefault(user.getIpAddress())) { // unset {{auto}} as it would set the server's ip address as a user ip address user.setIpAddress(null); } } - hub.setUser(user); + scopes.setUser(user); chain.doFilter(request, response); } diff --git a/sentry-spring/src/main/java/io/sentry/spring/checkin/SentryCheckInAdvice.java b/sentry-spring/src/main/java/io/sentry/spring/checkin/SentryCheckInAdvice.java index c29ad44900..edae74c2ac 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/checkin/SentryCheckInAdvice.java +++ b/sentry-spring/src/main/java/io/sentry/spring/checkin/SentryCheckInAdvice.java @@ -4,8 +4,8 @@ import io.sentry.CheckIn; import io.sentry.CheckInStatus; import io.sentry.DateUtils; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; +import io.sentry.ScopesAdapter; import io.sentry.SentryLevel; import io.sentry.protocol.SentryId; import io.sentry.util.Objects; @@ -30,16 +30,16 @@ @ApiStatus.Experimental @Open public class SentryCheckInAdvice implements MethodInterceptor, EmbeddedValueResolverAware { - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private @Nullable StringValueResolver resolver; public SentryCheckInAdvice() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } - public SentryCheckInAdvice(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + public SentryCheckInAdvice(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); } @Override @@ -69,7 +69,8 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl // Sentry should alert the user about missed checkins in this case since the monitor slug // won't match // what is configured in Sentry. - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -79,7 +80,8 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } if (ObjectUtils.isEmpty(monitorSlug)) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -87,8 +89,8 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl return invocation.proceed(); } - hub.pushScope(); - TracingUtils.startNewTrace(hub); + scopes.pushScope(); + TracingUtils.startNewTrace(scopes); @Nullable SentryId checkInId = null; final long startTime = System.currentTimeMillis(); @@ -96,7 +98,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl try { if (!isHeartbeatOnly) { - checkInId = hub.captureCheckIn(new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS)); + checkInId = scopes.captureCheckIn(new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS)); } return invocation.proceed(); } catch (Throwable e) { @@ -106,8 +108,8 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK; CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); - hub.captureCheckIn(checkIn); - hub.popScope(); + scopes.captureCheckIn(checkIn); + scopes.popScope(); } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/exception/SentryCaptureExceptionParameterAdvice.java b/sentry-spring/src/main/java/io/sentry/spring/exception/SentryCaptureExceptionParameterAdvice.java index bababbce77..119f39ba6c 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/exception/SentryCaptureExceptionParameterAdvice.java +++ b/sentry-spring/src/main/java/io/sentry/spring/exception/SentryCaptureExceptionParameterAdvice.java @@ -1,8 +1,8 @@ package io.sentry.spring.exception; import com.jakewharton.nopen.annotation.Open; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; +import io.sentry.ScopesAdapter; import io.sentry.exception.ExceptionMechanismException; import io.sentry.protocol.Mechanism; import io.sentry.util.Objects; @@ -22,14 +22,14 @@ @Open public class SentryCaptureExceptionParameterAdvice implements MethodInterceptor { private static final String MECHANISM_TYPE = "SentrySpring5CaptureExceptionParameterAdvice"; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; public SentryCaptureExceptionParameterAdvice() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } - public SentryCaptureExceptionParameterAdvice(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + public SentryCaptureExceptionParameterAdvice(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); } @Override @@ -58,6 +58,6 @@ private void captureException(final @NotNull Throwable throwable) { mechanism.setHandled(true); final Throwable mechanismException = new ExceptionMechanismException(mechanism, throwable, Thread.currentThread()); - hub.captureException(mechanismException); + scopes.captureException(mechanismException); } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/graphql/SentryBatchLoaderRegistry.java b/sentry-spring/src/main/java/io/sentry/spring/graphql/SentryBatchLoaderRegistry.java index 62a8669f89..f1d8717598 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/graphql/SentryBatchLoaderRegistry.java +++ b/sentry-spring/src/main/java/io/sentry/spring/graphql/SentryBatchLoaderRegistry.java @@ -1,11 +1,11 @@ package io.sentry.spring.graphql; -import static io.sentry.graphql.SentryInstrumentation.SENTRY_HUB_CONTEXT_KEY; +import static io.sentry.graphql.SentryInstrumentation.SENTRY_SCOPES_CONTEXT_KEY; import graphql.GraphQLContext; import io.sentry.Breadcrumb; -import io.sentry.IHub; -import io.sentry.NoOpHub; +import io.sentry.IScopes; +import io.sentry.NoOpScopes; import java.util.List; import java.util.Map; import java.util.Set; @@ -89,7 +89,7 @@ public BatchLoaderRegistry.RegistrationSpec withOptions(DataLoaderOptions public void registerBatchLoader(BiFunction, BatchLoaderEnvironment, Flux> loader) { delegate.registerBatchLoader( (keys, batchLoaderEnvironment) -> { - hubFromContext(batchLoaderEnvironment) + scopesFromContext(batchLoaderEnvironment) .addBreadcrumb(Breadcrumb.graphqlDataLoader(keys, keyType, valueType, name)); return loader.apply(keys, batchLoaderEnvironment); }); @@ -100,20 +100,20 @@ public void registerMappedBatchLoader( BiFunction, BatchLoaderEnvironment, Mono>> loader) { delegate.registerMappedBatchLoader( (keys, batchLoaderEnvironment) -> { - hubFromContext(batchLoaderEnvironment) + scopesFromContext(batchLoaderEnvironment) .addBreadcrumb(Breadcrumb.graphqlDataLoader(keys, keyType, valueType, name)); return loader.apply(keys, batchLoaderEnvironment); }); } - private @NotNull IHub hubFromContext(final @NotNull BatchLoaderEnvironment environment) { + private @NotNull IScopes scopesFromContext(final @NotNull BatchLoaderEnvironment environment) { Object context = environment.getContext(); if (context instanceof GraphQLContext) { GraphQLContext graphqlContext = (GraphQLContext) context; - return graphqlContext.getOrDefault(SENTRY_HUB_CONTEXT_KEY, NoOpHub.getInstance()); + return graphqlContext.getOrDefault(SENTRY_SCOPES_CONTEXT_KEY, NoOpScopes.getInstance()); } - return NoOpHub.getInstance(); + return NoOpScopes.getInstance(); } } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/graphql/SentryDgsSubscriptionHandler.java b/sentry-spring/src/main/java/io/sentry/spring/graphql/SentryDgsSubscriptionHandler.java index fb4e09e889..eef5fdae69 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/graphql/SentryDgsSubscriptionHandler.java +++ b/sentry-spring/src/main/java/io/sentry/spring/graphql/SentryDgsSubscriptionHandler.java @@ -1,7 +1,7 @@ package io.sentry.spring.graphql; import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.graphql.ExceptionReporter; import io.sentry.graphql.SentrySubscriptionHandler; @@ -17,7 +17,7 @@ public SentryDgsSubscriptionHandler() { @Override public @NotNull Object onSubscriptionResult( final @NotNull Object result, - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull ExceptionReporter exceptionReporter, final @NotNull InstrumentationFieldFetchParameters parameters) { if (result instanceof Flux) { @@ -25,7 +25,7 @@ public SentryDgsSubscriptionHandler() { return flux.doOnError( throwable -> { final @NotNull ExceptionReporter.ExceptionDetails exceptionDetails = - new ExceptionReporter.ExceptionDetails(hub, parameters.getEnvironment(), true); + new ExceptionReporter.ExceptionDetails(scopes, parameters.getEnvironment(), true); exceptionReporter.captureThrowable(throwable, exceptionDetails, null); }); } diff --git a/sentry-spring/src/main/java/io/sentry/spring/graphql/SentrySpringSubscriptionHandler.java b/sentry-spring/src/main/java/io/sentry/spring/graphql/SentrySpringSubscriptionHandler.java index a7809eb230..b3f0b9830e 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/graphql/SentrySpringSubscriptionHandler.java +++ b/sentry-spring/src/main/java/io/sentry/spring/graphql/SentrySpringSubscriptionHandler.java @@ -1,7 +1,7 @@ package io.sentry.spring.graphql; import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.graphql.ExceptionReporter; import io.sentry.graphql.SentrySubscriptionHandler; import org.jetbrains.annotations.NotNull; @@ -13,7 +13,7 @@ public final class SentrySpringSubscriptionHandler implements SentrySubscription @Override public @NotNull Object onSubscriptionResult( final @NotNull Object result, - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull ExceptionReporter exceptionReporter, final @NotNull InstrumentationFieldFetchParameters parameters) { if (result instanceof Flux) { @@ -21,7 +21,7 @@ public final class SentrySpringSubscriptionHandler implements SentrySubscription return flux.doOnError( throwable -> { final @NotNull ExceptionReporter.ExceptionDetails exceptionDetails = - new ExceptionReporter.ExceptionDetails(hub, parameters.getEnvironment(), true); + new ExceptionReporter.ExceptionDetails(scopes, parameters.getEnvironment(), true); if (throwable instanceof SubscriptionPublisherException && throwable.getCause() != null) { exceptionReporter.captureThrowable(throwable.getCause(), exceptionDetails, null); diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanAdvice.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanAdvice.java index 96c4275836..c1b7305d4f 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanAdvice.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanAdvice.java @@ -1,9 +1,9 @@ package io.sentry.spring.tracing; import com.jakewharton.nopen.annotation.Open; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ISpan; +import io.sentry.ScopesAdapter; import io.sentry.SpanStatus; import io.sentry.util.Objects; import java.lang.reflect.Method; @@ -22,20 +22,20 @@ @Open public class SentrySpanAdvice implements MethodInterceptor { private static final String TRACE_ORIGIN = "auto.function.spring.advice"; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; public SentrySpanAdvice() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } - public SentrySpanAdvice(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + public SentrySpanAdvice(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); } @SuppressWarnings("deprecation") @Override public Object invoke(final @NotNull MethodInvocation invocation) throws Throwable { - final ISpan activeSpan = hub.getSpan(); + final ISpan activeSpan = scopes.getSpan(); if (activeSpan == null || activeSpan.isNoOp()) { // there is no active transaction, we do not start new span diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java index 4067c69c8c..bdf8417642 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java @@ -8,7 +8,7 @@ import io.sentry.BaggageHeader; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.SpanDataConvention; import io.sentry.SpanStatus; @@ -27,10 +27,10 @@ @Open public class SentrySpanClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { private static final String TRACE_ORIGIN = "auto.http.spring.resttemplate"; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; - public SentrySpanClientHttpRequestInterceptor(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + public SentrySpanClientHttpRequestInterceptor(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); } @Override @@ -42,7 +42,7 @@ public SentrySpanClientHttpRequestInterceptor(final @NotNull IHub hub) { Integer responseStatusCode = null; ClientHttpResponse response = null; try { - final ISpan activeSpan = hub.getSpan(); + final ISpan activeSpan = scopes.getSpan(); if (activeSpan == null) { maybeAddTracingHeaders(request, null); return execution.execute(request, body); @@ -83,7 +83,7 @@ private void maybeAddTracingHeaders( final @NotNull HttpRequest request, final @Nullable ISpan span) { final @Nullable TracingUtils.TracingHeaders tracingHeaders = TracingUtils.traceIfAllowed( - hub, + scopes, request.getURI().toString(), request.getHeaders().get(BaggageHeader.BAGGAGE_HEADER), span); @@ -120,6 +120,6 @@ private void addBreadcrumb( hint.set(SPRING_REQUEST_INTERCEPTOR_RESPONSE, response); } - hub.addBreadcrumb(breadcrumb, hint); + scopes.addBreadcrumb(breadcrumb, hint); } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java index 00ad91bbb0..c526cb64cc 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java @@ -7,7 +7,7 @@ import io.sentry.BaggageHeader; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.SpanDataConvention; import io.sentry.SpanStatus; @@ -26,16 +26,16 @@ @Open public class SentrySpanClientWebRequestFilter implements ExchangeFilterFunction { private static final String TRACE_ORIGIN = "auto.http.spring.webclient"; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; - public SentrySpanClientWebRequestFilter(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + public SentrySpanClientWebRequestFilter(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); } @Override public @NotNull Mono filter( final @NotNull ClientRequest request, final @NotNull ExchangeFunction next) { - final ISpan activeSpan = hub.getSpan(); + final ISpan activeSpan = scopes.getSpan(); if (activeSpan == null) { addBreadcrumb(request, null); return next.exchange(maybeAddHeaders(request, null)); @@ -76,7 +76,7 @@ private ClientRequest maybeAddHeaders( final @Nullable TracingUtils.TracingHeaders tracingHeaders = TracingUtils.traceIfAllowed( - hub, + scopes, request.url().toString(), request.headers().get(BaggageHeader.BAGGAGE_HEADER), span); @@ -113,6 +113,6 @@ private void addBreadcrumb( hint.set(SPRING_EXCHANGE_FILTER_RESPONSE, response); } - hub.addBreadcrumb(breadcrumb, hint); + scopes.addBreadcrumb(breadcrumb, hint); } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java index 5af329f600..50cdb8dc3a 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java @@ -3,9 +3,9 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.BaggageHeader; import io.sentry.CustomSamplingContext; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ITransaction; +import io.sentry.ScopesAdapter; import io.sentry.SentryTraceHeader; import io.sentry.SpanStatus; import io.sentry.TransactionContext; @@ -35,7 +35,7 @@ public class SentryTracingFilter extends OncePerRequestFilter { private static final String TRACE_ORIGIN = "auto.http.spring.webmvc"; private final @NotNull TransactionNameProvider transactionNameProvider; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; /** * Creates filter that resolves transaction name using {@link SpringMvcTransactionNameProvider}. @@ -46,25 +46,26 @@ public class SentryTracingFilter extends OncePerRequestFilter { * javax.servlet.Filter}. */ public SentryTracingFilter() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } /** * Creates filter that resolves transaction name using transaction name provider given by * parameter. * - * @param hub - the hub + * @param scopes - the scopes * @param transactionNameProvider - transaction name provider. */ public SentryTracingFilter( - final @NotNull IHub hub, final @NotNull TransactionNameProvider transactionNameProvider) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + final @NotNull IScopes scopes, + final @NotNull TransactionNameProvider transactionNameProvider) { + this.scopes = Objects.requireNonNull(scopes, "scopes is required"); this.transactionNameProvider = Objects.requireNonNull(transactionNameProvider, "transactionNameProvider is required"); } - public SentryTracingFilter(final @NotNull IHub hub) { - this(hub, new SpringMvcTransactionNameProvider()); + public SentryTracingFilter(final @NotNull IScopes scopes) { + this(scopes, new SpringMvcTransactionNameProvider()); } @Override @@ -74,15 +75,15 @@ protected void doFilterInternal( final @NotNull FilterChain filterChain) throws ServletException, IOException { - if (hub.isEnabled()) { + if (scopes.isEnabled()) { final @Nullable String sentryTraceHeader = httpRequest.getHeader(SentryTraceHeader.SENTRY_TRACE_HEADER); final @Nullable List baggageHeader = Collections.list(httpRequest.getHeaders(BaggageHeader.BAGGAGE_HEADER)); final @Nullable TransactionContext transactionContext = - hub.continueTrace(sentryTraceHeader, baggageHeader); + scopes.continueTrace(sentryTraceHeader, baggageHeader); - if (hub.getOptions().isTracingEnabled() && shouldTraceRequest(httpRequest)) { + if (scopes.getOptions().isTracingEnabled() && shouldTraceRequest(httpRequest)) { doFilterWithTransaction(httpRequest, httpResponse, filterChain, transactionContext); } else { filterChain.doFilter(httpRequest, httpResponse); @@ -129,7 +130,7 @@ private void doFilterWithTransaction( } private boolean shouldTraceRequest(final @NotNull HttpServletRequest request) { - return hub.getOptions().isTraceOptionsRequests() + return scopes.getOptions().isTraceOptionsRequests() || !HttpMethod.OPTIONS.name().equals(request.getMethod()); } @@ -151,14 +152,14 @@ private ITransaction startTransaction( transactionOptions.setCustomSamplingContext(customSamplingContext); transactionOptions.setBindToScope(true); - return hub.startTransaction(transactionContext, transactionOptions); + return scopes.startTransaction(transactionContext, transactionOptions); } final TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setCustomSamplingContext(customSamplingContext); transactionOptions.setBindToScope(true); - return hub.startTransaction( + return scopes.startTransaction( new TransactionContext(name, TransactionNameSource.URL, "http.server"), transactionOptions); } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java index c417f14a14..8f4f5bbdfc 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java @@ -1,9 +1,9 @@ package io.sentry.spring.tracing; import com.jakewharton.nopen.annotation.Open; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ITransaction; +import io.sentry.ScopesAdapter; import io.sentry.SpanStatus; import io.sentry.TransactionContext; import io.sentry.TransactionOptions; @@ -27,14 +27,14 @@ @Open public class SentryTransactionAdvice implements MethodInterceptor { private static final String TRACE_ORIGIN = "auto.function.spring.advice"; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; public SentryTransactionAdvice() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } - public SentryTransactionAdvice(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + public SentryTransactionAdvice(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); } @SuppressWarnings("deprecation") @@ -67,11 +67,11 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } else { operation = "bean"; } - hub.pushScope(); + scopes.pushScope(); final TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setBindToScope(true); final ITransaction transaction = - hub.startTransaction( + scopes.startTransaction( new TransactionContext(nameAndSource.name, nameAndSource.source, operation), transactionOptions); transaction.getSpanContext().setOrigin(TRACE_ORIGIN); @@ -85,7 +85,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl throw e; } finally { transaction.finish(); - hub.popScope(); + scopes.popScope(); } } } @@ -105,7 +105,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } private boolean isTransactionActive() { - return hub.getSpan() != null; + return scopes.getSpan() != null; } private static class TransactionNameAndSource { diff --git a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryRequestResolver.java b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryRequestResolver.java index a710438c46..76e50985e5 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryRequestResolver.java +++ b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryRequestResolver.java @@ -1,7 +1,7 @@ package io.sentry.spring.webflux; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.protocol.Request; import io.sentry.util.HttpUtils; import io.sentry.util.Objects; @@ -20,10 +20,10 @@ @Open @ApiStatus.Experimental public class SentryRequestResolver { - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; - public SentryRequestResolver(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "options is required"); + public SentryRequestResolver(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); } public @NotNull Request resolveSentryRequest(final @NotNull ServerHttpRequest httpRequest) { @@ -36,7 +36,7 @@ public SentryRequestResolver(final @NotNull IHub hub) { urlDetails.applyToRequest(sentryRequest); sentryRequest.setHeaders(resolveHeadersMap(httpRequest.getHeaders())); - if (hub.getOptions().isSendDefaultPii()) { + if (scopes.getOptions().isSendDefaultPii()) { String headerName = HttpUtils.COOKIE_HEADER_NAME; sentryRequest.setCookies( toString( @@ -52,7 +52,8 @@ Map resolveHeadersMap(final HttpHeaders request) { for (Map.Entry> entry : request.entrySet()) { // do not copy personal information identifiable headers String headerName = entry.getKey(); - if (hub.getOptions().isSendDefaultPii() || !HttpUtils.containsSensitiveHeader(headerName)) { + if (scopes.getOptions().isSendDefaultPii() + || !HttpUtils.containsSensitiveHeader(headerName)) { headersMap.put( headerName, toString( diff --git a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryScheduleHook.java b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryScheduleHook.java index 7775d4e482..20f494168d 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryScheduleHook.java +++ b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryScheduleHook.java @@ -1,6 +1,6 @@ package io.sentry.spring.webflux; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.Sentry; import java.util.function.Function; import org.jetbrains.annotations.ApiStatus; @@ -13,16 +13,18 @@ @ApiStatus.Experimental public final class SentryScheduleHook implements Function { @Override + @SuppressWarnings("deprecation") public Runnable apply(final @NotNull Runnable runnable) { - final IHub newHub = Sentry.getCurrentHub().clone(); + // TODO fork instead + final IScopes newHub = Sentry.getCurrentScopes().clone(); return () -> { - final IHub oldState = Sentry.getCurrentHub(); - Sentry.setCurrentHub(newHub); + final IScopes oldState = Sentry.getCurrentScopes(); + Sentry.setCurrentScopes(newHub); try { runnable.run(); } finally { - Sentry.setCurrentHub(oldState); + Sentry.setCurrentScopes(oldState); } }; } diff --git a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebExceptionHandler.java b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebExceptionHandler.java index 98d3855a04..042d1d48ec 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebExceptionHandler.java +++ b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebExceptionHandler.java @@ -5,7 +5,7 @@ import static io.sentry.TypeCheckHint.WEBFLUX_EXCEPTION_HANDLER_RESPONSE; import io.sentry.Hint; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.SentryEvent; import io.sentry.SentryLevel; import io.sentry.exception.ExceptionMechanismException; @@ -26,10 +26,10 @@ @ApiStatus.Experimental public final class SentryWebExceptionHandler implements WebExceptionHandler { public static final String MECHANISM_TYPE = "Spring5WebFluxExceptionResolver"; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; - public SentryWebExceptionHandler(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + public SentryWebExceptionHandler(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); } @Override @@ -50,7 +50,7 @@ public SentryWebExceptionHandler(final @NotNull IHub hub) { hint.set(WEBFLUX_EXCEPTION_HANDLER_RESPONSE, serverWebExchange.getResponse()); hint.set(WEBFLUX_EXCEPTION_HANDLER_EXCHANGE, serverWebExchange); - hub.captureEvent(event, hint); + scopes.captureEvent(event, hint); } return Mono.error(ex); } diff --git a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java index f68a87ae0f..3bb7de5ae4 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java @@ -7,10 +7,10 @@ import io.sentry.Breadcrumb; import io.sentry.CustomSamplingContext; import io.sentry.Hint; -import io.sentry.IHub; import io.sentry.IScope; +import io.sentry.IScopes; import io.sentry.ITransaction; -import io.sentry.NoOpHub; +import io.sentry.NoOpScopes; import io.sentry.Sentry; import io.sentry.SentryTraceHeader; import io.sentry.SpanStatus; @@ -34,22 +34,24 @@ /** Manages {@link IScope} in Webflux request processing. */ @ApiStatus.Experimental public final class SentryWebFilter implements WebFilter { - public static final String SENTRY_HUB_KEY = "sentry-hub"; + public static final String SENTRY_SCOPES_KEY = "sentry-scopes"; + @Deprecated public static final String SENTRY_HUB_KEY = SENTRY_SCOPES_KEY; private static final String TRANSACTION_OP = "http.server"; private static final String TRACE_ORIGIN = "auto.spring.webflux"; private final @NotNull SentryRequestResolver sentryRequestResolver; - public SentryWebFilter(final @NotNull IHub hub) { - Objects.requireNonNull(hub, "hub is required"); - this.sentryRequestResolver = new SentryRequestResolver(hub); + public SentryWebFilter(final @NotNull IScopes scopes) { + Objects.requireNonNull(scopes, "scopes are required"); + this.sentryRequestResolver = new SentryRequestResolver(scopes); } @Override public Mono filter( final @NotNull ServerWebExchange serverWebExchange, final @NotNull WebFilterChain webFilterChain) { - @NotNull IHub requestHub = Sentry.cloneMainHub(); + @NotNull IScopes requestHub = Sentry.cloneMainHub(); + // TODO do not push / pop, use fork instead if (!requestHub.isEnabled()) { return webFilterChain.filter(serverWebExchange); } @@ -80,7 +82,8 @@ isTracingEnabled && shouldTraceRequest(requestHub, request) finishTransaction(serverWebExchange, transaction); } requestHub.popScope(); - Sentry.setCurrentHub(NoOpHub.getInstance()); + // TODO token based cleanup instead? + Sentry.setCurrentScopes(NoOpScopes.getInstance()); }) .doOnError( e -> { @@ -91,8 +94,8 @@ isTracingEnabled && shouldTraceRequest(requestHub, request) }) .doFirst( () -> { - serverWebExchange.getAttributes().put(SENTRY_HUB_KEY, requestHub); - Sentry.setCurrentHub(requestHub); + serverWebExchange.getAttributes().put(SENTRY_SCOPES_KEY, requestHub); + Sentry.setCurrentScopes(requestHub); requestHub.pushScope(); final ServerHttpResponse response = serverWebExchange.getResponse(); @@ -109,13 +112,13 @@ isTracingEnabled && shouldTraceRequest(requestHub, request) } private boolean shouldTraceRequest( - final @NotNull IHub hub, final @NotNull ServerHttpRequest request) { - return hub.getOptions().isTraceOptionsRequests() + final @NotNull IScopes scopes, final @NotNull ServerHttpRequest request) { + return scopes.getOptions().isTraceOptionsRequests() || !HttpMethod.OPTIONS.equals(request.getMethod()); } private @NotNull ITransaction startTransaction( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull ServerHttpRequest request, final @Nullable TransactionContext transactionContext) { final @NotNull String name = request.getMethod() + " " + request.getURI().getPath(); @@ -131,10 +134,10 @@ private boolean shouldTraceRequest( transactionContext.setTransactionNameSource(TransactionNameSource.URL); transactionContext.setOperation(TRANSACTION_OP); - return hub.startTransaction(transactionContext, transactionOptions); + return scopes.startTransaction(transactionContext, transactionOptions); } - return hub.startTransaction( + return scopes.startTransaction( new TransactionContext(name, TransactionNameSource.URL, TRANSACTION_OP), transactionOptions); } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/EnableSentryTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/EnableSentryTest.kt index 5a8fec3053..5182309416 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/EnableSentryTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/EnableSentryTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring import io.sentry.EventProcessor -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ITransportFactory import io.sentry.Integration import io.sentry.Sentry @@ -65,9 +65,9 @@ class EnableSentryTest { } @Test - fun `creates Sentry Hub`() { + fun `creates Sentry Scopes`() { contextRunner.run { - assertThat(it).hasSingleBean(IHub::class.java) + assertThat(it).hasSingleBean(IScopes::class.java) } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryCheckInAdviceTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryCheckInAdviceTest.kt index 4f94fd7ce0..23807e1fd9 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryCheckInAdviceTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryCheckInAdviceTest.kt @@ -2,7 +2,7 @@ package io.sentry.spring import io.sentry.CheckIn import io.sentry.CheckInStatus -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.protocol.SentryId @@ -55,19 +55,19 @@ class SentryCheckInAdviceTest { lateinit var sampleServiceSpringProperties: SampleServiceSpringProperties @Autowired - lateinit var hub: IHub + lateinit var scopes: IScopes @BeforeTest fun setup() { - reset(hub) - whenever(hub.options).thenReturn(SentryOptions()) + reset(scopes) + whenever(scopes.options).thenReturn(SentryOptions()) } @Test fun `when method is annotated with @SentryCheckIn, every method call creates two check-ins`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleService.hello() assertEquals(1, result) assertEquals(2, checkInCaptor.allValues.size) @@ -80,17 +80,17 @@ class SentryCheckInAdviceTest { assertEquals("monitor_slug_1", doneCheckIn.monitorSlug) assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) - val order = inOrder(hub) - order.verify(hub).pushScope() - order.verify(hub, times(2)).captureCheckIn(any()) - order.verify(hub).popScope() + val order = inOrder(scopes) + order.verify(scopes).pushScope() + order.verify(scopes, times(2)).captureCheckIn(any()) + order.verify(scopes).popScope() } @Test fun `when method is annotated with @SentryCheckIn, every method call creates two check-ins error`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) assertThrows { sampleService.oops() } @@ -104,17 +104,17 @@ class SentryCheckInAdviceTest { assertEquals("monitor_slug_1e", doneCheckIn.monitorSlug) assertEquals(CheckInStatus.ERROR.apiName(), doneCheckIn.status) - val order = inOrder(hub) - order.verify(hub).pushScope() - order.verify(hub, times(2)).captureCheckIn(any()) - order.verify(hub).popScope() + val order = inOrder(scopes) + order.verify(scopes).pushScope() + order.verify(scopes, times(2)).captureCheckIn(any()) + order.verify(scopes).popScope() } @Test fun `when method is annotated with @SentryCheckIn and heartbeat only, every method call creates only one check-in at the end`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceHeartbeat.hello() assertEquals(1, result) assertEquals(1, checkInCaptor.allValues.size) @@ -124,17 +124,17 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(hub) - order.verify(hub).pushScope() - order.verify(hub).captureCheckIn(any()) - order.verify(hub).popScope() + val order = inOrder(scopes) + order.verify(scopes).pushScope() + order.verify(scopes).captureCheckIn(any()) + order.verify(scopes).popScope() } @Test fun `when method is annotated with @SentryCheckIn and heartbeat only, every method call creates only one check-in at the end with error`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) assertThrows { sampleServiceHeartbeat.oops() } @@ -145,31 +145,31 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.ERROR.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(hub) - order.verify(hub).pushScope() - order.verify(hub).captureCheckIn(any()) - order.verify(hub).popScope() + val order = inOrder(scopes) + order.verify(scopes).pushScope() + order.verify(scopes).captureCheckIn(any()) + order.verify(scopes).popScope() } @Test fun `when method is annotated with @SentryCheckIn but slug is missing, does not create check-in`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceNoSlug.hello() assertEquals(1, result) assertEquals(0, checkInCaptor.allValues.size) - verify(hub, never()).pushScope() - verify(hub, never()).captureCheckIn(any()) - verify(hub, never()).popScope() + verify(scopes, never()).pushScope() + verify(scopes, never()).captureCheckIn(any()) + verify(scopes, never()).popScope() } @Test fun `when @SentryCheckIn is passed a spring property it is resolved correctly`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceSpringProperties.hello() assertEquals(1, result) assertEquals(1, checkInCaptor.allValues.size) @@ -179,17 +179,17 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(hub) - order.verify(hub).pushScope() - order.verify(hub).captureCheckIn(any()) - order.verify(hub).popScope() + val order = inOrder(scopes) + order.verify(scopes).pushScope() + order.verify(scopes).captureCheckIn(any()) + order.verify(scopes).popScope() } @Test fun `when @SentryCheckIn is passed a spring property that does not exist, raw value is used`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceSpringProperties.helloUnresolvedProperty() assertEquals(1, result) assertEquals(1, checkInCaptor.allValues.size) @@ -199,17 +199,17 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(hub) - order.verify(hub).pushScope() - order.verify(hub).captureCheckIn(any()) - order.verify(hub).popScope() + val order = inOrder(scopes) + order.verify(scopes).pushScope() + order.verify(scopes).captureCheckIn(any()) + order.verify(scopes).popScope() } @Test fun `when @SentryCheckIn is passed a spring property that causes an exception, raw value is used`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceSpringProperties.helloExceptionProperty() assertEquals(1, result) assertEquals(1, checkInCaptor.allValues.size) @@ -219,10 +219,10 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(hub) - order.verify(hub).pushScope() - order.verify(hub).captureCheckIn(any()) - order.verify(hub).popScope() + val order = inOrder(scopes) + order.verify(scopes).pushScope() + order.verify(scopes).captureCheckIn(any()) + order.verify(scopes).popScope() } @Configuration @@ -243,10 +243,10 @@ class SentryCheckInAdviceTest { open fun sampleServiceSpringProperties() = SampleServiceSpringProperties() @Bean - open fun hub(): IHub { - val hub = mock() - Sentry.setCurrentHub(hub) - return hub + open fun scopes(): IScopes { + val scopes = mock() + Sentry.setCurrentScopes(scopes) + return scopes } companion object { diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryExceptionResolverTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryExceptionResolverTest.kt index f3e4c32fb0..048166107b 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryExceptionResolverTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryExceptionResolverTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryEvent import io.sentry.SentryLevel import io.sentry.exception.ExceptionMechanismException @@ -17,7 +17,7 @@ import javax.servlet.http.HttpServletResponse import kotlin.test.Test class SentryExceptionResolverTest { - private val hub = mock() + private val scopes = mock() private val transactionNameProvider = mock() private val request = mock() @@ -26,10 +26,10 @@ class SentryExceptionResolverTest { @Test fun `when handles exception, sets wrapped exception for event`() { val eventCaptor = argumentCaptor() - whenever(hub.captureEvent(eventCaptor.capture(), any())).thenReturn(null) + whenever(scopes.captureEvent(eventCaptor.capture(), any())).thenReturn(null) val expectedCause = RuntimeException("test") - SentryExceptionResolver(hub, transactionNameProvider, 1) + SentryExceptionResolver(scopes, transactionNameProvider, 1) .resolveException(request, response, null, expectedCause) assertThat(eventCaptor.firstValue.throwable).isEqualTo(expectedCause) @@ -46,9 +46,9 @@ class SentryExceptionResolverTest { @Test fun `when handles exception, sets fatal level for event`() { val eventCaptor = argumentCaptor() - whenever(hub.captureEvent(eventCaptor.capture(), any())).thenReturn(null) + whenever(scopes.captureEvent(eventCaptor.capture(), any())).thenReturn(null) - SentryExceptionResolver(hub, transactionNameProvider, 1) + SentryExceptionResolver(scopes, transactionNameProvider, 1) .resolveException(request, response, null, RuntimeException("test")) assertThat(eventCaptor.firstValue.level).isEqualTo(SentryLevel.FATAL) @@ -59,9 +59,9 @@ class SentryExceptionResolverTest { val expectedTransactionName = "test-transaction" whenever(transactionNameProvider.provideTransactionName(any())).thenReturn(expectedTransactionName) val eventCaptor = argumentCaptor() - whenever(hub.captureEvent(eventCaptor.capture(), any())).thenReturn(null) + whenever(scopes.captureEvent(eventCaptor.capture(), any())).thenReturn(null) - SentryExceptionResolver(hub, transactionNameProvider, 1) + SentryExceptionResolver(scopes, transactionNameProvider, 1) .resolveException(request, response, null, RuntimeException("test")) assertThat(eventCaptor.firstValue.transaction).isEqualTo(expectedTransactionName) @@ -71,9 +71,9 @@ class SentryExceptionResolverTest { @Test fun `when handles exception, provides spring resolver hint`() { val hintCaptor = argumentCaptor() - whenever(hub.captureEvent(any(), hintCaptor.capture())).thenReturn(null) + whenever(scopes.captureEvent(any(), hintCaptor.capture())).thenReturn(null) - SentryExceptionResolver(hub, transactionNameProvider, 1) + SentryExceptionResolver(scopes, transactionNameProvider, 1) .resolveException(request, response, null, RuntimeException("test")) with(hintCaptor.firstValue) { @@ -86,8 +86,8 @@ class SentryExceptionResolverTest { fun `when custom create event method provided, uses it to capture event`() { val expectedEvent = SentryEvent() val eventCaptor = argumentCaptor() - whenever(hub.captureEvent(eventCaptor.capture(), any())).thenReturn(null) - val resolver = object : SentryExceptionResolver(hub, transactionNameProvider, 1) { + whenever(scopes.captureEvent(eventCaptor.capture(), any())).thenReturn(null) + val resolver = object : SentryExceptionResolver(scopes, transactionNameProvider, 1) { override fun createEvent(request: HttpServletRequest, ex: Exception) = expectedEvent } @@ -100,8 +100,8 @@ class SentryExceptionResolverTest { fun `when custom create hint method provided, uses it to capture event`() { val expectedHint = Hint() val hintCaptor = argumentCaptor() - whenever(hub.captureEvent(any(), hintCaptor.capture())).thenReturn(null) - val resolver = object : SentryExceptionResolver(hub, transactionNameProvider, 1) { + whenever(scopes.captureEvent(any(), hintCaptor.capture())).thenReturn(null) + val resolver = object : SentryExceptionResolver(scopes, transactionNameProvider, 1) { override fun createHint(request: HttpServletRequest, response: HttpServletResponse) = expectedHint } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryInitBeanPostProcessorTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryInitBeanPostProcessorTest.kt index 78ebf961c9..dc4a14e656 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryInitBeanPostProcessorTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryInitBeanPostProcessorTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring -import io.sentry.IHub +import io.sentry.IScopes import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.springframework.context.annotation.AnnotationConfigApplicationContext @@ -13,18 +13,18 @@ class SentryInitBeanPostProcessorTest { @Test fun closesSentryOnApplicationContextDestroy() { val ctx = AnnotationConfigApplicationContext(TestConfig::class.java) - val hub = ctx.getBean(IHub::class.java) + val scopes = ctx.getBean(IScopes::class.java) ctx.close() - verify(hub).close() + verify(scopes).close() } @Configuration open class TestConfig { @Bean(destroyMethod = "") - open fun hub() = mock() + open fun scopes() = mock() @Bean - open fun sentryInitBeanPostProcessor() = SentryInitBeanPostProcessor(hub()) + open fun sentryInitBeanPostProcessor() = SentryInitBeanPostProcessor(scopes()) } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryRequestHttpServletRequestProcessorTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryRequestHttpServletRequestProcessorTest.kt index 3fe9b60743..c7277a2240 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryRequestHttpServletRequestProcessorTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryRequestHttpServletRequestProcessorTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryEvent import io.sentry.SentryOptions import io.sentry.spring.tracing.SpringMvcTransactionNameProvider @@ -19,10 +19,10 @@ import kotlin.test.assertNotNull class SentryRequestHttpServletRequestProcessorTest { private class Fixture { - val hub = mock() + val scopes = mock() fun getSut(request: HttpServletRequest, options: SentryOptions = SentryOptions()): SentryRequestHttpServletRequestProcessor { - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) return SentryRequestHttpServletRequestProcessor(SpringMvcTransactionNameProvider(), request) } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringFilterTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringFilterTest.kt index ce83c4b9b7..c6ac952531 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringFilterTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringFilterTest.kt @@ -1,8 +1,8 @@ package io.sentry.spring import io.sentry.Breadcrumb -import io.sentry.IHub import io.sentry.IScope +import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -37,7 +37,7 @@ import kotlin.test.fail class SentrySpringFilterTest { private class Fixture { - val hub = mock() + val scopes = mock() val response = MockHttpServletResponse() val chain = mock() lateinit var scope: IScope @@ -45,15 +45,15 @@ class SentrySpringFilterTest { fun getSut(request: HttpServletRequest? = null, options: SentryOptions = SentryOptions()): SentrySpringFilter { scope = Scope(options) - whenever(hub.options).thenReturn(options) - whenever(hub.isEnabled).thenReturn(true) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) + whenever(scopes.options).thenReturn(options) + whenever(scopes.isEnabled).thenReturn(true) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) this.request = request ?: MockHttpServletRequest().apply { this.requestURI = "http://localhost:8080/some-uri" this.method = "post" } - return SentrySpringFilter(hub) + return SentrySpringFilter(scopes) } } @@ -64,7 +64,7 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).pushScope() + verify(fixture.scopes).pushScope() } @Test @@ -72,7 +72,7 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { it: Breadcrumb -> Assertions.assertThat(it.getData("url")).isEqualTo("http://localhost:8080/some-uri") Assertions.assertThat(it.getData("method")).isEqualTo("POST") @@ -87,7 +87,7 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).popScope() + verify(fixture.scopes).popScope() } @Test @@ -99,7 +99,7 @@ class SentrySpringFilterTest { listener.doFilter(fixture.request, fixture.response, fixture.chain) fail() } catch (e: Exception) { - verify(fixture.hub).popScope() + verify(fixture.scopes).popScope() } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryTaskDecoratorTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryTaskDecoratorTest.kt index e202deca86..3f34ab9d9d 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryTaskDecoratorTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryTaskDecoratorTest.kt @@ -32,28 +32,28 @@ class SentryTaskDecoratorTest { val sut = SentryTaskDecorator() - val mainHub = Sentry.getCurrentHub() - val threadedHub = Sentry.getCurrentHub().clone() + val mainHub = Sentry.getCurrentScopes() + val threadedHub = Sentry.getCurrentScopes().clone() executor.submit { - Sentry.setCurrentHub(threadedHub) + Sentry.setCurrentScopes(threadedHub) }.get() - assertEquals(mainHub, Sentry.getCurrentHub()) + assertEquals(mainHub, Sentry.getCurrentScopes()) val callableFuture = executor.submit( sut.decorate { - assertNotEquals(mainHub, Sentry.getCurrentHub()) - assertNotEquals(threadedHub, Sentry.getCurrentHub()) + assertNotEquals(mainHub, Sentry.getCurrentScopes()) + assertNotEquals(threadedHub, Sentry.getCurrentScopes()) } ) callableFuture.get() executor.submit { - assertNotEquals(mainHub, Sentry.getCurrentHub()) - assertEquals(threadedHub, Sentry.getCurrentHub()) + assertNotEquals(mainHub, Sentry.getCurrentScopes()) + assertEquals(threadedHub, Sentry.getCurrentScopes()) }.get() } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryUserFilterTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryUserFilterTest.kt index 2dac9a57ed..7f9be1ba8b 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryUserFilterTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryUserFilterTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryOptions import io.sentry.protocol.User import org.mockito.kotlin.check @@ -16,7 +16,7 @@ import kotlin.test.assertNull class SentryUserFilterTest { class Fixture { - val hub = mock() + val scopes = mock() val request = MockHttpServletRequest() val response = MockHttpServletResponse() val chain = mock() @@ -25,8 +25,8 @@ class SentryUserFilterTest { val options = SentryOptions().apply { this.isSendDefaultPii = isSendDefaultPii } - whenever(hub.options).thenReturn(options) - return SentryUserFilter(hub, userProviders) + whenever(scopes.options).thenReturn(options) + return SentryUserFilter(scopes, userProviders) } } @@ -52,7 +52,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).setUser( + verify(fixture.scopes).setUser( check { assertEquals(sampleUser, it) } @@ -72,7 +72,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).setUser( + verify(fixture.scopes).setUser( check { assertEquals(sampleUser, it) } @@ -92,7 +92,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).setUser( + verify(fixture.scopes).setUser( check { assertEquals(sampleUser, it) } @@ -118,7 +118,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).setUser( + verify(fixture.scopes).setUser( check { assertEquals(mapOf("key" to "value", "new-key" to "new-value"), it.others) } @@ -140,7 +140,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).setUser( + verify(fixture.scopes).setUser( check { assertEquals("192.168.0.1", it.ipAddress) } @@ -162,7 +162,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).setUser( + verify(fixture.scopes).setUser( check { assertNull(it.ipAddress) } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/exception/SentryCaptureExceptionParameterAdviceTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/exception/SentryCaptureExceptionParameterAdviceTest.kt index 39bc66fabb..dca1c4c592 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/exception/SentryCaptureExceptionParameterAdviceTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/exception/SentryCaptureExceptionParameterAdviceTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring.exception import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.Sentry import io.sentry.exception.ExceptionMechanismException import org.junit.runner.RunWith @@ -30,18 +30,18 @@ class SentryCaptureExceptionParameterAdviceTest { lateinit var sampleService: SampleService @Autowired - lateinit var hub: IHub + lateinit var scopes: IScopes @BeforeTest fun setup() { - reset(hub) + reset(scopes) } @Test fun `captures exception passed to method annotated with @SentryCaptureException`() { val exception = RuntimeException("test exception") sampleService.methodTakingAnException(exception) - verify(hub).captureException( + verify(scopes).captureException( check { assertTrue(it is ExceptionMechanismException) assertEquals(exception, it.throwable) @@ -60,10 +60,10 @@ class SentryCaptureExceptionParameterAdviceTest { open fun sampleService() = SampleService() @Bean - open fun hub(): IHub { - val hub = mock() - Sentry.setCurrentHub(hub) - return hub + open fun scopes(): IScopes { + val scopes = mock() + Sentry.setCurrentScopes(scopes) + return scopes } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/graphql/SentrySpringSubscriptionHandlerTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/graphql/SentrySpringSubscriptionHandlerTest.kt index df2df5b3ef..1aa97cd262 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/graphql/SentrySpringSubscriptionHandlerTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/graphql/SentrySpringSubscriptionHandlerTest.kt @@ -4,7 +4,7 @@ import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchPar import graphql.language.Document import graphql.language.OperationDefinition import graphql.schema.DataFetchingEnvironment -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.graphql.ExceptionReporter import org.junit.jupiter.api.assertThrows import org.mockito.kotlin.anyOrNull @@ -23,7 +23,7 @@ class SentrySpringSubscriptionHandlerTest { @Test fun `reports exception`() { val exception = IllegalStateException("some exception") - val hub = mock() + val scopes = mock() val exceptionReporter = mock() val parameters = mock() val dataFetchingEnvironment = mock() @@ -32,7 +32,7 @@ class SentrySpringSubscriptionHandlerTest { .build() whenever(dataFetchingEnvironment.document).thenReturn(document) whenever(parameters.environment).thenReturn(dataFetchingEnvironment) - val resultObject = SentrySpringSubscriptionHandler().onSubscriptionResult(Flux.error(exception), hub, exceptionReporter, parameters) + val resultObject = SentrySpringSubscriptionHandler().onSubscriptionResult(Flux.error(exception), scopes, exceptionReporter, parameters) assertThrows { (resultObject as Flux).blockFirst() } @@ -41,7 +41,7 @@ class SentrySpringSubscriptionHandlerTest { same(exception), org.mockito.kotlin.check { assertEquals(true, it.isSubscription) - assertSame(hub, it.hub) + assertSame(scopes, it.scopes) assertEquals("query testQuery\n", it.query) }, anyOrNull() @@ -52,7 +52,7 @@ class SentrySpringSubscriptionHandlerTest { fun `unwraps SubscriptionPublisherException and reports cause`() { val exception = IllegalStateException("some exception") val wrappedException = SubscriptionPublisherException(emptyList(), exception) - val hub = mock() + val scopes = mock() val exceptionReporter = mock() val parameters = mock() val dataFetchingEnvironment = mock() @@ -61,7 +61,7 @@ class SentrySpringSubscriptionHandlerTest { .build() whenever(dataFetchingEnvironment.document).thenReturn(document) whenever(parameters.environment).thenReturn(dataFetchingEnvironment) - val resultObject = SentrySpringSubscriptionHandler().onSubscriptionResult(Flux.error(wrappedException), hub, exceptionReporter, parameters) + val resultObject = SentrySpringSubscriptionHandler().onSubscriptionResult(Flux.error(wrappedException), scopes, exceptionReporter, parameters) assertThrows { (resultObject as Flux).blockFirst() } @@ -70,7 +70,7 @@ class SentrySpringSubscriptionHandlerTest { same(exception), org.mockito.kotlin.check { assertEquals(true, it.isSubscription) - assertSame(hub, it.hub) + assertSame(scopes, it.scopes) assertEquals("query testQuery\n", it.query) }, anyOrNull() diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/mvc/SentrySpringIntegrationTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/mvc/SentrySpringIntegrationTest.kt index 998b27c7e8..ad9734def6 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/mvc/SentrySpringIntegrationTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/mvc/SentrySpringIntegrationTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.mvc -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ITransportFactory import io.sentry.Sentry import io.sentry.SentryOptions @@ -104,7 +104,7 @@ class SentrySpringIntegrationTest { lateinit var anotherService: AnotherService @Autowired - lateinit var hub: IHub + lateinit var scopes: IScopes @LocalServerPort var port: Int? = null @@ -260,7 +260,7 @@ class SentrySpringIntegrationTest { try { someService.aMethodThrowing() } catch (e: Exception) { - hub.captureException(e) + scopes.captureException(e) } verify(transport).send( checkEvent { @@ -276,7 +276,7 @@ class SentrySpringIntegrationTest { try { someService.aMethodWithInnerSpanThrowing() } catch (e: Exception) { - hub.captureException(e) + scopes.captureException(e) } verify(transport).send( checkEvent { @@ -370,20 +370,20 @@ open class App { open fun springSecuritySentryUserProvider(sentryOptions: SentryOptions) = SpringSecuritySentryUserProvider(sentryOptions) @Bean - open fun sentryUserFilter(hub: IHub, @Lazy sentryUserProviders: List) = FilterRegistrationBean().apply { - this.filter = SentryUserFilter(hub, sentryUserProviders) + open fun sentryUserFilter(scopes: IScopes, @Lazy sentryUserProviders: List) = FilterRegistrationBean().apply { + this.filter = SentryUserFilter(scopes, sentryUserProviders) this.order = Ordered.LOWEST_PRECEDENCE } @Bean - open fun sentrySpringFilter(hub: IHub) = FilterRegistrationBean().apply { - this.filter = SentrySpringFilter(hub) + open fun sentrySpringFilter(scopes: IScopes) = FilterRegistrationBean().apply { + this.filter = SentrySpringFilter(scopes) this.order = Ordered.HIGHEST_PRECEDENCE } @Bean - open fun sentryTracingFilter(hub: IHub) = FilterRegistrationBean().apply { - this.filter = SentryTracingFilter(hub) + open fun sentryTracingFilter(scopes: IScopes) = FilterRegistrationBean().apply { + this.filter = SentryTracingFilter(scopes) this.order = Ordered.HIGHEST_PRECEDENCE + 1 // must run after SentrySpringFilter } @@ -391,13 +391,13 @@ open class App { open fun sentryTaskDecorator() = SentryTaskDecorator() @Bean - open fun webClient(hub: IHub): WebClient { + open fun webClient(scopes: IScopes): WebClient { return WebClient.builder() .filter( ExchangeFilterFunctions .basicAuthentication("user", "password") ) - .filter(SentrySpanClientWebRequestFilter(hub)).build() + .filter(SentrySpanClientWebRequestFilter(scopes)).build() } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentrySpanAdviceTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentrySpanAdviceTest.kt index dc6f00eb46..a6f59971c9 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentrySpanAdviceTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentrySpanAdviceTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.tracing -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.Scope import io.sentry.Sentry import io.sentry.SentryOptions @@ -37,20 +37,20 @@ class SentrySpanAdviceTest { lateinit var classAnnotatedWithOperationSampleService: ClassAnnotatedWithOperationSampleService @Autowired - lateinit var hub: IHub + lateinit var scopes: IScopes @BeforeTest fun setup() { - whenever(hub.options).thenReturn(SentryOptions()) + whenever(scopes.options).thenReturn(SentryOptions()) } @Test fun `when class is annotated with @SentrySpan, every method call attaches span to existing transaction`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) scope.setTransaction(tx) - whenever(hub.span).thenReturn(tx) + whenever(scopes.span).thenReturn(tx) val result = classAnnotatedSampleService.hello() assertEquals(1, result) assertEquals(1, tx.spans.size) @@ -62,10 +62,10 @@ class SentrySpanAdviceTest { @Test fun `when class is annotated with @SentrySpan with operation set, every method call attaches span to existing transaction`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) scope.setTransaction(tx) - whenever(hub.span).thenReturn(tx) + whenever(scopes.span).thenReturn(tx) val result = classAnnotatedWithOperationSampleService.hello() assertEquals(1, result) assertEquals(1, tx.spans.size) @@ -76,10 +76,10 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan with properties set, attaches span to existing transaction`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) scope.setTransaction(tx) - whenever(hub.span).thenReturn(tx) + whenever(scopes.span).thenReturn(tx) val result = sampleService.methodWithSpanDescriptionSet() assertEquals(1, result) assertEquals(1, tx.spans.size) @@ -90,10 +90,10 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan without properties set, attaches span to existing transaction and sets Span description as className dot methodName`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) scope.setTransaction(tx) - whenever(hub.span).thenReturn(tx) + whenever(scopes.span).thenReturn(tx) val result = sampleService.methodWithoutSpanDescriptionSet() assertEquals(2, result) assertEquals(1, tx.spans.size) @@ -104,10 +104,10 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan and returns, attached span has status OK`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) scope.setTransaction(tx) - whenever(hub.span).thenReturn(tx) + whenever(scopes.span).thenReturn(tx) sampleService.methodWithSpanDescriptionSet() assertEquals(SpanStatus.OK, tx.spans.first().status) } @@ -115,10 +115,10 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan and throws exception, attached span has throwable set and INTERNAL_ERROR status`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) scope.setTransaction(tx) - whenever(hub.span).thenReturn(tx) + whenever(scopes.span).thenReturn(tx) var throwable: Throwable? = null try { sampleService.methodThrowingException() @@ -131,7 +131,7 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan and there is no active transaction, span is not created and method is executed`() { - whenever(hub.span).thenReturn(null) + whenever(scopes.span).thenReturn(null) val result = sampleService.methodWithSpanDescriptionSet() assertEquals(1, result) } @@ -151,10 +151,10 @@ class SentrySpanAdviceTest { open fun classAnnotatedWithOperationSampleService() = ClassAnnotatedWithOperationSampleService() @Bean - open fun hub(): IHub { - val hub = mock() - Sentry.setCurrentHub(hub) - return hub + open fun scopes(): IScopes { + val scopes = mock() + Sentry.setCurrentScopes(scopes) + return scopes } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTracingFilterTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTracingFilterTest.kt index 6c4c06ebff..aca54809cc 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTracingFilterTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTracingFilterTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring.tracing -import io.sentry.IHub import io.sentry.ILogger +import io.sentry.IScopes import io.sentry.PropagationContext import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -38,7 +38,7 @@ import kotlin.test.fail class SentryTracingFilterTest { private class Fixture { - val hub = mock() + val scopes = mock() val request = MockHttpServletRequest() val response = MockHttpServletResponse() val chain = mock() @@ -50,7 +50,7 @@ class SentryTracingFilterTest { val logger = mock() init { - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) } fun getSut(isEnabled: Boolean = true, status: Int = 200, sentryTraceHeader: String? = null, baggageHeaders: List? = null): SentryTracingFilter { @@ -61,16 +61,16 @@ class SentryTracingFilterTest { whenever(transactionNameProvider.provideTransactionSource()).thenReturn(TransactionNameSource.CUSTOM) if (sentryTraceHeader != null) { request.addHeader("sentry-trace", sentryTraceHeader) - whenever(hub.startTransaction(any(), check { it.isBindToScope })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } + whenever(scopes.startTransaction(any(), check { it.isBindToScope })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } } if (baggageHeaders != null) { request.addHeader("baggage", baggageHeaders) } response.status = status - whenever(hub.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } - whenever(hub.isEnabled).thenReturn(isEnabled) - whenever(hub.continueTrace(any(), any())).thenAnswer { TransactionContext.fromPropagationContext(PropagationContext.fromHeaders(logger, it.arguments[0] as String?, it.arguments[1] as List?)) } - return SentryTracingFilter(hub, transactionNameProvider) + whenever(scopes.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } + whenever(scopes.isEnabled).thenReturn(isEnabled) + whenever(scopes.continueTrace(any(), any())).thenAnswer { TransactionContext.fromPropagationContext(PropagationContext.fromHeaders(logger, it.arguments[0] as String?, it.arguments[1] as List?)) } + return SentryTracingFilter(scopes, transactionNameProvider) } } @@ -82,7 +82,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("POST /product/12", it.name) assertEquals(TransactionNameSource.URL, it.transactionNameSource) @@ -95,7 +95,7 @@ class SentryTracingFilterTest { } ) verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.transaction).isEqualTo("POST /product/{id}") assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.OK) @@ -114,7 +114,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.INTERNAL_ERROR) }, @@ -130,7 +130,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.status).isNull() }, @@ -146,7 +146,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -163,7 +163,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isEqualTo(parentSpanId) }, @@ -174,15 +174,15 @@ class SentryTracingFilterTest { } @Test - fun `when hub is disabled, components are not invoked`() { + fun `when scopes is disabled, components are not invoked`() { val filter = fixture.getSut(isEnabled = false) filter.doFilter(fixture.request, fixture.response, fixture.chain) verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.hub).isEnabled - verifyNoMoreInteractions(fixture.hub) + verify(fixture.scopes).isEnabled + verifyNoMoreInteractions(fixture.scopes) verify(fixture.transactionNameProvider, never()).provideTransactionName(any()) } @@ -196,7 +196,7 @@ class SentryTracingFilterTest { fail("filter is expected to rethrow exception") } catch (_: Exception) { } - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.status).isEqualTo(SpanStatus.INTERNAL_ERROR) }, @@ -216,10 +216,10 @@ class SentryTracingFilterTest { verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.hub).isEnabled - verify(fixture.hub, times(2)).options - verify(fixture.hub).continueTrace(anyOrNull(), anyOrNull()) - verifyNoMoreInteractions(fixture.hub) + verify(fixture.scopes).isEnabled + verify(fixture.scopes, times(2)).options + verify(fixture.scopes).continueTrace(anyOrNull(), anyOrNull()) + verifyNoMoreInteractions(fixture.scopes) verify(fixture.transactionNameProvider, never()).provideTransactionName(any()) } @@ -233,7 +233,7 @@ class SentryTracingFilterTest { verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -253,7 +253,7 @@ class SentryTracingFilterTest { verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -275,9 +275,9 @@ class SentryTracingFilterTest { verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.hub).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) + verify(fixture.scopes).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) - verify(fixture.hub, never()).captureTransaction( + verify(fixture.scopes, never()).captureTransaction( anyOrNull(), anyOrNull(), anyOrNull(), diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt index 0fa9abbb29..f53acde8aa 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.tracing -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -44,13 +44,13 @@ class SentryTransactionAdviceTest { lateinit var classAnnotatedWithOperationSampleService: ClassAnnotatedWithOperationSampleService @Autowired - lateinit var hub: IHub + lateinit var scopes: IScopes @BeforeTest fun setup() { - reset(hub) - whenever(hub.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } - whenever(hub.options).thenReturn( + reset(scopes) + whenever(scopes.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } + whenever(scopes.options).thenReturn( SentryOptions().apply { dsn = "https://key@sentry.io/proj" } @@ -60,7 +60,7 @@ class SentryTransactionAdviceTest { @Test fun `creates transaction around method annotated with @SentryTransaction`() { sampleService.methodWithTransactionNameSet() - verify(hub).captureTransaction( + verify(scopes).captureTransaction( check { assertThat(it.transaction).isEqualTo("customName") assertThat(it.contexts.trace!!.operation).isEqualTo("bean") @@ -76,7 +76,7 @@ class SentryTransactionAdviceTest { @Test fun `when method annotated with @SentryTransaction throws exception, sets error status on transaction`() { assertThrows { sampleService.methodThrowingException() } - verify(hub).captureTransaction( + verify(scopes).captureTransaction( check { assertThat(it.status).isEqualTo(SpanStatus.INTERNAL_ERROR) }, @@ -89,7 +89,7 @@ class SentryTransactionAdviceTest { @Test fun `when @SentryTransaction has no name set, sets transaction name as className dot methodName`() { sampleService.methodWithoutTransactionNameSet() - verify(hub).captureTransaction( + verify(scopes).captureTransaction( check { assertThat(it.transaction).isEqualTo("SampleService.methodWithoutTransactionNameSet") assertThat(it.contexts.trace!!.operation).isEqualTo("op") @@ -102,18 +102,18 @@ class SentryTransactionAdviceTest { @Test fun `when transaction is already active, does not start new transaction`() { - whenever(hub.options).thenReturn(SentryOptions()) - whenever(hub.span).then { SentryTracer(TransactionContext("aTransaction", "op"), hub) } + whenever(scopes.options).thenReturn(SentryOptions()) + whenever(scopes.span).then { SentryTracer(TransactionContext("aTransaction", "op"), scopes) } sampleService.methodWithTransactionNameSet() - verify(hub, times(0)).captureTransaction(any(), any()) + verify(scopes, times(0)).captureTransaction(any(), any()) } @Test fun `creates transaction around method in class annotated with @SentryTransaction`() { classAnnotatedSampleService.hello() - verify(hub).captureTransaction( + verify(scopes).captureTransaction( check { assertThat(it.transaction).isEqualTo("ClassAnnotatedSampleService.hello") assertThat(it.contexts.trace!!.operation).isEqualTo("op") @@ -127,7 +127,7 @@ class SentryTransactionAdviceTest { @Test fun `creates transaction with operation set around method in class annotated with @SentryTransaction`() { classAnnotatedWithOperationSampleService.hello() - verify(hub).captureTransaction( + verify(scopes).captureTransaction( check { assertThat(it.transaction).isEqualTo("ClassAnnotatedWithOperationSampleService.hello") assertThat(it.contexts.trace!!.operation).isEqualTo("my-op") @@ -141,13 +141,13 @@ class SentryTransactionAdviceTest { @Test fun `pushes the scope when advice starts`() { classAnnotatedSampleService.hello() - verify(hub).pushScope() + verify(scopes).pushScope() } @Test fun `pops the scope when advice finishes`() { classAnnotatedSampleService.hello() - verify(hub).popScope() + verify(scopes).popScope() } @Configuration @@ -165,10 +165,10 @@ class SentryTransactionAdviceTest { open fun classAnnotatedWithOperationSampleService() = ClassAnnotatedWithOperationSampleService() @Bean - open fun hub(): IHub { - val hub = mock() - Sentry.setCurrentHub(hub) - return hub + open fun scopes(): IScopes { + val scopes = mock() + Sentry.setCurrentScopes(scopes) + return scopes } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryScheduleHookTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryScheduleHookTest.kt index b09011d544..7a8b2993f9 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryScheduleHookTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryScheduleHookTest.kt @@ -33,28 +33,28 @@ class SentryScheduleHookTest { val sut = SentryScheduleHook() - val mainHub = Sentry.getCurrentHub() - val threadedHub = Sentry.getCurrentHub().clone() + val mainHub = Sentry.getCurrentScopes() + val threadedHub = Sentry.getCurrentScopes().clone() executor.submit { Sentry.setCurrentHub(threadedHub) }.get() - assertEquals(mainHub, Sentry.getCurrentHub()) + assertEquals(mainHub, Sentry.getCurrentScopes()) val callableFuture = executor.submit( sut.apply { - assertNotEquals(mainHub, Sentry.getCurrentHub()) - assertNotEquals(threadedHub, Sentry.getCurrentHub()) + assertNotEquals(mainHub, Sentry.getCurrentScopes()) + assertNotEquals(threadedHub, Sentry.getCurrentScopes()) } ) callableFuture.get() executor.submit { - assertNotEquals(mainHub, Sentry.getCurrentHub()) - assertEquals(threadedHub, Sentry.getCurrentHub()) + assertNotEquals(mainHub, Sentry.getCurrentScopes()) + assertEquals(threadedHub, Sentry.getCurrentScopes()) }.get() } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt index be56a8391d..2113c748ee 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt @@ -2,8 +2,9 @@ package io.sentry.spring.webflux import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.IHub +import io.sentry.HubScopesWrapper import io.sentry.ILogger +import io.sentry.IScopes import io.sentry.PropagationContext import io.sentry.ScopeCallback import io.sentry.Sentry @@ -17,7 +18,7 @@ import io.sentry.TransactionOptions import io.sentry.protocol.SentryId import io.sentry.protocol.SentryTransaction import io.sentry.protocol.TransactionNameSource -import io.sentry.spring.webflux.SentryWebFilter.SENTRY_HUB_KEY +import io.sentry.spring.webflux.SentryWebFilter.SENTRY_SCOPES_KEY import org.assertj.core.api.Assertions.assertThat import org.mockito.Mockito import org.mockito.kotlin.any @@ -47,7 +48,7 @@ import kotlin.test.fail class SentryWebFluxTracingFilterTest { private class Fixture { - val hub = mock() + val scopes = mock() lateinit var request: MockServerHttpRequest lateinit var exchange: MockServerWebExchange val chain = mock() @@ -58,35 +59,37 @@ class SentryWebFluxTracingFilterTest { val logger = mock() init { - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) } fun getSut(isEnabled: Boolean = true, status: HttpStatus = HttpStatus.OK, sentryTraceHeader: String? = null, baggageHeaders: List? = null, method: HttpMethod = HttpMethod.POST): SentryWebFilter { var requestBuilder = MockServerHttpRequest.method(method, "/product/{id}", 12) if (sentryTraceHeader != null) { requestBuilder = requestBuilder.header("sentry-trace", sentryTraceHeader) - whenever(hub.startTransaction(any(), check { it.isBindToScope })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } + whenever(scopes.startTransaction(any(), check { it.isBindToScope })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } } if (baggageHeaders != null) { requestBuilder = requestBuilder.header("baggage", *baggageHeaders.toTypedArray()) } request = requestBuilder.build() exchange = MockServerWebExchange.builder(request).build() - exchange.attributes.put(SENTRY_HUB_KEY, hub) + exchange.attributes.put(SENTRY_SCOPES_KEY, scopes) exchange.attributes.put(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, PathPatternParser().parse("/product/{id}")) exchange.response.statusCode = status - whenever(hub.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } - whenever(hub.isEnabled).thenReturn(isEnabled) + whenever(scopes.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } + whenever(scopes.isEnabled).thenReturn(isEnabled) whenever(chain.filter(any())).thenReturn(Mono.create { s -> s.success() }) - whenever(hub.continueTrace(anyOrNull(), anyOrNull())).thenAnswer { TransactionContext.fromPropagationContext(PropagationContext.fromHeaders(logger, it.arguments[0] as String?, it.arguments[1] as List?)) } - return SentryWebFilter(hub) + whenever(scopes.continueTrace(anyOrNull(), anyOrNull())).thenAnswer { TransactionContext.fromPropagationContext(PropagationContext.fromHeaders(logger, it.arguments[0] as String?, it.arguments[1] as List?)) } + return SentryWebFilter(scopes) } } private val fixture = Fixture() - fun withMockHub(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { - it.`when` { Sentry.cloneMainHub() }.thenReturn(fixture.hub) + fun withMockScopes(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { + it.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(fixture.scopes)) + it.`when` { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes) + it.`when` { Sentry.cloneMainHub() }.thenReturn(fixture.scopes) closure.invoke() } @@ -94,10 +97,10 @@ class SentryWebFluxTracingFilterTest { fun `creates transaction around the request`() { val filter = fixture.getSut() - withMockHub { + withMockScopes { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("POST /product/12", it.name) assertEquals(TransactionNameSource.URL, it.transactionNameSource) @@ -110,7 +113,7 @@ class SentryWebFluxTracingFilterTest { } ) verify(fixture.chain).filter(fixture.exchange) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.transaction).isEqualTo("POST /product/{id}") assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.OK) @@ -129,10 +132,10 @@ class SentryWebFluxTracingFilterTest { fun `sets correct span status based on the response status`() { val filter = fixture.getSut(status = HttpStatus.INTERNAL_SERVER_ERROR) - withMockHub { + withMockScopes { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.INTERNAL_ERROR) assertThat(it.contexts.response!!.statusCode).isEqualTo(500) @@ -148,10 +151,10 @@ class SentryWebFluxTracingFilterTest { fun `does not set span status for response status that dont match predefined span statuses`() { val filter = fixture.getSut(status = HttpStatus.FOUND) - withMockHub { + withMockScopes { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.status).isNull() }, @@ -166,10 +169,10 @@ class SentryWebFluxTracingFilterTest { fun `when sentry trace is not present, transaction does not have parentSpanId set`() { val filter = fixture.getSut() - withMockHub { + withMockScopes { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -185,10 +188,10 @@ class SentryWebFluxTracingFilterTest { val parentSpanId = SpanId() val filter = fixture.getSut(sentryTraceHeader = "${SentryId()}-$parentSpanId-1") - withMockHub { + withMockScopes { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isEqualTo(parentSpanId) }, @@ -200,16 +203,16 @@ class SentryWebFluxTracingFilterTest { } @Test - fun `when hub is disabled, components are not invoked`() { + fun `when scopes is disabled, components are not invoked`() { val filter = fixture.getSut(isEnabled = false) - withMockHub { + withMockScopes { filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.hub).isEnabled - verifyNoMoreInteractions(fixture.hub) + verify(fixture.scopes).isEnabled + verifyNoMoreInteractions(fixture.scopes) } } @@ -217,7 +220,7 @@ class SentryWebFluxTracingFilterTest { fun `sets status to internal server error when chain throws exception`() { val filter = fixture.getSut() - withMockHub { + withMockScopes { whenever(fixture.chain.filter(any())).thenReturn(Mono.error(RuntimeException("error"))) try { @@ -225,7 +228,7 @@ class SentryWebFluxTracingFilterTest { fail("filter is expected to rethrow exception") } catch (_: Exception) { } - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.status).isEqualTo(SpanStatus.INTERNAL_ERROR) }, @@ -240,21 +243,21 @@ class SentryWebFluxTracingFilterTest { fun `does not track OPTIONS request with traceOptionsRequests=false`() { val filter = fixture.getSut(method = HttpMethod.OPTIONS) - withMockHub { + withMockScopes { fixture.options.isTraceOptionsRequests = false filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.hub).isEnabled - verify(fixture.hub, times(2)).options - verify(fixture.hub).continueTrace(anyOrNull(), anyOrNull()) - verify(fixture.hub).pushScope() - verify(fixture.hub).addBreadcrumb(any(), any()) - verify(fixture.hub).configureScope(any()) - verify(fixture.hub).popScope() - verifyNoMoreInteractions(fixture.hub) + verify(fixture.scopes).isEnabled + verify(fixture.scopes, times(2)).options + verify(fixture.scopes).continueTrace(anyOrNull(), anyOrNull()) + verify(fixture.scopes).pushScope() + verify(fixture.scopes).addBreadcrumb(any(), any()) + verify(fixture.scopes).configureScope(any()) + verify(fixture.scopes).popScope() + verifyNoMoreInteractions(fixture.scopes) } } @@ -262,14 +265,14 @@ class SentryWebFluxTracingFilterTest { fun `tracks OPTIONS request with traceOptionsRequests=true`() { val filter = fixture.getSut(method = HttpMethod.OPTIONS) - withMockHub { + withMockScopes { fixture.options.isTraceOptionsRequests = true filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -284,14 +287,14 @@ class SentryWebFluxTracingFilterTest { fun `tracks POST request with traceOptionsRequests=false`() { val filter = fixture.getSut(method = HttpMethod.POST) - withMockHub { + withMockScopes { fixture.options.isTraceOptionsRequests = false filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -310,19 +313,19 @@ class SentryWebFluxTracingFilterTest { fixture.options.enableTracing = false val filter = fixture.getSut(sentryTraceHeader = sentryTraceHeaderString, baggageHeaders = baggageHeaderStrings) - withMockHub { + withMockScopes { filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.hub, never()).captureTransaction( + verify(fixture.scopes, never()).captureTransaction( anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull() ) - verify(fixture.hub).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) + verify(fixture.scopes).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) } } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebfluxIntegrationTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebfluxIntegrationTest.kt index 0d4346c5ae..8c5aeb1c0a 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebfluxIntegrationTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebfluxIntegrationTest.kt @@ -1,8 +1,8 @@ package io.sentry.spring.webflux -import io.sentry.HubAdapter -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ITransportFactory +import io.sentry.ScopesAdapter import io.sentry.Sentry import io.sentry.checkEvent import io.sentry.checkTransaction @@ -160,13 +160,13 @@ open class App { open fun mockTransport() = transport @Bean - open fun hub() = HubAdapter.getInstance() + open fun scopes() = ScopesAdapter.getInstance() @Bean - open fun sentryFilter(hub: IHub) = SentryWebFilter(hub) + open fun sentryFilter(scopes: IScopes) = SentryWebFilter(scopes) @Bean - open fun sentryWebExceptionHandler(hub: IHub) = SentryWebExceptionHandler(hub) + open fun sentryWebExceptionHandler(scopes: IScopes) = SentryWebExceptionHandler(scopes) @Bean open fun sentryScheduleHookRegistrar() = ApplicationRunner { From e11f8b1628380e305e446c9f7b6d0c8c4d0111c0 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 16 Apr 2024 16:46:31 +0200 Subject: [PATCH 12/91] Hubs/Scopes Merge 12 - Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations (#3309) * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations --- .../api/sentry-spring-boot-jakarta.api | 2 +- .../boot/jakarta/SentryAutoConfiguration.java | 43 ++++----- .../SentrySpanRestClientCustomizer.java | 6 +- .../SentrySpanRestTemplateCustomizer.java | 6 +- .../SentrySpanWebClientCustomizer.java | 6 +- .../SentryWebfluxAutoConfiguration.java | 21 +++-- .../jakarta/SentryAutoConfigurationTest.kt | 12 +-- .../SentrySpanRestClientCustomizerTest.kt | 22 ++--- .../SentrySpanRestTemplateCustomizerTest.kt | 22 ++--- .../SentrySpanWebClientCustomizerTest.kt | 22 ++--- .../jakarta/it/SentrySpringIntegrationTest.kt | 6 +- .../api/sentry-spring-jakarta.api | 61 ++++++------ .../sentry/spring/jakarta/EnableSentry.java | 2 +- .../jakarta/SentryExceptionResolver.java | 10 +- .../spring/jakarta/SentryHubRegistrar.java | 4 +- .../jakarta/SentryInitBeanPostProcessor.java | 16 ++-- .../spring/jakarta/SentryRequestResolver.java | 16 ++-- .../spring/jakarta/SentrySpringFilter.java | 38 ++++---- .../spring/jakarta/SentryTaskDecorator.java | 18 ++-- .../spring/jakarta/SentryUserFilter.java | 12 +-- .../jakarta/checkin/SentryCheckInAdvice.java | 28 +++--- ...SentryCaptureExceptionParameterAdvice.java | 14 +-- .../graphql/SentryBatchLoaderRegistry.java | 16 ++-- .../graphql/SentryDgsSubscriptionHandler.java | 6 +- .../SentrySpringSubscriptionHandler.java | 6 +- .../jakarta/tracing/SentrySpanAdvice.java | 14 +-- ...entrySpanClientHttpRequestInterceptor.java | 18 ++-- .../SentrySpanClientWebRequestFilter.java | 14 +-- .../jakarta/tracing/SentryTracingFilter.java | 31 +++--- .../tracing/SentryTransactionAdvice.java | 20 ++-- .../webflux/AbstractSentryWebFilter.java | 35 +++---- .../spring/jakarta/webflux/ReactorUtils.java | 35 ++++--- .../SentryReactorThreadLocalAccessor.java | 18 ++-- .../webflux/SentryRequestResolver.java | 13 +-- .../jakarta/webflux/SentryScheduleHook.java | 12 ++- .../webflux/SentryWebExceptionHandler.java | 18 ++-- .../jakarta/webflux/SentryWebFilter.java | 16 ++-- ...entryWebFilterWithThreadLocalAccessor.java | 13 +-- .../sentry/spring/jakarta/EnableSentryTest.kt | 4 +- .../spring/jakarta/SentryCheckInAdviceTest.kt | 94 +++++++++---------- .../jakarta/SentryExceptionResolverTest.kt | 28 +++--- .../SentryInitBeanPostProcessorTest.kt | 10 +- ...yRequestHttpServletRequestProcessorTest.kt | 6 +- .../spring/jakarta/SentrySpringFilterTest.kt | 20 ++-- .../spring/jakarta/SentryTaskDecoratorTest.kt | 16 ++-- .../spring/jakarta/SentryUserFilterTest.kt | 20 ++-- ...ntryCaptureExceptionParameterAdviceTest.kt | 16 ++-- .../SentrySpringSubscriptionHandlerTest.kt | 14 +-- .../mvc/SentrySpringIntegrationTest.kt | 24 ++--- .../jakarta/tracing/SentrySpanAdviceTest.kt | 40 ++++---- .../tracing/SentryTracingFilterTest.kt | 52 +++++----- .../tracing/SentryTransactionAdviceTest.kt | 38 ++++---- .../jakarta/webflux/ReactorUtilsTest.kt | 45 ++++----- .../jakarta/webflux/SentryScheduleHookTest.kt | 16 ++-- .../webflux/SentryWebFluxTracingFilterTest.kt | 93 +++++++++--------- .../webflux/SentryWebfluxIntegrationTest.kt | 10 +- 56 files changed, 623 insertions(+), 595 deletions(-) diff --git a/sentry-spring-boot-jakarta/api/sentry-spring-boot-jakarta.api b/sentry-spring-boot-jakarta/api/sentry-spring-boot-jakarta.api index a392ff75ee..009036082e 100644 --- a/sentry-spring-boot-jakarta/api/sentry-spring-boot-jakarta.api +++ b/sentry-spring-boot-jakarta/api/sentry-spring-boot-jakarta.api @@ -62,7 +62,7 @@ public class io/sentry/spring/boot/jakarta/SentryProperties$Reactive { public class io/sentry/spring/boot/jakarta/SentryWebfluxAutoConfiguration { public fun ()V - public fun sentryWebExceptionHandler (Lio/sentry/IHub;)Lio/sentry/spring/jakarta/webflux/SentryWebExceptionHandler; + public fun sentryWebExceptionHandler (Lio/sentry/IScopes;)Lio/sentry/spring/jakarta/webflux/SentryWebExceptionHandler; } public class io/sentry/spring/boot/jakarta/graphql/SentryGraphqlAutoConfiguration { diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java index fbf3162280..b2c8df213c 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java @@ -3,10 +3,10 @@ import com.jakewharton.nopen.annotation.Open; import graphql.GraphQLError; import io.sentry.EventProcessor; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ITransportFactory; import io.sentry.Integration; +import io.sentry.ScopesAdapter; import io.sentry.Sentry; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryOptions; @@ -116,7 +116,7 @@ static class HubConfiguration { } @Bean - public @NotNull IHub sentryHub( + public @NotNull IScopes sentryHub( final @NotNull List> optionsConfigurations, final @NotNull SentryProperties options, final @NotNull ObjectProvider gitProperties) { @@ -139,7 +139,7 @@ static class HubConfiguration { // here we make sure that only classes that extend throwable are set on this field options.getIgnoredExceptionsForType().removeIf(it -> !Throwable.class.isAssignableFrom(it)); Sentry.init(options); - return HubAdapter.getInstance(); + return ScopesAdapter.getInstance(); } @Configuration(proxyBeanMethods = false) @@ -239,7 +239,7 @@ static class SentrySecurityConfiguration { * HttpServletRequest#getUserPrincipal()}. If Spring Security is auto-configured, its order is * set to run after Spring Security. * - * @param hub the Sentry hub + * @param scopes the Sentry scopes * @param sentryProperties the Sentry properties * @param sentryUserProvider the user provider * @return {@link SentryUserFilter} registration bean @@ -247,11 +247,11 @@ static class SentrySecurityConfiguration { @Bean @ConditionalOnBean(SentryUserProvider.class) public @NotNull FilterRegistrationBean sentryUserFilter( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull SentryProperties sentryProperties, final @NotNull List sentryUserProvider) { final FilterRegistrationBean filter = new FilterRegistrationBean<>(); - filter.setFilter(new SentryUserFilter(hub, sentryUserProvider)); + filter.setFilter(new SentryUserFilter(scopes, sentryUserProvider)); filter.setOrder(resolveUserFilterOrder(sentryProperties)); return filter; } @@ -263,8 +263,8 @@ static class SentrySecurityConfiguration { } @Bean - public @NotNull SentryRequestResolver sentryRequestResolver(final @NotNull IHub hub) { - return new SentryRequestResolver(hub); + public @NotNull SentryRequestResolver sentryRequestResolver(final @NotNull IScopes scopes) { + return new SentryRequestResolver(scopes); } @Bean @@ -276,12 +276,12 @@ static class SentrySecurityConfiguration { @Bean @ConditionalOnMissingBean(name = "sentrySpringFilter") public @NotNull FilterRegistrationBean sentrySpringFilter( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull SentryRequestResolver requestResolver, final @NotNull TransactionNameProvider transactionNameProvider) { FilterRegistrationBean filter = new FilterRegistrationBean<>( - new SentrySpringFilter(hub, requestResolver, transactionNameProvider)); + new SentrySpringFilter(scopes, requestResolver, transactionNameProvider)); filter.setOrder(SENTRY_SPRING_FILTER_PRECEDENCE); return filter; } @@ -289,9 +289,10 @@ static class SentrySecurityConfiguration { @Bean @ConditionalOnMissingBean(name = "sentryTracingFilter") public FilterRegistrationBean sentryTracingFilter( - final @NotNull IHub hub, final @NotNull TransactionNameProvider transactionNameProvider) { + final @NotNull IScopes scopes, + final @NotNull TransactionNameProvider transactionNameProvider) { FilterRegistrationBean filter = - new FilterRegistrationBean<>(new SentryTracingFilter(hub, transactionNameProvider)); + new FilterRegistrationBean<>(new SentryTracingFilter(scopes, transactionNameProvider)); filter.setOrder(SENTRY_SPRING_FILTER_PRECEDENCE + 1); // must run after SentrySpringFilter return filter; } @@ -300,11 +301,11 @@ public FilterRegistrationBean sentryTracingFilter( @ConditionalOnMissingBean @ConditionalOnClass(HandlerExceptionResolver.class) public @NotNull SentryExceptionResolver sentryExceptionResolver( - final @NotNull IHub sentryHub, + final @NotNull IScopes scopes, final @NotNull TransactionNameProvider transactionNameProvider, final @NotNull SentryProperties options) { return new SentryExceptionResolver( - sentryHub, transactionNameProvider, options.getExceptionResolverOrder()); + scopes, transactionNameProvider, options.getExceptionResolverOrder()); } } @@ -354,8 +355,8 @@ static class SentrySpanPointcutAutoConfiguration {} @Open static class SentryPerformanceRestTemplateConfiguration { @Bean - public SentrySpanRestTemplateCustomizer sentrySpanRestTemplateCustomizer(IHub hub) { - return new SentrySpanRestTemplateCustomizer(hub); + public SentrySpanRestTemplateCustomizer sentrySpanRestTemplateCustomizer(IScopes scopes) { + return new SentrySpanRestTemplateCustomizer(scopes); } } @@ -365,8 +366,8 @@ public SentrySpanRestTemplateCustomizer sentrySpanRestTemplateCustomizer(IHub hu @Open static class SentrySpanRestClientConfiguration { @Bean - public SentrySpanRestClientCustomizer sentrySpanRestClientCustomizer(IHub hub) { - return new SentrySpanRestClientCustomizer(hub); + public SentrySpanRestClientCustomizer sentrySpanRestClientCustomizer(IScopes scopes) { + return new SentrySpanRestClientCustomizer(scopes); } } @@ -376,8 +377,8 @@ public SentrySpanRestClientCustomizer sentrySpanRestClientCustomizer(IHub hub) { @Open static class SentryPerformanceWebClientConfiguration { @Bean - public SentrySpanWebClientCustomizer sentrySpanWebClientCustomizer(IHub hub) { - return new SentrySpanWebClientCustomizer(hub); + public SentrySpanWebClientCustomizer sentrySpanWebClientCustomizer(IScopes scopes) { + return new SentrySpanWebClientCustomizer(scopes); } } diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizer.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizer.java index 243a4d1f50..304fb6911e 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizer.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizer.java @@ -1,7 +1,7 @@ package io.sentry.spring.boot.jakarta; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.spring.jakarta.tracing.SentrySpanClientHttpRequestInterceptor; import org.jetbrains.annotations.NotNull; import org.springframework.boot.web.client.RestClientCustomizer; @@ -11,8 +11,8 @@ class SentrySpanRestClientCustomizer implements RestClientCustomizer { private final @NotNull SentrySpanClientHttpRequestInterceptor interceptor; - public SentrySpanRestClientCustomizer(final @NotNull IHub hub) { - this.interceptor = new SentrySpanClientHttpRequestInterceptor(hub, false); + public SentrySpanRestClientCustomizer(final @NotNull IScopes scopes) { + this.interceptor = new SentrySpanClientHttpRequestInterceptor(scopes, false); } @Override diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizer.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizer.java index c1ded006e8..4a0faa9875 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizer.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizer.java @@ -1,7 +1,7 @@ package io.sentry.spring.boot.jakarta; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.spring.jakarta.tracing.SentrySpanClientHttpRequestInterceptor; import java.util.ArrayList; import java.util.List; @@ -14,8 +14,8 @@ class SentrySpanRestTemplateCustomizer implements RestTemplateCustomizer { private final @NotNull SentrySpanClientHttpRequestInterceptor interceptor; - public SentrySpanRestTemplateCustomizer(final @NotNull IHub hub) { - this.interceptor = new SentrySpanClientHttpRequestInterceptor(hub); + public SentrySpanRestTemplateCustomizer(final @NotNull IScopes scopes) { + this.interceptor = new SentrySpanClientHttpRequestInterceptor(scopes); } @Override diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizer.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizer.java index afbe2eb6c4..d349ac4c6e 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizer.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizer.java @@ -1,7 +1,7 @@ package io.sentry.spring.boot.jakarta; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.spring.jakarta.tracing.SentrySpanClientWebRequestFilter; import org.jetbrains.annotations.NotNull; import org.springframework.boot.web.reactive.function.client.WebClientCustomizer; @@ -11,8 +11,8 @@ class SentrySpanWebClientCustomizer implements WebClientCustomizer { private final @NotNull SentrySpanClientWebRequestFilter filter; - public SentrySpanWebClientCustomizer(final @NotNull IHub hub) { - this.filter = new SentrySpanClientWebRequestFilter(hub); + public SentrySpanWebClientCustomizer(final @NotNull IScopes scopes) { + this.filter = new SentrySpanClientWebRequestFilter(scopes); } @Override diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryWebfluxAutoConfiguration.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryWebfluxAutoConfiguration.java index d1cda8b4d2..2bc8bc87ed 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryWebfluxAutoConfiguration.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryWebfluxAutoConfiguration.java @@ -1,8 +1,8 @@ package io.sentry.spring.boot.jakarta; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IHub; import io.sentry.IScope; +import io.sentry.IScopes; import io.sentry.spring.jakarta.webflux.SentryScheduleHook; import io.sentry.spring.jakarta.webflux.SentryWebExceptionHandler; import io.sentry.spring.jakarta.webflux.SentryWebFilter; @@ -28,7 +28,7 @@ /** Configures Sentry integration for Spring Webflux and Project Reactor. */ @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) -@ConditionalOnBean(IHub.class) +@ConditionalOnBean(IScopes.class) @ConditionalOnClass(Schedulers.class) @Open @ApiStatus.Experimental @@ -44,14 +44,14 @@ static class SentryWebfluxFilterThreadLocalAccessorConfiguration { * Configures a filter that sets up Sentry {@link IScope} for each request. * *

Makes use of newer reactor-core and context-propagation library feature - * ThreadLocalAccessor to propagate the Sentry hub. + * ThreadLocalAccessor to propagate the Sentry scopes. */ @Bean @Order(SENTRY_SPRING_FILTER_PRECEDENCE) public @NotNull SentryWebFilterWithThreadLocalAccessor sentryWebFilterWithContextPropagation( - final @NotNull IHub hub) { + final @NotNull IScopes scopes) { Hooks.enableAutomaticContextPropagation(); - return new SentryWebFilterWithThreadLocalAccessor(hub); + return new SentryWebFilterWithThreadLocalAccessor(scopes); } } @@ -60,7 +60,7 @@ static class SentryWebfluxFilterThreadLocalAccessorConfiguration { @Open static class SentryWebfluxFilterConfiguration { - /** Configures hook that sets correct hub on the executing thread. */ + /** Configures hook that sets correct scopes on the executing thread. */ @Bean public @NotNull ApplicationRunner sentryScheduleHookApplicationRunner() { return args -> { @@ -71,15 +71,16 @@ static class SentryWebfluxFilterConfiguration { /** Configures a filter that sets up Sentry {@link IScope} for each request. */ @Bean @Order(SENTRY_SPRING_FILTER_PRECEDENCE) - public @NotNull SentryWebFilter sentryWebFilter(final @NotNull IHub hub) { - return new SentryWebFilter(hub); + public @NotNull SentryWebFilter sentryWebFilter(final @NotNull IScopes scopes) { + return new SentryWebFilter(scopes); } } /** Configures exception handler that handles unhandled exceptions and sends them to Sentry. */ @Bean - public @NotNull SentryWebExceptionHandler sentryWebExceptionHandler(final @NotNull IHub hub) { - return new SentryWebExceptionHandler(hub); + public @NotNull SentryWebExceptionHandler sentryWebExceptionHandler( + final @NotNull IScopes scopes) { + return new SentryWebExceptionHandler(scopes); } static final class SentryLegacyFilterConfigurationCondition extends AnyNestedCondition { diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt index aecb1b4712..df0fe96cf0 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt @@ -5,7 +5,7 @@ import io.sentry.AsyncHttpTransportFactory import io.sentry.Breadcrumb import io.sentry.EventProcessor import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ITransportFactory import io.sentry.Integration import io.sentry.NoOpTransportFactory @@ -77,18 +77,18 @@ class SentryAutoConfigurationTest { .withConfiguration(AutoConfigurations.of(SentryAutoConfiguration::class.java, WebMvcAutoConfiguration::class.java)) @Test - fun `hub is not created when auto-configuration dsn is not set`() { + fun `scopes is not created when auto-configuration dsn is not set`() { contextRunner .run { - assertThat(it).doesNotHaveBean(IHub::class.java) + assertThat(it).doesNotHaveBean(IScopes::class.java) } } @Test - fun `hub is created when dsn is provided`() { + fun `scopes is created when dsn is provided`() { contextRunner.withPropertyValues("sentry.dsn=http://key@localhost/proj") .run { - assertThat(it).hasSingleBean(IHub::class.java) + assertThat(it).hasSingleBean(IScopes::class.java) } } @@ -961,7 +961,7 @@ class SentryAutoConfigurationTest { } class CustomIntegration : Integration { - override fun register(hub: IHub, options: SentryOptions) {} + override fun register(scopes: IScopes, options: SentryOptions) {} } @Configuration(proxyBeanMethods = false) diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizerTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizerTest.kt index f9f55ffede..9ba9bf57d2 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizerTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizerTest.kt @@ -2,7 +2,7 @@ package io.sentry.spring.boot.jakarta import io.sentry.BaggageHeader import io.sentry.Breadcrumb -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -36,18 +36,18 @@ import kotlin.test.assertTrue class SentrySpanRestClientCustomizerTest { class Fixture { val sentryOptions = SentryOptions() - val hub = mock() + val scopes = mock() val restClientBuilder = RestClient.builder() var mockServer = MockWebServer() val transaction: SentryTracer - internal val customizer = SentrySpanRestClientCustomizer(hub) + internal val customizer = SentrySpanRestClientCustomizer(scopes) val url = mockServer.url("/test/123").toString() val scope = Scope(sentryOptions) init { - whenever(hub.options).thenReturn(sentryOptions) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) - transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), hub) + whenever(scopes.options).thenReturn(sentryOptions) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) + transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), scopes) } fun getSut( @@ -75,7 +75,7 @@ class SentrySpanRestClientCustomizerTest { ) if (isTransactionActive) { - whenever(hub.span).thenReturn(transaction) + whenever(scopes.span).thenReturn(transaction) } return restClientBuilder.apply { @@ -247,7 +247,7 @@ class SentrySpanRestClientCustomizerTest { .body("content") .retrieve() .toEntity(String::class.java) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -269,7 +269,7 @@ class SentrySpanRestClientCustomizerTest { .toEntity(String::class.java) } catch (e: Throwable) { } - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -287,7 +287,7 @@ class SentrySpanRestClientCustomizerTest { .body("content") .retrieve() .toEntity(String::class.java) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -309,7 +309,7 @@ class SentrySpanRestClientCustomizerTest { .toEntity(String::class.java) } catch (e: Throwable) { } - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizerTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizerTest.kt index db5b25de44..4ab3205b31 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizerTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizerTest.kt @@ -2,7 +2,7 @@ package io.sentry.spring.boot.jakarta import io.sentry.BaggageHeader import io.sentry.Breadcrumb -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -37,21 +37,21 @@ import kotlin.test.assertTrue class SentrySpanRestTemplateCustomizerTest { class Fixture { val sentryOptions = SentryOptions() - val hub = mock() + val scopes = mock() val restTemplate = RestTemplateBuilder() .setConnectTimeout(Duration.ofSeconds(2)) .setReadTimeout(Duration.ofSeconds(2)) .build() var mockServer = MockWebServer() val transaction: SentryTracer - internal val customizer = SentrySpanRestTemplateCustomizer(hub) + internal val customizer = SentrySpanRestTemplateCustomizer(scopes) val url = mockServer.url("/test/123").toString() val scope = Scope(sentryOptions) init { - whenever(hub.options).thenReturn(sentryOptions) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) - transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), hub) + whenever(scopes.options).thenReturn(sentryOptions) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) + transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), scopes) } fun getSut(isTransactionActive: Boolean, status: HttpStatus = HttpStatus.OK, socketPolicy: SocketPolicy = SocketPolicy.KEEP_OPEN, includeMockServerInTracingOrigins: Boolean = true): RestTemplate { @@ -74,7 +74,7 @@ class SentrySpanRestTemplateCustomizerTest { ) if (isTransactionActive) { - whenever(hub.span).thenReturn(transaction) + whenever(scopes.span).thenReturn(transaction) } return restTemplate @@ -209,7 +209,7 @@ class SentrySpanRestTemplateCustomizerTest { @Test fun `when transaction is active adds breadcrumb when http calls succeeds`() { fixture.getSut(isTransactionActive = true).postForObject(fixture.url, "content", String::class.java) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -227,7 +227,7 @@ class SentrySpanRestTemplateCustomizerTest { fixture.getSut(isTransactionActive = true, status = HttpStatus.INTERNAL_SERVER_ERROR).getForObject(fixture.url, String::class.java) } catch (e: Throwable) { } - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -240,7 +240,7 @@ class SentrySpanRestTemplateCustomizerTest { @Test fun `when transaction is not active adds breadcrumb when http calls succeeds`() { fixture.getSut(isTransactionActive = false).postForObject(fixture.url, "content", String::class.java) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -258,7 +258,7 @@ class SentrySpanRestTemplateCustomizerTest { fixture.getSut(isTransactionActive = false, status = HttpStatus.INTERNAL_SERVER_ERROR).getForObject(fixture.url, String::class.java) } catch (e: Throwable) { } - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizerTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizerTest.kt index 51f0a6cb3d..d3fb5f7d31 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizerTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizerTest.kt @@ -2,8 +2,8 @@ package io.sentry.spring.boot.jakarta import io.sentry.BaggageHeader import io.sentry.Breadcrumb -import io.sentry.IHub import io.sentry.IScope +import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -39,10 +39,10 @@ class SentrySpanWebClientCustomizerTest { class Fixture { lateinit var sentryOptions: SentryOptions lateinit var scope: IScope - val hub = mock() + val scopes = mock() var mockServer = MockWebServer() lateinit var transaction: SentryTracer - private val customizer = SentrySpanWebClientCustomizer(hub) + private val customizer = SentrySpanWebClientCustomizer(scopes) fun getSut(isTransactionActive: Boolean, status: HttpStatus = HttpStatus.OK, throwIOException: Boolean = false, includeMockServerInTracingOrigins: Boolean = true): WebClient { sentryOptions = SentryOptions().apply { @@ -54,9 +54,9 @@ class SentrySpanWebClientCustomizerTest { dsn = "http://key@localhost/proj" } scope = Scope(sentryOptions) - whenever(hub.options).thenReturn(sentryOptions) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) - transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), hub) + whenever(scopes.options).thenReturn(sentryOptions) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) + transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), scopes) val webClientBuilder = WebClient.builder() customizer.customize(webClientBuilder) val webClient = webClientBuilder.build() @@ -64,7 +64,7 @@ class SentrySpanWebClientCustomizerTest { if (isTransactionActive) { val scope = Scope(sentryOptions) scope.transaction = transaction - whenever(hub.span).thenReturn(transaction) + whenever(scopes.span).thenReturn(transaction) } val dispatcher: Dispatcher = object : Dispatcher() { @@ -236,7 +236,7 @@ class SentrySpanWebClientCustomizerTest { .retrieve() .bodyToMono(String::class.java) .block() - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(uri.toString(), it.data["url"]) @@ -259,7 +259,7 @@ class SentrySpanWebClientCustomizerTest { .block() } catch (e: Throwable) { } - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(uri.toString(), it.data["url"]) @@ -279,7 +279,7 @@ class SentrySpanWebClientCustomizerTest { .retrieve() .bodyToMono(String::class.java) .block() - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(uri.toString(), it.data["url"]) @@ -302,7 +302,7 @@ class SentrySpanWebClientCustomizerTest { .block() } catch (e: Throwable) { } - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(uri.toString(), it.data["url"]) diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/it/SentrySpringIntegrationTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/it/SentrySpringIntegrationTest.kt index 4b8a0c26be..13a207d8eb 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/it/SentrySpringIntegrationTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/it/SentrySpringIntegrationTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.boot.jakarta.it -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ITransportFactory import io.sentry.Sentry import io.sentry.checkEvent @@ -60,7 +60,7 @@ class SentrySpringIntegrationTest { lateinit var transport: ITransport @SpyBean - lateinit var hub: IHub + lateinit var scopes: IScopes @LocalServerPort var port: Int? = null @@ -188,7 +188,7 @@ class SentrySpringIntegrationTest { restTemplate.getForEntity("http://localhost:$port/throws-handled", String::class.java) - verify(hub, never()).captureEvent(any()) + verify(scopes, never()).captureEvent(any()) } @Test diff --git a/sentry-spring-jakarta/api/sentry-spring-jakarta.api b/sentry-spring-jakarta/api/sentry-spring-jakarta.api index cebbd26b4f..13eb6033f9 100644 --- a/sentry-spring-jakarta/api/sentry-spring-jakarta.api +++ b/sentry-spring-jakarta/api/sentry-spring-jakarta.api @@ -22,7 +22,7 @@ public final class io/sentry/spring/jakarta/HttpServletRequestSentryUserProvider public class io/sentry/spring/jakarta/SentryExceptionResolver : org/springframework/core/Ordered, org/springframework/web/servlet/HandlerExceptionResolver { public static final field MECHANISM_TYPE Ljava/lang/String; - public fun (Lio/sentry/IHub;Lio/sentry/spring/jakarta/tracing/TransactionNameProvider;I)V + public fun (Lio/sentry/IScopes;Lio/sentry/spring/jakarta/tracing/TransactionNameProvider;I)V protected fun createEvent (Ljakarta/servlet/http/HttpServletRequest;Ljava/lang/Exception;)Lio/sentry/SentryEvent; protected fun createHint (Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;)Lio/sentry/Hint; public fun getOrder ()I @@ -47,14 +47,14 @@ public class io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessor : } public class io/sentry/spring/jakarta/SentryRequestResolver { - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun resolveSentryRequest (Ljakarta/servlet/http/HttpServletRequest;)Lio/sentry/protocol/Request; } public class io/sentry/spring/jakarta/SentrySpringFilter : org/springframework/web/filter/OncePerRequestFilter { public fun ()V - public fun (Lio/sentry/IHub;)V - public fun (Lio/sentry/IHub;Lio/sentry/spring/jakarta/SentryRequestResolver;Lio/sentry/spring/jakarta/tracing/TransactionNameProvider;)V + public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IScopes;Lio/sentry/spring/jakarta/SentryRequestResolver;Lio/sentry/spring/jakarta/tracing/TransactionNameProvider;)V protected fun doFilterInternal (Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;Ljakarta/servlet/FilterChain;)V } @@ -69,7 +69,7 @@ public final class io/sentry/spring/jakarta/SentryTaskDecorator : org/springfram } public class io/sentry/spring/jakarta/SentryUserFilter : org/springframework/web/filter/OncePerRequestFilter { - public fun (Lio/sentry/IHub;Ljava/util/List;)V + public fun (Lio/sentry/IScopes;Ljava/util/List;)V protected fun doFilterInternal (Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;Ljakarta/servlet/FilterChain;)V public fun getSentryUserProviders ()Ljava/util/List; } @@ -96,7 +96,7 @@ public abstract interface annotation class io/sentry/spring/jakarta/checkin/Sent public class io/sentry/spring/jakarta/checkin/SentryCheckInAdvice : org/aopalliance/intercept/MethodInterceptor, org/springframework/context/EmbeddedValueResolverAware { public fun ()V - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun invoke (Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object; public fun setEmbeddedValueResolver (Lorg/springframework/util/StringValueResolver;)V } @@ -127,7 +127,7 @@ public abstract interface annotation class io/sentry/spring/jakarta/exception/Se public class io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdvice : org/aopalliance/intercept/MethodInterceptor { public fun ()V - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun invoke (Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object; } @@ -169,7 +169,7 @@ public final class io/sentry/spring/jakarta/graphql/SentryDataFetcherExceptionRe public final class io/sentry/spring/jakarta/graphql/SentryDgsSubscriptionHandler : io/sentry/graphql/SentrySubscriptionHandler { public fun ()V - public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IHub;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; + public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IScopes;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; } public final class io/sentry/spring/jakarta/graphql/SentryGraphqlBeanPostProcessor : org/springframework/beans/factory/config/BeanPostProcessor, org/springframework/core/PriorityOrdered { @@ -188,7 +188,7 @@ public class io/sentry/spring/jakarta/graphql/SentryGraphqlConfiguration { public final class io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandler : io/sentry/graphql/SentrySubscriptionHandler { public fun ()V - public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IHub;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; + public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IScopes;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; } public class io/sentry/spring/jakarta/tracing/SentryAdviceConfiguration { @@ -207,18 +207,18 @@ public abstract interface annotation class io/sentry/spring/jakarta/tracing/Sent public class io/sentry/spring/jakarta/tracing/SentrySpanAdvice : org/aopalliance/intercept/MethodInterceptor { public fun ()V - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun invoke (Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object; } public class io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor : org/springframework/http/client/ClientHttpRequestInterceptor { - public fun (Lio/sentry/IHub;)V - public fun (Lio/sentry/IHub;Z)V + public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IScopes;Z)V public fun intercept (Lorg/springframework/http/HttpRequest;[BLorg/springframework/http/client/ClientHttpRequestExecution;)Lorg/springframework/http/client/ClientHttpResponse; } public class io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter : org/springframework/web/reactive/function/client/ExchangeFilterFunction { - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun filter (Lorg/springframework/web/reactive/function/client/ClientRequest;Lorg/springframework/web/reactive/function/client/ExchangeFunction;)Lreactor/core/publisher/Mono; } @@ -233,8 +233,8 @@ public class io/sentry/spring/jakarta/tracing/SentryTracingConfiguration { public class io/sentry/spring/jakarta/tracing/SentryTracingFilter : org/springframework/web/filter/OncePerRequestFilter { public fun ()V - public fun (Lio/sentry/IHub;)V - public fun (Lio/sentry/IHub;Lio/sentry/spring/jakarta/tracing/TransactionNameProvider;)V + public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IScopes;Lio/sentry/spring/jakarta/tracing/TransactionNameProvider;)V protected fun doFilterInternal (Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;Ljakarta/servlet/FilterChain;)V } @@ -246,7 +246,7 @@ public abstract interface annotation class io/sentry/spring/jakarta/tracing/Sent public class io/sentry/spring/jakarta/tracing/SentryTransactionAdvice : org/aopalliance/intercept/MethodInterceptor { public fun ()V - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun invoke (Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object; } @@ -268,21 +268,22 @@ public abstract interface class io/sentry/spring/jakarta/tracing/TransactionName public abstract class io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter : org/springframework/web/server/WebFilter { public static final field SENTRY_HUB_KEY Ljava/lang/String; - public fun (Lio/sentry/IHub;)V - protected fun doFinally (Lorg/springframework/web/server/ServerWebExchange;Lio/sentry/IHub;Lio/sentry/ITransaction;)V - protected fun doFirst (Lorg/springframework/web/server/ServerWebExchange;Lio/sentry/IHub;)V + public static final field SENTRY_SCOPES_KEY Ljava/lang/String; + public fun (Lio/sentry/IScopes;)V + protected fun doFinally (Lorg/springframework/web/server/ServerWebExchange;Lio/sentry/IScopes;Lio/sentry/ITransaction;)V + protected fun doFirst (Lorg/springframework/web/server/ServerWebExchange;Lio/sentry/IScopes;)V protected fun doOnError (Lio/sentry/ITransaction;Ljava/lang/Throwable;)V - protected fun maybeStartTransaction (Lio/sentry/IHub;Lorg/springframework/http/server/reactive/ServerHttpRequest;)Lio/sentry/ITransaction; - protected fun shouldTraceRequest (Lio/sentry/IHub;Lorg/springframework/http/server/reactive/ServerHttpRequest;)Z - protected fun startTransaction (Lio/sentry/IHub;Lorg/springframework/http/server/reactive/ServerHttpRequest;Lio/sentry/TransactionContext;)Lio/sentry/ITransaction; + protected fun maybeStartTransaction (Lio/sentry/IScopes;Lorg/springframework/http/server/reactive/ServerHttpRequest;)Lio/sentry/ITransaction; + protected fun shouldTraceRequest (Lio/sentry/IScopes;Lorg/springframework/http/server/reactive/ServerHttpRequest;)Z + protected fun startTransaction (Lio/sentry/IScopes;Lorg/springframework/http/server/reactive/ServerHttpRequest;Lio/sentry/TransactionContext;)Lio/sentry/ITransaction; } public final class io/sentry/spring/jakarta/webflux/ReactorUtils { public fun ()V public static fun withSentry (Lreactor/core/publisher/Flux;)Lreactor/core/publisher/Flux; public static fun withSentry (Lreactor/core/publisher/Mono;)Lreactor/core/publisher/Mono; - public static fun withSentryHub (Lreactor/core/publisher/Flux;Lio/sentry/IHub;)Lreactor/core/publisher/Flux; - public static fun withSentryHub (Lreactor/core/publisher/Mono;Lio/sentry/IHub;)Lreactor/core/publisher/Mono; + public static fun withSentryHub (Lreactor/core/publisher/Flux;Lio/sentry/IScopes;)Lreactor/core/publisher/Flux; + public static fun withSentryHub (Lreactor/core/publisher/Mono;Lio/sentry/IScopes;)Lreactor/core/publisher/Mono; public static fun withSentryNewMainHubClone (Lreactor/core/publisher/Flux;)Lreactor/core/publisher/Flux; public static fun withSentryNewMainHubClone (Lreactor/core/publisher/Mono;)Lreactor/core/publisher/Mono; } @@ -290,16 +291,16 @@ public final class io/sentry/spring/jakarta/webflux/ReactorUtils { public final class io/sentry/spring/jakarta/webflux/SentryReactorThreadLocalAccessor : io/micrometer/context/ThreadLocalAccessor { public static final field KEY Ljava/lang/String; public fun ()V - public fun getValue ()Lio/sentry/IHub; + public fun getValue ()Lio/sentry/IScopes; public synthetic fun getValue ()Ljava/lang/Object; public fun key ()Ljava/lang/Object; public fun reset ()V - public fun setValue (Lio/sentry/IHub;)V + public fun setValue (Lio/sentry/IScopes;)V public synthetic fun setValue (Ljava/lang/Object;)V } public class io/sentry/spring/jakarta/webflux/SentryRequestResolver { - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun resolveSentryRequest (Lorg/springframework/http/server/reactive/ServerHttpRequest;)Lio/sentry/protocol/Request; } @@ -311,18 +312,18 @@ public final class io/sentry/spring/jakarta/webflux/SentryScheduleHook : java/ut public final class io/sentry/spring/jakarta/webflux/SentryWebExceptionHandler : org/springframework/web/server/WebExceptionHandler { public static final field MECHANISM_TYPE Ljava/lang/String; - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun handle (Lorg/springframework/web/server/ServerWebExchange;Ljava/lang/Throwable;)Lreactor/core/publisher/Mono; } public class io/sentry/spring/jakarta/webflux/SentryWebFilter : io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter { - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun filter (Lorg/springframework/web/server/ServerWebExchange;Lorg/springframework/web/server/WebFilterChain;)Lreactor/core/publisher/Mono; } public final class io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor : io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter { public static final field TRACE_ORIGIN Ljava/lang/String; - public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IScopes;)V public fun filter (Lorg/springframework/web/server/ServerWebExchange;Lorg/springframework/web/server/WebFilterChain;)Lreactor/core/publisher/Mono; } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/EnableSentry.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/EnableSentry.java index 1395e035a1..e8cd91f3f4 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/EnableSentry.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/EnableSentry.java @@ -12,7 +12,7 @@ * *

    *
  • creates bean of type {@link io.sentry.SentryOptions} - *
  • registers {@link io.sentry.IHub} for sending Sentry events + *
  • registers {@link io.sentry.IScopes} for sending Sentry events *
  • registers {@link SentryExceptionResolver} to send Sentry event for any uncaught exception * in Spring MVC flow. *
diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryExceptionResolver.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryExceptionResolver.java index 6d4098d624..efa98ef581 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryExceptionResolver.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryExceptionResolver.java @@ -5,7 +5,7 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.Hint; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.SentryEvent; import io.sentry.SentryLevel; import io.sentry.exception.ExceptionMechanismException; @@ -29,15 +29,15 @@ public class SentryExceptionResolver implements HandlerExceptionResolver, Ordered { public static final String MECHANISM_TYPE = "Spring6ExceptionResolver"; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @NotNull TransactionNameProvider transactionNameProvider; private final int order; public SentryExceptionResolver( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull TransactionNameProvider transactionNameProvider, final int order) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); this.transactionNameProvider = Objects.requireNonNull(transactionNameProvider, "transactionNameProvider is required"); this.order = order; @@ -53,7 +53,7 @@ public SentryExceptionResolver( final SentryEvent event = createEvent(request, ex); final Hint hint = createHint(request, response); - hub.captureEvent(event, hint); + scopes.captureEvent(event, hint); // null = run other HandlerExceptionResolvers to actually handle the exception return null; diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryHubRegistrar.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryHubRegistrar.java index 4a8789c812..9598f0c926 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryHubRegistrar.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryHubRegistrar.java @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta; import com.jakewharton.nopen.annotation.Open; -import io.sentry.HubAdapter; +import io.sentry.ScopesAdapter; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryOptions; import io.sentry.protocol.SdkVersion; @@ -60,7 +60,7 @@ private void registerSentryOptions( private void registerSentryHubBean(final @NotNull BeanDefinitionRegistry registry) { final BeanDefinitionBuilder builder = - BeanDefinitionBuilder.genericBeanDefinition(HubAdapter.class); + BeanDefinitionBuilder.genericBeanDefinition(ScopesAdapter.class); builder.setInitMethodName("getInstance"); registry.registerBeanDefinition("sentryHub", builder.getBeanDefinition()); diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryInitBeanPostProcessor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryInitBeanPostProcessor.java index 9937bfbf1a..d33dfca8d8 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryInitBeanPostProcessor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryInitBeanPostProcessor.java @@ -2,10 +2,10 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.EventProcessor; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ITransportFactory; import io.sentry.Integration; +import io.sentry.ScopesAdapter; import io.sentry.Sentry; import io.sentry.SentryOptions; import io.sentry.SentryOptions.TracesSamplerCallback; @@ -27,15 +27,15 @@ public class SentryInitBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware, DisposableBean { private @Nullable ApplicationContext applicationContext; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; public SentryInitBeanPostProcessor() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } - SentryInitBeanPostProcessor(final @NotNull IHub hub) { - Objects.requireNonNull(hub, "hub is required"); - this.hub = hub; + SentryInitBeanPostProcessor(final @NotNull IScopes scopes) { + Objects.requireNonNull(scopes, "Scopes are required"); + this.scopes = scopes; } @Override @@ -86,6 +86,6 @@ public void setApplicationContext(final @NotNull ApplicationContext applicationC @Override public void destroy() { - hub.close(); + scopes.close(); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryRequestResolver.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryRequestResolver.java index e1bc45ac64..71f9079f9f 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryRequestResolver.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryRequestResolver.java @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.SentryLevel; import io.sentry.protocol.Request; import io.sentry.util.HttpUtils; @@ -20,11 +20,11 @@ @Open public class SentryRequestResolver { - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private volatile @Nullable List extraSecurityCookies; - public SentryRequestResolver(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "options is required"); + public SentryRequestResolver(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "options is required"); } // httpRequest.getRequestURL() returns StringBuffer which is considered an obsolete class. @@ -40,7 +40,7 @@ public SentryRequestResolver(final @NotNull IHub hub) { extractSecurityCookieNamesOrUseCached(httpRequest); sentryRequest.setHeaders(resolveHeadersMap(httpRequest, additionalSecurityCookieNames)); - if (hub.getOptions().isSendDefaultPii()) { + if (scopes.getOptions().isSendDefaultPii()) { String cookieName = HttpUtils.COOKIE_HEADER_NAME; final @Nullable List filteredHeaders = HttpUtils.filterOutSecurityCookiesFromHeader( @@ -57,7 +57,8 @@ Map resolveHeadersMap( final Map headersMap = new HashMap<>(); for (String headerName : Collections.list(request.getHeaderNames())) { // do not copy personal information identifiable headers - if (hub.getOptions().isSendDefaultPii() || !HttpUtils.containsSensitiveHeader(headerName)) { + if (scopes.getOptions().isSendDefaultPii() + || !HttpUtils.containsSensitiveHeader(headerName)) { final @Nullable List filteredHeaders = HttpUtils.filterOutSecurityCookiesFromHeader( request.getHeaders(headerName), headerName, additionalSecurityCookieNames); @@ -94,7 +95,8 @@ private List extractSecurityCookieNames(final @NotNull HttpServletReques } } } catch (Throwable t) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log(SentryLevel.WARNING, "Failed to extract session cookie name from request.", t); } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java index 129ea739d6..be06d3d253 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java @@ -8,8 +8,8 @@ import io.sentry.Breadcrumb; import io.sentry.EventProcessor; import io.sentry.Hint; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; +import io.sentry.ScopesAdapter; import io.sentry.SentryEvent; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -29,26 +29,26 @@ @Open public class SentrySpringFilter extends OncePerRequestFilter { - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @NotNull SentryRequestResolver requestResolver; private final @NotNull TransactionNameProvider transactionNameProvider; public SentrySpringFilter( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull SentryRequestResolver requestResolver, final @NotNull TransactionNameProvider transactionNameProvider) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); this.requestResolver = Objects.requireNonNull(requestResolver, "requestResolver is required"); this.transactionNameProvider = Objects.requireNonNull(transactionNameProvider, "transactionNameProvider is required"); } - public SentrySpringFilter(final @NotNull IHub hub) { - this(hub, new SentryRequestResolver(hub), new SpringMvcTransactionNameProvider()); + public SentrySpringFilter(final @NotNull IScopes scopes) { + this(scopes, new SentryRequestResolver(scopes), new SpringMvcTransactionNameProvider()); } public SentrySpringFilter() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } @Override @@ -57,20 +57,20 @@ protected void doFilterInternal( final @NotNull HttpServletResponse response, final @NotNull FilterChain filterChain) throws ServletException, IOException { - if (hub.isEnabled()) { + if (scopes.isEnabled()) { // request may qualify for caching request body, if so resolve cached request final HttpServletRequest request = resolveHttpServletRequest(servletRequest); - hub.pushScope(); + scopes.pushScope(); try { final Hint hint = new Hint(); hint.set(SPRING_REQUEST_FILTER_REQUEST, servletRequest); hint.set(SPRING_REQUEST_FILTER_RESPONSE, response); - hub.addBreadcrumb(Breadcrumb.http(request.getRequestURI(), request.getMethod()), hint); + scopes.addBreadcrumb(Breadcrumb.http(request.getRequestURI(), request.getMethod()), hint); configureScope(request); filterChain.doFilter(request, response); } finally { - hub.popScope(); + scopes.popScope(); } } else { filterChain.doFilter(servletRequest, response); @@ -79,7 +79,7 @@ protected void doFilterInternal( private void configureScope(HttpServletRequest request) { try { - hub.configureScope( + scopes.configureScope( scope -> { // set basic request information on the scope scope.setRequest(requestResolver.resolveSentryRequest(request)); @@ -92,11 +92,12 @@ private void configureScope(HttpServletRequest request) { // request processing if (request instanceof CachedBodyHttpServletRequest) { scope.addEventProcessor( - new RequestBodyExtractingEventProcessor(request, hub.getOptions())); + new RequestBodyExtractingEventProcessor(request, scopes.getOptions())); } }); } catch (Throwable e) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log(SentryLevel.ERROR, "Failed to set scope for HTTP request", e); } @@ -104,12 +105,13 @@ private void configureScope(HttpServletRequest request) { private @NotNull HttpServletRequest resolveHttpServletRequest( final @NotNull HttpServletRequest request) { - if (hub.getOptions().isSendDefaultPii() - && qualifiesForCaching(request, hub.getOptions().getMaxRequestBodySize())) { + if (scopes.getOptions().isSendDefaultPii() + && qualifiesForCaching(request, scopes.getOptions().getMaxRequestBodySize())) { try { return new CachedBodyHttpServletRequest(request); } catch (IOException e) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.WARNING, diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java index 5c4f37e521..c99abf3e21 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java @@ -1,6 +1,6 @@ package io.sentry.spring.jakarta; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.Sentry; import java.util.concurrent.Callable; import org.jetbrains.annotations.NotNull; @@ -8,22 +8,24 @@ import org.springframework.scheduling.annotation.Async; /** - * Sets a current hub on a thread running a {@link Runnable} given by parameter. Used to propagate - * the current {@link IHub} on the thread executing async task - like MVC controller methods - * returning a {@link Callable} or Spring beans methods annotated with {@link Async}. + * Sets a current scopes on a thread running a {@link Runnable} given by parameter. Used to + * propagate the current {@link IScopes} on the thread executing async task - like MVC controller + * methods returning a {@link Callable} or Spring beans methods annotated with {@link Async}. */ public final class SentryTaskDecorator implements TaskDecorator { @Override + @SuppressWarnings("deprecation") public @NotNull Runnable decorate(final @NotNull Runnable runnable) { - final IHub newHub = Sentry.getCurrentHub().clone(); + // TODO fork + final IScopes newHub = Sentry.getCurrentScopes().clone(); return () -> { - final IHub oldState = Sentry.getCurrentHub(); - Sentry.setCurrentHub(newHub); + final IScopes oldState = Sentry.getCurrentScopes(); + Sentry.setCurrentScopes(newHub); try { runnable.run(); } finally { - Sentry.setCurrentHub(oldState); + Sentry.setCurrentScopes(oldState); } }; } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryUserFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryUserFilter.java index f7b8bc62d6..31cc73a346 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryUserFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryUserFilter.java @@ -1,8 +1,8 @@ package io.sentry.spring.jakarta; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IHub; import io.sentry.IScope; +import io.sentry.IScopes; import io.sentry.IpAddressUtils; import io.sentry.protocol.User; import io.sentry.util.Objects; @@ -26,12 +26,12 @@ */ @Open public class SentryUserFilter extends OncePerRequestFilter { - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @NotNull List sentryUserProviders; public SentryUserFilter( - final @NotNull IHub hub, final @NotNull List sentryUserProviders) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + final @NotNull IScopes scopes, final @NotNull List sentryUserProviders) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); this.sentryUserProviders = Objects.requireNonNull(sentryUserProviders, "sentryUserProviders list is required"); } @@ -46,13 +46,13 @@ protected void doFilterInternal( for (final SentryUserProvider provider : sentryUserProviders) { apply(user, provider.provideUser()); } - if (hub.getOptions().isSendDefaultPii()) { + if (scopes.getOptions().isSendDefaultPii()) { if (IpAddressUtils.isDefault(user.getIpAddress())) { // unset {{auto}} as it would set the server's ip address as a user ip address user.setIpAddress(null); } } - hub.setUser(user); + scopes.setUser(user); chain.doFilter(request, response); } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/checkin/SentryCheckInAdvice.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/checkin/SentryCheckInAdvice.java index 5a4e329fa8..4a366a8b01 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/checkin/SentryCheckInAdvice.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/checkin/SentryCheckInAdvice.java @@ -4,8 +4,8 @@ import io.sentry.CheckIn; import io.sentry.CheckInStatus; import io.sentry.DateUtils; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; +import io.sentry.ScopesAdapter; import io.sentry.SentryLevel; import io.sentry.protocol.SentryId; import io.sentry.util.Objects; @@ -30,16 +30,16 @@ @ApiStatus.Experimental @Open public class SentryCheckInAdvice implements MethodInterceptor, EmbeddedValueResolverAware { - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private @Nullable StringValueResolver resolver; public SentryCheckInAdvice() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } - public SentryCheckInAdvice(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + public SentryCheckInAdvice(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); } @Override @@ -66,7 +66,8 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl // expressions. Testing shows this can also happen if properties cannot be resolved (without // an exception being thrown). Sentry should alert the user about missed checkins in this // case since the monitor slug won't match what is configured in Sentry. - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -76,7 +77,8 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } if (ObjectUtils.isEmpty(monitorSlug)) { - hub.getOptions() + scopes + .getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -84,8 +86,8 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl return invocation.proceed(); } - hub.pushScope(); - TracingUtils.startNewTrace(hub); + scopes.pushScope(); + TracingUtils.startNewTrace(scopes); @Nullable SentryId checkInId = null; final long startTime = System.currentTimeMillis(); @@ -93,7 +95,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl try { if (!isHeartbeatOnly) { - checkInId = hub.captureCheckIn(new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS)); + checkInId = scopes.captureCheckIn(new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS)); } return invocation.proceed(); } catch (Throwable e) { @@ -103,8 +105,8 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK; CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); - hub.captureCheckIn(checkIn); - hub.popScope(); + scopes.captureCheckIn(checkIn); + scopes.popScope(); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdvice.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdvice.java index f989382045..c6537f853c 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdvice.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdvice.java @@ -1,8 +1,8 @@ package io.sentry.spring.jakarta.exception; import com.jakewharton.nopen.annotation.Open; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; +import io.sentry.ScopesAdapter; import io.sentry.exception.ExceptionMechanismException; import io.sentry.protocol.Mechanism; import io.sentry.util.Objects; @@ -22,14 +22,14 @@ @Open public class SentryCaptureExceptionParameterAdvice implements MethodInterceptor { private static final String MECHANISM_TYPE = "SentrySpring6CaptureExceptionParameterAdvice"; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; public SentryCaptureExceptionParameterAdvice() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } - public SentryCaptureExceptionParameterAdvice(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + public SentryCaptureExceptionParameterAdvice(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); } @Override @@ -58,6 +58,6 @@ private void captureException(final @NotNull Throwable throwable) { mechanism.setHandled(true); final Throwable mechanismException = new ExceptionMechanismException(mechanism, throwable, Thread.currentThread()); - hub.captureException(mechanismException); + scopes.captureException(mechanismException); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryBatchLoaderRegistry.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryBatchLoaderRegistry.java index 1d5576c596..3e2223b694 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryBatchLoaderRegistry.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryBatchLoaderRegistry.java @@ -1,11 +1,11 @@ package io.sentry.spring.jakarta.graphql; -import static io.sentry.graphql.SentryInstrumentation.SENTRY_HUB_CONTEXT_KEY; +import static io.sentry.graphql.SentryInstrumentation.SENTRY_SCOPES_CONTEXT_KEY; import graphql.GraphQLContext; import io.sentry.Breadcrumb; -import io.sentry.IHub; -import io.sentry.NoOpHub; +import io.sentry.IScopes; +import io.sentry.NoOpScopes; import java.util.List; import java.util.Map; import java.util.Set; @@ -89,7 +89,7 @@ public BatchLoaderRegistry.RegistrationSpec withOptions(DataLoaderOptions public void registerBatchLoader(BiFunction, BatchLoaderEnvironment, Flux> loader) { delegate.registerBatchLoader( (keys, batchLoaderEnvironment) -> { - hubFromContext(batchLoaderEnvironment) + scopesFromContext(batchLoaderEnvironment) .addBreadcrumb(Breadcrumb.graphqlDataLoader(keys, keyType, valueType, name)); return loader.apply(keys, batchLoaderEnvironment); }); @@ -100,20 +100,20 @@ public void registerMappedBatchLoader( BiFunction, BatchLoaderEnvironment, Mono>> loader) { delegate.registerMappedBatchLoader( (keys, batchLoaderEnvironment) -> { - hubFromContext(batchLoaderEnvironment) + scopesFromContext(batchLoaderEnvironment) .addBreadcrumb(Breadcrumb.graphqlDataLoader(keys, keyType, valueType, name)); return loader.apply(keys, batchLoaderEnvironment); }); } - private @NotNull IHub hubFromContext(final @NotNull BatchLoaderEnvironment environment) { + private @NotNull IScopes scopesFromContext(final @NotNull BatchLoaderEnvironment environment) { Object context = environment.getContext(); if (context instanceof GraphQLContext) { GraphQLContext graphqlContext = (GraphQLContext) context; - return graphqlContext.getOrDefault(SENTRY_HUB_CONTEXT_KEY, NoOpHub.getInstance()); + return graphqlContext.getOrDefault(SENTRY_SCOPES_CONTEXT_KEY, NoOpScopes.getInstance()); } - return NoOpHub.getInstance(); + return NoOpScopes.getInstance(); } } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryDgsSubscriptionHandler.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryDgsSubscriptionHandler.java index 83a090954d..a7a6cccd3e 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryDgsSubscriptionHandler.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryDgsSubscriptionHandler.java @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta.graphql; import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.graphql.ExceptionReporter; import io.sentry.graphql.SentrySubscriptionHandler; @@ -17,7 +17,7 @@ public SentryDgsSubscriptionHandler() { @Override public @NotNull Object onSubscriptionResult( final @NotNull Object result, - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull ExceptionReporter exceptionReporter, final @NotNull InstrumentationFieldFetchParameters parameters) { if (result instanceof Flux) { @@ -25,7 +25,7 @@ public SentryDgsSubscriptionHandler() { return flux.doOnError( throwable -> { final @NotNull ExceptionReporter.ExceptionDetails exceptionDetails = - new ExceptionReporter.ExceptionDetails(hub, parameters.getEnvironment(), true); + new ExceptionReporter.ExceptionDetails(scopes, parameters.getEnvironment(), true); exceptionReporter.captureThrowable(throwable, exceptionDetails, null); }); } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandler.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandler.java index 4c51981035..eec86f5e8b 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandler.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandler.java @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta.graphql; import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.graphql.ExceptionReporter; import io.sentry.graphql.SentrySubscriptionHandler; import org.jetbrains.annotations.NotNull; @@ -13,7 +13,7 @@ public final class SentrySpringSubscriptionHandler implements SentrySubscription @Override public @NotNull Object onSubscriptionResult( final @NotNull Object result, - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull ExceptionReporter exceptionReporter, final @NotNull InstrumentationFieldFetchParameters parameters) { if (result instanceof Flux) { @@ -21,7 +21,7 @@ public final class SentrySpringSubscriptionHandler implements SentrySubscription return flux.doOnError( throwable -> { final @NotNull ExceptionReporter.ExceptionDetails exceptionDetails = - new ExceptionReporter.ExceptionDetails(hub, parameters.getEnvironment(), true); + new ExceptionReporter.ExceptionDetails(scopes, parameters.getEnvironment(), true); if (throwable instanceof SubscriptionPublisherException && throwable.getCause() != null) { exceptionReporter.captureThrowable(throwable.getCause(), exceptionDetails, null); diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanAdvice.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanAdvice.java index 5f345a4e02..e8de36487f 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanAdvice.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanAdvice.java @@ -1,9 +1,9 @@ package io.sentry.spring.jakarta.tracing; import com.jakewharton.nopen.annotation.Open; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ISpan; +import io.sentry.ScopesAdapter; import io.sentry.SpanStatus; import io.sentry.util.Objects; import java.lang.reflect.Method; @@ -22,20 +22,20 @@ @Open public class SentrySpanAdvice implements MethodInterceptor { private static final String TRACE_ORIGIN = "auto.function.spring_jakarta.advice"; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; public SentrySpanAdvice() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } - public SentrySpanAdvice(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + public SentrySpanAdvice(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); } @SuppressWarnings("deprecation") @Override public Object invoke(final @NotNull MethodInvocation invocation) throws Throwable { - final ISpan activeSpan = hub.getSpan(); + final ISpan activeSpan = scopes.getSpan(); if (activeSpan == null || activeSpan.isNoOp()) { // there is no active transaction, we do not start new span diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java index 59c1926ff3..7a787fb29d 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java @@ -8,7 +8,7 @@ import io.sentry.BaggageHeader; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.SpanDataConvention; import io.sentry.SpanStatus; @@ -28,16 +28,16 @@ public class SentrySpanClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { private static final String TRACE_ORIGIN_REST_TEMPLATE = "auto.http.spring_jakarta.resttemplate"; private static final String TRACE_ORIGIN_REST_CLIENT = "auto.http.spring_jakarta.restclient"; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; private final @NotNull String traceOrigin; - public SentrySpanClientHttpRequestInterceptor(final @NotNull IHub hub) { - this(hub, true); + public SentrySpanClientHttpRequestInterceptor(final @NotNull IScopes scopes) { + this(scopes, true); } public SentrySpanClientHttpRequestInterceptor( - final @NotNull IHub hub, final @NotNull boolean isRestTemplate) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + final @NotNull IScopes scopes, final @NotNull boolean isRestTemplate) { + this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); this.traceOrigin = isRestTemplate ? TRACE_ORIGIN_REST_TEMPLATE : TRACE_ORIGIN_REST_CLIENT; } @@ -50,7 +50,7 @@ public SentrySpanClientHttpRequestInterceptor( Integer responseStatusCode = null; ClientHttpResponse response = null; try { - final ISpan activeSpan = hub.getSpan(); + final ISpan activeSpan = scopes.getSpan(); if (activeSpan == null) { maybeAddTracingHeaders(request, null); return execution.execute(request, body); @@ -91,7 +91,7 @@ private void maybeAddTracingHeaders( final @NotNull HttpRequest request, final @Nullable ISpan span) { final @Nullable TracingUtils.TracingHeaders tracingHeaders = TracingUtils.traceIfAllowed( - hub, + scopes, request.getURI().toString(), request.getHeaders().get(BaggageHeader.BAGGAGE_HEADER), span); @@ -128,6 +128,6 @@ private void addBreadcrumb( hint.set(SPRING_REQUEST_INTERCEPTOR_RESPONSE, response); } - hub.addBreadcrumb(breadcrumb, hint); + scopes.addBreadcrumb(breadcrumb, hint); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java index 4ac511721e..bc5c0edfab 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java @@ -7,7 +7,7 @@ import io.sentry.BaggageHeader; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.SpanDataConvention; import io.sentry.SpanStatus; @@ -25,16 +25,16 @@ @Open public class SentrySpanClientWebRequestFilter implements ExchangeFilterFunction { private static final String TRACE_ORIGIN = "auto.http.spring_jakarta.webclient"; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; - public SentrySpanClientWebRequestFilter(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + public SentrySpanClientWebRequestFilter(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); } @Override public @NotNull Mono filter( final @NotNull ClientRequest request, final @NotNull ExchangeFunction next) { - final ISpan activeSpan = hub.getSpan(); + final ISpan activeSpan = scopes.getSpan(); if (activeSpan == null) { final @NotNull ClientRequest modifiedRequest = maybeAddTracingHeaders(request, null); addBreadcrumb(modifiedRequest, null); @@ -74,7 +74,7 @@ public SentrySpanClientWebRequestFilter(final @NotNull IHub hub) { final @Nullable TracingUtils.TracingHeaders tracingHeaders = TracingUtils.traceIfAllowed( - hub, + scopes, request.url().toString(), request.headers().get(BaggageHeader.BAGGAGE_HEADER), span); @@ -111,6 +111,6 @@ private void addBreadcrumb( hint.set(SPRING_EXCHANGE_FILTER_RESPONSE, response); } - hub.addBreadcrumb(breadcrumb, hint); + scopes.addBreadcrumb(breadcrumb, hint); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java index ed91f85a29..097528ac44 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java @@ -3,9 +3,9 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.BaggageHeader; import io.sentry.CustomSamplingContext; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ITransaction; +import io.sentry.ScopesAdapter; import io.sentry.SentryTraceHeader; import io.sentry.SpanStatus; import io.sentry.TransactionContext; @@ -38,7 +38,7 @@ public class SentryTracingFilter extends OncePerRequestFilter { private static final String TRACE_ORIGIN = "auto.http.spring_jakarta.webmvc"; private final @NotNull TransactionNameProvider transactionNameProvider; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; /** * Creates filter that resolves transaction name using {@link SpringMvcTransactionNameProvider}. @@ -49,25 +49,26 @@ public class SentryTracingFilter extends OncePerRequestFilter { * jakarta.servlet.Filter}. */ public SentryTracingFilter() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } /** * Creates filter that resolves transaction name using transaction name provider given by * parameter. * - * @param hub - the hub + * @param scopes - the scopes * @param transactionNameProvider - transaction name provider. */ public SentryTracingFilter( - final @NotNull IHub hub, final @NotNull TransactionNameProvider transactionNameProvider) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + final @NotNull IScopes scopes, + final @NotNull TransactionNameProvider transactionNameProvider) { + this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); this.transactionNameProvider = Objects.requireNonNull(transactionNameProvider, "transactionNameProvider is required"); } - public SentryTracingFilter(final @NotNull IHub hub) { - this(hub, new SpringMvcTransactionNameProvider()); + public SentryTracingFilter(final @NotNull IScopes scopes) { + this(scopes, new SpringMvcTransactionNameProvider()); } @Override @@ -76,14 +77,14 @@ protected void doFilterInternal( final @NotNull HttpServletResponse httpResponse, final @NotNull FilterChain filterChain) throws ServletException, IOException { - if (hub.isEnabled()) { + if (scopes.isEnabled()) { final @Nullable String sentryTraceHeader = httpRequest.getHeader(SentryTraceHeader.SENTRY_TRACE_HEADER); final @Nullable List baggageHeader = Collections.list(httpRequest.getHeaders(BaggageHeader.BAGGAGE_HEADER)); final @Nullable TransactionContext transactionContext = - hub.continueTrace(sentryTraceHeader, baggageHeader); - if (hub.getOptions().isTracingEnabled() && shouldTraceRequest(httpRequest)) { + scopes.continueTrace(sentryTraceHeader, baggageHeader); + if (scopes.getOptions().isTracingEnabled() && shouldTraceRequest(httpRequest)) { doFilterWithTransaction(httpRequest, httpResponse, filterChain, transactionContext); } else { filterChain.doFilter(httpRequest, httpResponse); @@ -130,7 +131,7 @@ private void doFilterWithTransaction( } private boolean shouldTraceRequest(final @NotNull HttpServletRequest request) { - return hub.getOptions().isTraceOptionsRequests() + return scopes.getOptions().isTraceOptionsRequests() || !HttpMethod.OPTIONS.name().equals(request.getMethod()); } @@ -152,14 +153,14 @@ private ITransaction startTransaction( transactionOptions.setCustomSamplingContext(customSamplingContext); transactionOptions.setBindToScope(true); - return hub.startTransaction(transactionContext, transactionOptions); + return scopes.startTransaction(transactionContext, transactionOptions); } final TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setCustomSamplingContext(customSamplingContext); transactionOptions.setBindToScope(true); - return hub.startTransaction( + return scopes.startTransaction( new TransactionContext(name, TransactionNameSource.URL, "http.server"), transactionOptions); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java index 5b264defa4..f04b3dd7a6 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java @@ -1,9 +1,9 @@ package io.sentry.spring.jakarta.tracing; import com.jakewharton.nopen.annotation.Open; -import io.sentry.HubAdapter; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.ITransaction; +import io.sentry.ScopesAdapter; import io.sentry.SpanStatus; import io.sentry.TransactionContext; import io.sentry.TransactionOptions; @@ -28,14 +28,14 @@ public class SentryTransactionAdvice implements MethodInterceptor { private static final String TRACE_ORIGIN = "auto.function.spring_jakarta.advice"; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; public SentryTransactionAdvice() { - this(HubAdapter.getInstance()); + this(ScopesAdapter.getInstance()); } - public SentryTransactionAdvice(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + public SentryTransactionAdvice(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); } @SuppressWarnings("deprecation") @@ -68,11 +68,11 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } else { operation = "bean"; } - hub.pushScope(); + scopes.pushScope(); final TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setBindToScope(true); final ITransaction transaction = - hub.startTransaction( + scopes.startTransaction( new TransactionContext(nameAndSource.name, nameAndSource.source, operation), transactionOptions); transaction.getSpanContext().setOrigin(TRACE_ORIGIN); @@ -86,7 +86,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl throw e; } finally { transaction.finish(); - hub.popScope(); + scopes.popScope(); } } } @@ -106,7 +106,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } private boolean isTransactionActive() { - return hub.getSpan() != null; + return scopes.getSpan() != null; } private static class TransactionNameAndSource { diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java index bf25aa5f49..3321874dd8 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java @@ -7,10 +7,10 @@ import io.sentry.Breadcrumb; import io.sentry.CustomSamplingContext; import io.sentry.Hint; -import io.sentry.IHub; import io.sentry.IScope; +import io.sentry.IScopes; import io.sentry.ITransaction; -import io.sentry.NoOpHub; +import io.sentry.NoOpScopes; import io.sentry.Sentry; import io.sentry.SentryTraceHeader; import io.sentry.SpanStatus; @@ -34,16 +34,17 @@ @ApiStatus.Experimental public abstract class AbstractSentryWebFilter implements WebFilter { private final @NotNull SentryRequestResolver sentryRequestResolver; - public static final String SENTRY_HUB_KEY = "sentry-hub"; + public static final String SENTRY_SCOPES_KEY = "sentry-scopes"; + @Deprecated public static final String SENTRY_HUB_KEY = SENTRY_SCOPES_KEY; private static final String TRANSACTION_OP = "http.server"; - public AbstractSentryWebFilter(final @NotNull IHub hub) { - Objects.requireNonNull(hub, "hub is required"); - this.sentryRequestResolver = new SentryRequestResolver(hub); + public AbstractSentryWebFilter(final @NotNull IScopes scopes) { + Objects.requireNonNull(scopes, "scopes are required"); + this.sentryRequestResolver = new SentryRequestResolver(scopes); } protected @Nullable ITransaction maybeStartTransaction( - final @NotNull IHub requestHub, final @NotNull ServerHttpRequest request) { + final @NotNull IScopes requestHub, final @NotNull ServerHttpRequest request) { if (requestHub.isEnabled()) { final @NotNull HttpHeaders headers = request.getHeaders(); final @Nullable String sentryTraceHeader = @@ -62,21 +63,23 @@ public AbstractSentryWebFilter(final @NotNull IHub hub) { protected void doFinally( final @NotNull ServerWebExchange serverWebExchange, - final @NotNull IHub requestHub, + final @NotNull IScopes requestHub, final @Nullable ITransaction transaction) { if (transaction != null) { finishTransaction(serverWebExchange, transaction); } if (requestHub.isEnabled()) { + // TODO close lifecycle token instead of popscope requestHub.popScope(); } - Sentry.setCurrentHub(NoOpHub.getInstance()); + Sentry.setCurrentScopes(NoOpScopes.getInstance()); } protected void doFirst( - final @NotNull ServerWebExchange serverWebExchange, final @NotNull IHub requestHub) { + final @NotNull ServerWebExchange serverWebExchange, final @NotNull IScopes requestHub) { if (requestHub.isEnabled()) { - serverWebExchange.getAttributes().put(SENTRY_HUB_KEY, requestHub); + serverWebExchange.getAttributes().put(SENTRY_SCOPES_KEY, requestHub); + // TODO fork instead requestHub.pushScope(); final ServerHttpRequest request = serverWebExchange.getRequest(); final ServerHttpResponse response = serverWebExchange.getResponse(); @@ -100,8 +103,8 @@ protected void doOnError(final @Nullable ITransaction transaction, final @NotNul } protected boolean shouldTraceRequest( - final @NotNull IHub hub, final @NotNull ServerHttpRequest request) { - return hub.getOptions().isTraceOptionsRequests() + final @NotNull IScopes scopes, final @NotNull ServerHttpRequest request) { + return scopes.getOptions().isTraceOptionsRequests() || !HttpMethod.OPTIONS.equals(request.getMethod()); } @@ -130,7 +133,7 @@ private void finishTransaction(ServerWebExchange exchange, ITransaction transact } protected @NotNull ITransaction startTransaction( - final @NotNull IHub hub, + final @NotNull IScopes scopes, final @NotNull ServerHttpRequest request, final @Nullable TransactionContext transactionContext) { final @NotNull String name = request.getMethod() + " " + request.getURI().getPath(); @@ -146,10 +149,10 @@ private void finishTransaction(ServerWebExchange exchange, ITransaction transact transactionContext.setTransactionNameSource(TransactionNameSource.URL); transactionContext.setOperation(TRANSACTION_OP); - return hub.startTransaction(transactionContext, transactionOptions); + return scopes.startTransaction(transactionContext, transactionOptions); } - return hub.startTransaction( + return scopes.startTransaction( new TransactionContext(name, TransactionNameSource.URL, TRANSACTION_OP), transactionOptions); } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java index 4af641af29..41dd2e4bc0 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java @@ -1,6 +1,6 @@ package io.sentry.spring.jakarta.webflux; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.Sentry; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -8,11 +8,12 @@ import reactor.core.publisher.Mono; import reactor.util.context.Context; +// TODO deprecate and replace with "withSentryScopes" etc. @ApiStatus.Experimental public final class ReactorUtils { /** - * Writes the current Sentry {@link IHub} to the {@link Context} and uses {@link + * Writes the current Sentry {@link IScopes} to the {@link Context} and uses {@link * io.micrometer.context.ThreadLocalAccessor} to propagate it. * *

This requires - reactor.core.publisher.Hooks#enableAutomaticContextPropagation() to be @@ -20,14 +21,16 @@ public final class ReactorUtils { * having `io.projectreactor:reactor-core:3.5.3+` (provided by Spring Boot 3.0.3+) */ @ApiStatus.Experimental + @SuppressWarnings("deprecation") public static Mono withSentry(final @NotNull Mono mono) { - final @NotNull IHub oldHub = Sentry.getCurrentHub(); - final @NotNull IHub clonedHub = oldHub.clone(); + final @NotNull IScopes oldHub = Sentry.getCurrentScopes(); + // TODO fork + final @NotNull IScopes clonedHub = oldHub.clone(); return withSentryHub(mono, clonedHub); } /** - * Writes a new Sentry {@link IHub} cloned from the main hub to the {@link Context} and uses + * Writes a new Sentry {@link IScopes} cloned from the main hub to the {@link Context} and uses * {@link io.micrometer.context.ThreadLocalAccessor} to propagate it. * *

This requires - reactor.core.publisher.Hooks#enableAutomaticContextPropagation() to be @@ -36,12 +39,12 @@ public static Mono withSentry(final @NotNull Mono mono) { */ @ApiStatus.Experimental public static Mono withSentryNewMainHubClone(final @NotNull Mono mono) { - final @NotNull IHub hub = Sentry.cloneMainHub(); + final @NotNull IScopes hub = Sentry.cloneMainHub(); return withSentryHub(mono, hub); } /** - * Writes the given Sentry {@link IHub} to the {@link Context} and uses {@link + * Writes the given Sentry {@link IScopes} to the {@link Context} and uses {@link * io.micrometer.context.ThreadLocalAccessor} to propagate it. * *

This requires - reactor.core.publisher.Hooks#enableAutomaticContextPropagation() to be @@ -49,7 +52,7 @@ public static Mono withSentryNewMainHubClone(final @NotNull Mono mono) * having `io.projectreactor:reactor-core:3.5.3+` (provided by Spring Boot 3.0.3+) */ @ApiStatus.Experimental - public static Mono withSentryHub(final @NotNull Mono mono, final @NotNull IHub hub) { + public static Mono withSentryHub(final @NotNull Mono mono, final @NotNull IScopes hub) { /** * WARNING: Cannot set the hub as current. It would be used by others to clone again causing * shared hubs and scopes and thus leading to issues like unrelated breadcrumbs showing up in @@ -62,7 +65,7 @@ public static Mono withSentryHub(final @NotNull Mono mono, final @NotN } /** - * Writes the current Sentry {@link IHub} to the {@link Context} and uses {@link + * Writes the current Sentry {@link IScopes} to the {@link Context} and uses {@link * io.micrometer.context.ThreadLocalAccessor} to propagate it. * *

This requires - reactor.core.publisher.Hooks#enableAutomaticContextPropagation() to be @@ -70,15 +73,17 @@ public static Mono withSentryHub(final @NotNull Mono mono, final @NotN * having `io.projectreactor:reactor-core:3.5.3+` (provided by Spring Boot 3.0.3+) */ @ApiStatus.Experimental + @SuppressWarnings("deprecation") public static Flux withSentry(final @NotNull Flux flux) { - final @NotNull IHub oldHub = Sentry.getCurrentHub(); - final @NotNull IHub clonedHub = oldHub.clone(); + final @NotNull IScopes oldHub = Sentry.getCurrentScopes(); + // TODO fork + final @NotNull IScopes clonedHub = oldHub.clone(); return withSentryHub(flux, clonedHub); } /** - * Writes a new Sentry {@link IHub} cloned from the main hub to the {@link Context} and uses + * Writes a new Sentry {@link IScopes} cloned from the main hub to the {@link Context} and uses * {@link io.micrometer.context.ThreadLocalAccessor} to propagate it. * *

This requires - reactor.core.publisher.Hooks#enableAutomaticContextPropagation() to be @@ -87,12 +92,12 @@ public static Flux withSentry(final @NotNull Flux flux) { */ @ApiStatus.Experimental public static Flux withSentryNewMainHubClone(final @NotNull Flux flux) { - final @NotNull IHub hub = Sentry.cloneMainHub(); + final @NotNull IScopes hub = Sentry.cloneMainHub(); return withSentryHub(flux, hub); } /** - * Writes the given Sentry {@link IHub} to the {@link Context} and uses {@link + * Writes the given Sentry {@link IScopes} to the {@link Context} and uses {@link * io.micrometer.context.ThreadLocalAccessor} to propagate it. * *

This requires - reactor.core.publisher.Hooks#enableAutomaticContextPropagation() to be @@ -100,7 +105,7 @@ public static Flux withSentryNewMainHubClone(final @NotNull Flux flux) * having `io.projectreactor:reactor-core:3.5.3+` (provided by Spring Boot 3.0.3+) */ @ApiStatus.Experimental - public static Flux withSentryHub(final @NotNull Flux flux, final @NotNull IHub hub) { + public static Flux withSentryHub(final @NotNull Flux flux, final @NotNull IScopes hub) { /** * WARNING: Cannot set the hub as current. It would be used by others to clone again causing * shared hubs and scopes and thus leading to issues like unrelated breadcrumbs showing up in diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryReactorThreadLocalAccessor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryReactorThreadLocalAccessor.java index c64cf3d634..9b7e51db73 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryReactorThreadLocalAccessor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryReactorThreadLocalAccessor.java @@ -1,15 +1,15 @@ package io.sentry.spring.jakarta.webflux; import io.micrometer.context.ThreadLocalAccessor; -import io.sentry.IHub; -import io.sentry.NoOpHub; +import io.sentry.IScopes; +import io.sentry.NoOpScopes; import io.sentry.Sentry; import org.jetbrains.annotations.ApiStatus; @ApiStatus.Experimental -public final class SentryReactorThreadLocalAccessor implements ThreadLocalAccessor { +public final class SentryReactorThreadLocalAccessor implements ThreadLocalAccessor { - public static final String KEY = "sentry-hub"; + public static final String KEY = "sentry-scopes"; @Override public Object key() { @@ -17,18 +17,18 @@ public Object key() { } @Override - public IHub getValue() { - return Sentry.getCurrentHub(); + public IScopes getValue() { + return Sentry.getCurrentScopes(); } @Override - public void setValue(IHub value) { - Sentry.setCurrentHub(value); + public void setValue(IScopes value) { + Sentry.setCurrentScopes(value); } @Override @SuppressWarnings("deprecation") public void reset() { - Sentry.setCurrentHub(NoOpHub.getInstance()); + Sentry.setCurrentScopes(NoOpScopes.getInstance()); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryRequestResolver.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryRequestResolver.java index 2f41ba93ca..d58291ade6 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryRequestResolver.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryRequestResolver.java @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta.webflux; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.protocol.Request; import io.sentry.util.HttpUtils; import io.sentry.util.Objects; @@ -20,10 +20,10 @@ @Open @ApiStatus.Experimental public class SentryRequestResolver { - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; - public SentryRequestResolver(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "options is required"); + public SentryRequestResolver(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); } public @NotNull Request resolveSentryRequest(final @NotNull ServerHttpRequest httpRequest) { @@ -36,7 +36,7 @@ public SentryRequestResolver(final @NotNull IHub hub) { urlDetails.applyToRequest(sentryRequest); sentryRequest.setHeaders(resolveHeadersMap(httpRequest.getHeaders())); - if (hub.getOptions().isSendDefaultPii()) { + if (scopes.getOptions().isSendDefaultPii()) { String headerName = HttpUtils.COOKIE_HEADER_NAME; sentryRequest.setCookies( toString( @@ -52,7 +52,8 @@ Map resolveHeadersMap(final HttpHeaders request) { for (Map.Entry> entry : request.entrySet()) { // do not copy personal information identifiable headers String headerName = entry.getKey(); - if (hub.getOptions().isSendDefaultPii() || !HttpUtils.containsSensitiveHeader(headerName)) { + if (scopes.getOptions().isSendDefaultPii() + || !HttpUtils.containsSensitiveHeader(headerName)) { headersMap.put( headerName, toString( diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryScheduleHook.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryScheduleHook.java index 2bf27f246d..882a0b268a 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryScheduleHook.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryScheduleHook.java @@ -1,6 +1,6 @@ package io.sentry.spring.jakarta.webflux; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.Sentry; import java.util.function.Function; import org.jetbrains.annotations.ApiStatus; @@ -13,16 +13,18 @@ @ApiStatus.Experimental public final class SentryScheduleHook implements Function { @Override + @SuppressWarnings("deprecation") public Runnable apply(final @NotNull Runnable runnable) { - final IHub newHub = Sentry.getCurrentHub().clone(); + // TODO fork instead + final IScopes newHub = Sentry.getCurrentScopes().clone(); return () -> { - final IHub oldState = Sentry.getCurrentHub(); - Sentry.setCurrentHub(newHub); + final IScopes oldState = Sentry.getCurrentScopes(); + Sentry.setCurrentScopes(newHub); try { runnable.run(); } finally { - Sentry.setCurrentHub(oldState); + Sentry.setCurrentScopes(oldState); } }; } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebExceptionHandler.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebExceptionHandler.java index 24439cd0e9..40b0ed4e87 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebExceptionHandler.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebExceptionHandler.java @@ -5,7 +5,7 @@ import static io.sentry.TypeCheckHint.WEBFLUX_EXCEPTION_HANDLER_RESPONSE; import io.sentry.Hint; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.SentryEvent; import io.sentry.SentryLevel; import io.sentry.exception.ExceptionMechanismException; @@ -27,18 +27,18 @@ @ApiStatus.Experimental public final class SentryWebExceptionHandler implements WebExceptionHandler { public static final String MECHANISM_TYPE = "Spring6WebFluxExceptionResolver"; - private final @NotNull IHub hub; + private final @NotNull IScopes scopes; - public SentryWebExceptionHandler(final @NotNull IHub hub) { - this.hub = Objects.requireNonNull(hub, "hub is required"); + public SentryWebExceptionHandler(final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); } @Override public @NotNull Mono handle( final @NotNull ServerWebExchange serverWebExchange, final @NotNull Throwable ex) { - final @Nullable IHub requestHub = - serverWebExchange.getAttributeOrDefault(SentryWebFilter.SENTRY_HUB_KEY, null); - final @NotNull IHub hubToUse = requestHub != null ? requestHub : hub; + final @Nullable IScopes requestScopes = + serverWebExchange.getAttributeOrDefault(SentryWebFilter.SENTRY_SCOPES_KEY, null); + final @NotNull IScopes scopesToUse = requestScopes != null ? requestScopes : scopes; return ReactorUtils.withSentryHub( Mono.just(ex) @@ -61,12 +61,12 @@ public SentryWebExceptionHandler(final @NotNull IHub hub) { WEBFLUX_EXCEPTION_HANDLER_RESPONSE, serverWebExchange.getResponse()); hint.set(WEBFLUX_EXCEPTION_HANDLER_EXCHANGE, serverWebExchange); - hub.captureEvent(event, hint); + scopes.captureEvent(event, hint); } return it; }), - hubToUse) + scopesToUse) .flatMap(it -> Mono.error(ex)); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilter.java index a57a389499..dab985eecf 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilter.java @@ -1,8 +1,8 @@ package io.sentry.spring.jakarta.webflux; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IHub; import io.sentry.IScope; +import io.sentry.IScopes; import io.sentry.ITransaction; import io.sentry.Sentry; import org.jetbrains.annotations.ApiStatus; @@ -20,28 +20,28 @@ public class SentryWebFilter extends AbstractSentryWebFilter { private static final String TRACE_ORIGIN = "auto.spring_jakarta.webflux"; - public SentryWebFilter(final @NotNull IHub hub) { - super(hub); + public SentryWebFilter(final @NotNull IScopes scopes) { + super(scopes); } @Override public Mono filter( final @NotNull ServerWebExchange serverWebExchange, final @NotNull WebFilterChain webFilterChain) { - @NotNull IHub requestHub = Sentry.cloneMainHub(); + @NotNull IScopes requestScopes = Sentry.cloneMainHub(); final ServerHttpRequest request = serverWebExchange.getRequest(); - final @Nullable ITransaction transaction = maybeStartTransaction(requestHub, request); + final @Nullable ITransaction transaction = maybeStartTransaction(requestScopes, request); if (transaction != null) { transaction.getSpanContext().setOrigin(TRACE_ORIGIN); } return webFilterChain .filter(serverWebExchange) - .doFinally(__ -> doFinally(serverWebExchange, requestHub, transaction)) + .doFinally(__ -> doFinally(serverWebExchange, requestScopes, transaction)) .doOnError(e -> doOnError(transaction, e)) .doFirst( () -> { - Sentry.setCurrentHub(requestHub); - doFirst(serverWebExchange, requestHub); + Sentry.setCurrentScopes(requestScopes); + doFirst(serverWebExchange, requestScopes); }); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor.java index 278c2b8e7e..e760ef8f3e 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor.java @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta.webflux; -import io.sentry.IHub; import io.sentry.IScope; +import io.sentry.IScopes; import io.sentry.ITransaction; import io.sentry.Sentry; import org.jetbrains.annotations.ApiStatus; @@ -17,8 +17,8 @@ public final class SentryWebFilterWithThreadLocalAccessor extends AbstractSentry public static final String TRACE_ORIGIN = "auto.spring_jakarta.webflux"; - public SentryWebFilterWithThreadLocalAccessor(final @NotNull IHub hub) { - super(hub); + public SentryWebFilterWithThreadLocalAccessor(final @NotNull IScopes scopes) { + super(scopes); } @Override @@ -33,14 +33,15 @@ public Mono filter( __ -> doFinally( serverWebExchange, - Sentry.getCurrentHub(), + Sentry.getCurrentScopes(), transactionContainer.transaction)) .doOnError(e -> doOnError(transactionContainer.transaction, e)) .doFirst( () -> { - doFirst(serverWebExchange, Sentry.getCurrentHub()); + doFirst(serverWebExchange, Sentry.getCurrentScopes()); final ITransaction transaction = - maybeStartTransaction(Sentry.getCurrentHub(), serverWebExchange.getRequest()); + maybeStartTransaction( + Sentry.getCurrentScopes(), serverWebExchange.getRequest()); transactionContainer.transaction = transaction; if (transaction != null) { transaction.getSpanContext().setOrigin(TRACE_ORIGIN); diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/EnableSentryTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/EnableSentryTest.kt index 7b51bbc1e7..37853c3101 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/EnableSentryTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/EnableSentryTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta import io.sentry.EventProcessor -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ITransportFactory import io.sentry.Integration import io.sentry.Sentry @@ -67,7 +67,7 @@ class EnableSentryTest { @Test fun `creates Sentry Hub`() { contextRunner.run { - assertThat(it).hasSingleBean(IHub::class.java) + assertThat(it).hasSingleBean(IScopes::class.java) } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryCheckInAdviceTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryCheckInAdviceTest.kt index 05b97ba24c..5d093f50f1 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryCheckInAdviceTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryCheckInAdviceTest.kt @@ -2,7 +2,7 @@ package io.sentry.spring.jakarta import io.sentry.CheckIn import io.sentry.CheckInStatus -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.protocol.SentryId @@ -54,19 +54,19 @@ class SentryCheckInAdviceTest { lateinit var sampleServiceSpringProperties: SampleServiceSpringProperties @Autowired - lateinit var hub: IHub + lateinit var scopes: IScopes @BeforeTest fun setup() { - reset(hub) - whenever(hub.options).thenReturn(SentryOptions()) + reset(scopes) + whenever(scopes.options).thenReturn(SentryOptions()) } @Test fun `when method is annotated with @SentryCheckIn, every method call creates two check-ins`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleService.hello() assertEquals(1, result) assertEquals(2, checkInCaptor.allValues.size) @@ -79,17 +79,17 @@ class SentryCheckInAdviceTest { assertEquals("monitor_slug_1", doneCheckIn.monitorSlug) assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) - val order = inOrder(hub) - order.verify(hub).pushScope() - order.verify(hub, times(2)).captureCheckIn(any()) - order.verify(hub).popScope() + val order = inOrder(scopes) + order.verify(scopes).pushScope() + order.verify(scopes, times(2)).captureCheckIn(any()) + order.verify(scopes).popScope() } @Test fun `when method is annotated with @SentryCheckIn, every method call creates two check-ins error`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) assertThrows { sampleService.oops() } @@ -103,17 +103,17 @@ class SentryCheckInAdviceTest { assertEquals("monitor_slug_1e", doneCheckIn.monitorSlug) assertEquals(CheckInStatus.ERROR.apiName(), doneCheckIn.status) - val order = inOrder(hub) - order.verify(hub).pushScope() - order.verify(hub, times(2)).captureCheckIn(any()) - order.verify(hub).popScope() + val order = inOrder(scopes) + order.verify(scopes).pushScope() + order.verify(scopes, times(2)).captureCheckIn(any()) + order.verify(scopes).popScope() } @Test fun `when method is annotated with @SentryCheckIn and heartbeat only, every method call creates only one check-in at the end`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceHeartbeat.hello() assertEquals(1, result) assertEquals(1, checkInCaptor.allValues.size) @@ -123,17 +123,17 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(hub) - order.verify(hub).pushScope() - order.verify(hub).captureCheckIn(any()) - order.verify(hub).popScope() + val order = inOrder(scopes) + order.verify(scopes).pushScope() + order.verify(scopes).captureCheckIn(any()) + order.verify(scopes).popScope() } @Test fun `when method is annotated with @SentryCheckIn and heartbeat only, every method call creates only one check-in at the end with error`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) assertThrows { sampleServiceHeartbeat.oops() } @@ -144,31 +144,31 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.ERROR.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(hub) - order.verify(hub).pushScope() - order.verify(hub).captureCheckIn(any()) - order.verify(hub).popScope() + val order = inOrder(scopes) + order.verify(scopes).pushScope() + order.verify(scopes).captureCheckIn(any()) + order.verify(scopes).popScope() } @Test fun `when method is annotated with @SentryCheckIn but slug is missing, does not create check-in`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceNoSlug.hello() assertEquals(1, result) assertEquals(0, checkInCaptor.allValues.size) - verify(hub, never()).pushScope() - verify(hub, never()).captureCheckIn(any()) - verify(hub, never()).popScope() + verify(scopes, never()).pushScope() + verify(scopes, never()).captureCheckIn(any()) + verify(scopes, never()).popScope() } @Test fun `when @SentryCheckIn is passed a spring property it is resolved correctly`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceSpringProperties.hello() assertEquals(1, result) assertEquals(1, checkInCaptor.allValues.size) @@ -178,17 +178,17 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(hub) - order.verify(hub).pushScope() - order.verify(hub).captureCheckIn(any()) - order.verify(hub).popScope() + val order = inOrder(scopes) + order.verify(scopes).pushScope() + order.verify(scopes).captureCheckIn(any()) + order.verify(scopes).popScope() } @Test fun `when @SentryCheckIn is passed a spring property that does not exist, raw value is used`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceSpringProperties.helloUnresolvedProperty() assertEquals(1, result) assertEquals(1, checkInCaptor.allValues.size) @@ -198,17 +198,17 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(hub) - order.verify(hub).pushScope() - order.verify(hub).captureCheckIn(any()) - order.verify(hub).popScope() + val order = inOrder(scopes) + order.verify(scopes).pushScope() + order.verify(scopes).captureCheckIn(any()) + order.verify(scopes).popScope() } @Test fun `when @SentryCheckIn is passed a spring property that causes an exception, raw value is used`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceSpringProperties.helloExceptionProperty() assertEquals(1, result) assertEquals(1, checkInCaptor.allValues.size) @@ -218,10 +218,10 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(hub) - order.verify(hub).pushScope() - order.verify(hub).captureCheckIn(any()) - order.verify(hub).popScope() + val order = inOrder(scopes) + order.verify(scopes).pushScope() + order.verify(scopes).captureCheckIn(any()) + order.verify(scopes).popScope() } @Configuration @@ -242,10 +242,10 @@ class SentryCheckInAdviceTest { open fun sampleServiceSpringProperties() = SampleServiceSpringProperties() @Bean - open fun hub(): IHub { - val hub = mock() - Sentry.setCurrentHub(hub) - return hub + open fun scopes(): IScopes { + val scopes = mock() + Sentry.setCurrentScopes(scopes) + return scopes } companion object { diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryExceptionResolverTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryExceptionResolverTest.kt index 797d5aa5ac..431137aabd 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryExceptionResolverTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryExceptionResolverTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryEvent import io.sentry.SentryLevel import io.sentry.exception.ExceptionMechanismException @@ -17,7 +17,7 @@ import org.mockito.kotlin.whenever import kotlin.test.Test class SentryExceptionResolverTest { - private val hub = mock() + private val scopes = mock() private val transactionNameProvider = mock() private val request = mock() @@ -26,10 +26,10 @@ class SentryExceptionResolverTest { @Test fun `when handles exception, sets wrapped exception for event`() { val eventCaptor = argumentCaptor() - whenever(hub.captureEvent(eventCaptor.capture(), any())).thenReturn(null) + whenever(scopes.captureEvent(eventCaptor.capture(), any())).thenReturn(null) val expectedCause = RuntimeException("test") - SentryExceptionResolver(hub, transactionNameProvider, 1) + SentryExceptionResolver(scopes, transactionNameProvider, 1) .resolveException(request, response, null, expectedCause) assertThat(eventCaptor.firstValue.throwable).isEqualTo(expectedCause) @@ -46,9 +46,9 @@ class SentryExceptionResolverTest { @Test fun `when handles exception, sets fatal level for event`() { val eventCaptor = argumentCaptor() - whenever(hub.captureEvent(eventCaptor.capture(), any())).thenReturn(null) + whenever(scopes.captureEvent(eventCaptor.capture(), any())).thenReturn(null) - SentryExceptionResolver(hub, transactionNameProvider, 1) + SentryExceptionResolver(scopes, transactionNameProvider, 1) .resolveException(request, response, null, RuntimeException("test")) assertThat(eventCaptor.firstValue.level).isEqualTo(SentryLevel.FATAL) @@ -59,9 +59,9 @@ class SentryExceptionResolverTest { val expectedTransactionName = "test-transaction" whenever(transactionNameProvider.provideTransactionName(any())).thenReturn(expectedTransactionName) val eventCaptor = argumentCaptor() - whenever(hub.captureEvent(eventCaptor.capture(), any())).thenReturn(null) + whenever(scopes.captureEvent(eventCaptor.capture(), any())).thenReturn(null) - SentryExceptionResolver(hub, transactionNameProvider, 1) + SentryExceptionResolver(scopes, transactionNameProvider, 1) .resolveException(request, response, null, RuntimeException("test")) assertThat(eventCaptor.firstValue.transaction).isEqualTo(expectedTransactionName) @@ -71,9 +71,9 @@ class SentryExceptionResolverTest { @Test fun `when handles exception, provides spring resolver hint`() { val hintCaptor = argumentCaptor() - whenever(hub.captureEvent(any(), hintCaptor.capture())).thenReturn(null) + whenever(scopes.captureEvent(any(), hintCaptor.capture())).thenReturn(null) - SentryExceptionResolver(hub, transactionNameProvider, 1) + SentryExceptionResolver(scopes, transactionNameProvider, 1) .resolveException(request, response, null, RuntimeException("test")) with(hintCaptor.firstValue) { @@ -86,8 +86,8 @@ class SentryExceptionResolverTest { fun `when custom create event method provided, uses it to capture event`() { val expectedEvent = SentryEvent() val eventCaptor = argumentCaptor() - whenever(hub.captureEvent(eventCaptor.capture(), any())).thenReturn(null) - val resolver = object : SentryExceptionResolver(hub, transactionNameProvider, 1) { + whenever(scopes.captureEvent(eventCaptor.capture(), any())).thenReturn(null) + val resolver = object : SentryExceptionResolver(scopes, transactionNameProvider, 1) { override fun createEvent(request: HttpServletRequest, ex: Exception) = expectedEvent } @@ -100,8 +100,8 @@ class SentryExceptionResolverTest { fun `when custom create hint method provided, uses it to capture event`() { val expectedHint = Hint() val hintCaptor = argumentCaptor() - whenever(hub.captureEvent(any(), hintCaptor.capture())).thenReturn(null) - val resolver = object : SentryExceptionResolver(hub, transactionNameProvider, 1) { + whenever(scopes.captureEvent(any(), hintCaptor.capture())).thenReturn(null) + val resolver = object : SentryExceptionResolver(scopes, transactionNameProvider, 1) { override fun createHint(request: HttpServletRequest, response: HttpServletResponse) = expectedHint } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryInitBeanPostProcessorTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryInitBeanPostProcessorTest.kt index 4168605823..54b6acb0f3 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryInitBeanPostProcessorTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryInitBeanPostProcessorTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.jakarta -import io.sentry.IHub +import io.sentry.IScopes import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.springframework.context.annotation.AnnotationConfigApplicationContext @@ -13,18 +13,18 @@ class SentryInitBeanPostProcessorTest { @Test fun closesSentryOnApplicationContextDestroy() { val ctx = AnnotationConfigApplicationContext(TestConfig::class.java) - val hub = ctx.getBean(IHub::class.java) + val scopes = ctx.getBean(IScopes::class.java) ctx.close() - verify(hub).close() + verify(scopes).close() } @Configuration open class TestConfig { @Bean(destroyMethod = "") - open fun hub() = mock() + open fun scopes() = mock() @Bean - open fun sentryInitBeanPostProcessor() = SentryInitBeanPostProcessor(hub()) + open fun sentryInitBeanPostProcessor() = SentryInitBeanPostProcessor(scopes()) } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessorTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessorTest.kt index 279abce417..8faa243f83 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessorTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessorTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryEvent import io.sentry.SentryOptions import io.sentry.spring.jakarta.tracing.SpringMvcTransactionNameProvider @@ -19,10 +19,10 @@ import kotlin.test.assertNotNull class SentryRequestHttpServletRequestProcessorTest { private class Fixture { - val hub = mock() + val scopes = mock() fun getSut(request: HttpServletRequest, options: SentryOptions = SentryOptions()): SentryRequestHttpServletRequestProcessor { - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) return SentryRequestHttpServletRequestProcessor(SpringMvcTransactionNameProvider(), request) } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentrySpringFilterTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentrySpringFilterTest.kt index 4e1bbb0ee5..b6bce77a0b 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentrySpringFilterTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentrySpringFilterTest.kt @@ -1,8 +1,8 @@ package io.sentry.spring.jakarta import io.sentry.Breadcrumb -import io.sentry.IHub import io.sentry.IScope +import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -37,7 +37,7 @@ import kotlin.test.fail class SentrySpringFilterTest { private class Fixture { - val hub = mock() + val scopes = mock() val response = MockHttpServletResponse() val chain = mock() lateinit var scope: IScope @@ -45,15 +45,15 @@ class SentrySpringFilterTest { fun getSut(request: HttpServletRequest? = null, options: SentryOptions = SentryOptions()): SentrySpringFilter { scope = Scope(options) - whenever(hub.options).thenReturn(options) - whenever(hub.isEnabled).thenReturn(true) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) + whenever(scopes.options).thenReturn(options) + whenever(scopes.isEnabled).thenReturn(true) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) this.request = request ?: MockHttpServletRequest().apply { this.requestURI = "http://localhost:8080/some-uri" this.method = "post" } - return SentrySpringFilter(hub) + return SentrySpringFilter(scopes) } } @@ -64,7 +64,7 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).pushScope() + verify(fixture.scopes).pushScope() } @Test @@ -72,7 +72,7 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { it: Breadcrumb -> Assertions.assertThat(it.getData("url")).isEqualTo("http://localhost:8080/some-uri") Assertions.assertThat(it.getData("method")).isEqualTo("POST") @@ -87,7 +87,7 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).popScope() + verify(fixture.scopes).popScope() } @Test @@ -99,7 +99,7 @@ class SentrySpringFilterTest { listener.doFilter(fixture.request, fixture.response, fixture.chain) fail() } catch (e: Exception) { - verify(fixture.hub).popScope() + verify(fixture.scopes).popScope() } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryTaskDecoratorTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryTaskDecoratorTest.kt index aa809259d1..e5f8704b49 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryTaskDecoratorTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryTaskDecoratorTest.kt @@ -32,28 +32,28 @@ class SentryTaskDecoratorTest { val sut = SentryTaskDecorator() - val mainHub = Sentry.getCurrentHub() - val threadedHub = Sentry.getCurrentHub().clone() + val mainHub = Sentry.getCurrentScopes() + val threadedHub = Sentry.getCurrentScopes().clone() executor.submit { - Sentry.setCurrentHub(threadedHub) + Sentry.setCurrentScopes(threadedHub) }.get() - assertEquals(mainHub, Sentry.getCurrentHub()) + assertEquals(mainHub, Sentry.getCurrentScopes()) val callableFuture = executor.submit( sut.decorate { - assertNotEquals(mainHub, Sentry.getCurrentHub()) - assertNotEquals(threadedHub, Sentry.getCurrentHub()) + assertNotEquals(mainHub, Sentry.getCurrentScopes()) + assertNotEquals(threadedHub, Sentry.getCurrentScopes()) } ) callableFuture.get() executor.submit { - assertNotEquals(mainHub, Sentry.getCurrentHub()) - assertEquals(threadedHub, Sentry.getCurrentHub()) + assertNotEquals(mainHub, Sentry.getCurrentScopes()) + assertEquals(threadedHub, Sentry.getCurrentScopes()) }.get() } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryUserFilterTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryUserFilterTest.kt index 2f128cc4bb..b30dc937e5 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryUserFilterTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryUserFilterTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.jakarta -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.SentryOptions import io.sentry.protocol.User import jakarta.servlet.FilterChain @@ -16,7 +16,7 @@ import kotlin.test.assertNull class SentryUserFilterTest { class Fixture { - val hub = mock() + val scopes = mock() val request = MockHttpServletRequest() val response = MockHttpServletResponse() val chain = mock() @@ -25,8 +25,8 @@ class SentryUserFilterTest { val options = SentryOptions().apply { this.isSendDefaultPii = isSendDefaultPii } - whenever(hub.options).thenReturn(options) - return SentryUserFilter(hub, userProviders) + whenever(scopes.options).thenReturn(options) + return SentryUserFilter(scopes, userProviders) } } @@ -52,7 +52,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).setUser( + verify(fixture.scopes).setUser( check { assertEquals(sampleUser, it) } @@ -72,7 +72,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).setUser( + verify(fixture.scopes).setUser( check { assertEquals(sampleUser, it) } @@ -92,7 +92,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).setUser( + verify(fixture.scopes).setUser( check { assertEquals(sampleUser, it) } @@ -118,7 +118,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).setUser( + verify(fixture.scopes).setUser( check { assertEquals(mapOf("key" to "value", "new-key" to "new-value"), it.others) } @@ -140,7 +140,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).setUser( + verify(fixture.scopes).setUser( check { assertEquals("192.168.0.1", it.ipAddress) } @@ -162,7 +162,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).setUser( + verify(fixture.scopes).setUser( check { assertNull(it.ipAddress) } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdviceTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdviceTest.kt index 0da5047251..3f8371ca3d 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdviceTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdviceTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta.exception import io.sentry.Hint -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.Sentry import io.sentry.exception.ExceptionMechanismException import org.junit.runner.RunWith @@ -30,18 +30,18 @@ class SentryCaptureExceptionParameterAdviceTest { lateinit var sampleService: SampleService @Autowired - lateinit var hub: IHub + lateinit var scopes: IScopes @BeforeTest fun setup() { - reset(hub) + reset(scopes) } @Test fun `captures exception passed to method annotated with @SentryCaptureException`() { val exception = RuntimeException("test exception") sampleService.methodTakingAnException(exception) - verify(hub).captureException( + verify(scopes).captureException( check { assertTrue(it is ExceptionMechanismException) assertEquals(exception, it.throwable) @@ -60,10 +60,10 @@ class SentryCaptureExceptionParameterAdviceTest { open fun sampleService() = SampleService() @Bean - open fun hub(): IHub { - val hub = mock() - Sentry.setCurrentHub(hub) - return hub + open fun scopes(): IScopes { + val scopes = mock() + Sentry.setCurrentScopes(scopes) + return scopes } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandlerTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandlerTest.kt index 3f71eaf23e..c2ebb95e48 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandlerTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandlerTest.kt @@ -4,7 +4,7 @@ import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchPar import graphql.language.Document import graphql.language.OperationDefinition import graphql.schema.DataFetchingEnvironment -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.graphql.ExceptionReporter import io.sentry.spring.jakarta.graphql.SentrySpringSubscriptionHandler import org.junit.jupiter.api.assertThrows @@ -24,7 +24,7 @@ class SentrySpringSubscriptionHandlerTest { @Test fun `reports exception`() { val exception = IllegalStateException("some exception") - val hub = mock() + val scopes = mock() val exceptionReporter = mock() val parameters = mock() val dataFetchingEnvironment = mock() @@ -33,7 +33,7 @@ class SentrySpringSubscriptionHandlerTest { .build() whenever(dataFetchingEnvironment.document).thenReturn(document) whenever(parameters.environment).thenReturn(dataFetchingEnvironment) - val resultObject = SentrySpringSubscriptionHandler().onSubscriptionResult(Flux.error(exception), hub, exceptionReporter, parameters) + val resultObject = SentrySpringSubscriptionHandler().onSubscriptionResult(Flux.error(exception), scopes, exceptionReporter, parameters) assertThrows { (resultObject as Flux).blockFirst() } @@ -42,7 +42,7 @@ class SentrySpringSubscriptionHandlerTest { same(exception), org.mockito.kotlin.check { assertEquals(true, it.isSubscription) - assertSame(hub, it.hub) + assertSame(scopes, it.scopes) assertEquals("query testQuery\n", it.query) }, anyOrNull() @@ -53,7 +53,7 @@ class SentrySpringSubscriptionHandlerTest { fun `unwraps SubscriptionPublisherException and reports cause`() { val exception = IllegalStateException("some exception") val wrappedException = SubscriptionPublisherException(emptyList(), exception) - val hub = mock() + val scopes = mock() val exceptionReporter = mock() val parameters = mock() val dataFetchingEnvironment = mock() @@ -62,7 +62,7 @@ class SentrySpringSubscriptionHandlerTest { .build() whenever(dataFetchingEnvironment.document).thenReturn(document) whenever(parameters.environment).thenReturn(dataFetchingEnvironment) - val resultObject = SentrySpringSubscriptionHandler().onSubscriptionResult(Flux.error(wrappedException), hub, exceptionReporter, parameters) + val resultObject = SentrySpringSubscriptionHandler().onSubscriptionResult(Flux.error(wrappedException), scopes, exceptionReporter, parameters) assertThrows { (resultObject as Flux).blockFirst() } @@ -71,7 +71,7 @@ class SentrySpringSubscriptionHandlerTest { same(exception), org.mockito.kotlin.check { assertEquals(true, it.isSubscription) - assertSame(hub, it.hub) + assertSame(scopes, it.scopes) assertEquals("query testQuery\n", it.query) }, anyOrNull() diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/mvc/SentrySpringIntegrationTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/mvc/SentrySpringIntegrationTest.kt index f472a75a65..0b4be869d0 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/mvc/SentrySpringIntegrationTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/mvc/SentrySpringIntegrationTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.jakarta.mvc -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ITransportFactory import io.sentry.Sentry import io.sentry.SentryOptions @@ -104,7 +104,7 @@ class SentrySpringIntegrationTest { lateinit var anotherService: AnotherService @Autowired - lateinit var hub: IHub + lateinit var scopes: IScopes @LocalServerPort var port: Int? = null @@ -260,7 +260,7 @@ class SentrySpringIntegrationTest { try { someService.aMethodThrowing() } catch (e: Exception) { - hub.captureException(e) + scopes.captureException(e) } verify(transport).send( checkEvent { @@ -276,7 +276,7 @@ class SentrySpringIntegrationTest { try { someService.aMethodWithInnerSpanThrowing() } catch (e: Exception) { - hub.captureException(e) + scopes.captureException(e) } verify(transport).send( checkEvent { @@ -370,20 +370,20 @@ open class App { open fun springSecuritySentryUserProvider(sentryOptions: SentryOptions) = SpringSecuritySentryUserProvider(sentryOptions) @Bean - open fun sentryUserFilter(hub: IHub, @Lazy sentryUserProviders: List) = FilterRegistrationBean().apply { - this.filter = SentryUserFilter(hub, sentryUserProviders) + open fun sentryUserFilter(scopes: IScopes, @Lazy sentryUserProviders: List) = FilterRegistrationBean().apply { + this.filter = SentryUserFilter(scopes, sentryUserProviders) this.order = Ordered.LOWEST_PRECEDENCE } @Bean - open fun sentrySpringFilter(hub: IHub) = FilterRegistrationBean().apply { - this.filter = SentrySpringFilter(hub) + open fun sentrySpringFilter(scopes: IScopes) = FilterRegistrationBean().apply { + this.filter = SentrySpringFilter(scopes) this.order = Ordered.HIGHEST_PRECEDENCE } @Bean - open fun sentryTracingFilter(hub: IHub) = FilterRegistrationBean().apply { - this.filter = SentryTracingFilter(hub) + open fun sentryTracingFilter(scopes: IScopes) = FilterRegistrationBean().apply { + this.filter = SentryTracingFilter(scopes) this.order = Ordered.HIGHEST_PRECEDENCE + 1 // must run after SentrySpringFilter } @@ -391,13 +391,13 @@ open class App { open fun sentryTaskDecorator() = SentryTaskDecorator() @Bean - open fun webClient(hub: IHub): WebClient { + open fun webClient(scopes: IScopes): WebClient { return WebClient.builder() .filter( ExchangeFilterFunctions .basicAuthentication("user", "password") ) - .filter(SentrySpanClientWebRequestFilter(hub)).build() + .filter(SentrySpanClientWebRequestFilter(scopes)).build() } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentrySpanAdviceTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentrySpanAdviceTest.kt index 2910e7aac6..8b74b08fb1 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentrySpanAdviceTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentrySpanAdviceTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.jakarta.tracing -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.Scope import io.sentry.Sentry import io.sentry.SentryOptions @@ -37,20 +37,20 @@ class SentrySpanAdviceTest { lateinit var classAnnotatedWithOperationSampleService: ClassAnnotatedWithOperationSampleService @Autowired - lateinit var hub: IHub + lateinit var scopes: IScopes @BeforeTest fun setup() { - whenever(hub.options).thenReturn(SentryOptions()) + whenever(scopes.options).thenReturn(SentryOptions()) } @Test fun `when class is annotated with @SentrySpan, every method call attaches span to existing transaction`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) scope.setTransaction(tx) - whenever(hub.span).thenReturn(tx) + whenever(scopes.span).thenReturn(tx) val result = classAnnotatedSampleService.hello() assertEquals(1, result) assertEquals(1, tx.spans.size) @@ -62,10 +62,10 @@ class SentrySpanAdviceTest { @Test fun `when class is annotated with @SentrySpan with operation set, every method call attaches span to existing transaction`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) scope.setTransaction(tx) - whenever(hub.span).thenReturn(tx) + whenever(scopes.span).thenReturn(tx) val result = classAnnotatedWithOperationSampleService.hello() assertEquals(1, result) assertEquals(1, tx.spans.size) @@ -76,10 +76,10 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan with properties set, attaches span to existing transaction`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) scope.setTransaction(tx) - whenever(hub.span).thenReturn(tx) + whenever(scopes.span).thenReturn(tx) val result = sampleService.methodWithSpanDescriptionSet() assertEquals(1, result) assertEquals(1, tx.spans.size) @@ -90,10 +90,10 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan without properties set, attaches span to existing transaction and sets Span description as className dot methodName`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) scope.setTransaction(tx) - whenever(hub.span).thenReturn(tx) + whenever(scopes.span).thenReturn(tx) val result = sampleService.methodWithoutSpanDescriptionSet() assertEquals(2, result) assertEquals(1, tx.spans.size) @@ -104,10 +104,10 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan and returns, attached span has status OK`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) scope.setTransaction(tx) - whenever(hub.span).thenReturn(tx) + whenever(scopes.span).thenReturn(tx) sampleService.methodWithSpanDescriptionSet() assertEquals(SpanStatus.OK, tx.spans.first().status) } @@ -115,10 +115,10 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan and throws exception, attached span has throwable set and INTERNAL_ERROR status`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) scope.setTransaction(tx) - whenever(hub.span).thenReturn(tx) + whenever(scopes.span).thenReturn(tx) var throwable: Throwable? = null try { sampleService.methodThrowingException() @@ -131,7 +131,7 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan and there is no active transaction, span is not created and method is executed`() { - whenever(hub.span).thenReturn(null) + whenever(scopes.span).thenReturn(null) val result = sampleService.methodWithSpanDescriptionSet() assertEquals(1, result) } @@ -151,10 +151,10 @@ class SentrySpanAdviceTest { open fun classAnnotatedWithOperationSampleService() = ClassAnnotatedWithOperationSampleService() @Bean - open fun hub(): IHub { - val hub = mock() - Sentry.setCurrentHub(hub) - return hub + open fun scopes(): IScopes { + val scopes = mock() + Sentry.setCurrentScopes(scopes) + return scopes } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTracingFilterTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTracingFilterTest.kt index 0424696fad..265d607b70 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTracingFilterTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTracingFilterTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta.tracing -import io.sentry.IHub import io.sentry.ILogger +import io.sentry.IScopes import io.sentry.PropagationContext import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -38,7 +38,7 @@ import kotlin.test.fail class SentryTracingFilterTest { private class Fixture { - val hub = mock() + val scopes = mock() val request = MockHttpServletRequest() val response = MockHttpServletResponse() val chain = mock() @@ -50,7 +50,7 @@ class SentryTracingFilterTest { val logger = mock() init { - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) } fun getSut(isEnabled: Boolean = true, status: Int = 200, sentryTraceHeader: String? = null, baggageHeaders: List? = null): SentryTracingFilter { @@ -61,16 +61,16 @@ class SentryTracingFilterTest { whenever(transactionNameProvider.provideTransactionSource()).thenReturn(TransactionNameSource.CUSTOM) if (sentryTraceHeader != null) { request.addHeader("sentry-trace", sentryTraceHeader) - whenever(hub.startTransaction(any(), check { it.isBindToScope })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } + whenever(scopes.startTransaction(any(), check { it.isBindToScope })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } } if (baggageHeaders != null) { request.addHeader("baggage", baggageHeaders) } response.status = status - whenever(hub.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } - whenever(hub.isEnabled).thenReturn(isEnabled) - whenever(hub.continueTrace(any(), any())).thenAnswer { TransactionContext.fromPropagationContext(PropagationContext.fromHeaders(logger, it.arguments[0] as String?, it.arguments[1] as List?)) } - return SentryTracingFilter(hub, transactionNameProvider) + whenever(scopes.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } + whenever(scopes.isEnabled).thenReturn(isEnabled) + whenever(scopes.continueTrace(any(), any())).thenAnswer { TransactionContext.fromPropagationContext(PropagationContext.fromHeaders(logger, it.arguments[0] as String?, it.arguments[1] as List?)) } + return SentryTracingFilter(scopes, transactionNameProvider) } } @@ -82,7 +82,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("POST /product/12", it.name) assertEquals(TransactionNameSource.URL, it.transactionNameSource) @@ -95,7 +95,7 @@ class SentryTracingFilterTest { } ) verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.transaction).isEqualTo("POST /product/{id}") assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.OK) @@ -114,7 +114,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.INTERNAL_ERROR) }, @@ -130,7 +130,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.status).isNull() }, @@ -146,7 +146,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -163,7 +163,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isEqualTo(parentSpanId) }, @@ -174,15 +174,15 @@ class SentryTracingFilterTest { } @Test - fun `when hub is disabled, components are not invoked`() { + fun `when scopes is disabled, components are not invoked`() { val filter = fixture.getSut(isEnabled = false) filter.doFilter(fixture.request, fixture.response, fixture.chain) verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.hub).isEnabled - verifyNoMoreInteractions(fixture.hub) + verify(fixture.scopes).isEnabled + verifyNoMoreInteractions(fixture.scopes) verify(fixture.transactionNameProvider, never()).provideTransactionName(any()) } @@ -196,7 +196,7 @@ class SentryTracingFilterTest { fail("filter is expected to rethrow exception") } catch (_: Exception) { } - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.status).isEqualTo(SpanStatus.INTERNAL_ERROR) }, @@ -216,10 +216,10 @@ class SentryTracingFilterTest { verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.hub).isEnabled - verify(fixture.hub).continueTrace(anyOrNull(), anyOrNull()) - verify(fixture.hub, times(2)).options - verifyNoMoreInteractions(fixture.hub) + verify(fixture.scopes).isEnabled + verify(fixture.scopes).continueTrace(anyOrNull(), anyOrNull()) + verify(fixture.scopes, times(2)).options + verifyNoMoreInteractions(fixture.scopes) verify(fixture.transactionNameProvider, never()).provideTransactionName(any()) } @@ -233,7 +233,7 @@ class SentryTracingFilterTest { verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -253,7 +253,7 @@ class SentryTracingFilterTest { verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -275,9 +275,9 @@ class SentryTracingFilterTest { verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.hub).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) + verify(fixture.scopes).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) - verify(fixture.hub, never()).captureTransaction( + verify(fixture.scopes, never()).captureTransaction( anyOrNull(), anyOrNull(), anyOrNull(), diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt index 5d2863310f..390b4d8241 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.jakarta.tracing -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -44,13 +44,13 @@ class SentryTransactionAdviceTest { lateinit var classAnnotatedWithOperationSampleService: ClassAnnotatedWithOperationSampleService @Autowired - lateinit var hub: IHub + lateinit var scopes: IScopes @BeforeTest fun setup() { - reset(hub) - whenever(hub.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } - whenever(hub.options).thenReturn( + reset(scopes) + whenever(scopes.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } + whenever(scopes.options).thenReturn( SentryOptions().apply { dsn = "https://key@sentry.io/proj" } @@ -60,7 +60,7 @@ class SentryTransactionAdviceTest { @Test fun `creates transaction around method annotated with @SentryTransaction`() { sampleService.methodWithTransactionNameSet() - verify(hub).captureTransaction( + verify(scopes).captureTransaction( check { assertThat(it.transaction).isEqualTo("customName") assertThat(it.contexts.trace!!.operation).isEqualTo("bean") @@ -76,7 +76,7 @@ class SentryTransactionAdviceTest { @Test fun `when method annotated with @SentryTransaction throws exception, sets error status on transaction`() { assertThrows { sampleService.methodThrowingException() } - verify(hub).captureTransaction( + verify(scopes).captureTransaction( check { assertThat(it.status).isEqualTo(SpanStatus.INTERNAL_ERROR) }, @@ -89,7 +89,7 @@ class SentryTransactionAdviceTest { @Test fun `when @SentryTransaction has no name set, sets transaction name as className dot methodName`() { sampleService.methodWithoutTransactionNameSet() - verify(hub).captureTransaction( + verify(scopes).captureTransaction( check { assertThat(it.transaction).isEqualTo("SampleService.methodWithoutTransactionNameSet") assertThat(it.contexts.trace!!.operation).isEqualTo("op") @@ -102,18 +102,18 @@ class SentryTransactionAdviceTest { @Test fun `when transaction is already active, does not start new transaction`() { - whenever(hub.options).thenReturn(SentryOptions()) - whenever(hub.span).then { SentryTracer(TransactionContext("aTransaction", "op"), hub) } + whenever(scopes.options).thenReturn(SentryOptions()) + whenever(scopes.span).then { SentryTracer(TransactionContext("aTransaction", "op"), scopes) } sampleService.methodWithTransactionNameSet() - verify(hub, times(0)).captureTransaction(any(), any()) + verify(scopes, times(0)).captureTransaction(any(), any()) } @Test fun `creates transaction around method in class annotated with @SentryTransaction`() { classAnnotatedSampleService.hello() - verify(hub).captureTransaction( + verify(scopes).captureTransaction( check { assertThat(it.transaction).isEqualTo("ClassAnnotatedSampleService.hello") assertThat(it.contexts.trace!!.operation).isEqualTo("op") @@ -127,7 +127,7 @@ class SentryTransactionAdviceTest { @Test fun `creates transaction with operation set around method in class annotated with @SentryTransaction`() { classAnnotatedWithOperationSampleService.hello() - verify(hub).captureTransaction( + verify(scopes).captureTransaction( check { assertThat(it.transaction).isEqualTo("ClassAnnotatedWithOperationSampleService.hello") assertThat(it.contexts.trace!!.operation).isEqualTo("my-op") @@ -141,13 +141,13 @@ class SentryTransactionAdviceTest { @Test fun `pushes the scope when advice starts`() { classAnnotatedSampleService.hello() - verify(hub).pushScope() + verify(scopes).pushScope() } @Test fun `pops the scope when advice finishes`() { classAnnotatedSampleService.hello() - verify(hub).popScope() + verify(scopes).popScope() } @Configuration @@ -165,10 +165,10 @@ class SentryTransactionAdviceTest { open fun classAnnotatedWithOperationSampleService() = ClassAnnotatedWithOperationSampleService() @Bean - open fun hub(): IHub { - val hub = mock() - Sentry.setCurrentHub(hub) - return hub + open fun scopes(): IScopes { + val scopes = mock() + Sentry.setCurrentScopes(scopes) + return scopes } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/ReactorUtilsTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/ReactorUtilsTest.kt index ad335333ed..9c851cde11 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/ReactorUtilsTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/ReactorUtilsTest.kt @@ -1,7 +1,8 @@ package io.sentry.spring.jakarta.webflux import io.sentry.IHub -import io.sentry.NoOpHub +import io.sentry.IScopes +import io.sentry.NoOpScopes import io.sentry.Sentry import org.mockito.kotlin.mock import org.mockito.kotlin.verify @@ -26,18 +27,18 @@ class ReactorUtilsTest { @AfterTest fun teardown() { - Sentry.setCurrentHub(NoOpHub.getInstance()) + Sentry.setCurrentScopes(NoOpScopes.getInstance()) } @Test fun `propagates hub inside mono`() { - val hubToUse = mock() - var hubInside: IHub? = null + val hubToUse = mock() + var hubInside: IScopes? = null val mono = ReactorUtils.withSentryHub( Mono.just("hello") .publishOn(Schedulers.boundedElastic()) .map { it -> - hubInside = Sentry.getCurrentHub() + hubInside = Sentry.getCurrentScopes() it }, hubToUse @@ -49,13 +50,13 @@ class ReactorUtilsTest { @Test fun `propagates hub inside flux`() { - val hubToUse = mock() - var hubInside: IHub? = null + val hubToUse = mock() + var hubInside: IScopes? = null val flux = ReactorUtils.withSentryHub( Flux.just("hello") .publishOn(Schedulers.boundedElastic()) .map { it -> - hubInside = Sentry.getCurrentHub() + hubInside = Sentry.getCurrentScopes() it }, hubToUse @@ -67,12 +68,12 @@ class ReactorUtilsTest { @Test fun `without reactive utils hub is not propagated to mono`() { - val hubToUse = mock() - var hubInside: IHub? = null + val hubToUse = mock() + var hubInside: IScopes? = null val mono = Mono.just("hello") .publishOn(Schedulers.boundedElastic()) .map { it -> - hubInside = Sentry.getCurrentHub() + hubInside = Sentry.getCurrentScopes() it } @@ -82,12 +83,12 @@ class ReactorUtilsTest { @Test fun `without reactive utils hub is not propagated to flux`() { - val hubToUse = mock() - var hubInside: IHub? = null + val hubToUse = mock() + var hubInside: IScopes? = null val flux = Flux.just("hello") .publishOn(Schedulers.boundedElastic()) .map { it -> - hubInside = Sentry.getCurrentHub() + hubInside = Sentry.getCurrentScopes() it } @@ -97,21 +98,21 @@ class ReactorUtilsTest { @Test fun `clones hub for mono`() { - val mockHub = mock() - whenever(mockHub.clone()).thenReturn(mock()) - Sentry.setCurrentHub(mockHub) + val mockScopes = mock() + whenever(mockScopes.clone()).thenReturn(mock()) + Sentry.setCurrentScopes(mockScopes) ReactorUtils.withSentry(Mono.just("hello")).block() - verify(mockHub).clone() + verify(mockScopes).clone() } @Test fun `clones hub for flux`() { - val mockHub = mock() - whenever(mockHub.clone()).thenReturn(mock()) - Sentry.setCurrentHub(mockHub) + val mockScopes = mock() + whenever(mockScopes.clone()).thenReturn(mock()) + Sentry.setCurrentScopes(mockScopes) ReactorUtils.withSentry(Flux.just("hello")).blockFirst() - verify(mockHub).clone() + verify(mockScopes).clone() } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryScheduleHookTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryScheduleHookTest.kt index 1eb06d0afe..5403caa7e0 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryScheduleHookTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryScheduleHookTest.kt @@ -33,28 +33,28 @@ class SentryScheduleHookTest { val sut = SentryScheduleHook() - val mainHub = Sentry.getCurrentHub() - val threadedHub = Sentry.getCurrentHub().clone() + val mainHub = Sentry.getCurrentScopes() + val threadedHub = Sentry.getCurrentScopes().clone() executor.submit { - Sentry.setCurrentHub(threadedHub) + Sentry.setCurrentScopes(threadedHub) }.get() - assertEquals(mainHub, Sentry.getCurrentHub()) + assertEquals(mainHub, Sentry.getCurrentScopes()) val callableFuture = executor.submit( sut.apply { - assertNotEquals(mainHub, Sentry.getCurrentHub()) - assertNotEquals(threadedHub, Sentry.getCurrentHub()) + assertNotEquals(mainHub, Sentry.getCurrentScopes()) + assertNotEquals(threadedHub, Sentry.getCurrentScopes()) } ) callableFuture.get() executor.submit { - assertNotEquals(mainHub, Sentry.getCurrentHub()) - assertEquals(threadedHub, Sentry.getCurrentHub()) + assertNotEquals(mainHub, Sentry.getCurrentScopes()) + assertEquals(threadedHub, Sentry.getCurrentScopes()) }.get() } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt index eac0b9c60f..e936394039 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt @@ -2,8 +2,9 @@ package io.sentry.spring.jakarta.webflux import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.IHub +import io.sentry.HubScopesWrapper import io.sentry.ILogger +import io.sentry.IScopes import io.sentry.PropagationContext import io.sentry.ScopeCallback import io.sentry.Sentry @@ -17,7 +18,7 @@ import io.sentry.TransactionOptions import io.sentry.protocol.SentryId import io.sentry.protocol.SentryTransaction import io.sentry.protocol.TransactionNameSource -import io.sentry.spring.jakarta.webflux.AbstractSentryWebFilter.SENTRY_HUB_KEY +import io.sentry.spring.jakarta.webflux.AbstractSentryWebFilter.SENTRY_SCOPES_KEY import org.assertj.core.api.Assertions.assertThat import org.mockito.Mockito import org.mockito.kotlin.any @@ -47,7 +48,7 @@ import kotlin.test.fail class SentryWebFluxTracingFilterTest { private class Fixture { - val hub = mock() + val scopes = mock() lateinit var request: MockServerHttpRequest lateinit var exchange: MockServerWebExchange val chain = mock() @@ -58,45 +59,47 @@ class SentryWebFluxTracingFilterTest { val logger = mock() init { - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) } fun getSut(isEnabled: Boolean = true, status: HttpStatus = HttpStatus.OK, sentryTraceHeader: String? = null, baggageHeaders: List? = null, method: HttpMethod = HttpMethod.POST): SentryWebFilter { var requestBuilder = MockServerHttpRequest.method(method, "/product/{id}", 12) if (sentryTraceHeader != null) { requestBuilder = requestBuilder.header("sentry-trace", sentryTraceHeader) - whenever(hub.startTransaction(any(), check { it.isBindToScope })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } + whenever(scopes.startTransaction(any(), check { it.isBindToScope })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } } if (baggageHeaders != null) { requestBuilder = requestBuilder.header("baggage", *baggageHeaders.toTypedArray()) } request = requestBuilder.build() exchange = MockServerWebExchange.builder(request).build() - exchange.attributes.put(SENTRY_HUB_KEY, hub) + exchange.attributes.put(SENTRY_SCOPES_KEY, scopes) exchange.attributes.put(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, PathPatternParser().parse("/product/{id}")) exchange.response.statusCode = status - whenever(hub.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } - whenever(hub.isEnabled).thenReturn(isEnabled) + whenever(scopes.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } + whenever(scopes.isEnabled).thenReturn(isEnabled) whenever(chain.filter(any())).thenReturn(Mono.create { s -> s.success() }) - whenever(hub.continueTrace(anyOrNull(), anyOrNull())).thenAnswer { TransactionContext.fromPropagationContext(PropagationContext.fromHeaders(logger, it.arguments[0] as String?, it.arguments[1] as List?)) } - return SentryWebFilter(hub) + whenever(scopes.continueTrace(anyOrNull(), anyOrNull())).thenAnswer { TransactionContext.fromPropagationContext(PropagationContext.fromHeaders(logger, it.arguments[0] as String?, it.arguments[1] as List?)) } + return SentryWebFilter(scopes) } } private val fixture = Fixture() - fun withMockHub(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { - it.`when` { Sentry.cloneMainHub() }.thenReturn(fixture.hub) + fun withMockScopes(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { + it.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(fixture.scopes)) + it.`when` { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes) + it.`when` { Sentry.cloneMainHub() }.thenReturn(fixture.scopes) closure.invoke() } @Test fun `creates transaction around the request`() { val filter = fixture.getSut() - withMockHub { + withMockScopes { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("POST /product/12", it.name) assertEquals(TransactionNameSource.URL, it.transactionNameSource) @@ -109,7 +112,7 @@ class SentryWebFluxTracingFilterTest { } ) verify(fixture.chain).filter(fixture.exchange) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.transaction).isEqualTo("POST /product/{id}") assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.OK) @@ -128,10 +131,10 @@ class SentryWebFluxTracingFilterTest { fun `sets correct span status based on the response status`() { val filter = fixture.getSut(status = HttpStatus.INTERNAL_SERVER_ERROR) - withMockHub { + withMockScopes { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.INTERNAL_ERROR) assertThat(it.contexts.response!!.statusCode).isEqualTo(500) @@ -147,10 +150,10 @@ class SentryWebFluxTracingFilterTest { fun `does not set span status for response status that dont match predefined span statuses`() { val filter = fixture.getSut(status = HttpStatus.FOUND) - withMockHub { + withMockScopes { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.status).isNull() }, @@ -165,10 +168,10 @@ class SentryWebFluxTracingFilterTest { fun `when sentry trace is not present, transaction does not have parentSpanId set`() { val filter = fixture.getSut() - withMockHub { + withMockScopes { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -184,10 +187,10 @@ class SentryWebFluxTracingFilterTest { val parentSpanId = SpanId() val filter = fixture.getSut(sentryTraceHeader = "${SentryId()}-$parentSpanId-1") - withMockHub { + withMockScopes { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isEqualTo(parentSpanId) }, @@ -199,16 +202,16 @@ class SentryWebFluxTracingFilterTest { } @Test - fun `when hub is disabled, components are not invoked`() { + fun `when scopes is disabled, components are not invoked`() { val filter = fixture.getSut(isEnabled = false) - withMockHub { + withMockScopes { filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.hub, times(3)).isEnabled - verifyNoMoreInteractions(fixture.hub) + verify(fixture.scopes, times(3)).isEnabled + verifyNoMoreInteractions(fixture.scopes) } } @@ -216,7 +219,7 @@ class SentryWebFluxTracingFilterTest { fun `sets status to internal server error when chain throws exception`() { val filter = fixture.getSut() - withMockHub { + withMockScopes { whenever(fixture.chain.filter(any())).thenReturn(Mono.error(RuntimeException("error"))) try { @@ -224,7 +227,7 @@ class SentryWebFluxTracingFilterTest { fail("filter is expected to rethrow exception") } catch (_: Exception) { } - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.status).isEqualTo(SpanStatus.INTERNAL_ERROR) }, @@ -239,21 +242,21 @@ class SentryWebFluxTracingFilterTest { fun `does not track OPTIONS request with traceOptionsRequests=false`() { val filter = fixture.getSut(method = HttpMethod.OPTIONS) - withMockHub { + withMockScopes { fixture.options.isTraceOptionsRequests = false filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.hub, times(3)).isEnabled - verify(fixture.hub, times(2)).options - verify(fixture.hub).continueTrace(anyOrNull(), anyOrNull()) - verify(fixture.hub).pushScope() - verify(fixture.hub).addBreadcrumb(any(), any()) - verify(fixture.hub).configureScope(any()) - verify(fixture.hub).popScope() - verifyNoMoreInteractions(fixture.hub) + verify(fixture.scopes, times(3)).isEnabled + verify(fixture.scopes, times(2)).options + verify(fixture.scopes).continueTrace(anyOrNull(), anyOrNull()) + verify(fixture.scopes).pushScope() + verify(fixture.scopes).addBreadcrumb(any(), any()) + verify(fixture.scopes).configureScope(any()) + verify(fixture.scopes).popScope() + verifyNoMoreInteractions(fixture.scopes) } } @@ -261,14 +264,14 @@ class SentryWebFluxTracingFilterTest { fun `tracks OPTIONS request with traceOptionsRequests=true`() { val filter = fixture.getSut(method = HttpMethod.OPTIONS) - withMockHub { + withMockScopes { fixture.options.isTraceOptionsRequests = true filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -283,14 +286,14 @@ class SentryWebFluxTracingFilterTest { fun `tracks POST request with traceOptionsRequests=false`() { val filter = fixture.getSut(method = HttpMethod.POST) - withMockHub { + withMockScopes { fixture.options.isTraceOptionsRequests = false filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -309,19 +312,19 @@ class SentryWebFluxTracingFilterTest { fixture.options.enableTracing = false val filter = fixture.getSut(sentryTraceHeader = sentryTraceHeaderString, baggageHeaders = baggageHeaderStrings) - withMockHub { + withMockScopes { filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.hub, never()).captureTransaction( + verify(fixture.scopes, never()).captureTransaction( anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull() ) - verify(fixture.hub).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) + verify(fixture.scopes).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) } } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebfluxIntegrationTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebfluxIntegrationTest.kt index 3f4628ea3c..59f9a700b6 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebfluxIntegrationTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebfluxIntegrationTest.kt @@ -1,8 +1,8 @@ package io.sentry.spring.jakarta.webflux -import io.sentry.HubAdapter -import io.sentry.IHub +import io.sentry.IScopes import io.sentry.ITransportFactory +import io.sentry.ScopesAdapter import io.sentry.Sentry import io.sentry.checkEvent import io.sentry.checkTransaction @@ -160,13 +160,13 @@ open class App { open fun mockTransport() = transport @Bean - open fun hub() = HubAdapter.getInstance() + open fun scopes() = ScopesAdapter.getInstance() @Bean - open fun sentryFilter(hub: IHub) = SentryWebFilter(hub) + open fun sentryFilter(scopes: IScopes) = SentryWebFilter(scopes) @Bean - open fun sentryWebExceptionHandler(hub: IHub) = SentryWebExceptionHandler(hub) + open fun sentryWebExceptionHandler(scopes: IScopes) = SentryWebExceptionHandler(scopes) @Bean open fun sentryScheduleHookRegistrar() = ApplicationRunner { From ec30e19c28c10915856f6186efa7b178d124b2d7 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 16 Apr 2024 16:47:00 +0200 Subject: [PATCH 13/91] Hubs/Scopes Merge 13 - Replace `IHub` with `IScopes` in samples (#3310) * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples --- .../java/io/sentry/samples/android/ProfilingActivity.kt | 2 +- .../java/io/sentry/samples/spring/jakarta/AppConfig.java | 6 +++--- .../java/io/sentry/samples/spring/jakarta/WebConfig.java | 8 ++++---- .../src/main/java/io/sentry/samples/spring/AppConfig.java | 6 +++--- .../src/main/java/io/sentry/samples/spring/WebConfig.java | 8 ++++---- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/ProfilingActivity.kt b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/ProfilingActivity.kt index 610fc1534d..a7004deb35 100644 --- a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/ProfilingActivity.kt +++ b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/ProfilingActivity.kt @@ -100,7 +100,7 @@ class ProfilingActivity : AppCompatActivity() { val traceData = ProfilingTraceData(profile, t) // Create envelope item from copied profile val item = - SentryEnvelopeItem.fromProfilingTrace(traceData, Long.MAX_VALUE, Sentry.getCurrentHub().options.serializer) + SentryEnvelopeItem.fromProfilingTrace(traceData, Long.MAX_VALUE, Sentry.getCurrentScopes().options.serializer) val itemData = item.data // Compress the envelope item using Gzip diff --git a/sentry-samples/sentry-samples-spring-jakarta/src/main/java/io/sentry/samples/spring/jakarta/AppConfig.java b/sentry-samples/sentry-samples-spring-jakarta/src/main/java/io/sentry/samples/spring/jakarta/AppConfig.java index f78c3f71d5..72ecb14e2f 100644 --- a/sentry-samples/sentry-samples-spring-jakarta/src/main/java/io/sentry/samples/spring/jakarta/AppConfig.java +++ b/sentry-samples/sentry-samples-spring-jakarta/src/main/java/io/sentry/samples/spring/jakarta/AppConfig.java @@ -1,6 +1,6 @@ package io.sentry.samples.spring.jakarta; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.spring.jakarta.SentryUserFilter; import io.sentry.spring.jakarta.SentryUserProvider; import java.util.List; @@ -14,7 +14,7 @@ public class AppConfig { @Bean SentryUserFilter sentryUserFilter( - final IHub hub, final List sentryUserProviders) { - return new SentryUserFilter(hub, sentryUserProviders); + final IScopes scopes, final List sentryUserProviders) { + return new SentryUserFilter(scopes, sentryUserProviders); } } diff --git a/sentry-samples/sentry-samples-spring-jakarta/src/main/java/io/sentry/samples/spring/jakarta/WebConfig.java b/sentry-samples/sentry-samples-spring-jakarta/src/main/java/io/sentry/samples/spring/jakarta/WebConfig.java index 92b48b138c..73d425b286 100644 --- a/sentry-samples/sentry-samples-spring-jakarta/src/main/java/io/sentry/samples/spring/jakarta/WebConfig.java +++ b/sentry-samples/sentry-samples-spring-jakarta/src/main/java/io/sentry/samples/spring/jakarta/WebConfig.java @@ -1,6 +1,6 @@ package io.sentry.samples.spring.jakarta; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.spring.jakarta.tracing.SentrySpanClientHttpRequestInterceptor; import java.util.Collections; import org.springframework.context.annotation.Bean; @@ -20,14 +20,14 @@ public class WebConfig { * Creates a {@link RestTemplate} which calls are intercepted with {@link * SentrySpanClientHttpRequestInterceptor} to create spans around HTTP calls. * - * @param hub - sentry hub + * @param scopes - sentry scopes * @return RestTemplate */ @Bean - RestTemplate restTemplate(IHub hub) { + RestTemplate restTemplate(IScopes scopes) { RestTemplate restTemplate = new RestTemplate(); SentrySpanClientHttpRequestInterceptor sentryRestTemplateInterceptor = - new SentrySpanClientHttpRequestInterceptor(hub); + new SentrySpanClientHttpRequestInterceptor(scopes); restTemplate.setInterceptors(Collections.singletonList(sentryRestTemplateInterceptor)); return restTemplate; } diff --git a/sentry-samples/sentry-samples-spring/src/main/java/io/sentry/samples/spring/AppConfig.java b/sentry-samples/sentry-samples-spring/src/main/java/io/sentry/samples/spring/AppConfig.java index 7d46f09fb9..89a968834a 100644 --- a/sentry-samples/sentry-samples-spring/src/main/java/io/sentry/samples/spring/AppConfig.java +++ b/sentry-samples/sentry-samples-spring/src/main/java/io/sentry/samples/spring/AppConfig.java @@ -1,6 +1,6 @@ package io.sentry.samples.spring; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.spring.SentryUserFilter; import io.sentry.spring.SentryUserProvider; import java.util.List; @@ -14,7 +14,7 @@ public class AppConfig { @Bean SentryUserFilter sentryUserFilter( - final IHub hub, final List sentryUserProviders) { - return new SentryUserFilter(hub, sentryUserProviders); + final IScopes scopes, final List sentryUserProviders) { + return new SentryUserFilter(scopes, sentryUserProviders); } } diff --git a/sentry-samples/sentry-samples-spring/src/main/java/io/sentry/samples/spring/WebConfig.java b/sentry-samples/sentry-samples-spring/src/main/java/io/sentry/samples/spring/WebConfig.java index e135cbe233..2990ba8a38 100644 --- a/sentry-samples/sentry-samples-spring/src/main/java/io/sentry/samples/spring/WebConfig.java +++ b/sentry-samples/sentry-samples-spring/src/main/java/io/sentry/samples/spring/WebConfig.java @@ -1,6 +1,6 @@ package io.sentry.samples.spring; -import io.sentry.IHub; +import io.sentry.IScopes; import io.sentry.spring.tracing.SentrySpanClientHttpRequestInterceptor; import java.util.Collections; import org.springframework.context.annotation.Bean; @@ -20,14 +20,14 @@ public class WebConfig { * Creates a {@link RestTemplate} which calls are intercepted with {@link * SentrySpanClientHttpRequestInterceptor} to create spans around HTTP calls. * - * @param hub - sentry hub + * @param scopes - sentry scopes * @return RestTemplate */ @Bean - RestTemplate restTemplate(IHub hub) { + RestTemplate restTemplate(IScopes scopes) { RestTemplate restTemplate = new RestTemplate(); SentrySpanClientHttpRequestInterceptor sentryRestTemplateInterceptor = - new SentrySpanClientHttpRequestInterceptor(hub); + new SentrySpanClientHttpRequestInterceptor(scopes); restTemplate.setInterceptors(Collections.singletonList(sentryRestTemplateInterceptor)); return restTemplate; } From 7a0cd9f0fb0ce5efdfbf6886df77024e88e8ca18 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 19 Apr 2024 13:57:39 +0200 Subject: [PATCH 14/91] Hubs/Scopes Merge 14 - Add `Scopes` to replace `Hub` (#3311) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github --- sentry/api/sentry.api | 76 ++ sentry/src/main/java/io/sentry/IScope.java | 11 + sentry/src/main/java/io/sentry/NoOpScope.java | 17 + sentry/src/main/java/io/sentry/Scope.java | 28 + sentry/src/main/java/io/sentry/Scopes.java | 1093 +++++++++++++++++ sentry/src/main/java/io/sentry/Sentry.java | 8 +- 6 files changed, 1231 insertions(+), 2 deletions(-) create mode 100644 sentry/src/main/java/io/sentry/Scopes.java diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 52eb5df888..24935be274 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -661,10 +661,12 @@ public abstract interface class io/sentry/IScope { public abstract fun endSession ()Lio/sentry/Session; public abstract fun getAttachments ()Ljava/util/List; public abstract fun getBreadcrumbs ()Ljava/util/Queue; + public abstract fun getClient ()Lio/sentry/ISentryClient; public abstract fun getContexts ()Lio/sentry/protocol/Contexts; public abstract fun getEventProcessors ()Ljava/util/List; public abstract fun getExtras ()Ljava/util/Map; public abstract fun getFingerprint ()Ljava/util/List; + public abstract fun getLastEventId ()Lio/sentry/protocol/SentryId; public abstract fun getLevel ()Lio/sentry/SentryLevel; public abstract fun getOptions ()Lio/sentry/SentryOptions; public abstract fun getPropagationContext ()Lio/sentry/PropagationContext; @@ -679,6 +681,7 @@ public abstract interface class io/sentry/IScope { public abstract fun removeContexts (Ljava/lang/String;)V public abstract fun removeExtra (Ljava/lang/String;)V public abstract fun removeTag (Ljava/lang/String;)V + public abstract fun setClient (Lio/sentry/ISentryClient;)V public abstract fun setContexts (Ljava/lang/String;Ljava/lang/Boolean;)V public abstract fun setContexts (Ljava/lang/String;Ljava/lang/Character;)V public abstract fun setContexts (Ljava/lang/String;Ljava/lang/Number;)V @@ -688,6 +691,7 @@ public abstract interface class io/sentry/IScope { public abstract fun setContexts (Ljava/lang/String;[Ljava/lang/Object;)V public abstract fun setExtra (Ljava/lang/String;Ljava/lang/String;)V public abstract fun setFingerprint (Ljava/util/List;)V + public abstract fun setLastEventId (Lio/sentry/protocol/SentryId;)V public abstract fun setLevel (Lio/sentry/SentryLevel;)V public abstract fun setPropagationContext (Lio/sentry/PropagationContext;)V public abstract fun setRequest (Lio/sentry/protocol/Request;)V @@ -1286,11 +1290,13 @@ public final class io/sentry/NoOpScope : io/sentry/IScope { public fun endSession ()Lio/sentry/Session; public fun getAttachments ()Ljava/util/List; public fun getBreadcrumbs ()Ljava/util/Queue; + public fun getClient ()Lio/sentry/ISentryClient; public fun getContexts ()Lio/sentry/protocol/Contexts; public fun getEventProcessors ()Ljava/util/List; public fun getExtras ()Ljava/util/Map; public fun getFingerprint ()Ljava/util/List; public static fun getInstance ()Lio/sentry/NoOpScope; + public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getLevel ()Lio/sentry/SentryLevel; public fun getOptions ()Lio/sentry/SentryOptions; public fun getPropagationContext ()Lio/sentry/PropagationContext; @@ -1305,6 +1311,7 @@ public final class io/sentry/NoOpScope : io/sentry/IScope { public fun removeContexts (Ljava/lang/String;)V public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V + public fun setClient (Lio/sentry/ISentryClient;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Boolean;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Character;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Number;)V @@ -1314,6 +1321,7 @@ public final class io/sentry/NoOpScope : io/sentry/IScope { public fun setContexts (Ljava/lang/String;[Ljava/lang/Object;)V public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V public fun setFingerprint (Ljava/util/List;)V + public fun setLastEventId (Lio/sentry/protocol/SentryId;)V public fun setLevel (Lio/sentry/SentryLevel;)V public fun setPropagationContext (Lio/sentry/PropagationContext;)V public fun setRequest (Lio/sentry/protocol/Request;)V @@ -1708,10 +1716,12 @@ public final class io/sentry/Scope : io/sentry/IScope { public fun endSession ()Lio/sentry/Session; public fun getAttachments ()Ljava/util/List; public fun getBreadcrumbs ()Ljava/util/Queue; + public fun getClient ()Lio/sentry/ISentryClient; public fun getContexts ()Lio/sentry/protocol/Contexts; public fun getEventProcessors ()Ljava/util/List; public fun getExtras ()Ljava/util/Map; public fun getFingerprint ()Ljava/util/List; + public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getLevel ()Lio/sentry/SentryLevel; public fun getOptions ()Lio/sentry/SentryOptions; public fun getPropagationContext ()Lio/sentry/PropagationContext; @@ -1726,6 +1736,7 @@ public final class io/sentry/Scope : io/sentry/IScope { public fun removeContexts (Ljava/lang/String;)V public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V + public fun setClient (Lio/sentry/ISentryClient;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Boolean;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Character;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Number;)V @@ -1735,6 +1746,7 @@ public final class io/sentry/Scope : io/sentry/IScope { public fun setContexts (Ljava/lang/String;[Ljava/lang/Object;)V public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V public fun setFingerprint (Ljava/util/List;)V + public fun setLastEventId (Lio/sentry/protocol/SentryId;)V public fun setLevel (Lio/sentry/SentryLevel;)V public fun setPropagationContext (Lio/sentry/PropagationContext;)V public fun setRequest (Lio/sentry/protocol/Request;)V @@ -1780,6 +1792,70 @@ public abstract class io/sentry/ScopeObserverAdapter : io/sentry/IScopeObserver public fun setUser (Lio/sentry/protocol/User;)V } +public final class io/sentry/Scopes : io/sentry/IScopes, io/sentry/metrics/MetricsApi$IMetricsInterface { + public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V + public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V + public fun bindClient (Lio/sentry/ISentryClient;)V + public fun captureCheckIn (Lio/sentry/CheckIn;)Lio/sentry/protocol/SentryId; + public fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; + public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; + public fun captureUserFeedback (Lio/sentry/UserFeedback;)V + public fun clearBreadcrumbs ()V + public fun clone ()Lio/sentry/IHub; + public synthetic fun clone ()Ljava/lang/Object; + public fun close ()V + public fun close (Z)V + public fun configureScope (Lio/sentry/ScopeCallback;)V + public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; + public fun endSession ()V + public fun flush (J)V + public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/Scopes; + public fun forkedScopes (Ljava/lang/String;)Lio/sentry/Scopes; + public fun getBaggage ()Lio/sentry/BaggageHeader; + public fun getCreator ()Ljava/lang/String; + public fun getDefaultTagsForMetrics ()Ljava/util/Map; + public fun getGlobalScope ()Lio/sentry/IScope; + public fun getIsolationScope ()Lio/sentry/IScope; + public fun getLastEventId ()Lio/sentry/protocol/SentryId; + public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; + public fun getMetricsAggregator ()Lio/sentry/IMetricsAggregator; + public fun getOptions ()Lio/sentry/SentryOptions; + public fun getParent ()Lio/sentry/Scopes; + public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; + public fun getScope ()Lio/sentry/IScope; + public fun getSpan ()Lio/sentry/ISpan; + public fun getTraceparent ()Lio/sentry/SentryTraceHeader; + public fun getTransaction ()Lio/sentry/ITransaction; + public fun isAncestorOf (Lio/sentry/Scopes;)Z + public fun isCrashedLastRun ()Ljava/lang/Boolean; + public fun isEnabled ()Z + public fun isHealthy ()Z + public fun metrics ()Lio/sentry/metrics/MetricsApi; + public fun popScope ()V + public fun pushScope ()V + public fun removeExtra (Ljava/lang/String;)V + public fun removeTag (Ljava/lang/String;)V + public fun reportFullyDisplayed ()V + public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V + public fun setFingerprint (Ljava/util/List;)V + public fun setLevel (Lio/sentry/SentryLevel;)V + public fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V + public fun setTag (Ljava/lang/String;Ljava/lang/String;)V + public fun setTransaction (Ljava/lang/String;)V + public fun setUser (Lio/sentry/protocol/User;)V + public fun startSession ()V + public fun startSpanForMetric (Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ISpan; + public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; + public fun traceHeaders ()Lio/sentry/SentryTraceHeader; + public fun withScope (Lio/sentry/ScopeCallback;)V +} + public final class io/sentry/ScopesAdapter : io/sentry/IScopes { public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V diff --git a/sentry/src/main/java/io/sentry/IScope.java b/sentry/src/main/java/io/sentry/IScope.java index 3842fb2c3a..3bc25ce8e9 100644 --- a/sentry/src/main/java/io/sentry/IScope.java +++ b/sentry/src/main/java/io/sentry/IScope.java @@ -2,6 +2,7 @@ import io.sentry.protocol.Contexts; import io.sentry.protocol.Request; +import io.sentry.protocol.SentryId; import io.sentry.protocol.User; import java.util.Collection; import java.util.List; @@ -370,4 +371,14 @@ public interface IScope { */ @NotNull IScope clone(); + + void setLastEventId(final @NotNull SentryId lastEventId); + + @NotNull + SentryId getLastEventId(); + + void setClient(final @NotNull ISentryClient client); + + @NotNull + ISentryClient getClient(); } diff --git a/sentry/src/main/java/io/sentry/NoOpScope.java b/sentry/src/main/java/io/sentry/NoOpScope.java index c756fb49a3..f7336d6edb 100644 --- a/sentry/src/main/java/io/sentry/NoOpScope.java +++ b/sentry/src/main/java/io/sentry/NoOpScope.java @@ -2,6 +2,7 @@ import io.sentry.protocol.Contexts; import io.sentry.protocol.Request; +import io.sentry.protocol.SentryId; import io.sentry.protocol.User; import java.util.ArrayDeque; import java.util.ArrayList; @@ -236,6 +237,9 @@ public void setPropagationContext(@NotNull PropagationContext propagationContext return new PropagationContext(); } + @Override + public void setLastEventId(@NotNull SentryId lastEventId) {} + /** * Clones the Scope * @@ -245,4 +249,17 @@ public void setPropagationContext(@NotNull PropagationContext propagationContext public @NotNull IScope clone() { return NoOpScope.getInstance(); } + + @Override + public @NotNull SentryId getLastEventId() { + return SentryId.EMPTY_ID; + } + + @Override + public void setClient(@NotNull ISentryClient client) {} + + @Override + public @NotNull ISentryClient getClient() { + return NoOpSentryClient.getInstance(); + } } diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index 91c9fcd8cf..aa35ccfd7a 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -3,6 +3,7 @@ import io.sentry.protocol.App; import io.sentry.protocol.Contexts; import io.sentry.protocol.Request; +import io.sentry.protocol.SentryId; import io.sentry.protocol.TransactionNameSource; import io.sentry.protocol.User; import io.sentry.util.CollectionUtils; @@ -22,6 +23,8 @@ /** Scope data to be sent with the event */ public final class Scope implements IScope { + private volatile @NotNull SentryId lastEventId; + /** Scope's SentryLevel */ private @Nullable SentryLevel level; @@ -80,6 +83,8 @@ public final class Scope implements IScope { private @NotNull PropagationContext propagationContext; + private @NotNull ISentryClient client = NoOpSentryClient.getInstance(); + /** * Scope's ctor * @@ -89,6 +94,7 @@ public Scope(final @NotNull SentryOptions options) { this.options = Objects.requireNonNull(options, "SentryOptions is required."); this.breadcrumbs = createBreadcrumbsList(this.options.getMaxBreadcrumbs()); this.propagationContext = new PropagationContext(); + this.lastEventId = SentryId.EMPTY_ID; } private Scope(final @NotNull Scope scope) { @@ -97,6 +103,8 @@ private Scope(final @NotNull Scope scope) { this.session = scope.session; this.options = scope.options; this.level = scope.level; + // TODO should we do this? didn't do it for Hub + this.lastEventId = scope.getLastEventId(); final User userRef = scope.user; this.user = userRef != null ? new User(userRef) : null; @@ -945,6 +953,26 @@ public void setPropagationContext(final @NotNull PropagationContext propagationC return new Scope(this); } + @Override + public void setLastEventId(@NotNull SentryId lastEventId) { + this.lastEventId = lastEventId; + } + + @Override + public @NotNull SentryId getLastEventId() { + return lastEventId; + } + + @Override + public void setClient(@NotNull ISentryClient client) { + this.client = client; + } + + @Override + public @NotNull ISentryClient getClient() { + return client; + } + /** The IWithTransaction callback */ @ApiStatus.Internal public interface IWithTransaction { diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java new file mode 100644 index 0000000000..618864b649 --- /dev/null +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -0,0 +1,1093 @@ +package io.sentry; + +import io.sentry.clientreport.DiscardReason; +import io.sentry.hints.SessionEndHint; +import io.sentry.hints.SessionStartHint; +import io.sentry.metrics.LocalMetricsAggregator; +import io.sentry.metrics.MetricsApi; +import io.sentry.protocol.SentryId; +import io.sentry.protocol.SentryTransaction; +import io.sentry.protocol.User; +import io.sentry.transport.RateLimiter; +import io.sentry.util.ExceptionUtils; +import io.sentry.util.HintUtils; +import io.sentry.util.Objects; +import io.sentry.util.Pair; +import io.sentry.util.TracingUtils; +import java.io.Closeable; +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class Scopes implements IScopes, MetricsApi.IMetricsInterface { + + private final @NotNull IScope scope; + private final @NotNull IScope isolationScope; + // TODO just for debugging + @SuppressWarnings("UnusedVariable") + private final @Nullable Scopes parentScopes; + + private final @NotNull String creator; + + // TODO should this be set on all scopes (global, isolation, current)? + private final @NotNull SentryOptions options; + private volatile boolean isEnabled; + private final @NotNull TracesSampler tracesSampler; + + // TODO should this go on global scope? + private final @NotNull Map, String>> throwableToSpan = + Collections.synchronizedMap(new WeakHashMap<>()); + private final @NotNull TransactionPerformanceCollector transactionPerformanceCollector; + private final @NotNull MetricsApi metricsApi; + + Scopes( + final @NotNull IScope scope, + final @NotNull IScope isolationScope, + final @NotNull SentryOptions options, + final @NotNull String creator) { + this(scope, isolationScope, null, options, creator); + } + + private Scopes( + final @NotNull IScope scope, + final @NotNull IScope isolationScope, + final @Nullable Scopes parentScopes, + final @NotNull SentryOptions options, + final @NotNull String creator) { + validateOptions(options); + + this.scope = scope; + this.isolationScope = isolationScope; + this.parentScopes = parentScopes; + this.creator = creator; + this.options = options; + this.tracesSampler = new TracesSampler(options); + this.transactionPerformanceCollector = options.getTransactionPerformanceCollector(); + + this.isEnabled = true; + + this.metricsApi = new MetricsApi(this); + } + + public @NotNull String getCreator() { + return creator; + } + + // TODO add to IScopes interface + public @NotNull IScope getScope() { + return scope; + } + + // TODO add to IScopes interface + public @NotNull IScope getIsolationScope() { + return isolationScope; + } + + // TODO add to IScopes interface? + public @Nullable Scopes getParent() { + return parentScopes; + } + + // TODO add to IScopes interface? + public boolean isAncestorOf(final @Nullable Scopes otherScopes) { + if (otherScopes == null) { + return false; + } + + if (this == otherScopes) { + return true; + } + + final @Nullable Scopes parent = otherScopes.getParent(); + if (parent != null) { + return isAncestorOf(parent); + } + + return false; + } + + // TODO add to IScopes interface + public @NotNull Scopes forkedScopes(final @NotNull String creator) { + return new Scopes(scope.clone(), isolationScope.clone(), this, options, creator); + } + + // TODO add to IScopes interface + public @NotNull Scopes forkedCurrentScope(final @NotNull String creator) { + return new Scopes(scope.clone(), isolationScope, this, options, creator); + } + + // // TODO in Sentry.init? + // public static Scopes forkedRoots(final @NotNull SentryOptions options, final @NotNull String + // creator) { + // return new Scopes(ROOT_SCOPE.clone(), ROOT_ISOLATION_SCOPE.clone(), options, creator); + // } + + // TODO always read from root scope? + @Override + public boolean isEnabled() { + return isEnabled; + } + + @Override + public @NotNull SentryId captureEvent(@NotNull SentryEvent event, @Nullable Hint hint) { + return captureEventInternal(event, hint, null); + } + + @Override + public @NotNull SentryId captureEvent( + @NotNull SentryEvent event, @Nullable Hint hint, @NotNull ScopeCallback callback) { + return captureEventInternal(event, hint, callback); + } + + private @NotNull SentryId captureEventInternal( + final @NotNull SentryEvent event, + final @Nullable Hint hint, + final @Nullable ScopeCallback scopeCallback) { + SentryId sentryId = SentryId.EMPTY_ID; + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, "Instance is disabled and this 'captureEvent' call is a no-op."); + } else if (event == null) { + options.getLogger().log(SentryLevel.WARNING, "captureEvent called with null parameter."); + } else { + try { + assignTraceContext(event); + final IScope localScope = buildLocalScope(getCombinedScopeView(), scopeCallback); + + sentryId = getClient().captureEvent(event, localScope, hint); + updateLastEventId(sentryId); + } catch (Throwable e) { + options + .getLogger() + .log( + SentryLevel.ERROR, "Error while capturing event with id: " + event.getEventId(), e); + } + } + return sentryId; + } + + private @NotNull ISentryClient getClient() { + return getCombinedScopeView().getClient(); + } + + private void assignTraceContext(final @NotNull SentryEvent event) { + if (options.isTracingEnabled() && event.getThrowable() != null) { + final Pair, String> pair = + throwableToSpan.get(ExceptionUtils.findRootCause(event.getThrowable())); + if (pair != null) { + final WeakReference spanWeakRef = pair.getFirst(); + if (event.getContexts().getTrace() == null && spanWeakRef != null) { + final ISpan span = spanWeakRef.get(); + if (span != null) { + event.getContexts().setTrace(span.getSpanContext()); + } + } + final String transactionName = pair.getSecond(); + if (event.getTransaction() == null && transactionName != null) { + event.setTransaction(transactionName); + } + } + } + } + + private IScope buildLocalScope( + final @NotNull IScope parentScope, final @Nullable ScopeCallback callback) { + if (callback != null) { + try { + final IScope localScope = parentScope.clone(); + callback.run(localScope); + return localScope; + } catch (Throwable t) { + options.getLogger().log(SentryLevel.ERROR, "Error in the 'ScopeCallback' callback.", t); + } + } + return parentScope; + } + + @Override + public @NotNull SentryId captureMessage( + final @NotNull String message, final @NotNull SentryLevel level) { + return captureMessageInternal(message, level, null); + } + + @Override + public @NotNull SentryId captureMessage( + final @NotNull String message, + final @NotNull SentryLevel level, + final @NotNull ScopeCallback callback) { + return captureMessageInternal(message, level, callback); + } + + private @NotNull SentryId captureMessageInternal( + final @NotNull String message, + final @NotNull SentryLevel level, + final @Nullable ScopeCallback scopeCallback) { + SentryId sentryId = SentryId.EMPTY_ID; + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Instance is disabled and this 'captureMessage' call is a no-op."); + } else if (message == null) { + options.getLogger().log(SentryLevel.WARNING, "captureMessage called with null parameter."); + } else { + try { + final IScope localScope = buildLocalScope(getCombinedScopeView(), scopeCallback); + + sentryId = getClient().captureMessage(message, level, localScope); + } catch (Throwable e) { + options.getLogger().log(SentryLevel.ERROR, "Error while capturing message: " + message, e); + } + } + updateLastEventId(sentryId); + return sentryId; + } + + @ApiStatus.Internal + @Override + public @NotNull SentryId captureEnvelope( + final @NotNull SentryEnvelope envelope, final @Nullable Hint hint) { + Objects.requireNonNull(envelope, "SentryEnvelope is required."); + + SentryId sentryId = SentryId.EMPTY_ID; + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Instance is disabled and this 'captureEnvelope' call is a no-op."); + } else { + try { + final SentryId capturedEnvelopeId = getClient().captureEnvelope(envelope, hint); + if (capturedEnvelopeId != null) { + sentryId = capturedEnvelopeId; + } + } catch (Throwable e) { + options.getLogger().log(SentryLevel.ERROR, "Error while capturing envelope.", e); + } + } + return sentryId; + } + + @Override + public @NotNull SentryId captureException( + final @NotNull Throwable throwable, final @Nullable Hint hint) { + return captureExceptionInternal(throwable, hint, null); + } + + @Override + public @NotNull SentryId captureException( + final @NotNull Throwable throwable, + final @Nullable Hint hint, + final @NotNull ScopeCallback callback) { + + return captureExceptionInternal(throwable, hint, callback); + } + + private @NotNull SentryId captureExceptionInternal( + final @NotNull Throwable throwable, + final @Nullable Hint hint, + final @Nullable ScopeCallback scopeCallback) { + SentryId sentryId = SentryId.EMPTY_ID; + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Instance is disabled and this 'captureException' call is a no-op."); + } else if (throwable == null) { + options.getLogger().log(SentryLevel.WARNING, "captureException called with null parameter."); + } else { + try { + final SentryEvent event = new SentryEvent(throwable); + assignTraceContext(event); + + final IScope localScope = buildLocalScope(getCombinedScopeView(), scopeCallback); + + sentryId = getClient().captureEvent(event, localScope, hint); + } catch (Throwable e) { + options + .getLogger() + .log( + SentryLevel.ERROR, "Error while capturing exception: " + throwable.getMessage(), e); + } + } + updateLastEventId(sentryId); + return sentryId; + } + + @Override + public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Instance is disabled and this 'captureUserFeedback' call is a no-op."); + } else { + try { + getClient().captureUserFeedback(userFeedback); + } catch (Throwable e) { + options + .getLogger() + .log( + SentryLevel.ERROR, + "Error while capturing captureUserFeedback: " + userFeedback.toString(), + e); + } + } + } + + @Override + public void startSession() { + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, "Instance is disabled and this 'startSession' call is a no-op."); + } else { + final Scope.SessionPair pair = getCombinedScopeView().startSession(); + if (pair != null) { + // TODO: add helper overload `captureSessions` to pass a list of sessions and submit a + // single envelope + // Or create the envelope here with both items and call `captureEnvelope` + if (pair.getPrevious() != null) { + final Hint hint = HintUtils.createWithTypeCheckHint(new SessionEndHint()); + + getClient().captureSession(pair.getPrevious(), hint); + } + + final Hint hint = HintUtils.createWithTypeCheckHint(new SessionStartHint()); + + getClient().captureSession(pair.getCurrent(), hint); + } else { + options.getLogger().log(SentryLevel.WARNING, "Session could not be started."); + } + } + } + + @Override + public void endSession() { + if (!isEnabled()) { + options + .getLogger() + .log(SentryLevel.WARNING, "Instance is disabled and this 'endSession' call is a no-op."); + } else { + final Session previousSession = getCombinedScopeView().endSession(); + if (previousSession != null) { + final Hint hint = HintUtils.createWithTypeCheckHint(new SessionEndHint()); + + getClient().captureSession(previousSession, hint); + } + } + } + + private IScope getCombinedScopeView() { + // TODO combine global, isolation and current scope + return scope; + } + + @Override + public void close() { + close(false); + } + + @Override + @SuppressWarnings("FutureReturnValueIgnored") + public void close(final boolean isRestarting) { + if (!isEnabled()) { + options + .getLogger() + .log(SentryLevel.WARNING, "Instance is disabled and this 'close' call is a no-op."); + } else { + try { + for (Integration integration : options.getIntegrations()) { + if (integration instanceof Closeable) { + try { + ((Closeable) integration).close(); + } catch (IOException e) { + options + .getLogger() + .log(SentryLevel.WARNING, "Failed to close the integration {}.", integration, e); + } + } + } + + // TODO which scopes do we call this on? isolation and current scope? + configureScope(scope -> scope.clear()); + options.getTransactionProfiler().close(); + options.getTransactionPerformanceCollector().close(); + final @NotNull ISentryExecutorService executorService = options.getExecutorService(); + if (isRestarting) { + executorService.submit(() -> executorService.close(options.getShutdownTimeoutMillis())); + } else { + executorService.close(options.getShutdownTimeoutMillis()); + } + + // TODO: should we end session before closing client? + getClient().close(isRestarting); + } catch (Throwable e) { + options.getLogger().log(SentryLevel.ERROR, "Error while closing the Hub.", e); + } + isEnabled = false; + } + } + + @Override + public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb, final @Nullable Hint hint) { + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Instance is disabled and this 'addBreadcrumb' call is a no-op."); + } else if (breadcrumb == null) { + options.getLogger().log(SentryLevel.WARNING, "addBreadcrumb called with null parameter."); + } else { + getDefaultWriteScope().addBreadcrumb(breadcrumb, hint); + } + } + + private IScope getDefaultConfigureScope() { + // TODO configurable default scope via SentryOptions, Android = global or isolation, backend = + // isolation + return scope; + } + + private IScope getDefaultWriteScope() { + // TODO configurable default scope via SentryOptions, Android = global or isolation, backend = + // isolation + return getIsolationScope(); + } + + @Override + public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb) { + addBreadcrumb(breadcrumb, new Hint()); + } + + @Override + public void setLevel(final @Nullable SentryLevel level) { + if (!isEnabled()) { + options + .getLogger() + .log(SentryLevel.WARNING, "Instance is disabled and this 'setLevel' call is a no-op."); + } else { + getDefaultWriteScope().setLevel(level); + } + } + + @Override + public void setTransaction(final @Nullable String transaction) { + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Instance is disabled and this 'setTransaction' call is a no-op."); + } else if (transaction != null) { + getDefaultWriteScope().setTransaction(transaction); + } else { + options.getLogger().log(SentryLevel.WARNING, "Transaction cannot be null"); + } + } + + @Override + public void setUser(final @Nullable User user) { + if (!isEnabled()) { + options + .getLogger() + .log(SentryLevel.WARNING, "Instance is disabled and this 'setUser' call is a no-op."); + } else { + getDefaultWriteScope().setUser(user); + } + } + + @Override + public void setFingerprint(final @NotNull List fingerprint) { + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Instance is disabled and this 'setFingerprint' call is a no-op."); + } else if (fingerprint == null) { + options.getLogger().log(SentryLevel.WARNING, "setFingerprint called with null parameter."); + } else { + getDefaultWriteScope().setFingerprint(fingerprint); + } + } + + @Override + public void clearBreadcrumbs() { + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Instance is disabled and this 'clearBreadcrumbs' call is a no-op."); + } else { + getDefaultWriteScope().clearBreadcrumbs(); + } + } + + @Override + public void setTag(final @NotNull String key, final @NotNull String value) { + if (!isEnabled()) { + options + .getLogger() + .log(SentryLevel.WARNING, "Instance is disabled and this 'setTag' call is a no-op."); + } else if (key == null || value == null) { + options.getLogger().log(SentryLevel.WARNING, "setTag called with null parameter."); + } else { + getDefaultWriteScope().setTag(key, value); + } + } + + @Override + public void removeTag(final @NotNull String key) { + if (!isEnabled()) { + options + .getLogger() + .log(SentryLevel.WARNING, "Instance is disabled and this 'removeTag' call is a no-op."); + } else if (key == null) { + options.getLogger().log(SentryLevel.WARNING, "removeTag called with null parameter."); + } else { + getDefaultWriteScope().removeTag(key); + } + } + + @Override + public void setExtra(final @NotNull String key, final @NotNull String value) { + if (!isEnabled()) { + options + .getLogger() + .log(SentryLevel.WARNING, "Instance is disabled and this 'setExtra' call is a no-op."); + } else if (key == null || value == null) { + options.getLogger().log(SentryLevel.WARNING, "setExtra called with null parameter."); + } else { + getDefaultWriteScope().setExtra(key, value); + } + } + + @Override + public void removeExtra(final @NotNull String key) { + if (!isEnabled()) { + options + .getLogger() + .log(SentryLevel.WARNING, "Instance is disabled and this 'removeExtra' call is a no-op."); + } else if (key == null) { + options.getLogger().log(SentryLevel.WARNING, "removeExtra called with null parameter."); + } else { + getDefaultWriteScope().removeExtra(key); + } + } + + private void updateLastEventId(final @NotNull SentryId lastEventId) { + scope.setLastEventId(lastEventId); + isolationScope.setLastEventId(lastEventId); + getGlobalScope().setLastEventId(lastEventId); + } + + // TODO add to IScopes interface + public @NotNull IScope getGlobalScope() { + // TODO return singleton global scope here + return scope; + } + + @Override + public @NotNull SentryId getLastEventId() { + // TODO read all scopes here / read default scope? + // returning scope.lastEventId isn't ideal because changed to child scope are not stored in + // there + return getGlobalScope().getLastEventId(); + } + + // TODO needs to be deprecated because there's no more stack + // TODO needs to return a lifecycle token + @Override + public void pushScope() { + if (!isEnabled()) { + options + .getLogger() + .log(SentryLevel.WARNING, "Instance is disabled and this 'pushScope' call is a no-op."); + } else { + // Scopes scopes = this.forkedScopes("pushScope"); + // return scopes.makeCurrent(); + } + } + + // public SentryLifecycleToken makeCurrent() { + // // TODO store.set(this); + // } + + // TODO needs to be deprecated because there's no more stack + @Override + public void popScope() { + if (!isEnabled()) { + options + .getLogger() + .log(SentryLevel.WARNING, "Instance is disabled and this 'popScope' call is a no-op."); + } else { + // TODO how to remove fork? + // TODO getParentScopes().makeCurrent()? + } + } + + // TODO lots of testing required to see how ThreadLocal is affected + @Override + public void withScope(final @NotNull ScopeCallback callback) { + if (!isEnabled()) { + try { + callback.run(NoOpScope.getInstance()); + } catch (Throwable e) { + options.getLogger().log(SentryLevel.ERROR, "Error in the 'withScope' callback.", e); + } + + } else { + Scopes forkedScopes = forkedScopes("withScope"); + // TODO should forkedScopes be made current inside callback? + // TODO forkedScopes.makeCurrent()? + try { + callback.run(forkedScopes.getScope()); + } catch (Throwable e) { + options.getLogger().log(SentryLevel.ERROR, "Error in the 'withScope' callback.", e); + } + } + } + + @Override + public void configureScope(final @NotNull ScopeCallback callback) { + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Instance is disabled and this 'configureScope' call is a no-op."); + } else { + try { + callback.run(getDefaultConfigureScope()); + } catch (Throwable e) { + options.getLogger().log(SentryLevel.ERROR, "Error in the 'configureScope' callback.", e); + } + } + } + + @Override + public void bindClient(final @NotNull ISentryClient client) { + if (!isEnabled()) { + options + .getLogger() + .log(SentryLevel.WARNING, "Instance is disabled and this 'bindClient' call is a no-op."); + } else { + if (client != null) { + options.getLogger().log(SentryLevel.DEBUG, "New client bound to scope."); + getDefaultWriteScope().setClient(client); + } else { + options.getLogger().log(SentryLevel.DEBUG, "NoOp client bound to scope."); + getDefaultWriteScope().setClient(NoOpSentryClient.getInstance()); + } + } + } + + @Override + public boolean isHealthy() { + return getClient().isHealthy(); + } + + @Override + public void flush(long timeoutMillis) { + if (!isEnabled()) { + options + .getLogger() + .log(SentryLevel.WARNING, "Instance is disabled and this 'flush' call is a no-op."); + } else { + try { + getClient().flush(timeoutMillis); + } catch (Throwable e) { + options.getLogger().log(SentryLevel.ERROR, "Error in the 'client.flush'.", e); + } + } + } + + @Override + @SuppressWarnings("deprecation") + public @NotNull IHub clone() { + if (!isEnabled()) { + options.getLogger().log(SentryLevel.WARNING, "Disabled Hub cloned."); + } + return new HubScopesWrapper(forkedScopes("scopes clone")); + } + + @ApiStatus.Internal + @Override + public @NotNull SentryId captureTransaction( + final @NotNull SentryTransaction transaction, + final @Nullable TraceContext traceContext, + final @Nullable Hint hint, + final @Nullable ProfilingTraceData profilingTraceData) { + Objects.requireNonNull(transaction, "transaction is required"); + + SentryId sentryId = SentryId.EMPTY_ID; + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Instance is disabled and this 'captureTransaction' call is a no-op."); + } else { + if (!transaction.isFinished()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Transaction: %s is not finished and this 'captureTransaction' call is a no-op.", + transaction.getEventId()); + } else { + if (!Boolean.TRUE.equals(transaction.isSampled())) { + options + .getLogger() + .log( + SentryLevel.DEBUG, + "Transaction %s was dropped due to sampling decision.", + transaction.getEventId()); + if (options.getBackpressureMonitor().getDownsampleFactor() > 0) { + options + .getClientReportRecorder() + .recordLostEvent(DiscardReason.BACKPRESSURE, DataCategory.Transaction); + } else { + options + .getClientReportRecorder() + .recordLostEvent(DiscardReason.SAMPLE_RATE, DataCategory.Transaction); + } + } else { + try { + sentryId = + getClient() + .captureTransaction( + transaction, + traceContext, + getCombinedScopeView(), + hint, + profilingTraceData); + } catch (Throwable e) { + options + .getLogger() + .log( + SentryLevel.ERROR, + "Error while capturing transaction with id: " + transaction.getEventId(), + e); + } + } + } + } + return sentryId; + } + + @Override + public @NotNull ITransaction startTransaction( + final @NotNull TransactionContext transactionContext, + final @NotNull TransactionOptions transactionOptions) { + return createTransaction(transactionContext, transactionOptions); + } + + private @NotNull ITransaction createTransaction( + final @NotNull TransactionContext transactionContext, + final @NotNull TransactionOptions transactionOptions) { + Objects.requireNonNull(transactionContext, "transactionContext is required"); + + ITransaction transaction; + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Instance is disabled and this 'startTransaction' returns a no-op."); + transaction = NoOpTransaction.getInstance(); + } else if (!options.getInstrumenter().equals(transactionContext.getInstrumenter())) { + options + .getLogger() + .log( + SentryLevel.DEBUG, + "Returning no-op for instrumenter %s as the SDK has been configured to use instrumenter %s", + transactionContext.getInstrumenter(), + options.getInstrumenter()); + transaction = NoOpTransaction.getInstance(); + } else if (!options.isTracingEnabled()) { + options + .getLogger() + .log( + SentryLevel.INFO, "Tracing is disabled and this 'startTransaction' returns a no-op."); + transaction = NoOpTransaction.getInstance(); + } else { + final SamplingContext samplingContext = + new SamplingContext(transactionContext, transactionOptions.getCustomSamplingContext()); + @NotNull TracesSamplingDecision samplingDecision = tracesSampler.sample(samplingContext); + transactionContext.setSamplingDecision(samplingDecision); + + transaction = + new SentryTracer( + transactionContext, this, transactionOptions, transactionPerformanceCollector); + + // The listener is called only if the transaction exists, as the transaction is needed to + // stop it + if (samplingDecision.getSampled() && samplingDecision.getProfileSampled()) { + final ITransactionProfiler transactionProfiler = options.getTransactionProfiler(); + // If the profiler is not running, we start and bind it here. + if (!transactionProfiler.isRunning()) { + transactionProfiler.start(); + transactionProfiler.bindTransaction(transaction); + } else if (transactionOptions.isAppStartTransaction()) { + // If the profiler is running and the current transaction is the app start, we bind it. + transactionProfiler.bindTransaction(transaction); + } + } + } + if (transactionOptions.isBindToScope()) { + configureScope(scope -> scope.setTransaction(transaction)); + } + return transaction; + } + + @Deprecated + @SuppressWarnings("InlineMeSuggester") + @Override + public @Nullable SentryTraceHeader traceHeaders() { + return getTraceparent(); + } + + @Override + @ApiStatus.Internal + public void setSpanContext( + final @NotNull Throwable throwable, + final @NotNull ISpan span, + final @NotNull String transactionName) { + Objects.requireNonNull(throwable, "throwable is required"); + Objects.requireNonNull(span, "span is required"); + Objects.requireNonNull(transactionName, "transactionName is required"); + // to match any cause, span context is always attached to the root cause of the exception + final Throwable rootCause = ExceptionUtils.findRootCause(throwable); + // the most inner span should be assigned to a throwable + if (!throwableToSpan.containsKey(rootCause)) { + throwableToSpan.put(rootCause, new Pair<>(new WeakReference<>(span), transactionName)); + } + } + + // TODO this seems unused + @Nullable + SpanContext getSpanContext(final @NotNull Throwable throwable) { + Objects.requireNonNull(throwable, "throwable is required"); + final Throwable rootCause = ExceptionUtils.findRootCause(throwable); + final Pair, String> pair = this.throwableToSpan.get(rootCause); + if (pair != null) { + final WeakReference spanWeakRef = pair.getFirst(); + if (spanWeakRef != null) { + final ISpan span = spanWeakRef.get(); + if (span != null) { + return span.getSpanContext(); + } + } + } + return null; + } + + @Override + public @Nullable ISpan getSpan() { + ISpan span = null; + if (!isEnabled()) { + options + .getLogger() + .log(SentryLevel.WARNING, "Instance is disabled and this 'getSpan' call is a no-op."); + } else { + span = getScope().getSpan(); + } + return span; + } + + @Override + @ApiStatus.Internal + public @Nullable ITransaction getTransaction() { + ITransaction span = null; + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Instance is disabled and this 'getTransaction' call is a no-op."); + } else { + span = getScope().getTransaction(); + } + return span; + } + + @Override + public @NotNull SentryOptions getOptions() { + return options; + } + + @Override + public @Nullable Boolean isCrashedLastRun() { + return SentryCrashLastRunState.getInstance() + .isCrashedLastRun(options.getCacheDirPath(), !options.isEnableAutoSessionTracking()); + } + + @Override + public void reportFullyDisplayed() { + if (options.isEnableTimeToFullDisplayTracing()) { + options.getFullyDisplayedReporter().reportFullyDrawn(); + } + } + + @Override + public @Nullable TransactionContext continueTrace( + final @Nullable String sentryTrace, final @Nullable List baggageHeaders) { + @NotNull + PropagationContext propagationContext = + PropagationContext.fromHeaders(getOptions().getLogger(), sentryTrace, baggageHeaders); + // TODO should this go on isolation scope? + configureScope( + (scope) -> { + scope.setPropagationContext(propagationContext); + }); + if (options.isTracingEnabled()) { + return TransactionContext.fromPropagationContext(propagationContext); + } else { + return null; + } + } + + @Override + public @Nullable SentryTraceHeader getTraceparent() { + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Instance is disabled and this 'getTraceparent' call is a no-op."); + } else { + final @Nullable TracingUtils.TracingHeaders headers = + TracingUtils.trace(this, null, getSpan()); + if (headers != null) { + return headers.getSentryTraceHeader(); + } + } + + return null; + } + + @Override + public @Nullable BaggageHeader getBaggage() { + if (!isEnabled()) { + options + .getLogger() + .log(SentryLevel.WARNING, "Instance is disabled and this 'getBaggage' call is a no-op."); + } else { + final @Nullable TracingUtils.TracingHeaders headers = + TracingUtils.trace(this, null, getSpan()); + if (headers != null) { + return headers.getBaggageHeader(); + } + } + + return null; + } + + @Override + @ApiStatus.Experimental + public @NotNull SentryId captureCheckIn(final @NotNull CheckIn checkIn) { + SentryId sentryId = SentryId.EMPTY_ID; + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Instance is disabled and this 'captureCheckIn' call is a no-op."); + } else { + try { + sentryId = getClient().captureCheckIn(checkIn, getCombinedScopeView(), null); + } catch (Throwable e) { + options.getLogger().log(SentryLevel.ERROR, "Error while capturing check-in for slug", e); + } + } + updateLastEventId(sentryId); + return sentryId; + } + + @ApiStatus.Internal + @Override + public @Nullable RateLimiter getRateLimiter() { + return getClient().getRateLimiter(); + } + + @Override + public @NotNull MetricsApi metrics() { + return metricsApi; + } + + @Override + public @NotNull IMetricsAggregator getMetricsAggregator() { + return getClient().getMetricsAggregator(); + } + + @Override + public @NotNull Map getDefaultTagsForMetrics() { + if (!options.isEnableDefaultTagsForMetrics()) { + return Collections.emptyMap(); + } + + final @NotNull Map tags = new HashMap<>(); + final @Nullable String release = options.getRelease(); + if (release != null) { + tags.put("release", release); + } + + final @Nullable String environment = options.getEnvironment(); + if (environment != null) { + tags.put("environment", environment); + } + + final @Nullable String txnName = getCombinedScopeView().getTransactionName(); + if (txnName != null) { + tags.put("transaction", txnName); + } + return Collections.unmodifiableMap(tags); + } + + @Override + public @Nullable ISpan startSpanForMetric(@NotNull String op, @NotNull String description) { + final @Nullable ISpan span = getSpan(); + if (span != null) { + return span.startChild(op, description); + } + return null; + } + + @Override + public @Nullable LocalMetricsAggregator getLocalMetricsAggregator() { + if (!options.isEnableSpanLocalMetricAggregation()) { + return null; + } + final @Nullable ISpan span = getSpan(); + if (span != null) { + return span.getLocalMetricsAggregator(); + } + return null; + } + + private static void validateOptions(final @NotNull SentryOptions options) { + Objects.requireNonNull(options, "SentryOptions is required."); + if (options.getDsn() == null || options.getDsn().isEmpty()) { + throw new IllegalArgumentException( + "Hub requires a DSN to be instantiated. Considering using the NoOpHub if no DSN is available."); + } + } +} diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 206ffd8d20..f4996bb8ad 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -254,8 +254,9 @@ private static synchronized void init( Sentry.globalHubMode = globalHubMode; final IScopes hub = getCurrentScopes(); - // TODO new Scopes() - mainScopes = new Hub(options); + final IScope rootScope = new Scope(options); + final IScope rootIsolationScope = new Scope(options); + mainScopes = new Scopes(rootScope, rootIsolationScope, options, "Sentry.init"); currentScopes.set(mainScopes); @@ -800,6 +801,7 @@ public static void removeExtra(final @NotNull String key) { public static void pushScope() { // pushScope is no-op in global hub mode if (!globalHubMode) { + // TODO this might have to behave differently from Scopes.pushScope getCurrentScopes().pushScope(); } } @@ -808,6 +810,7 @@ public static void pushScope() { public static void popScope() { // popScope is no-op in global hub mode if (!globalHubMode) { + // TODO this might have to behave differently from Scopes.popScope getCurrentScopes().popScope(); } } @@ -818,6 +821,7 @@ public static void popScope() { * @param callback the callback */ public static void withScope(final @NotNull ScopeCallback callback) { + // TODO this might have to behave differently from Scopes.withScope getCurrentScopes().withScope(callback); } From edb4be2a04b506b60a04d0972242ddcd2bbe69d2 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 19 Apr 2024 14:00:24 +0200 Subject: [PATCH 15/91] Hubs/Scopes Merge 15 - Replace `ThreadLocal` with scope storage (#3317) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage --- sentry/api/sentry.api | 24 +++++++++++ .../java/io/sentry/DefaultScopesStorage.java | 42 +++++++++++++++++++ .../main/java/io/sentry/IScopesStorage.java | 13 ++++++ .../java/io/sentry/ISentryLifecycleToken.java | 8 ++++ .../java/io/sentry/NoOpScopesStorage.java | 40 ++++++++++++++++++ sentry/src/main/java/io/sentry/Sentry.java | 42 +++++++++++-------- 6 files changed, 152 insertions(+), 17 deletions(-) create mode 100644 sentry/src/main/java/io/sentry/DefaultScopesStorage.java create mode 100644 sentry/src/main/java/io/sentry/IScopesStorage.java create mode 100644 sentry/src/main/java/io/sentry/ISentryLifecycleToken.java create mode 100644 sentry/src/main/java/io/sentry/NoOpScopesStorage.java diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 24935be274..32ce777a3d 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -259,6 +259,13 @@ public final class io/sentry/DeduplicateMultithreadedEventProcessor : io/sentry/ public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } +public final class io/sentry/DefaultScopesStorage : io/sentry/IScopesStorage { + public fun ()V + public fun close ()V + public fun get ()Lio/sentry/IScopes; + public fun set (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; +} + public final class io/sentry/DefaultTransactionPerformanceCollector : io/sentry/TransactionPerformanceCollector { public fun (Lio/sentry/SentryOptions;)V public fun close ()V @@ -792,6 +799,12 @@ public abstract interface class io/sentry/IScopes { public abstract fun withScope (Lio/sentry/ScopeCallback;)V } +public abstract interface class io/sentry/IScopesStorage { + public abstract fun close ()V + public abstract fun get ()Lio/sentry/IScopes; + public abstract fun set (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; +} + public abstract interface class io/sentry/ISentryClient { public abstract fun captureCheckIn (Lio/sentry/CheckIn;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureEnvelope (Lio/sentry/SentryEnvelope;)Lio/sentry/protocol/SentryId; @@ -831,6 +844,10 @@ public abstract interface class io/sentry/ISentryExecutorService { public abstract fun submit (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Future; } +public abstract interface class io/sentry/ISentryLifecycleToken : java/lang/AutoCloseable { + public abstract fun close ()V +} + public abstract interface class io/sentry/ISerializer { public abstract fun deserialize (Ljava/io/Reader;Ljava/lang/Class;)Ljava/lang/Object; public abstract fun deserializeCollection (Ljava/io/Reader;Ljava/lang/Class;Lio/sentry/JsonDeserializer;)Ljava/lang/Object; @@ -1390,6 +1407,13 @@ public final class io/sentry/NoOpScopes : io/sentry/IScopes { public fun withScope (Lio/sentry/ScopeCallback;)V } +public final class io/sentry/NoOpScopesStorage : io/sentry/IScopesStorage { + public fun close ()V + public fun get ()Lio/sentry/IScopes; + public static fun getInstance ()Lio/sentry/NoOpScopesStorage; + public fun set (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; +} + public final class io/sentry/NoOpSpan : io/sentry/ISpan { public fun finish ()V public fun finish (Lio/sentry/SpanStatus;)V diff --git a/sentry/src/main/java/io/sentry/DefaultScopesStorage.java b/sentry/src/main/java/io/sentry/DefaultScopesStorage.java new file mode 100644 index 0000000000..12902a1dff --- /dev/null +++ b/sentry/src/main/java/io/sentry/DefaultScopesStorage.java @@ -0,0 +1,42 @@ +package io.sentry; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class DefaultScopesStorage implements IScopesStorage { + + private static final @NotNull ThreadLocal currentScopes = new ThreadLocal<>(); + + @Override + public ISentryLifecycleToken set(@Nullable IScopes scopes) { + final @Nullable IScopes oldScopes = get(); + currentScopes.set(scopes); + return new DefaultScopesLifecycleToken(oldScopes); + } + + @Override + public @Nullable IScopes get() { + return currentScopes.get(); + } + + @Override + public void close() { + // TODO prevent further storing? would this cause problems if singleton, closed and + // re-initialized? + currentScopes.remove(); + } + + static final class DefaultScopesLifecycleToken implements ISentryLifecycleToken { + + private final @Nullable IScopes oldValue; + + DefaultScopesLifecycleToken(final @Nullable IScopes scopes) { + this.oldValue = scopes; + } + + @Override + public void close() { + currentScopes.set(oldValue); + } + } +} diff --git a/sentry/src/main/java/io/sentry/IScopesStorage.java b/sentry/src/main/java/io/sentry/IScopesStorage.java new file mode 100644 index 0000000000..92f6b587c4 --- /dev/null +++ b/sentry/src/main/java/io/sentry/IScopesStorage.java @@ -0,0 +1,13 @@ +package io.sentry; + +import org.jetbrains.annotations.Nullable; + +public interface IScopesStorage { + + ISentryLifecycleToken set(final @Nullable IScopes scopes); + + @Nullable + IScopes get(); + + void close(); +} diff --git a/sentry/src/main/java/io/sentry/ISentryLifecycleToken.java b/sentry/src/main/java/io/sentry/ISentryLifecycleToken.java new file mode 100644 index 0000000000..2d0ad180f7 --- /dev/null +++ b/sentry/src/main/java/io/sentry/ISentryLifecycleToken.java @@ -0,0 +1,8 @@ +package io.sentry; + +public interface ISentryLifecycleToken extends AutoCloseable { + + // overridden to not have a checked exception on the method. + @Override + void close(); +} diff --git a/sentry/src/main/java/io/sentry/NoOpScopesStorage.java b/sentry/src/main/java/io/sentry/NoOpScopesStorage.java new file mode 100644 index 0000000000..fa507987ae --- /dev/null +++ b/sentry/src/main/java/io/sentry/NoOpScopesStorage.java @@ -0,0 +1,40 @@ +package io.sentry; + +import org.jetbrains.annotations.Nullable; + +public final class NoOpScopesStorage implements IScopesStorage { + private static final NoOpScopesStorage instance = new NoOpScopesStorage(); + + private NoOpScopesStorage() {} + + public static NoOpScopesStorage getInstance() { + return instance; + } + + @Override + public ISentryLifecycleToken set(@Nullable IScopes scopes) { + return NoOpScopesLifecycleToken.getInstance(); + } + + @Override + public @Nullable IScopes get() { + return NoOpScopes.getInstance(); + } + + @Override + public void close() {} + + static final class NoOpScopesLifecycleToken implements ISentryLifecycleToken { + + private static final NoOpScopesLifecycleToken instance = new NoOpScopesLifecycleToken(); + + private NoOpScopesLifecycleToken() {} + + public static NoOpScopesLifecycleToken getInstance() { + return instance; + } + + @Override + public void close() {} + } +} diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index f4996bb8ad..e01ca2f281 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -43,8 +43,7 @@ public final class Sentry { private Sentry() {} - /** Holds Hubs per thread or only mainScopes if globalHubMode is enabled. */ - private static final @NotNull ThreadLocal currentScopes = new ThreadLocal<>(); + private static volatile @NotNull IScopesStorage scopesStorage = new DefaultScopesStorage(); /** The Main Hub or NoOp if Sentry is disabled. */ private static volatile @NotNull IScopes mainScopes = NoOpScopes.getInstance(); @@ -83,13 +82,17 @@ private Sentry() {} if (globalHubMode) { return mainScopes; } - IScopes hub = currentScopes.get(); - if (hub == null || hub.isNoOp()) { + IScopes scopes = getScopesStorage().get(); + if (scopes == null || scopes.isNoOp()) { // TODO fork instead - hub = mainScopes.clone(); - currentScopes.set(hub); + scopes = mainScopes.clone(); + getScopesStorage().set(scopes); } - return hub; + return scopes; + } + + private static @NotNull IScopesStorage getScopesStorage() { + return scopesStorage; } /** @@ -110,14 +113,15 @@ private Sentry() {} @ApiStatus.Internal // exposed for the coroutines integration in SentryContext @Deprecated - @SuppressWarnings("deprecation") + @SuppressWarnings({"deprecation", "InlineMeSuggester"}) public static void setCurrentHub(final @NotNull IHub hub) { - currentScopes.set(hub); + setCurrentScopes(hub); } @ApiStatus.Internal // exposed for the coroutines integration in SentryContext - public static void setCurrentScopes(final @NotNull IScopes scopes) { - currentScopes.set(scopes); + public static @NotNull ISentryLifecycleToken setCurrentScopes(final @NotNull IScopes scopes) { + return getScopesStorage().set(scopes); + } } /** @@ -253,14 +257,18 @@ private static synchronized void init( options.getLogger().log(SentryLevel.INFO, "GlobalHubMode: '%s'", String.valueOf(globalHubMode)); Sentry.globalHubMode = globalHubMode; - final IScopes hub = getCurrentScopes(); + final IScopes scopes = getCurrentScopes(); final IScope rootScope = new Scope(options); - final IScope rootIsolationScope = new Scope(options); - mainScopes = new Scopes(rootScope, rootIsolationScope, options, "Sentry.init"); + // TODO should use separate isolation scope: + // final IScope rootIsolationScope = new Scope(options); + // TODO should be: + // getGlobalScope().bindClient(new SentryClient(options)); + rootScope.bindClient(new SentryClient(options)); + mainScopes = new Scopes(rootScope, rootScope, options, "Sentry.init"); - currentScopes.set(mainScopes); + getScopesStorage().set(mainScopes); - hub.close(true); + scopes.close(true); // If the executorService passed in the init is the same that was previously closed, we have to // set a new one @@ -508,7 +516,7 @@ public static synchronized void close() { final IScopes scopes = getCurrentScopes(); mainScopes = NoOpScopes.getInstance(); // remove thread local to avoid memory leak - currentScopes.remove(); + getScopesStorage().close(); scopes.close(false); } From a1bfa9592a885ef6665b499936f816f39e8ae779 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 19 Apr 2024 14:03:45 +0200 Subject: [PATCH 16/91] Hubs / Scopes Merge 16 - Move client and throwable to span map to scope (#3318) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope --- sentry/api/sentry.api | 9 ++ sentry/src/main/java/io/sentry/IScope.java | 11 ++- sentry/src/main/java/io/sentry/NoOpScope.java | 9 +- sentry/src/main/java/io/sentry/Scope.java | 52 +++++++++++- sentry/src/main/java/io/sentry/Scopes.java | 83 ++++++------------- 5 files changed, 105 insertions(+), 59 deletions(-) diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 32ce777a3d..e17c36b2e9 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -660,6 +660,8 @@ public abstract interface class io/sentry/IScope { public abstract fun addBreadcrumb (Lio/sentry/Breadcrumb;)V public abstract fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V public abstract fun addEventProcessor (Lio/sentry/EventProcessor;)V + public abstract fun assignTraceContext (Lio/sentry/SentryEvent;)V + public abstract fun bindClient (Lio/sentry/ISentryClient;)V public abstract fun clear ()V public abstract fun clearAttachments ()V public abstract fun clearBreadcrumbs ()V @@ -703,6 +705,7 @@ public abstract interface class io/sentry/IScope { public abstract fun setPropagationContext (Lio/sentry/PropagationContext;)V public abstract fun setRequest (Lio/sentry/protocol/Request;)V public abstract fun setScreen (Ljava/lang/String;)V + public abstract fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V public abstract fun setTag (Ljava/lang/String;Ljava/lang/String;)V public abstract fun setTransaction (Lio/sentry/ITransaction;)V public abstract fun setTransaction (Ljava/lang/String;)V @@ -1298,6 +1301,8 @@ public final class io/sentry/NoOpScope : io/sentry/IScope { public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V public fun addEventProcessor (Lio/sentry/EventProcessor;)V + public fun assignTraceContext (Lio/sentry/SentryEvent;)V + public fun bindClient (Lio/sentry/ISentryClient;)V public fun clear ()V public fun clearAttachments ()V public fun clearBreadcrumbs ()V @@ -1343,6 +1348,7 @@ public final class io/sentry/NoOpScope : io/sentry/IScope { public fun setPropagationContext (Lio/sentry/PropagationContext;)V public fun setRequest (Lio/sentry/protocol/Request;)V public fun setScreen (Ljava/lang/String;)V + public fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V public fun setTransaction (Lio/sentry/ITransaction;)V public fun setTransaction (Ljava/lang/String;)V @@ -1731,6 +1737,8 @@ public final class io/sentry/Scope : io/sentry/IScope { public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V public fun addEventProcessor (Lio/sentry/EventProcessor;)V + public fun assignTraceContext (Lio/sentry/SentryEvent;)V + public fun bindClient (Lio/sentry/ISentryClient;)V public fun clear ()V public fun clearAttachments ()V public fun clearBreadcrumbs ()V @@ -1775,6 +1783,7 @@ public final class io/sentry/Scope : io/sentry/IScope { public fun setPropagationContext (Lio/sentry/PropagationContext;)V public fun setRequest (Lio/sentry/protocol/Request;)V public fun setScreen (Ljava/lang/String;)V + public fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V public fun setTransaction (Lio/sentry/ITransaction;)V public fun setTransaction (Ljava/lang/String;)V diff --git a/sentry/src/main/java/io/sentry/IScope.java b/sentry/src/main/java/io/sentry/IScope.java index 3bc25ce8e9..d761ccc192 100644 --- a/sentry/src/main/java/io/sentry/IScope.java +++ b/sentry/src/main/java/io/sentry/IScope.java @@ -377,8 +377,17 @@ public interface IScope { @NotNull SentryId getLastEventId(); - void setClient(final @NotNull ISentryClient client); + void bindClient(final @NotNull ISentryClient client); @NotNull ISentryClient getClient(); + + @ApiStatus.Internal + void assignTraceContext(final @NotNull SentryEvent event); + + @ApiStatus.Internal + void setSpanContext( + final @NotNull Throwable throwable, + final @NotNull ISpan span, + final @NotNull String transactionName); } diff --git a/sentry/src/main/java/io/sentry/NoOpScope.java b/sentry/src/main/java/io/sentry/NoOpScope.java index f7336d6edb..400a7e739f 100644 --- a/sentry/src/main/java/io/sentry/NoOpScope.java +++ b/sentry/src/main/java/io/sentry/NoOpScope.java @@ -256,10 +256,17 @@ public void setLastEventId(@NotNull SentryId lastEventId) {} } @Override - public void setClient(@NotNull ISentryClient client) {} + public void bindClient(@NotNull ISentryClient client) {} @Override public @NotNull ISentryClient getClient() { return NoOpSentryClient.getInstance(); } + + @Override + public void assignTraceContext(@NotNull SentryEvent event) {} + + @Override + public void setSpanContext( + @NotNull Throwable throwable, @NotNull ISpan span, @NotNull String transactionName) {} } diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index aa35ccfd7a..fcbcd74650 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -7,13 +7,18 @@ import io.sentry.protocol.TransactionNameSource; import io.sentry.protocol.User; import io.sentry.util.CollectionUtils; +import io.sentry.util.ExceptionUtils; import io.sentry.util.Objects; +import io.sentry.util.Pair; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Queue; +import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import org.jetbrains.annotations.ApiStatus; @@ -85,6 +90,11 @@ public final class Scope implements IScope { private @NotNull ISentryClient client = NoOpSentryClient.getInstance(); + // TODO intended only for global scope + // TODO test for memory leak + private final @NotNull Map, String>> throwableToSpan = + Collections.synchronizedMap(new WeakHashMap<>()); + /** * Scope's ctor * @@ -103,6 +113,7 @@ private Scope(final @NotNull Scope scope) { this.session = scope.session; this.options = scope.options; this.level = scope.level; + this.client = scope.client; // TODO should we do this? didn't do it for Hub this.lastEventId = scope.getLastEventId(); @@ -964,7 +975,7 @@ public void setLastEventId(@NotNull SentryId lastEventId) { } @Override - public void setClient(@NotNull ISentryClient client) { + public void bindClient(@NotNull ISentryClient client) { this.client = client; } @@ -973,6 +984,45 @@ public void setClient(@NotNull ISentryClient client) { return client; } + @Override + @ApiStatus.Internal + public void assignTraceContext(final @NotNull SentryEvent event) { + if (options.isTracingEnabled() && event.getThrowable() != null) { + final Pair, String> pair = + throwableToSpan.get(ExceptionUtils.findRootCause(event.getThrowable())); + if (pair != null) { + final WeakReference spanWeakRef = pair.getFirst(); + if (event.getContexts().getTrace() == null && spanWeakRef != null) { + final ISpan span = spanWeakRef.get(); + if (span != null) { + event.getContexts().setTrace(span.getSpanContext()); + } + } + final String transactionName = pair.getSecond(); + if (event.getTransaction() == null && transactionName != null) { + event.setTransaction(transactionName); + } + } + } + } + + @Override + @ApiStatus.Internal + public void setSpanContext( + final @NotNull Throwable throwable, + final @NotNull ISpan span, + final @NotNull String transactionName) { + Objects.requireNonNull(throwable, "throwable is required"); + Objects.requireNonNull(span, "span is required"); + Objects.requireNonNull(transactionName, "transactionName is required"); + // to match any cause, span context is always attached to the root cause of the exception + final Throwable rootCause = ExceptionUtils.findRootCause(throwable); + // the most inner span should be assigned to a throwable + if (!throwableToSpan.containsKey(rootCause)) { + throwableToSpan.put(rootCause, new Pair<>(new WeakReference<>(span), transactionName)); + } + } + /** The IWithTransaction callback */ @ApiStatus.Internal public interface IWithTransaction { diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 618864b649..b384d39c05 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -9,19 +9,15 @@ import io.sentry.protocol.SentryTransaction; import io.sentry.protocol.User; import io.sentry.transport.RateLimiter; -import io.sentry.util.ExceptionUtils; import io.sentry.util.HintUtils; import io.sentry.util.Objects; -import io.sentry.util.Pair; import io.sentry.util.TracingUtils; import java.io.Closeable; import java.io.IOException; -import java.lang.ref.WeakReference; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.WeakHashMap; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,10 +36,6 @@ public final class Scopes implements IScopes, MetricsApi.IMetricsInterface { private final @NotNull SentryOptions options; private volatile boolean isEnabled; private final @NotNull TracesSampler tracesSampler; - - // TODO should this go on global scope? - private final @NotNull Map, String>> throwableToSpan = - Collections.synchronizedMap(new WeakHashMap<>()); private final @NotNull TransactionPerformanceCollector transactionPerformanceCollector; private final @NotNull MetricsApi metricsApi; @@ -120,7 +112,10 @@ public boolean isAncestorOf(final @Nullable Scopes otherScopes) { // TODO add to IScopes interface public @NotNull Scopes forkedCurrentScope(final @NotNull String creator) { - return new Scopes(scope.clone(), isolationScope, this, options, creator); + IScope clone = scope.clone(); + // TODO should use isolation scope + // return new Scopes(clone, isolationScope, this, options, creator); + return new Scopes(clone, clone, this, options, creator); } // // TODO in Sentry.init? @@ -180,23 +175,7 @@ public boolean isEnabled() { } private void assignTraceContext(final @NotNull SentryEvent event) { - if (options.isTracingEnabled() && event.getThrowable() != null) { - final Pair, String> pair = - throwableToSpan.get(ExceptionUtils.findRootCause(event.getThrowable())); - if (pair != null) { - final WeakReference spanWeakRef = pair.getFirst(); - if (event.getContexts().getTrace() == null && spanWeakRef != null) { - final ISpan span = spanWeakRef.get(); - if (span != null) { - event.getContexts().setTrace(span.getSpanContext()); - } - } - final String transactionName = pair.getSecond(); - if (event.getTransaction() == null && transactionName != null) { - event.setTransaction(transactionName); - } - } - } + Sentry.getGlobalScope().assignTraceContext(event); } private IScope buildLocalScope( @@ -691,10 +670,10 @@ public void bindClient(final @NotNull ISentryClient client) { } else { if (client != null) { options.getLogger().log(SentryLevel.DEBUG, "New client bound to scope."); - getDefaultWriteScope().setClient(client); + getDefaultWriteScope().bindClient(client); } else { options.getLogger().log(SentryLevel.DEBUG, "NoOp client bound to scope."); - getDefaultWriteScope().setClient(NoOpSentryClient.getInstance()); + getDefaultWriteScope().bindClient(NoOpSentryClient.getInstance()); } } } @@ -871,34 +850,26 @@ public void setSpanContext( final @NotNull Throwable throwable, final @NotNull ISpan span, final @NotNull String transactionName) { - Objects.requireNonNull(throwable, "throwable is required"); - Objects.requireNonNull(span, "span is required"); - Objects.requireNonNull(transactionName, "transactionName is required"); - // to match any cause, span context is always attached to the root cause of the exception - final Throwable rootCause = ExceptionUtils.findRootCause(throwable); - // the most inner span should be assigned to a throwable - if (!throwableToSpan.containsKey(rootCause)) { - throwableToSpan.put(rootCause, new Pair<>(new WeakReference<>(span), transactionName)); - } - } - - // TODO this seems unused - @Nullable - SpanContext getSpanContext(final @NotNull Throwable throwable) { - Objects.requireNonNull(throwable, "throwable is required"); - final Throwable rootCause = ExceptionUtils.findRootCause(throwable); - final Pair, String> pair = this.throwableToSpan.get(rootCause); - if (pair != null) { - final WeakReference spanWeakRef = pair.getFirst(); - if (spanWeakRef != null) { - final ISpan span = spanWeakRef.get(); - if (span != null) { - return span.getSpanContext(); - } - } - } - return null; - } + Sentry.getGlobalScope().setSpanContext(throwable, span, transactionName); + } + + // // TODO this seems unused + // @Nullable + // SpanContext getSpanContext(final @NotNull Throwable throwable) { + // Objects.requireNonNull(throwable, "throwable is required"); + // final Throwable rootCause = ExceptionUtils.findRootCause(throwable); + // final Pair, String> pair = this.throwableToSpan.get(rootCause); + // if (pair != null) { + // final WeakReference spanWeakRef = pair.getFirst(); + // if (spanWeakRef != null) { + // final ISpan span = spanWeakRef.get(); + // if (span != null) { + // return span.getSpanContext(); + // } + // } + // } + // return null; + // } @Override public @Nullable ISpan getSpan() { From 6390bc659f4bf362f3cb2c52f3b55f07a26f9fb2 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 19 Apr 2024 14:06:31 +0200 Subject: [PATCH 17/91] Hubs / Scopes Merge 17 - Add global scope (#3319) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes --- sentry/api/sentry.api | 1 + sentry/src/main/java/io/sentry/Scopes.java | 5 +++-- sentry/src/main/java/io/sentry/Sentry.java | 7 +++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index e17c36b2e9..44f4fe4d89 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -2004,6 +2004,7 @@ public final class io/sentry/Sentry { public static fun getBaggage ()Lio/sentry/BaggageHeader; public static fun getCurrentHub ()Lio/sentry/IHub; public static fun getCurrentScopes ()Lio/sentry/IScopes; + public static fun getGlobalScope ()Lio/sentry/IScope; public static fun getLastEventId ()Lio/sentry/protocol/SentryId; public static fun getSpan ()Lio/sentry/ISpan; public static fun getTraceparent ()Lio/sentry/SentryTraceHeader; diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index b384d39c05..c440b995ec 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -579,8 +579,9 @@ private void updateLastEventId(final @NotNull SentryId lastEventId) { // TODO add to IScopes interface public @NotNull IScope getGlobalScope() { - // TODO return singleton global scope here - return scope; + // TODO should be: + return Sentry.getGlobalScope(); + // return scope; } @Override diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index e01ca2f281..76ada357f2 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -47,6 +47,8 @@ private Sentry() {} /** The Main Hub or NoOp if Sentry is disabled. */ private static volatile @NotNull IScopes mainScopes = NoOpScopes.getInstance(); + // TODO cannot pass options here + private static volatile @NotNull IScope globalScope = new Scope(new SentryOptions()); /** Default value for globalHubMode is false */ private static final boolean GLOBAL_HUB_DEFAULT_MODE = false; @@ -122,6 +124,9 @@ public static void setCurrentHub(final @NotNull IHub hub) { public static @NotNull ISentryLifecycleToken setCurrentScopes(final @NotNull IScopes scopes) { return getScopesStorage().set(scopes); } + + public static @NotNull IScope getGlobalScope() { + return globalScope; } /** @@ -264,6 +269,8 @@ private static synchronized void init( // TODO should be: // getGlobalScope().bindClient(new SentryClient(options)); rootScope.bindClient(new SentryClient(options)); + // TODO shouldn't replace global scope + globalScope = rootScope; mainScopes = new Scopes(rootScope, rootScope, options, "Sentry.init"); getScopesStorage().set(mainScopes); From 6ee5169191bf574f76866ceb7d0ff3d330cd7996 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 19 Apr 2024 14:15:56 +0200 Subject: [PATCH 18/91] Hubs / Scopes Merge 18 - Implement `pushScope` ,`popScope` and `withScope` for `Scopes` (#3321) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes --- .../webflux/SentryWebFluxTracingFilterTest.kt | 4 +- .../spring/webflux/SentryWebFilter.java | 4 +- .../webflux/SentryWebFluxTracingFilterTest.kt | 4 +- sentry/api/sentry.api | 72 ++++++++++++++++--- sentry/src/main/java/io/sentry/Hub.java | 3 +- .../src/main/java/io/sentry/HubAdapter.java | 4 +- .../main/java/io/sentry/HubScopesWrapper.java | 4 +- sentry/src/main/java/io/sentry/IScopes.java | 3 +- sentry/src/main/java/io/sentry/NoOpHub.java | 4 +- .../src/main/java/io/sentry/NoOpScopes.java | 4 +- sentry/src/main/java/io/sentry/Scopes.java | 25 ++++--- .../main/java/io/sentry/ScopesAdapter.java | 4 +- sentry/src/main/java/io/sentry/Sentry.java | 5 +- sentry/src/test/java/io/sentry/NoOpHubTest.kt | 4 +- 14 files changed, 106 insertions(+), 38 deletions(-) diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt index e936394039..ddbbe75817 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt @@ -252,10 +252,10 @@ class SentryWebFluxTracingFilterTest { verify(fixture.scopes, times(3)).isEnabled verify(fixture.scopes, times(2)).options verify(fixture.scopes).continueTrace(anyOrNull(), anyOrNull()) - verify(fixture.scopes).pushScope() + verify(fixture.scopes).pushScope() // TODO don't verify(fixture.scopes).addBreadcrumb(any(), any()) verify(fixture.scopes).configureScope(any()) - verify(fixture.scopes).popScope() + verify(fixture.scopes).popScope() // TODO don't verifyNoMoreInteractions(fixture.scopes) } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java index 3bb7de5ae4..4d39e092bc 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java @@ -81,7 +81,7 @@ isTracingEnabled && shouldTraceRequest(requestHub, request) if (transaction != null) { finishTransaction(serverWebExchange, transaction); } - requestHub.popScope(); + requestHub.popScope(); // TODO don't // TODO token based cleanup instead? Sentry.setCurrentScopes(NoOpScopes.getInstance()); }) @@ -96,7 +96,7 @@ isTracingEnabled && shouldTraceRequest(requestHub, request) () -> { serverWebExchange.getAttributes().put(SENTRY_SCOPES_KEY, requestHub); Sentry.setCurrentScopes(requestHub); - requestHub.pushScope(); + requestHub.pushScope(); // TODO don't final ServerHttpResponse response = serverWebExchange.getResponse(); final Hint hint = new Hint(); diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt index 2113c748ee..1a31dcaa10 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt @@ -253,10 +253,10 @@ class SentryWebFluxTracingFilterTest { verify(fixture.scopes).isEnabled verify(fixture.scopes, times(2)).options verify(fixture.scopes).continueTrace(anyOrNull(), anyOrNull()) - verify(fixture.scopes).pushScope() + verify(fixture.scopes).pushScope() // TODO don't verify(fixture.scopes).addBreadcrumb(any(), any()) verify(fixture.scopes).configureScope(any()) - verify(fixture.scopes).popScope() + verify(fixture.scopes).popScope() // TODO don't verifyNoMoreInteractions(fixture.scopes) } } diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 44f4fe4d89..aca363c3ca 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -456,7 +456,7 @@ public final class io/sentry/Hub : io/sentry/IHub, io/sentry/metrics/MetricsApi$ public fun isHealthy ()Z public fun metrics ()Lio/sentry/metrics/MetricsApi; public fun popScope ()V - public fun pushScope ()V + public fun pushScope ()Lio/sentry/ISentryLifecycleToken; public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V public fun reportFullyDisplayed ()V @@ -510,7 +510,60 @@ public final class io/sentry/HubAdapter : io/sentry/IHub { public fun isHealthy ()Z public fun metrics ()Lio/sentry/metrics/MetricsApi; public fun popScope ()V - public fun pushScope ()V + public fun pushScope ()Lio/sentry/ISentryLifecycleToken; + public fun removeExtra (Ljava/lang/String;)V + public fun removeTag (Ljava/lang/String;)V + public fun reportFullyDisplayed ()V + public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V + public fun setFingerprint (Ljava/util/List;)V + public fun setLevel (Lio/sentry/SentryLevel;)V + public fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V + public fun setTag (Ljava/lang/String;Ljava/lang/String;)V + public fun setTransaction (Ljava/lang/String;)V + public fun setUser (Lio/sentry/protocol/User;)V + public fun startSession ()V + public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; + public fun traceHeaders ()Lio/sentry/SentryTraceHeader; + public fun withScope (Lio/sentry/ScopeCallback;)V +} + +public final class io/sentry/HubScopesWrapper : io/sentry/IHub { + public fun (Lio/sentry/IScopes;)V + public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V + public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V + public fun bindClient (Lio/sentry/ISentryClient;)V + public fun captureCheckIn (Lio/sentry/CheckIn;)Lio/sentry/protocol/SentryId; + public fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; + public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; + public fun captureUserFeedback (Lio/sentry/UserFeedback;)V + public fun clearBreadcrumbs ()V + public fun clone ()Lio/sentry/IHub; + public synthetic fun clone ()Ljava/lang/Object; + public fun close ()V + public fun close (Z)V + public fun configureScope (Lio/sentry/ScopeCallback;)V + public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; + public fun endSession ()V + public fun flush (J)V + public fun getBaggage ()Lio/sentry/BaggageHeader; + public fun getLastEventId ()Lio/sentry/protocol/SentryId; + public fun getOptions ()Lio/sentry/SentryOptions; + public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; + public fun getSpan ()Lio/sentry/ISpan; + public fun getTraceparent ()Lio/sentry/SentryTraceHeader; + public fun getTransaction ()Lio/sentry/ITransaction; + public fun isCrashedLastRun ()Ljava/lang/Boolean; + public fun isEnabled ()Z + public fun isHealthy ()Z + public fun metrics ()Lio/sentry/metrics/MetricsApi; + public fun popScope ()V + public fun pushScope ()Lio/sentry/ISentryLifecycleToken; public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V public fun reportFullyDisplayed ()V @@ -781,7 +834,7 @@ public abstract interface class io/sentry/IScopes { public fun isNoOp ()Z public abstract fun metrics ()Lio/sentry/metrics/MetricsApi; public abstract fun popScope ()V - public abstract fun pushScope ()V + public abstract fun pushScope ()Lio/sentry/ISentryLifecycleToken; public abstract fun removeExtra (Ljava/lang/String;)V public abstract fun removeTag (Ljava/lang/String;)V public fun reportFullDisplayed ()V @@ -1271,7 +1324,7 @@ public final class io/sentry/NoOpHub : io/sentry/IHub { public fun isNoOp ()Z public fun metrics ()Lio/sentry/metrics/MetricsApi; public fun popScope ()V - public fun pushScope ()V + public fun pushScope ()Lio/sentry/ISentryLifecycleToken; public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V public fun reportFullyDisplayed ()V @@ -1396,7 +1449,7 @@ public final class io/sentry/NoOpScopes : io/sentry/IScopes { public fun isNoOp ()Z public fun metrics ()Lio/sentry/metrics/MetricsApi; public fun popScope ()V - public fun pushScope ()V + public fun pushScope ()Lio/sentry/ISentryLifecycleToken; public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V public fun reportFullyDisplayed ()V @@ -1869,9 +1922,10 @@ public final class io/sentry/Scopes : io/sentry/IScopes, io/sentry/metrics/Metri public fun isCrashedLastRun ()Ljava/lang/Boolean; public fun isEnabled ()Z public fun isHealthy ()Z + public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; public fun metrics ()Lio/sentry/metrics/MetricsApi; public fun popScope ()V - public fun pushScope ()V + public fun pushScope ()Lio/sentry/ISentryLifecycleToken; public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V public fun reportFullyDisplayed ()V @@ -1925,7 +1979,7 @@ public final class io/sentry/ScopesAdapter : io/sentry/IScopes { public fun isHealthy ()Z public fun metrics ()Lio/sentry/metrics/MetricsApi; public fun popScope ()V - public fun pushScope ()V + public fun pushScope ()Lio/sentry/ISentryLifecycleToken; public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V public fun reportFullyDisplayed ()V @@ -2020,13 +2074,13 @@ public final class io/sentry/Sentry { public static fun isHealthy ()Z public static fun metrics ()Lio/sentry/metrics/MetricsApi; public static fun popScope ()V - public static fun pushScope ()V + public static fun pushScope ()Lio/sentry/ISentryLifecycleToken; public static fun removeExtra (Ljava/lang/String;)V public static fun removeTag (Ljava/lang/String;)V public static fun reportFullDisplayed ()V public static fun reportFullyDisplayed ()V public static fun setCurrentHub (Lio/sentry/IHub;)V - public static fun setCurrentScopes (Lio/sentry/IScopes;)V + public static fun setCurrentScopes (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; public static fun setExtra (Ljava/lang/String;Ljava/lang/String;)V public static fun setFingerprint (Ljava/util/List;)V public static fun setLevel (Lio/sentry/SentryLevel;)V diff --git a/sentry/src/main/java/io/sentry/Hub.java b/sentry/src/main/java/io/sentry/Hub.java index 6a98bb2367..d56305be5d 100644 --- a/sentry/src/main/java/io/sentry/Hub.java +++ b/sentry/src/main/java/io/sentry/Hub.java @@ -526,7 +526,7 @@ public void removeExtra(final @NotNull String key) { } @Override - public void pushScope() { + public @NotNull ISentryLifecycleToken pushScope() { if (!isEnabled()) { options .getLogger() @@ -536,6 +536,7 @@ public void pushScope() { final StackItem newItem = new StackItem(options, item.getClient(), item.getScope().clone()); stack.push(newItem); } + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); } @Override diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index 746d51f0cc..f7970200ce 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -154,8 +154,8 @@ public void removeExtra(@NotNull String key) { } @Override - public void pushScope() { - Sentry.pushScope(); + public @NotNull ISentryLifecycleToken pushScope() { + return Sentry.pushScope(); } @Override diff --git a/sentry/src/main/java/io/sentry/HubScopesWrapper.java b/sentry/src/main/java/io/sentry/HubScopesWrapper.java index 9b294d05b7..90c485d7f4 100644 --- a/sentry/src/main/java/io/sentry/HubScopesWrapper.java +++ b/sentry/src/main/java/io/sentry/HubScopesWrapper.java @@ -149,8 +149,8 @@ public void removeExtra(@NotNull String key) { } @Override - public void pushScope() { - scopes.pushScope(); + public @NotNull ISentryLifecycleToken pushScope() { + return scopes.pushScope(); } @Override diff --git a/sentry/src/main/java/io/sentry/IScopes.java b/sentry/src/main/java/io/sentry/IScopes.java index 03662457e4..29edb6627d 100644 --- a/sentry/src/main/java/io/sentry/IScopes.java +++ b/sentry/src/main/java/io/sentry/IScopes.java @@ -306,7 +306,8 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { SentryId getLastEventId(); /** Pushes a new scope while inheriting the current scope's data. */ - void pushScope(); + @NotNull + ISentryLifecycleToken pushScope(); /** Removes the first scope */ void popScope(); diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index 704bd2b44a..1d5134d865 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -124,7 +124,9 @@ public void removeExtra(@NotNull String key) {} } @Override - public void pushScope() {} + public @NotNull ISentryLifecycleToken pushScope() { + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + } @Override public void popScope() {} diff --git a/sentry/src/main/java/io/sentry/NoOpScopes.java b/sentry/src/main/java/io/sentry/NoOpScopes.java index 6fef262944..edf7ce1ecb 100644 --- a/sentry/src/main/java/io/sentry/NoOpScopes.java +++ b/sentry/src/main/java/io/sentry/NoOpScopes.java @@ -122,7 +122,9 @@ public void removeExtra(@NotNull String key) {} } @Override - public void pushScope() {} + public @NotNull ISentryLifecycleToken pushScope() { + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + } @Override public void popScope() {} diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index c440b995ec..5e3de8a855 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -595,20 +595,21 @@ private void updateLastEventId(final @NotNull SentryId lastEventId) { // TODO needs to be deprecated because there's no more stack // TODO needs to return a lifecycle token @Override - public void pushScope() { + public ISentryLifecycleToken pushScope() { if (!isEnabled()) { options .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'pushScope' call is a no-op."); + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); } else { - // Scopes scopes = this.forkedScopes("pushScope"); - // return scopes.makeCurrent(); + Scopes scopes = this.forkedCurrentScope("pushScope"); + return scopes.makeCurrent(); } } - // public SentryLifecycleToken makeCurrent() { - // // TODO store.set(this); - // } + public ISentryLifecycleToken makeCurrent() { + return Sentry.setCurrentScopes(this); + } // TODO needs to be deprecated because there's no more stack @Override @@ -618,8 +619,11 @@ public void popScope() { .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'popScope' call is a no-op."); } else { - // TODO how to remove fork? - // TODO getParentScopes().makeCurrent()? + final @Nullable Scopes parent = getParent(); + if (parent != null) { + // TODO this is never closed + parent.makeCurrent(); + } } } @@ -634,7 +638,7 @@ public void withScope(final @NotNull ScopeCallback callback) { } } else { - Scopes forkedScopes = forkedScopes("withScope"); + Scopes forkedScopes = forkedCurrentScope("withScope"); // TODO should forkedScopes be made current inside callback? // TODO forkedScopes.makeCurrent()? try { @@ -705,7 +709,8 @@ public void flush(long timeoutMillis) { if (!isEnabled()) { options.getLogger().log(SentryLevel.WARNING, "Disabled Hub cloned."); } - return new HubScopesWrapper(forkedScopes("scopes clone")); + // TODO should this fork isolation scope as well? + return new HubScopesWrapper(forkedCurrentScope("scopes clone")); } @ApiStatus.Internal diff --git a/sentry/src/main/java/io/sentry/ScopesAdapter.java b/sentry/src/main/java/io/sentry/ScopesAdapter.java index 1ecd31e248..3ffdc01186 100644 --- a/sentry/src/main/java/io/sentry/ScopesAdapter.java +++ b/sentry/src/main/java/io/sentry/ScopesAdapter.java @@ -150,8 +150,8 @@ public void removeExtra(@NotNull String key) { } @Override - public void pushScope() { - Sentry.pushScope(); + public @NotNull ISentryLifecycleToken pushScope() { + return Sentry.pushScope(); } @Override diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 76ada357f2..d9ebaa78be 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -813,12 +813,13 @@ public static void removeExtra(final @NotNull String key) { } /** Pushes a new scope while inheriting the current scope's data. */ - public static void pushScope() { + public static @NotNull ISentryLifecycleToken pushScope() { // pushScope is no-op in global hub mode if (!globalHubMode) { // TODO this might have to behave differently from Scopes.pushScope - getCurrentScopes().pushScope(); + return getCurrentScopes().pushScope(); } + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); } /** Removes the first scope */ diff --git a/sentry/src/test/java/io/sentry/NoOpHubTest.kt b/sentry/src/test/java/io/sentry/NoOpHubTest.kt index dbbfb4b4f1..94af1acc9f 100644 --- a/sentry/src/test/java/io/sentry/NoOpHubTest.kt +++ b/sentry/src/test/java/io/sentry/NoOpHubTest.kt @@ -71,7 +71,9 @@ class NoOpHubTest { } @Test - fun `pushScope is no op`() = sut.pushScope() + fun `pushScope is no op`() { + sut.pushScope() + } @Test fun `popScope is no op`() = sut.popScope() From dd992aa0d5f4fe3a71dcf6797dc10bf626e56ab0 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 19 Apr 2024 14:26:13 +0200 Subject: [PATCH 19/91] Hubs/Scopes Merge 19 - Add `pushIsolationScope` and fork methods (#3343) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope --- sentry/api/sentry.api | 53 ------------------- sentry/src/main/java/io/sentry/Hub.java | 30 +++++++++++ .../src/main/java/io/sentry/HubAdapter.java | 31 +++++++++++ .../main/java/io/sentry/HubScopesWrapper.java | 30 +++++++++++ sentry/src/main/java/io/sentry/IScopes.java | 45 ++++++++++++++++ sentry/src/main/java/io/sentry/NoOpHub.java | 30 +++++++++++ .../src/main/java/io/sentry/NoOpScopes.java | 30 +++++++++++ sentry/src/main/java/io/sentry/Scopes.java | 45 +++++++++------- .../main/java/io/sentry/ScopesAdapter.java | 31 +++++++++++ sentry/src/main/java/io/sentry/Sentry.java | 19 ++++++- 10 files changed, 270 insertions(+), 74 deletions(-) diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index aca363c3ca..8ba2f393d8 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -580,59 +580,6 @@ public final class io/sentry/HubScopesWrapper : io/sentry/IHub { public fun withScope (Lio/sentry/ScopeCallback;)V } -public final class io/sentry/HubScopesWrapper : io/sentry/IHub { - public fun (Lio/sentry/IScopes;)V - public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V - public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V - public fun bindClient (Lio/sentry/ISentryClient;)V - public fun captureCheckIn (Lio/sentry/CheckIn;)Lio/sentry/protocol/SentryId; - public fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; - public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; - public fun captureUserFeedback (Lio/sentry/UserFeedback;)V - public fun clearBreadcrumbs ()V - public fun clone ()Lio/sentry/IHub; - public synthetic fun clone ()Ljava/lang/Object; - public fun close ()V - public fun close (Z)V - public fun configureScope (Lio/sentry/ScopeCallback;)V - public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; - public fun endSession ()V - public fun flush (J)V - public fun getBaggage ()Lio/sentry/BaggageHeader; - public fun getLastEventId ()Lio/sentry/protocol/SentryId; - public fun getOptions ()Lio/sentry/SentryOptions; - public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; - public fun getSpan ()Lio/sentry/ISpan; - public fun getTraceparent ()Lio/sentry/SentryTraceHeader; - public fun getTransaction ()Lio/sentry/ITransaction; - public fun isCrashedLastRun ()Ljava/lang/Boolean; - public fun isEnabled ()Z - public fun isHealthy ()Z - public fun metrics ()Lio/sentry/metrics/MetricsApi; - public fun popScope ()V - public fun pushScope ()V - public fun removeExtra (Ljava/lang/String;)V - public fun removeTag (Ljava/lang/String;)V - public fun reportFullyDisplayed ()V - public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V - public fun setFingerprint (Ljava/util/List;)V - public fun setLevel (Lio/sentry/SentryLevel;)V - public fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V - public fun setTag (Ljava/lang/String;Ljava/lang/String;)V - public fun setTransaction (Ljava/lang/String;)V - public fun setUser (Lio/sentry/protocol/User;)V - public fun startSession ()V - public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; - public fun traceHeaders ()Lio/sentry/SentryTraceHeader; - public fun withScope (Lio/sentry/ScopeCallback;)V -} - public abstract interface class io/sentry/IConnectionStatusProvider { public abstract fun addConnectionStatusObserver (Lio/sentry/IConnectionStatusProvider$IConnectionStatusObserver;)Z public abstract fun getConnectionStatus ()Lio/sentry/IConnectionStatusProvider$ConnectionStatus; diff --git a/sentry/src/main/java/io/sentry/Hub.java b/sentry/src/main/java/io/sentry/Hub.java index d56305be5d..dd468d1eca 100644 --- a/sentry/src/main/java/io/sentry/Hub.java +++ b/sentry/src/main/java/io/sentry/Hub.java @@ -539,6 +539,11 @@ public void removeExtra(final @NotNull String key) { return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); } + @Override + public @NotNull ISentryLifecycleToken pushIsolationScope() { + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + } + @Override public @NotNull SentryOptions getOptions() { return this.stack.peek().getOptions(); @@ -652,6 +657,31 @@ public void flush(long timeoutMillis) { return new Hub(this.options, new Stack(this.stack)); } + @Override + public @NotNull IScopes forkedScopes(@NotNull String creator) { + return Sentry.forkedScopes(creator); + } + + @Override + public @NotNull IScopes forkedCurrentScope(@NotNull String creator) { + return Sentry.forkedCurrentScope(creator); + } + + @Override + public @NotNull ISentryLifecycleToken makeCurrent() { + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + } + + @Override + public @NotNull IScope getScope() { + return Sentry.getCurrentScopes().getScope(); + } + + @Override + public @NotNull IScope getIsolationScope() { + return Sentry.getCurrentScopes().getIsolationScope(); + } + @ApiStatus.Internal @Override public @NotNull SentryId captureTransaction( diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index f7970200ce..d813a11391 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -158,6 +158,11 @@ public void removeExtra(@NotNull String key) { return Sentry.pushScope(); } + @Override + public @NotNull ISentryLifecycleToken pushIsolationScope() { + return Sentry.pushIsolationScope(); + } + @Override public void popScope() { Sentry.popScope(); @@ -193,6 +198,32 @@ public void flush(long timeoutMillis) { return Sentry.getCurrentScopes().clone(); } + @Override + public @NotNull IScopes forkedScopes(@NotNull String creator) { + return Sentry.forkedScopes(creator); + } + + @Override + public @NotNull IScopes forkedCurrentScope(@NotNull String creator) { + return Sentry.forkedCurrentScope(creator); + } + + @Override + public @NotNull ISentryLifecycleToken makeCurrent() { + // TODO this wouldn't do anything since it replaced the current with the same Scopes + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + } + + @Override + public @NotNull IScope getScope() { + return Sentry.getCurrentScopes().getScope(); + } + + @Override + public @NotNull IScope getIsolationScope() { + return Sentry.getCurrentScopes().getIsolationScope(); + } + @Override public @NotNull SentryId captureTransaction( @NotNull SentryTransaction transaction, diff --git a/sentry/src/main/java/io/sentry/HubScopesWrapper.java b/sentry/src/main/java/io/sentry/HubScopesWrapper.java index 90c485d7f4..c3b2b19d80 100644 --- a/sentry/src/main/java/io/sentry/HubScopesWrapper.java +++ b/sentry/src/main/java/io/sentry/HubScopesWrapper.java @@ -153,6 +153,11 @@ public void removeExtra(@NotNull String key) { return scopes.pushScope(); } + @Override + public @NotNull ISentryLifecycleToken pushIsolationScope() { + return scopes.pushIsolationScope(); + } + @Override public void popScope() { scopes.popScope(); @@ -188,6 +193,31 @@ public void flush(long timeoutMillis) { return scopes.clone(); } + @Override + public @NotNull IScopes forkedScopes(@NotNull String creator) { + return scopes.forkedScopes(creator); + } + + @Override + public @NotNull IScopes forkedCurrentScope(@NotNull String creator) { + return scopes.forkedCurrentScope(creator); + } + + @Override + public @NotNull ISentryLifecycleToken makeCurrent() { + return scopes.makeCurrent(); + } + + @Override + public @NotNull IScope getScope() { + return scopes.getScope(); + } + + @Override + public @NotNull IScope getIsolationScope() { + return scopes.getIsolationScope(); + } + @ApiStatus.Internal @Override public @NotNull SentryId captureTransaction( diff --git a/sentry/src/main/java/io/sentry/IScopes.java b/sentry/src/main/java/io/sentry/IScopes.java index 29edb6627d..1ad2d62887 100644 --- a/sentry/src/main/java/io/sentry/IScopes.java +++ b/sentry/src/main/java/io/sentry/IScopes.java @@ -309,6 +309,9 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { @NotNull ISentryLifecycleToken pushScope(); + @NotNull + ISentryLifecycleToken pushIsolationScope(); + /** Removes the first scope */ void popScope(); @@ -354,12 +357,54 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { /** * Clones the Hub * + * @deprecated please use {@link IScopes#forkedScopes(String)} or {@link + * IScopes#forkedCurrentScope(String)} instead. * @return the cloned Hub */ @NotNull @Deprecated IHub clone(); + /** + * Creates a fork of both current and isolation scope. + * + * @param creator debug information to see why scopes where forked + * @return forked Scopes + */ + @NotNull + IScopes forkedScopes(final @NotNull String creator); + + /** + * Creates a fork of current scope without forking isolation scope. + * + * @param creator debug information to see why scopes where forked + * @return forked Scopes + */ + @NotNull + IScopes forkedCurrentScope(final @NotNull String creator); + + /** + * Stores this Scopes in store, making it the current one that is used by static API. + * + * @return a token you should call .close() on when you're done. + */ + @NotNull + ISentryLifecycleToken makeCurrent(); + + /** + * Returns the current scope of this Scopes. + * + * @return scope + */ + public @NotNull IScope getScope(); + + /** + * Returns the isolation scope of this Scopes. + * + * @return isolation scope + */ + public @NotNull IScope getIsolationScope(); + /** * Captures the transaction and enqueues it for sending to Sentry server. * diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index 1d5134d865..890c41d43e 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -128,6 +128,11 @@ public void removeExtra(@NotNull String key) {} return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); } + @Override + public @NotNull ISentryLifecycleToken pushIsolationScope() { + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + } + @Override public void popScope() {} @@ -155,6 +160,31 @@ public void flush(long timeoutMillis) {} return instance; } + @Override + public @NotNull IScopes forkedScopes(@NotNull String creator) { + return NoOpScopes.getInstance(); + } + + @Override + public @NotNull IScopes forkedCurrentScope(@NotNull String creator) { + return NoOpScopes.getInstance(); + } + + @Override + public @NotNull ISentryLifecycleToken makeCurrent() { + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + } + + @Override + public @NotNull IScope getScope() { + return NoOpScope.getInstance(); + } + + @Override + public @NotNull IScope getIsolationScope() { + return NoOpScope.getInstance(); + } + @Override public @NotNull SentryId captureTransaction( final @NotNull SentryTransaction transaction, diff --git a/sentry/src/main/java/io/sentry/NoOpScopes.java b/sentry/src/main/java/io/sentry/NoOpScopes.java index edf7ce1ecb..aa2b0fd2f3 100644 --- a/sentry/src/main/java/io/sentry/NoOpScopes.java +++ b/sentry/src/main/java/io/sentry/NoOpScopes.java @@ -126,6 +126,11 @@ public void removeExtra(@NotNull String key) {} return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); } + @Override + public @NotNull ISentryLifecycleToken pushIsolationScope() { + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + } + @Override public void popScope() {} @@ -154,6 +159,31 @@ public void flush(long timeoutMillis) {} return NoOpHub.getInstance(); } + @Override + public @NotNull IScopes forkedScopes(@NotNull String creator) { + return NoOpScopes.getInstance(); + } + + @Override + public @NotNull IScopes forkedCurrentScope(@NotNull String creator) { + return NoOpScopes.getInstance(); + } + + @Override + public @NotNull ISentryLifecycleToken makeCurrent() { + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + } + + @Override + public @NotNull IScope getScope() { + return NoOpScope.getInstance(); + } + + @Override + public @NotNull IScope getIsolationScope() { + return NoOpScope.getInstance(); + } + @Override public @NotNull SentryId captureTransaction( final @NotNull SentryTransaction transaction, diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 5e3de8a855..f844119be3 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -72,12 +72,12 @@ private Scopes( return creator; } - // TODO add to IScopes interface + @Override public @NotNull IScope getScope() { return scope; } - // TODO add to IScopes interface + @Override public @NotNull IScope getIsolationScope() { return isolationScope; } @@ -105,25 +105,16 @@ public boolean isAncestorOf(final @Nullable Scopes otherScopes) { return false; } - // TODO add to IScopes interface - public @NotNull Scopes forkedScopes(final @NotNull String creator) { + @Override + public @NotNull IScopes forkedScopes(final @NotNull String creator) { return new Scopes(scope.clone(), isolationScope.clone(), this, options, creator); } - // TODO add to IScopes interface - public @NotNull Scopes forkedCurrentScope(final @NotNull String creator) { - IScope clone = scope.clone(); - // TODO should use isolation scope - // return new Scopes(clone, isolationScope, this, options, creator); - return new Scopes(clone, clone, this, options, creator); + @Override + public @NotNull IScopes forkedCurrentScope(final @NotNull String creator) { + return new Scopes(scope.clone(), isolationScope, this, options, creator); } - // // TODO in Sentry.init? - // public static Scopes forkedRoots(final @NotNull SentryOptions options, final @NotNull String - // creator) { - // return new Scopes(ROOT_SCOPE.clone(), ROOT_ISOLATION_SCOPE.clone(), options, creator); - // } - // TODO always read from root scope? @Override public boolean isEnabled() { @@ -602,12 +593,28 @@ public ISentryLifecycleToken pushScope() { .log(SentryLevel.WARNING, "Instance is disabled and this 'pushScope' call is a no-op."); return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); } else { - Scopes scopes = this.forkedCurrentScope("pushScope"); + final @NotNull IScopes scopes = this.forkedCurrentScope("pushScope"); return scopes.makeCurrent(); } } - public ISentryLifecycleToken makeCurrent() { + @Override + public ISentryLifecycleToken pushIsolationScope() { + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Instance is disabled and this 'pushIsolationScope' call is a no-op."); + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + } else { + final @NotNull IScopes scopes = this.forkedScopes("pushIsolationScope"); + return scopes.makeCurrent(); + } + } + + @Override + public @NotNull ISentryLifecycleToken makeCurrent() { return Sentry.setCurrentScopes(this); } @@ -638,7 +645,7 @@ public void withScope(final @NotNull ScopeCallback callback) { } } else { - Scopes forkedScopes = forkedCurrentScope("withScope"); + final @NotNull IScopes forkedScopes = forkedCurrentScope("withScope"); // TODO should forkedScopes be made current inside callback? // TODO forkedScopes.makeCurrent()? try { diff --git a/sentry/src/main/java/io/sentry/ScopesAdapter.java b/sentry/src/main/java/io/sentry/ScopesAdapter.java index 3ffdc01186..fe79a42731 100644 --- a/sentry/src/main/java/io/sentry/ScopesAdapter.java +++ b/sentry/src/main/java/io/sentry/ScopesAdapter.java @@ -154,6 +154,11 @@ public void removeExtra(@NotNull String key) { return Sentry.pushScope(); } + @Override + public @NotNull ISentryLifecycleToken pushIsolationScope() { + return Sentry.pushIsolationScope(); + } + @Override public void popScope() { Sentry.popScope(); @@ -190,6 +195,32 @@ public void flush(long timeoutMillis) { return Sentry.getCurrentScopes().clone(); } + @Override + public @NotNull IScopes forkedScopes(@NotNull String creator) { + return Sentry.forkedScopes(creator); + } + + @Override + public @NotNull IScopes forkedCurrentScope(@NotNull String creator) { + return Sentry.forkedCurrentScope(creator); + } + + @Override + public @NotNull ISentryLifecycleToken makeCurrent() { + // TODO this wouldn't do anything since it replaced the current with the same Scopes + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + } + + @Override + public @NotNull IScope getScope() { + return Sentry.getCurrentScopes().getScope(); + } + + @Override + public @NotNull IScope getIsolationScope() { + return Sentry.getCurrentScopes().getIsolationScope(); + } + @ApiStatus.Internal @Override public @NotNull SentryId captureTransaction( diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index d9ebaa78be..aac1b9d66d 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -113,6 +113,14 @@ private Sentry() {} return mainScopes.clone(); } + public static @NotNull IScopes forkedScopes(final @NotNull String creator) { + return getCurrentScopes().forkedScopes(creator); + } + + public static @NotNull IScopes forkedCurrentScope(final @NotNull String creator) { + return getCurrentScopes().forkedCurrentScope(creator); + } + @ApiStatus.Internal // exposed for the coroutines integration in SentryContext @Deprecated @SuppressWarnings({"deprecation", "InlineMeSuggester"}) @@ -822,11 +830,19 @@ public static void removeExtra(final @NotNull String key) { return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); } + /** Pushes a new isolation and current scope while inheriting the current scope's data. */ + public static @NotNull ISentryLifecycleToken pushIsolationScope() { + // pushScope is no-op in global hub mode + if (!globalHubMode) { + return getCurrentScopes().pushIsolationScope(); + } + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + } + /** Removes the first scope */ public static void popScope() { // popScope is no-op in global hub mode if (!globalHubMode) { - // TODO this might have to behave differently from Scopes.popScope getCurrentScopes().popScope(); } } @@ -837,7 +853,6 @@ public static void popScope() { * @param callback the callback */ public static void withScope(final @NotNull ScopeCallback callback) { - // TODO this might have to behave differently from Scopes.withScope getCurrentScopes().withScope(callback); } From 385666db6d6eac96f7e3e0d0a28f307a16f019a4 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 19 Apr 2024 14:28:34 +0200 Subject: [PATCH 20/91] Hubs/Scopes Merge 20 - Use separate scope for current, isolation and global scope (#3344) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes --- sentry/src/main/java/io/sentry/Sentry.java | 41 +++++++++----------- sentry/src/test/java/io/sentry/SentryTest.kt | 10 ++--- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index aac1b9d66d..1a8c7db4b5 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -45,8 +45,8 @@ private Sentry() {} private static volatile @NotNull IScopesStorage scopesStorage = new DefaultScopesStorage(); - /** The Main Hub or NoOp if Sentry is disabled. */ - private static volatile @NotNull IScopes mainScopes = NoOpScopes.getInstance(); + /** The root Scopes or NoOp if Sentry is disabled. */ + private static volatile @NotNull IScopes rootScopes = NoOpScopes.getInstance(); // TODO cannot pass options here private static volatile @NotNull IScope globalScope = new Scope(new SentryOptions()); @@ -67,7 +67,7 @@ private Sentry() {} private static final long classCreationTimestamp = System.currentTimeMillis(); /** - * Returns the current (threads) hub, if none, clones the mainScopes and returns it. + * Returns the current (threads) hub, if none, clones the rootScopes and returns it. * * @return the hub */ @@ -82,12 +82,11 @@ private Sentry() {} @SuppressWarnings("deprecation") public static @NotNull IScopes getCurrentScopes() { if (globalHubMode) { - return mainScopes; + return rootScopes; } IScopes scopes = getScopesStorage().get(); if (scopes == null || scopes.isNoOp()) { - // TODO fork instead - scopes = mainScopes.clone(); + scopes = rootScopes.forkedScopes("getCurrentScopes"); getScopesStorage().set(scopes); } return scopes; @@ -98,19 +97,18 @@ private Sentry() {} } /** - * Returns a new hub which is cloned from the mainScopes. + * Returns a new Scopes which is cloned from the rootScopes. * * @return the hub */ @ApiStatus.Internal @ApiStatus.Experimental @SuppressWarnings("deprecation") - public static @NotNull IScopes cloneMainHub() { + public static @NotNull IScopes forkedRootScopes(final @NotNull String creator) { if (globalHubMode) { - return mainScopes; + return rootScopes; } - // TODO fork instead - return mainScopes.clone(); + return rootScopes.forkedScopes(creator); } public static @NotNull IScopes forkedScopes(final @NotNull String creator) { @@ -123,9 +121,9 @@ private Sentry() {} @ApiStatus.Internal // exposed for the coroutines integration in SentryContext @Deprecated - @SuppressWarnings({"deprecation", "InlineMeSuggester"}) - public static void setCurrentHub(final @NotNull IHub hub) { - setCurrentScopes(hub); + @SuppressWarnings({"deprecation"}) + public static @NotNull ISentryLifecycleToken setCurrentHub(final @NotNull IHub hub) { + return setCurrentScopes(hub); } @ApiStatus.Internal // exposed for the coroutines integration in SentryContext @@ -272,16 +270,13 @@ private static synchronized void init( final IScopes scopes = getCurrentScopes(); final IScope rootScope = new Scope(options); - // TODO should use separate isolation scope: - // final IScope rootIsolationScope = new Scope(options); - // TODO should be: - // getGlobalScope().bindClient(new SentryClient(options)); - rootScope.bindClient(new SentryClient(options)); + final IScope rootIsolationScope = new Scope(options); // TODO shouldn't replace global scope - globalScope = rootScope; - mainScopes = new Scopes(rootScope, rootScope, options, "Sentry.init"); + globalScope = new Scope(options); + globalScope.bindClient(new SentryClient(options)); + rootScopes = new Scopes(rootScope, rootIsolationScope, options, "Sentry.init"); - getScopesStorage().set(mainScopes); + getScopesStorage().set(rootScopes); scopes.close(true); @@ -529,7 +524,7 @@ private static boolean initConfigurations(final @NotNull SentryOptions options) /** Close the SDK */ public static synchronized void close() { final IScopes scopes = getCurrentScopes(); - mainScopes = NoOpScopes.getInstance(); + rootScopes = NoOpScopes.getInstance(); // remove thread local to avoid memory leak getScopesStorage().close(); scopes.close(false); diff --git a/sentry/src/test/java/io/sentry/SentryTest.kt b/sentry/src/test/java/io/sentry/SentryTest.kt index 70728d2900..77d443f909 100644 --- a/sentry/src/test/java/io/sentry/SentryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTest.kt @@ -422,7 +422,7 @@ class SentryTest { assertNotNull(scopes) assertFalse(Sentry.getCurrentScopes().isNoOp) - val newMainHubClone = Sentry.cloneMainHub() + val newMainHubClone = Sentry.forkedRootScopes("test") newMainHubClone.addBreadcrumb("breadcrumbMainClone") scopes.captureMessage("messageCurrent") @@ -473,7 +473,7 @@ class SentryTest { assertNotNull(scopes) assertFalse(scopes.isNoOp) - val newMainHubClone = Sentry.cloneMainHub() + val newMainHubClone = Sentry.forkedRootScopes("test") newMainHubClone.addBreadcrumb("breadcrumbMainClone") scopes.captureMessage("messageCurrent") @@ -921,7 +921,7 @@ class SentryTest { } @Test - fun `getSpan calls returns root span if globalscopes mode is enabled on Android`() { + fun `getSpan calls returns root span if globalHubMode is enabled on Android`() { PlatformTestManipulator.pretendIsAndroid(true) Sentry.init({ it.dsn = dsn @@ -938,7 +938,7 @@ class SentryTest { } @Test - fun `getSpan calls returns child span if globalscopes mode is enabled, but the platform is not Android`() { + fun `getSpan calls returns child span if globalHubMode is enabled, but the platform is not Android`() { PlatformTestManipulator.pretendIsAndroid(false) Sentry.init({ it.dsn = dsn @@ -954,7 +954,7 @@ class SentryTest { } @Test - fun `getSpan calls returns child span if globalscopes mode is disabled`() { + fun `getSpan calls returns child span if globalHubMode is disabled`() { Sentry.init({ it.dsn = dsn it.enableTracing = true From a941eb8a4448f024fd4b2892f45fdf48bb24e5bd Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 22 Apr 2024 14:43:28 +0200 Subject: [PATCH 21/91] Hubs/Scopes Merge 21 - Allow controlling which scope `configureScope` uses (#3345) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses --- .../core/AndroidOptionsInitializer.java | 3 ++ sentry/src/main/java/io/sentry/Hub.java | 3 +- .../src/main/java/io/sentry/HubAdapter.java | 4 +- .../main/java/io/sentry/HubScopesWrapper.java | 4 +- sentry/src/main/java/io/sentry/IScopes.java | 11 +++++- sentry/src/main/java/io/sentry/NoOpHub.java | 2 +- .../src/main/java/io/sentry/NoOpScopes.java | 2 +- sentry/src/main/java/io/sentry/ScopeType.java | 7 ++++ sentry/src/main/java/io/sentry/Scopes.java | 38 +++++++++++++------ .../main/java/io/sentry/ScopesAdapter.java | 4 +- sentry/src/main/java/io/sentry/Sentry.java | 12 +++++- .../main/java/io/sentry/SentryOptions.java | 10 +++++ .../src/test/java/io/sentry/HubAdapterTest.kt | 3 +- .../test/java/io/sentry/ScopesAdapterTest.kt | 3 +- 14 files changed, 82 insertions(+), 24 deletions(-) create mode 100644 sentry/src/main/java/io/sentry/ScopeType.java diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java index 372448b8e7..605de4c0a8 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java @@ -10,6 +10,7 @@ import io.sentry.ILogger; import io.sentry.ITransactionProfiler; import io.sentry.NoOpConnectionStatusProvider; +import io.sentry.ScopeType; import io.sentry.SendFireAndForgetEnvelopeSender; import io.sentry.SendFireAndForgetOutboxSender; import io.sentry.SentryLevel; @@ -98,6 +99,8 @@ static void loadDefaultAndMetadataOptions( // Firstly set the logger, if `debug=true` configured, logging can start asap. options.setLogger(logger); + options.setDefaultScopeType(ScopeType.CURRENT); + options.setDateProvider(new SentryAndroidDateProvider()); // set a lower flush timeout on Android to avoid ANRs diff --git a/sentry/src/main/java/io/sentry/Hub.java b/sentry/src/main/java/io/sentry/Hub.java index dd468d1eca..35740f4c3e 100644 --- a/sentry/src/main/java/io/sentry/Hub.java +++ b/sentry/src/main/java/io/sentry/Hub.java @@ -594,7 +594,8 @@ public void withScope(final @NotNull ScopeCallback callback) { } @Override - public void configureScope(final @NotNull ScopeCallback callback) { + public void configureScope( + final @Nullable ScopeType scopeType, final @NotNull ScopeCallback callback) { if (!isEnabled()) { options .getLogger() diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index d813a11391..f0d7335a80 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -174,8 +174,8 @@ public void withScope(@NotNull ScopeCallback callback) { } @Override - public void configureScope(@NotNull ScopeCallback callback) { - Sentry.configureScope(callback); + public void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback) { + Sentry.configureScope(scopeType, callback); } @Override diff --git a/sentry/src/main/java/io/sentry/HubScopesWrapper.java b/sentry/src/main/java/io/sentry/HubScopesWrapper.java index c3b2b19d80..3309e59671 100644 --- a/sentry/src/main/java/io/sentry/HubScopesWrapper.java +++ b/sentry/src/main/java/io/sentry/HubScopesWrapper.java @@ -169,8 +169,8 @@ public void withScope(@NotNull ScopeCallback callback) { } @Override - public void configureScope(@NotNull ScopeCallback callback) { - scopes.configureScope(callback); + public void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback) { + scopes.configureScope(scopeType, callback); } @Override diff --git a/sentry/src/main/java/io/sentry/IScopes.java b/sentry/src/main/java/io/sentry/IScopes.java index 1ad2d62887..af6f41ae13 100644 --- a/sentry/src/main/java/io/sentry/IScopes.java +++ b/sentry/src/main/java/io/sentry/IScopes.java @@ -331,7 +331,16 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { * * @param callback The configure scope callback. */ - void configureScope(@NotNull ScopeCallback callback); + default void configureScope(@NotNull ScopeCallback callback) { + configureScope(null, callback); + } + + /** + * Configures the scope through the callback. + * + * @param callback The configure scope callback. + */ + void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback); /** * Binds a different client to the hub diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index 890c41d43e..ac1c542a94 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -142,7 +142,7 @@ public void withScope(@NotNull ScopeCallback callback) { } @Override - public void configureScope(@NotNull ScopeCallback callback) {} + public void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback) {} @Override public void bindClient(@NotNull ISentryClient client) {} diff --git a/sentry/src/main/java/io/sentry/NoOpScopes.java b/sentry/src/main/java/io/sentry/NoOpScopes.java index aa2b0fd2f3..de75ff8178 100644 --- a/sentry/src/main/java/io/sentry/NoOpScopes.java +++ b/sentry/src/main/java/io/sentry/NoOpScopes.java @@ -140,7 +140,7 @@ public void withScope(@NotNull ScopeCallback callback) { } @Override - public void configureScope(@NotNull ScopeCallback callback) {} + public void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback) {} @Override public void bindClient(@NotNull ISentryClient client) {} diff --git a/sentry/src/main/java/io/sentry/ScopeType.java b/sentry/src/main/java/io/sentry/ScopeType.java new file mode 100644 index 0000000000..d54c2b635c --- /dev/null +++ b/sentry/src/main/java/io/sentry/ScopeType.java @@ -0,0 +1,7 @@ +package io.sentry; + +public enum ScopeType { + CURRENT, + ISOLATION, + GLOBAL; +} diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index f844119be3..c37dddfd31 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -428,16 +428,31 @@ public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb, final @Nullable } } - private IScope getDefaultConfigureScope() { - // TODO configurable default scope via SentryOptions, Android = global or isolation, backend = - // isolation - return scope; - } + private IScope getSpecificScope(final @Nullable ScopeType scopeType) { + if (scopeType != null) { + switch (scopeType) { + case CURRENT: + return scope; + case ISOLATION: + return isolationScope; + case GLOBAL: + return getGlobalScope(); + default: + break; + } + } - private IScope getDefaultWriteScope() { - // TODO configurable default scope via SentryOptions, Android = global or isolation, backend = - // isolation - return getIsolationScope(); + switch (getOptions().getDefaultScopeType()) { + case CURRENT: + return scope; + case ISOLATION: + return isolationScope; + case GLOBAL: + return getGlobalScope(); + default: + // calm the compiler + return scope; + } } @Override @@ -657,7 +672,8 @@ public void withScope(final @NotNull ScopeCallback callback) { } @Override - public void configureScope(final @NotNull ScopeCallback callback) { + public void configureScope( + final @Nullable ScopeType scopeType, final @NotNull ScopeCallback callback) { if (!isEnabled()) { options .getLogger() @@ -666,7 +682,7 @@ public void configureScope(final @NotNull ScopeCallback callback) { "Instance is disabled and this 'configureScope' call is a no-op."); } else { try { - callback.run(getDefaultConfigureScope()); + callback.run(getSpecificScope(scopeType)); } catch (Throwable e) { options.getLogger().log(SentryLevel.ERROR, "Error in the 'configureScope' callback.", e); } diff --git a/sentry/src/main/java/io/sentry/ScopesAdapter.java b/sentry/src/main/java/io/sentry/ScopesAdapter.java index fe79a42731..d3b0f43bf2 100644 --- a/sentry/src/main/java/io/sentry/ScopesAdapter.java +++ b/sentry/src/main/java/io/sentry/ScopesAdapter.java @@ -170,8 +170,8 @@ public void withScope(@NotNull ScopeCallback callback) { } @Override - public void configureScope(@NotNull ScopeCallback callback) { - Sentry.configureScope(callback); + public void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback) { + Sentry.configureScope(scopeType, callback); } @Override diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 1a8c7db4b5..090cba0d66 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -857,7 +857,17 @@ public static void withScope(final @NotNull ScopeCallback callback) { * @param callback The configure scope callback. */ public static void configureScope(final @NotNull ScopeCallback callback) { - getCurrentScopes().configureScope(callback); + configureScope(null, callback); + } + + /** + * Configures the scope through the callback. + * + * @param callback The configure scope callback. + */ + public static void configureScope( + final @Nullable ScopeType scopeType, final @NotNull ScopeCallback callback) { + getCurrentScopes().configureScope(scopeType, callback); } /** diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index b3bf66a1c2..c693907121 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -479,6 +479,8 @@ public class SentryOptions { @ApiStatus.Experimental private @Nullable Cron cron = null; + private @NotNull ScopeType defaultScopeType = ScopeType.ISOLATION; + /** * Adds an event processor * @@ -2385,6 +2387,14 @@ public void setCron(@Nullable Cron cron) { this.cron = cron; } + public void setDefaultScopeType(final @NotNull ScopeType scopeType) { + this.defaultScopeType = scopeType; + } + + public @NotNull ScopeType getDefaultScopeType() { + return defaultScopeType; + } + /** The BeforeSend callback */ public interface BeforeSendCallback { diff --git a/sentry/src/test/java/io/sentry/HubAdapterTest.kt b/sentry/src/test/java/io/sentry/HubAdapterTest.kt index 0e7e1d0f77..c8e17bfb2b 100644 --- a/sentry/src/test/java/io/sentry/HubAdapterTest.kt +++ b/sentry/src/test/java/io/sentry/HubAdapterTest.kt @@ -3,6 +3,7 @@ package io.sentry import io.sentry.protocol.SentryTransaction import io.sentry.protocol.User import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.reset @@ -185,7 +186,7 @@ class HubAdapterTest { @Test fun `configureScope calls Hub`() { val scopeCallback = mock() HubAdapter.getInstance().configureScope(scopeCallback) - verify(scopes).configureScope(eq(scopeCallback)) + verify(scopes).configureScope(anyOrNull(), eq(scopeCallback)) } @Test fun `bindClient calls Hub`() { diff --git a/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt b/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt index 85a0b6ef75..7637e6c74e 100644 --- a/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt +++ b/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt @@ -3,6 +3,7 @@ package io.sentry import io.sentry.protocol.SentryTransaction import io.sentry.protocol.User import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.reset @@ -185,7 +186,7 @@ class ScopesAdapterTest { @Test fun `configureScope calls Hub`() { val scopeCallback = mock() ScopesAdapter.getInstance().configureScope(scopeCallback) - verify(scopes).configureScope(eq(scopeCallback)) + verify(scopes).configureScope(anyOrNull(), eq(scopeCallback)) } @Test fun `bindClient calls Hub`() { From 9546564b1f21bf563078c819d630d57ed342a4be Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 22 Apr 2024 14:47:05 +0200 Subject: [PATCH 22/91] Hubs/Scopes Merge 22 - Combine global, isolation and current scope (#3346) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes --- .../android/core/InternalSentrySdkTest.kt | 4 +- .../src/main/java/io/sentry/Breadcrumb.java | 8 +- .../java/io/sentry/CombinedContextsView.java | 214 ++++++++ .../java/io/sentry/CombinedScopeView.java | 456 ++++++++++++++++++ sentry/src/main/java/io/sentry/Scopes.java | 46 +- sentry/src/main/java/io/sentry/Sentry.java | 2 +- .../java/io/sentry/protocol/Contexts.java | 4 +- 7 files changed, 702 insertions(+), 32 deletions(-) create mode 100644 sentry/src/main/java/io/sentry/CombinedContextsView.java create mode 100644 sentry/src/main/java/io/sentry/CombinedScopeView.java diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt index a10d8add7b..b34e79991f 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt @@ -118,7 +118,7 @@ class InternalSentrySdkTest { @Test fun `current scope returns obj when hub is active`() { - Sentry.setCurrentHub( + Sentry.setCurrentScopes( Hub( SentryOptions().apply { dsn = "https://key@uri/1234567" @@ -131,7 +131,7 @@ class InternalSentrySdkTest { @Test fun `current scope returns a copy of the scope`() { - Sentry.setCurrentHub( + Sentry.setCurrentScopes( Hub( SentryOptions().apply { dsn = "https://key@uri/1234567" diff --git a/sentry/src/main/java/io/sentry/Breadcrumb.java b/sentry/src/main/java/io/sentry/Breadcrumb.java index fe2055c336..5f43ab6d29 100644 --- a/sentry/src/main/java/io/sentry/Breadcrumb.java +++ b/sentry/src/main/java/io/sentry/Breadcrumb.java @@ -17,7 +17,7 @@ import org.jetbrains.annotations.Nullable; /** Series of application events */ -public final class Breadcrumb implements JsonUnknown, JsonSerializable { +public final class Breadcrumb implements JsonUnknown, JsonSerializable, Comparable { /** A timestamp representing when the breadcrumb occurred. */ private final @NotNull Date timestamp; @@ -660,6 +660,12 @@ public void setUnknown(@Nullable Map unknown) { this.unknown = unknown; } + @Override + @SuppressWarnings("JavaUtilDate") + public int compareTo(@NotNull Breadcrumb o) { + return timestamp.compareTo(o.timestamp); + } + public static final class JsonKeys { public static final String TIMESTAMP = "timestamp"; public static final String MESSAGE = "message"; diff --git a/sentry/src/main/java/io/sentry/CombinedContextsView.java b/sentry/src/main/java/io/sentry/CombinedContextsView.java new file mode 100644 index 0000000000..3362a8ea1e --- /dev/null +++ b/sentry/src/main/java/io/sentry/CombinedContextsView.java @@ -0,0 +1,214 @@ +package io.sentry; + +import io.sentry.protocol.App; +import io.sentry.protocol.Browser; +import io.sentry.protocol.Contexts; +import io.sentry.protocol.Device; +import io.sentry.protocol.Gpu; +import io.sentry.protocol.OperatingSystem; +import io.sentry.protocol.Response; +import io.sentry.protocol.SentryRuntime; +import io.sentry.util.HintUtils; +import java.io.IOException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class CombinedContextsView extends Contexts { + + private static final long serialVersionUID = 3585992094653318439L; + private final @NotNull Contexts globalContexts; + private final @NotNull Contexts isolationContexts; + private final @NotNull Contexts currentContexts; + + private final @NotNull ScopeType defaultScopeType; + + public CombinedContextsView( + final @NotNull Contexts globalContexts, + final @NotNull Contexts isolationContexts, + final @NotNull Contexts currentContexts, + final @NotNull ScopeType defaultScopeType) { + this.globalContexts = globalContexts; + this.isolationContexts = isolationContexts; + this.currentContexts = currentContexts; + this.defaultScopeType = defaultScopeType; + } + + @Override + public @Nullable SpanContext getTrace() { + final @Nullable SpanContext current = currentContexts.getTrace(); + if (current != null) { + return current; + } + final @Nullable SpanContext isolation = isolationContexts.getTrace(); + if (isolation != null) { + return isolation; + } + return globalContexts.getTrace(); + } + + @Override + public void setTrace(@Nullable SpanContext traceContext) { + getDefaultContexts().setTrace(traceContext); + } + + private Contexts getDefaultContexts() { + switch (defaultScopeType) { + case CURRENT: + return currentContexts; + case ISOLATION: + return isolationContexts; + case GLOBAL: + return globalContexts; + default: + return currentContexts; + } + } + + @Override + public @Nullable App getApp() { + final @Nullable App current = currentContexts.getApp(); + if (current != null) { + return current; + } + final @Nullable App isolation = isolationContexts.getApp(); + if (isolation != null) { + return isolation; + } + return globalContexts.getApp(); + } + + @Override + public void setApp(@NotNull App app) { + getDefaultContexts().setApp(app); + } + + @Override + public @Nullable Browser getBrowser() { + final @Nullable Browser current = currentContexts.getBrowser(); + if (current != null) { + return current; + } + final @Nullable Browser isolation = isolationContexts.getBrowser(); + if (isolation != null) { + return isolation; + } + return globalContexts.getBrowser(); + } + + @Override + public void setBrowser(@NotNull Browser browser) { + getDefaultContexts().setBrowser(browser); + } + + @Override + public @Nullable Device getDevice() { + final @Nullable Device current = currentContexts.getDevice(); + if (current != null) { + return current; + } + final @Nullable Device isolation = isolationContexts.getDevice(); + if (isolation != null) { + return isolation; + } + return globalContexts.getDevice(); + } + + @Override + public void setDevice(@NotNull Device device) { + getDefaultContexts().setDevice(device); + } + + @Override + public @Nullable OperatingSystem getOperatingSystem() { + final @Nullable OperatingSystem current = currentContexts.getOperatingSystem(); + if (current != null) { + return current; + } + final @Nullable OperatingSystem isolation = isolationContexts.getOperatingSystem(); + if (isolation != null) { + return isolation; + } + return globalContexts.getOperatingSystem(); + } + + @Override + public void setOperatingSystem(@NotNull OperatingSystem operatingSystem) { + getDefaultContexts().setOperatingSystem(operatingSystem); + } + + @Override + public @Nullable SentryRuntime getRuntime() { + final @Nullable SentryRuntime current = currentContexts.getRuntime(); + if (current != null) { + return current; + } + final @Nullable SentryRuntime isolation = isolationContexts.getRuntime(); + if (isolation != null) { + return isolation; + } + return globalContexts.getRuntime(); + } + + @Override + public void setRuntime(@NotNull SentryRuntime runtime) { + getDefaultContexts().setRuntime(runtime); + } + + @Override + public @Nullable Gpu getGpu() { + final @Nullable Gpu current = currentContexts.getGpu(); + if (current != null) { + return current; + } + final @Nullable Gpu isolation = isolationContexts.getGpu(); + if (isolation != null) { + return isolation; + } + return globalContexts.getGpu(); + } + + @Override + public void setGpu(@NotNull Gpu gpu) { + getDefaultContexts().setGpu(gpu); + } + + @Override + public @Nullable Response getResponse() { + final @Nullable Response current = currentContexts.getResponse(); + if (current != null) { + return current; + } + final @Nullable Response isolation = isolationContexts.getResponse(); + if (isolation != null) { + return isolation; + } + return globalContexts.getResponse(); + } + + @Override + public void withResponse(HintUtils.SentryConsumer callback) { + if (currentContexts.getResponse() != null) { + currentContexts.withResponse(callback); + } else if (isolationContexts.getResponse() != null) { + isolationContexts.withResponse(callback); + } else if (globalContexts.getResponse() != null) { + globalContexts.withResponse(callback); + } else { + getDefaultContexts().withResponse(callback); + } + } + + @Override + public void setResponse(@NotNull Response response) { + getDefaultContexts().setResponse(response); + } + + @Override + public void serialize(@NotNull ObjectWriter writer, @NotNull ILogger logger) throws IOException { + final @NotNull Contexts allContexts = new Contexts(); + allContexts.putAll(globalContexts); + allContexts.putAll(isolationContexts); + allContexts.putAll(currentContexts); + allContexts.serialize(writer, logger); + } +} diff --git a/sentry/src/main/java/io/sentry/CombinedScopeView.java b/sentry/src/main/java/io/sentry/CombinedScopeView.java new file mode 100644 index 0000000000..b9f8ab2c70 --- /dev/null +++ b/sentry/src/main/java/io/sentry/CombinedScopeView.java @@ -0,0 +1,456 @@ +package io.sentry; + +import io.sentry.protocol.Contexts; +import io.sentry.protocol.Request; +import io.sentry.protocol.SentryId; +import io.sentry.protocol.User; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class CombinedScopeView implements IScope { + + private final IScope globalScope; + private final IScope isolationScope; + private final IScope scope; + + public CombinedScopeView( + final @NotNull IScope globalScope, + final @NotNull IScope isolationScope, + final @NotNull IScope scope) { + this.globalScope = globalScope; + this.isolationScope = isolationScope; + this.scope = scope; + } + + @Override + public @Nullable SentryLevel getLevel() { + final @Nullable SentryLevel current = scope.getLevel(); + if (current != null) { + return current; + } + final @Nullable SentryLevel isolation = isolationScope.getLevel(); + if (isolation != null) { + return isolation; + } + return globalScope.getLevel(); + } + + @Override + public void setLevel(@Nullable SentryLevel level) { + getDefaultWriteScope().setLevel(level); + } + + @Override + public @Nullable String getTransactionName() { + final @Nullable String current = scope.getTransactionName(); + if (current != null) { + return current; + } + final @Nullable String isolation = isolationScope.getTransactionName(); + if (isolation != null) { + return isolation; + } + return globalScope.getTransactionName(); + } + + @Override + public void setTransaction(@NotNull String transaction) { + getDefaultWriteScope().setTransaction(transaction); + } + + @Override + public @Nullable ISpan getSpan() { + final @Nullable ISpan current = scope.getSpan(); + if (current != null) { + return current; + } + final @Nullable ISpan isolation = isolationScope.getSpan(); + if (isolation != null) { + return isolation; + } + return globalScope.getSpan(); + } + + @Override + public void setTransaction(@Nullable ITransaction transaction) { + getDefaultWriteScope().setTransaction(transaction); + } + + @Override + public @Nullable User getUser() { + final @Nullable User current = scope.getUser(); + if (current != null) { + return current; + } + final @Nullable User isolation = isolationScope.getUser(); + if (isolation != null) { + return isolation; + } + return globalScope.getUser(); + } + + @Override + public void setUser(@Nullable User user) { + getDefaultWriteScope().setUser(user); + } + + @Override + public @Nullable String getScreen() { + final @Nullable String current = scope.getScreen(); + if (current != null) { + return current; + } + final @Nullable String isolation = isolationScope.getScreen(); + if (isolation != null) { + return isolation; + } + return globalScope.getScreen(); + } + + @Override + public void setScreen(@Nullable String screen) { + getDefaultWriteScope().setScreen(screen); + } + + @Override + public @Nullable Request getRequest() { + final @Nullable Request current = scope.getRequest(); + if (current != null) { + return current; + } + final @Nullable Request isolation = isolationScope.getRequest(); + if (isolation != null) { + return isolation; + } + return globalScope.getRequest(); + } + + @Override + public void setRequest(@Nullable Request request) { + getDefaultWriteScope().setRequest(request); + } + + @Override + public @NotNull List getFingerprint() { + final @Nullable List current = scope.getFingerprint(); + if (!current.isEmpty()) { + return current; + } + final @Nullable List isolation = isolationScope.getFingerprint(); + if (!isolation.isEmpty()) { + return isolation; + } + return globalScope.getFingerprint(); + } + + @Override + public void setFingerprint(@NotNull List fingerprint) { + getDefaultWriteScope().setFingerprint(fingerprint); + } + + @Override + public @NotNull Queue getBreadcrumbs() { + final @NotNull List allBreadcrumbs = new ArrayList<>(); + allBreadcrumbs.addAll(globalScope.getBreadcrumbs()); + allBreadcrumbs.addAll(isolationScope.getBreadcrumbs()); + allBreadcrumbs.addAll(scope.getBreadcrumbs()); + Collections.sort(allBreadcrumbs); + + // TODO test oldest are removed first + final @NotNull Queue breadcrumbs = + createBreadcrumbsList(scope.getOptions().getMaxBreadcrumbs()); + breadcrumbs.addAll(allBreadcrumbs); + + return breadcrumbs; + } + + /** + * Creates a breadcrumb list with the max number of breadcrumbs + * + * @param maxBreadcrumb the max number of breadcrumbs + * @return the breadcrumbs queue + */ + // TODO copied from Scope, should reuse instead + private @NotNull Queue createBreadcrumbsList(final int maxBreadcrumb) { + return SynchronizedQueue.synchronizedQueue(new CircularFifoQueue<>(maxBreadcrumb)); + } + + @Override + public void addBreadcrumb(@NotNull Breadcrumb breadcrumb, @Nullable Hint hint) { + getDefaultWriteScope().addBreadcrumb(breadcrumb, hint); + } + + @Override + public void addBreadcrumb(@NotNull Breadcrumb breadcrumb) { + getDefaultWriteScope().addBreadcrumb(breadcrumb); + } + + @Override + public void clearBreadcrumbs() { + getDefaultWriteScope().clearBreadcrumbs(); + } + + @Override + public void clearTransaction() { + getDefaultWriteScope().clearTransaction(); + } + + @Override + public @Nullable ITransaction getTransaction() { + final @Nullable ITransaction current = scope.getTransaction(); + if (current != null) { + return current; + } + final @Nullable ITransaction isolation = isolationScope.getTransaction(); + if (isolation != null) { + return isolation; + } + return globalScope.getTransaction(); + } + + @Override + public void clear() { + getDefaultWriteScope().clear(); + } + + @Override + public @NotNull Map getTags() { + final @NotNull Map allTags = new ConcurrentHashMap<>(); + allTags.putAll(globalScope.getTags()); + allTags.putAll(isolationScope.getTags()); + allTags.putAll(scope.getTags()); + return allTags; + } + + @Override + public void setTag(@NotNull String key, @NotNull String value) { + getDefaultWriteScope().setTag(key, value); + } + + @Override + public void removeTag(@NotNull String key) { + getDefaultWriteScope().removeTag(key); + } + + @Override + public @NotNull Map getExtras() { + final @NotNull Map allTags = new ConcurrentHashMap<>(); + allTags.putAll(globalScope.getExtras()); + allTags.putAll(isolationScope.getExtras()); + allTags.putAll(scope.getExtras()); + return allTags; + } + + @Override + public void setExtra(@NotNull String key, @NotNull String value) { + getDefaultWriteScope().setExtra(key, value); + } + + @Override + public void removeExtra(@NotNull String key) { + getDefaultWriteScope().removeExtra(key); + } + + @Override + public @NotNull Contexts getContexts() { + return new CombinedContextsView( + globalScope.getContexts(), + isolationScope.getContexts(), + scope.getContexts(), + getOptions().getDefaultScopeType()); + } + + @Override + public void setContexts(@NotNull String key, @NotNull Object value) { + getDefaultWriteScope().setContexts(key, value); + } + + @Override + public void setContexts(@NotNull String key, @NotNull Boolean value) { + getDefaultWriteScope().setContexts(key, value); + } + + @Override + public void setContexts(@NotNull String key, @NotNull String value) { + getDefaultWriteScope().setContexts(key, value); + } + + @Override + public void setContexts(@NotNull String key, @NotNull Number value) { + getDefaultWriteScope().setContexts(key, value); + } + + @Override + public void setContexts(@NotNull String key, @NotNull Collection value) { + getDefaultWriteScope().setContexts(key, value); + } + + @Override + public void setContexts(@NotNull String key, @NotNull Object[] value) { + getDefaultWriteScope().setContexts(key, value); + } + + @Override + public void setContexts(@NotNull String key, @NotNull Character value) { + getDefaultWriteScope().setContexts(key, value); + } + + @Override + public void removeContexts(@NotNull String key) { + getDefaultWriteScope().removeContexts(key); + } + + private @NotNull IScope getDefaultWriteScope() { + if (ScopeType.CURRENT.equals(getOptions().getDefaultScopeType())) { + return scope; + } + if (ScopeType.ISOLATION.equals(getOptions().getDefaultScopeType())) { + return isolationScope; + } + return globalScope; + } + + @Override + public @NotNull List getAttachments() { + final @NotNull List allAttachments = new CopyOnWriteArrayList<>(); + allAttachments.addAll(globalScope.getAttachments()); + allAttachments.addAll(isolationScope.getAttachments()); + allAttachments.addAll(scope.getAttachments()); + return allAttachments; + } + + @Override + public void addAttachment(@NotNull Attachment attachment) { + getDefaultWriteScope().addAttachment(attachment); + } + + @Override + public void clearAttachments() { + getDefaultWriteScope().clearAttachments(); + } + + @Override + public @NotNull List getEventProcessors() { + // TODO mechanism for ordering event processors + final @NotNull List allEventProcessors = new CopyOnWriteArrayList<>(); + allEventProcessors.addAll(globalScope.getEventProcessors()); + allEventProcessors.addAll(isolationScope.getEventProcessors()); + allEventProcessors.addAll(scope.getEventProcessors()); + return allEventProcessors; + } + + @Override + public void addEventProcessor(@NotNull EventProcessor eventProcessor) { + getDefaultWriteScope().addEventProcessor(eventProcessor); + } + + @Override + public @Nullable Session withSession(Scope.@NotNull IWithSession sessionCallback) { + return getDefaultWriteScope().withSession(sessionCallback); + } + + @Override + public @Nullable Scope.SessionPair startSession() { + return getDefaultWriteScope().startSession(); + } + + @Override + public @Nullable Session endSession() { + return getDefaultWriteScope().endSession(); + } + + @Override + public void withTransaction(Scope.@NotNull IWithTransaction callback) { + getDefaultWriteScope().withTransaction(callback); + } + + @Override + public @NotNull SentryOptions getOptions() { + return scope.getOptions(); + } + + @Override + public @Nullable Session getSession() { + final @Nullable Session current = scope.getSession(); + if (current != null) { + return current; + } + final @Nullable Session isolation = isolationScope.getSession(); + if (isolation != null) { + return isolation; + } + return globalScope.getSession(); + } + + @Override + public void setPropagationContext(@NotNull PropagationContext propagationContext) { + getDefaultWriteScope().setPropagationContext(propagationContext); + } + + @Override + public @NotNull PropagationContext getPropagationContext() { + return getDefaultWriteScope().getPropagationContext(); + } + + @Override + public @NotNull PropagationContext withPropagationContext( + Scope.@NotNull IWithPropagationContext callback) { + return getDefaultWriteScope().withPropagationContext(callback); + } + + @Override + public @NotNull IScope clone() { + // TODO just return a new CombinedScopeView with forked scope? + return getDefaultWriteScope().clone(); + } + + @Override + public void setLastEventId(@NotNull SentryId lastEventId) { + globalScope.setLastEventId(lastEventId); + isolationScope.setLastEventId(lastEventId); + scope.setLastEventId(lastEventId); + } + + @Override + public @NotNull SentryId getLastEventId() { + return globalScope.getLastEventId(); + } + + @Override + public void bindClient(@NotNull ISentryClient client) { + getDefaultWriteScope().bindClient(client); + } + + @Override + public @NotNull ISentryClient getClient() { + // TODO checking for noop here doesn't allow disabling via client, is that ok? + final @Nullable ISentryClient current = scope.getClient(); + if (!(current instanceof NoOpSentryClient)) { + return current; + } + final @Nullable ISentryClient isolation = isolationScope.getClient(); + if (!(isolation instanceof NoOpSentryClient)) { + return isolation; + } + return globalScope.getClient(); + } + + @Override + public void assignTraceContext(@NotNull SentryEvent event) { + globalScope.assignTraceContext(event); + } + + @Override + public void setSpanContext( + @NotNull Throwable throwable, @NotNull ISpan span, @NotNull String transactionName) { + globalScope.setSpanContext(throwable, span, transactionName); + } +} diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index c37dddfd31..daf143ba66 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -166,7 +166,7 @@ public boolean isEnabled() { } private void assignTraceContext(final @NotNull SentryEvent event) { - Sentry.getGlobalScope().assignTraceContext(event); + getCombinedScopeView().assignTraceContext(event); } private IScope buildLocalScope( @@ -363,8 +363,7 @@ public void endSession() { } private IScope getCombinedScopeView() { - // TODO combine global, isolation and current scope - return scope; + return new CombinedScopeView(getGlobalScope(), isolationScope, scope); } @Override @@ -424,7 +423,7 @@ public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb, final @Nullable } else if (breadcrumb == null) { options.getLogger().log(SentryLevel.WARNING, "addBreadcrumb called with null parameter."); } else { - getDefaultWriteScope().addBreadcrumb(breadcrumb, hint); + getCombinedScopeView().addBreadcrumb(breadcrumb, hint); } } @@ -467,7 +466,7 @@ public void setLevel(final @Nullable SentryLevel level) { .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'setLevel' call is a no-op."); } else { - getDefaultWriteScope().setLevel(level); + getCombinedScopeView().setLevel(level); } } @@ -480,7 +479,7 @@ public void setTransaction(final @Nullable String transaction) { SentryLevel.WARNING, "Instance is disabled and this 'setTransaction' call is a no-op."); } else if (transaction != null) { - getDefaultWriteScope().setTransaction(transaction); + getCombinedScopeView().setTransaction(transaction); } else { options.getLogger().log(SentryLevel.WARNING, "Transaction cannot be null"); } @@ -493,7 +492,7 @@ public void setUser(final @Nullable User user) { .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'setUser' call is a no-op."); } else { - getDefaultWriteScope().setUser(user); + getCombinedScopeView().setUser(user); } } @@ -508,7 +507,7 @@ public void setFingerprint(final @NotNull List fingerprint) { } else if (fingerprint == null) { options.getLogger().log(SentryLevel.WARNING, "setFingerprint called with null parameter."); } else { - getDefaultWriteScope().setFingerprint(fingerprint); + getCombinedScopeView().setFingerprint(fingerprint); } } @@ -521,7 +520,7 @@ public void clearBreadcrumbs() { SentryLevel.WARNING, "Instance is disabled and this 'clearBreadcrumbs' call is a no-op."); } else { - getDefaultWriteScope().clearBreadcrumbs(); + getCombinedScopeView().clearBreadcrumbs(); } } @@ -534,7 +533,7 @@ public void setTag(final @NotNull String key, final @NotNull String value) { } else if (key == null || value == null) { options.getLogger().log(SentryLevel.WARNING, "setTag called with null parameter."); } else { - getDefaultWriteScope().setTag(key, value); + getCombinedScopeView().setTag(key, value); } } @@ -547,7 +546,7 @@ public void removeTag(final @NotNull String key) { } else if (key == null) { options.getLogger().log(SentryLevel.WARNING, "removeTag called with null parameter."); } else { - getDefaultWriteScope().removeTag(key); + getCombinedScopeView().removeTag(key); } } @@ -560,7 +559,7 @@ public void setExtra(final @NotNull String key, final @NotNull String value) { } else if (key == null || value == null) { options.getLogger().log(SentryLevel.WARNING, "setExtra called with null parameter."); } else { - getDefaultWriteScope().setExtra(key, value); + getCombinedScopeView().setExtra(key, value); } } @@ -573,14 +572,12 @@ public void removeExtra(final @NotNull String key) { } else if (key == null) { options.getLogger().log(SentryLevel.WARNING, "removeExtra called with null parameter."); } else { - getDefaultWriteScope().removeExtra(key); + getCombinedScopeView().removeExtra(key); } } private void updateLastEventId(final @NotNull SentryId lastEventId) { - scope.setLastEventId(lastEventId); - isolationScope.setLastEventId(lastEventId); - getGlobalScope().setLastEventId(lastEventId); + getCombinedScopeView().setLastEventId(lastEventId); } // TODO add to IScopes interface @@ -592,14 +589,9 @@ private void updateLastEventId(final @NotNull SentryId lastEventId) { @Override public @NotNull SentryId getLastEventId() { - // TODO read all scopes here / read default scope? - // returning scope.lastEventId isn't ideal because changed to child scope are not stored in - // there - return getGlobalScope().getLastEventId(); + return getCombinedScopeView().getLastEventId(); } - // TODO needs to be deprecated because there's no more stack - // TODO needs to return a lifecycle token @Override public ISentryLifecycleToken pushScope() { if (!isEnabled()) { @@ -698,10 +690,10 @@ public void bindClient(final @NotNull ISentryClient client) { } else { if (client != null) { options.getLogger().log(SentryLevel.DEBUG, "New client bound to scope."); - getDefaultWriteScope().bindClient(client); + getCombinedScopeView().bindClient(client); } else { options.getLogger().log(SentryLevel.DEBUG, "NoOp client bound to scope."); - getDefaultWriteScope().bindClient(NoOpSentryClient.getInstance()); + getCombinedScopeView().bindClient(NoOpSentryClient.getInstance()); } } } @@ -879,7 +871,7 @@ public void setSpanContext( final @NotNull Throwable throwable, final @NotNull ISpan span, final @NotNull String transactionName) { - Sentry.getGlobalScope().setSpanContext(throwable, span, transactionName); + getCombinedScopeView().setSpanContext(throwable, span, transactionName); } // // TODO this seems unused @@ -908,7 +900,7 @@ public void setSpanContext( .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'getSpan' call is a no-op."); } else { - span = getScope().getSpan(); + span = getCombinedScopeView().getSpan(); } return span; } @@ -924,7 +916,7 @@ public void setSpanContext( SentryLevel.WARNING, "Instance is disabled and this 'getTransaction' call is a no-op."); } else { - span = getScope().getTransaction(); + span = getCombinedScopeView().getTransaction(); } return span; } diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 090cba0d66..42ccc3cf63 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -121,7 +121,7 @@ private Sentry() {} @ApiStatus.Internal // exposed for the coroutines integration in SentryContext @Deprecated - @SuppressWarnings({"deprecation"}) + @SuppressWarnings({"deprecation", "InlineMeSuggester"}) public static @NotNull ISentryLifecycleToken setCurrentHub(final @NotNull IHub hub) { return setCurrentScopes(hub); } diff --git a/sentry/src/main/java/io/sentry/protocol/Contexts.java b/sentry/src/main/java/io/sentry/protocol/Contexts.java index 21be9fd8a5..aa157e665e 100644 --- a/sentry/src/main/java/io/sentry/protocol/Contexts.java +++ b/sentry/src/main/java/io/sentry/protocol/Contexts.java @@ -1,5 +1,6 @@ package io.sentry.protocol; +import com.jakewharton.nopen.annotation.Open; import io.sentry.ILogger; import io.sentry.JsonDeserializer; import io.sentry.JsonObjectReader; @@ -17,7 +18,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public final class Contexts extends ConcurrentHashMap implements JsonSerializable { +@Open +public class Contexts extends ConcurrentHashMap implements JsonSerializable { private static final long serialVersionUID = 252445813254943011L; /** Response lock, Ops should be atomic */ From 28144c619423f64aec91f0907117fb5d67855113 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 22 Apr 2024 15:13:15 +0200 Subject: [PATCH 23/91] Hubs/Scopes Merge 23 - Use new API for CRONS integrations (#3347) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper --- .../io/sentry/log4j2/SentryAppenderTest.kt | 1 + sentry-quartz/api/sentry-quartz.api | 1 + .../io/sentry/quartz/SentryJobListener.java | 8 ++- .../jakarta/checkin/SentryCheckInAdvice.java | 5 +- .../spring/jakarta/SentryCheckInAdviceTest.kt | 46 +++++++++-------- .../spring/checkin/SentryCheckInAdvice.java | 5 +- .../sentry/spring/SentryCheckInAdviceTest.kt | 50 ++++++++++--------- .../java/io/sentry/util/CheckInUtils.java | 6 +-- .../java/io/sentry/util/LifecycleHelper.java | 15 ++++++ .../java/io/sentry/util/CheckInUtilsTest.kt | 45 ++++++++++++----- 10 files changed, 117 insertions(+), 65 deletions(-) create mode 100644 sentry/src/main/java/io/sentry/util/LifecycleHelper.java diff --git a/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt b/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt index 3786555a61..2713bb7f17 100644 --- a/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt +++ b/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt @@ -78,6 +78,7 @@ class SentryAppenderTest { @BeforeTest fun `clear MDC`() { ThreadContext.clearAll() + Sentry.close() } @Test diff --git a/sentry-quartz/api/sentry-quartz.api b/sentry-quartz/api/sentry-quartz.api index 23fce49e7d..21ca2abca6 100644 --- a/sentry-quartz/api/sentry-quartz.api +++ b/sentry-quartz/api/sentry-quartz.api @@ -5,6 +5,7 @@ public final class io/sentry/quartz/BuildConfig { public final class io/sentry/quartz/SentryJobListener : org/quartz/JobListener { public static final field SENTRY_CHECK_IN_ID_KEY Ljava/lang/String; + public static final field SENTRY_LIFECYCLE_TOKEN_KEY Ljava/lang/String; public static final field SENTRY_SLUG_KEY Ljava/lang/String; public fun ()V public fun (Lio/sentry/IScopes;)V diff --git a/sentry-quartz/src/main/java/io/sentry/quartz/SentryJobListener.java b/sentry-quartz/src/main/java/io/sentry/quartz/SentryJobListener.java index f9c22022cc..03f1acbbed 100644 --- a/sentry-quartz/src/main/java/io/sentry/quartz/SentryJobListener.java +++ b/sentry-quartz/src/main/java/io/sentry/quartz/SentryJobListener.java @@ -4,10 +4,12 @@ import io.sentry.CheckIn; import io.sentry.CheckInStatus; import io.sentry.IScopes; +import io.sentry.ISentryLifecycleToken; import io.sentry.ScopesAdapter; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryLevel; import io.sentry.protocol.SentryId; +import io.sentry.util.LifecycleHelper; import io.sentry.util.Objects; import io.sentry.util.TracingUtils; import org.jetbrains.annotations.ApiStatus; @@ -23,6 +25,7 @@ public final class SentryJobListener implements JobListener { public static final String SENTRY_CHECK_IN_ID_KEY = "sentry-checkin-id"; public static final String SENTRY_SLUG_KEY = "sentry-slug"; + public static final String SENTRY_LIFECYCLE_TOKEN_KEY = "sentry-lifecycle"; private final @NotNull IScopes scopes; @@ -49,13 +52,14 @@ public void jobToBeExecuted(final @NotNull JobExecutionContext context) { if (maybeSlug == null) { return; } - scopes.pushScope(); + final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); TracingUtils.startNewTrace(scopes); final @NotNull String slug = maybeSlug; final @NotNull CheckIn checkIn = new CheckIn(slug, CheckInStatus.IN_PROGRESS); final @NotNull SentryId checkInId = scopes.captureCheckIn(checkIn); context.put(SENTRY_CHECK_IN_ID_KEY, checkInId); context.put(SENTRY_SLUG_KEY, slug); + context.put(SENTRY_LIFECYCLE_TOKEN_KEY, lifecycleToken); } catch (Throwable t) { scopes .getOptions() @@ -103,7 +107,7 @@ public void jobWasExecuted(JobExecutionContext context, JobExecutionException jo .getLogger() .log(SentryLevel.ERROR, "Unable to capture check-in in jobWasExecuted.", t); } finally { - scopes.popScope(); + LifecycleHelper.close(context.get(SENTRY_LIFECYCLE_TOKEN_KEY)); } } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/checkin/SentryCheckInAdvice.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/checkin/SentryCheckInAdvice.java index 4a366a8b01..e51647cba8 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/checkin/SentryCheckInAdvice.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/checkin/SentryCheckInAdvice.java @@ -5,6 +5,7 @@ import io.sentry.CheckInStatus; import io.sentry.DateUtils; import io.sentry.IScopes; +import io.sentry.ISentryLifecycleToken; import io.sentry.ScopesAdapter; import io.sentry.SentryLevel; import io.sentry.protocol.SentryId; @@ -86,7 +87,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl return invocation.proceed(); } - scopes.pushScope(); + final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); TracingUtils.startNewTrace(scopes); @Nullable SentryId checkInId = null; @@ -106,7 +107,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); scopes.captureCheckIn(checkIn); - scopes.popScope(); + lifecycleToken.close(); } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryCheckInAdviceTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryCheckInAdviceTest.kt index 5d093f50f1..e87b5f5b26 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryCheckInAdviceTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryCheckInAdviceTest.kt @@ -3,6 +3,7 @@ package io.sentry.spring.jakarta import io.sentry.CheckIn import io.sentry.CheckInStatus import io.sentry.IScopes +import io.sentry.ISentryLifecycleToken import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.protocol.SentryId @@ -56,10 +57,13 @@ class SentryCheckInAdviceTest { @Autowired lateinit var scopes: IScopes + val lifecycleToken = mock() + @BeforeTest fun setup() { reset(scopes) whenever(scopes.options).thenReturn(SentryOptions()) + whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) } @Test @@ -79,10 +83,10 @@ class SentryCheckInAdviceTest { assertEquals("monitor_slug_1", doneCheckIn.monitorSlug) assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) - val order = inOrder(scopes) - order.verify(scopes).pushScope() + val order = inOrder(scopes, lifecycleToken) + order.verify(scopes).pushIsolationScope() order.verify(scopes, times(2)).captureCheckIn(any()) - order.verify(scopes).popScope() + order.verify(lifecycleToken).close() } @Test @@ -103,10 +107,10 @@ class SentryCheckInAdviceTest { assertEquals("monitor_slug_1e", doneCheckIn.monitorSlug) assertEquals(CheckInStatus.ERROR.apiName(), doneCheckIn.status) - val order = inOrder(scopes) - order.verify(scopes).pushScope() + val order = inOrder(scopes, lifecycleToken) + order.verify(scopes).pushIsolationScope() order.verify(scopes, times(2)).captureCheckIn(any()) - order.verify(scopes).popScope() + order.verify(lifecycleToken).close() } @Test @@ -123,10 +127,10 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes) - order.verify(scopes).pushScope() + val order = inOrder(scopes, lifecycleToken) + order.verify(scopes).pushIsolationScope() order.verify(scopes).captureCheckIn(any()) - order.verify(scopes).popScope() + order.verify(lifecycleToken).close() } @Test @@ -144,10 +148,10 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.ERROR.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes) - order.verify(scopes).pushScope() + val order = inOrder(scopes, lifecycleToken) + order.verify(scopes).pushIsolationScope() order.verify(scopes).captureCheckIn(any()) - order.verify(scopes).popScope() + order.verify(lifecycleToken).close() } @Test @@ -178,10 +182,10 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes) - order.verify(scopes).pushScope() + val order = inOrder(scopes, lifecycleToken) + order.verify(scopes).pushIsolationScope() order.verify(scopes).captureCheckIn(any()) - order.verify(scopes).popScope() + order.verify(lifecycleToken).close() } @Test @@ -198,10 +202,10 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes) - order.verify(scopes).pushScope() + val order = inOrder(scopes, lifecycleToken) + order.verify(scopes).pushIsolationScope() order.verify(scopes).captureCheckIn(any()) - order.verify(scopes).popScope() + order.verify(lifecycleToken).close() } @Test @@ -218,10 +222,10 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes) - order.verify(scopes).pushScope() + val order = inOrder(scopes, lifecycleToken) + order.verify(scopes).pushIsolationScope() order.verify(scopes).captureCheckIn(any()) - order.verify(scopes).popScope() + order.verify(lifecycleToken).close() } @Configuration diff --git a/sentry-spring/src/main/java/io/sentry/spring/checkin/SentryCheckInAdvice.java b/sentry-spring/src/main/java/io/sentry/spring/checkin/SentryCheckInAdvice.java index edae74c2ac..9e59093b16 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/checkin/SentryCheckInAdvice.java +++ b/sentry-spring/src/main/java/io/sentry/spring/checkin/SentryCheckInAdvice.java @@ -5,6 +5,7 @@ import io.sentry.CheckInStatus; import io.sentry.DateUtils; import io.sentry.IScopes; +import io.sentry.ISentryLifecycleToken; import io.sentry.ScopesAdapter; import io.sentry.SentryLevel; import io.sentry.protocol.SentryId; @@ -89,7 +90,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl return invocation.proceed(); } - scopes.pushScope(); + final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); TracingUtils.startNewTrace(scopes); @Nullable SentryId checkInId = null; @@ -109,7 +110,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); scopes.captureCheckIn(checkIn); - scopes.popScope(); + lifecycleToken.close(); } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryCheckInAdviceTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryCheckInAdviceTest.kt index 23807e1fd9..57bd293756 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryCheckInAdviceTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryCheckInAdviceTest.kt @@ -3,6 +3,7 @@ package io.sentry.spring import io.sentry.CheckIn import io.sentry.CheckInStatus import io.sentry.IScopes +import io.sentry.ISentryLifecycleToken import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.protocol.SentryId @@ -57,10 +58,13 @@ class SentryCheckInAdviceTest { @Autowired lateinit var scopes: IScopes + val lifecycleToken = mock() + @BeforeTest fun setup() { reset(scopes) whenever(scopes.options).thenReturn(SentryOptions()) + whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) } @Test @@ -80,10 +84,10 @@ class SentryCheckInAdviceTest { assertEquals("monitor_slug_1", doneCheckIn.monitorSlug) assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) - val order = inOrder(scopes) - order.verify(scopes).pushScope() + val order = inOrder(scopes, lifecycleToken) + order.verify(scopes).pushIsolationScope() order.verify(scopes, times(2)).captureCheckIn(any()) - order.verify(scopes).popScope() + order.verify(lifecycleToken).close() } @Test @@ -104,10 +108,10 @@ class SentryCheckInAdviceTest { assertEquals("monitor_slug_1e", doneCheckIn.monitorSlug) assertEquals(CheckInStatus.ERROR.apiName(), doneCheckIn.status) - val order = inOrder(scopes) - order.verify(scopes).pushScope() + val order = inOrder(scopes, lifecycleToken) + order.verify(scopes).pushIsolationScope() order.verify(scopes, times(2)).captureCheckIn(any()) - order.verify(scopes).popScope() + order.verify(lifecycleToken).close() } @Test @@ -124,10 +128,10 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes) - order.verify(scopes).pushScope() + val order = inOrder(scopes, lifecycleToken) + order.verify(scopes).pushIsolationScope() order.verify(scopes).captureCheckIn(any()) - order.verify(scopes).popScope() + order.verify(lifecycleToken).close() } @Test @@ -145,10 +149,10 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.ERROR.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes) - order.verify(scopes).pushScope() + val order = inOrder(scopes, lifecycleToken) + order.verify(scopes).pushIsolationScope() order.verify(scopes).captureCheckIn(any()) - order.verify(scopes).popScope() + order.verify(lifecycleToken).close() } @Test @@ -160,9 +164,9 @@ class SentryCheckInAdviceTest { assertEquals(1, result) assertEquals(0, checkInCaptor.allValues.size) - verify(scopes, never()).pushScope() + verify(scopes, never()).pushIsolationScope() verify(scopes, never()).captureCheckIn(any()) - verify(scopes, never()).popScope() + verify(lifecycleToken, never()).close() } @Test @@ -179,10 +183,10 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes) - order.verify(scopes).pushScope() + val order = inOrder(scopes, lifecycleToken) + order.verify(scopes).pushIsolationScope() order.verify(scopes).captureCheckIn(any()) - order.verify(scopes).popScope() + order.verify(lifecycleToken).close() } @Test @@ -199,10 +203,10 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes) - order.verify(scopes).pushScope() + val order = inOrder(scopes, lifecycleToken) + order.verify(scopes).pushIsolationScope() order.verify(scopes).captureCheckIn(any()) - order.verify(scopes).popScope() + order.verify(lifecycleToken).close() } @Test @@ -219,10 +223,10 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes) - order.verify(scopes).pushScope() + val order = inOrder(scopes, lifecycleToken) + order.verify(scopes).pushIsolationScope() order.verify(scopes).captureCheckIn(any()) - order.verify(scopes).popScope() + order.verify(lifecycleToken).close() } @Configuration diff --git a/sentry/src/main/java/io/sentry/util/CheckInUtils.java b/sentry/src/main/java/io/sentry/util/CheckInUtils.java index 6719e24839..d42cf4edf4 100644 --- a/sentry/src/main/java/io/sentry/util/CheckInUtils.java +++ b/sentry/src/main/java/io/sentry/util/CheckInUtils.java @@ -4,6 +4,7 @@ import io.sentry.CheckInStatus; import io.sentry.DateUtils; import io.sentry.IScopes; +import io.sentry.ISentryLifecycleToken; import io.sentry.MonitorConfig; import io.sentry.Sentry; import io.sentry.protocol.SentryId; @@ -30,12 +31,11 @@ public static U withCheckIn( final @Nullable MonitorConfig monitorConfig, final @NotNull Callable callable) throws Exception { + final @NotNull ISentryLifecycleToken lifecycleToken = Sentry.pushIsolationScope(); final @NotNull IScopes scopes = Sentry.getCurrentScopes(); final long startTime = System.currentTimeMillis(); boolean didError = false; - // TODO fork instead - scopes.pushScope(); TracingUtils.startNewTrace(scopes); CheckIn inProgressCheckIn = new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS); @@ -53,7 +53,7 @@ public static U withCheckIn( CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); scopes.captureCheckIn(checkIn); - scopes.popScope(); + lifecycleToken.close(); } } diff --git a/sentry/src/main/java/io/sentry/util/LifecycleHelper.java b/sentry/src/main/java/io/sentry/util/LifecycleHelper.java new file mode 100644 index 0000000000..4a029f620c --- /dev/null +++ b/sentry/src/main/java/io/sentry/util/LifecycleHelper.java @@ -0,0 +1,15 @@ +package io.sentry.util; + +import io.sentry.ISentryLifecycleToken; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class LifecycleHelper { + + public static void close(final @Nullable Object tokenObject) { + if (tokenObject != null && tokenObject instanceof ISentryLifecycleToken) { + final @NotNull ISentryLifecycleToken token = (ISentryLifecycleToken) tokenObject; + token.close(); + } + } +} diff --git a/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt b/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt index 9f330348b0..7058083ac9 100644 --- a/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt @@ -3,6 +3,7 @@ package io.sentry.util import io.sentry.CheckInStatus import io.sentry.HubScopesWrapper import io.sentry.IScopes +import io.sentry.ISentryLifecycleToken import io.sentry.MonitorConfig import io.sentry.MonitorSchedule import io.sentry.MonitorScheduleUnit @@ -58,16 +59,21 @@ class CheckInUtilsTest { fun `sends check-in for wrapped supplier`() { Mockito.mockStatic(Sentry::class.java).use { sentry -> val scopes = mock() + val lifecycleToken = mock() sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) sentry.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(scopes)) + sentry.`when` { Sentry.pushIsolationScope() }.then { + scopes.pushIsolationScope() + lifecycleToken + } whenever(scopes.options).thenReturn(SentryOptions()) val returnValue = CheckInUtils.withCheckIn("monitor-1") { return@withCheckIn "test1" } assertEquals("test1", returnValue) - inOrder(scopes) { - verify(scopes).pushScope() + inOrder(scopes, lifecycleToken) { + verify(scopes).pushIsolationScope() verify(scopes).configureScope(any()) verify(scopes).captureCheckIn( check { @@ -81,7 +87,7 @@ class CheckInUtilsTest { assertEquals(CheckInStatus.OK.apiName(), it.status) } ) - verify(scopes).popScope() + verify(lifecycleToken).close() } } } @@ -90,8 +96,13 @@ class CheckInUtilsTest { fun `sends check-in for wrapped supplier with exception`() { Mockito.mockStatic(Sentry::class.java).use { sentry -> val scopes = mock() + val lifecycleToken = mock() sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) sentry.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(scopes)) + sentry.`when` { Sentry.pushIsolationScope() }.then { + scopes.pushIsolationScope() + lifecycleToken + } try { CheckInUtils.withCheckIn("monitor-1") { @@ -102,8 +113,8 @@ class CheckInUtilsTest { assertEquals("thrown on purpose", e.message) } - inOrder(scopes) { - verify(scopes).pushScope() + inOrder(scopes, lifecycleToken) { + verify(scopes).pushIsolationScope() verify(scopes).configureScope(any()) verify(scopes).captureCheckIn( check { @@ -117,7 +128,7 @@ class CheckInUtilsTest { assertEquals(CheckInStatus.ERROR.apiName(), it.status) } ) - verify(scopes).popScope() + verify(lifecycleToken).close() } } } @@ -126,8 +137,13 @@ class CheckInUtilsTest { fun `sends check-in for wrapped supplier with upsert`() { Mockito.mockStatic(Sentry::class.java).use { sentry -> val scopes = mock() + val lifecycleToken = mock() sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) sentry.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(scopes)) + sentry.`when` { Sentry.pushIsolationScope() }.then { + scopes.pushIsolationScope() + lifecycleToken + } whenever(scopes.options).thenReturn(SentryOptions()) val monitorConfig = MonitorConfig(MonitorSchedule.interval(7, MonitorScheduleUnit.DAY)) val returnValue = CheckInUtils.withCheckIn("monitor-1", monitorConfig) { @@ -135,8 +151,8 @@ class CheckInUtilsTest { } assertEquals("test1", returnValue) - inOrder(scopes) { - verify(scopes).pushScope() + inOrder(scopes, lifecycleToken) { + verify(scopes).pushIsolationScope() verify(scopes).configureScope(any()) verify(scopes).captureCheckIn( check { @@ -151,7 +167,7 @@ class CheckInUtilsTest { assertEquals(CheckInStatus.OK.apiName(), it.status) } ) - verify(scopes).popScope() + verify(lifecycleToken).close() } } } @@ -160,8 +176,13 @@ class CheckInUtilsTest { fun `sends check-in for wrapped supplier with upsert and thresholds`() { Mockito.mockStatic(Sentry::class.java).use { sentry -> val scopes = mock() + val lifecycleToken = mock() sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) sentry.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(scopes)) + sentry.`when` { Sentry.pushIsolationScope() }.then { + scopes.pushIsolationScope() + lifecycleToken + } whenever(scopes.options).thenReturn(SentryOptions()) val monitorConfig = MonitorConfig(MonitorSchedule.interval(7, MonitorScheduleUnit.DAY)).apply { failureIssueThreshold = 10 @@ -172,8 +193,8 @@ class CheckInUtilsTest { } assertEquals("test1", returnValue) - inOrder(scopes) { - verify(scopes).pushScope() + inOrder(scopes, lifecycleToken) { + verify(scopes).pushIsolationScope() verify(scopes).configureScope(any()) verify(scopes).captureCheckIn( check { @@ -188,7 +209,7 @@ class CheckInUtilsTest { assertEquals(CheckInStatus.OK.apiName(), it.status) } ) - verify(scopes).popScope() + verify(lifecycleToken).close() } } } From 9a64a0b1b65795bf563cb7e060a734404319a6e5 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 22 Apr 2024 15:25:06 +0200 Subject: [PATCH 24/91] Hubs/Scopes Merge 24 - Use new API in Spring integrations (#3348) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API --- .../api/sentry-spring-jakarta.api | 8 +-- .../spring/jakarta/SentrySpringFilter.java | 5 +- .../spring/jakarta/SentryTaskDecorator.java | 12 ++-- .../tracing/SentryTransactionAdvice.java | 5 +- .../webflux/AbstractSentryWebFilter.java | 6 -- .../spring/jakarta/webflux/ReactorUtils.java | 64 ++++++++----------- .../jakarta/webflux/SentryScheduleHook.java | 12 ++-- .../webflux/SentryWebExceptionHandler.java | 2 +- .../jakarta/webflux/SentryWebFilter.java | 2 +- ...entryWebFilterWithThreadLocalAccessor.java | 2 +- .../spring/jakarta/SentrySpringFilterTest.kt | 9 ++- .../spring/jakarta/SentryTaskDecoratorTest.kt | 18 +++--- .../tracing/SentryTransactionAdviceTest.kt | 8 ++- .../jakarta/webflux/ReactorUtilsTest.kt | 62 +++++++++--------- .../jakarta/webflux/SentryScheduleHookTest.kt | 18 +++--- .../webflux/SentryWebFluxTracingFilterTest.kt | 8 +-- .../io/sentry/spring/SentrySpringFilter.java | 5 +- .../io/sentry/spring/SentryTaskDecorator.java | 12 ++-- .../tracing/SentryTransactionAdvice.java | 5 +- .../spring/webflux/SentryScheduleHook.java | 12 ++-- .../spring/webflux/SentryWebFilter.java | 5 +- .../sentry/spring/SentrySpringFilterTest.kt | 9 ++- .../sentry/spring/SentryTaskDecoratorTest.kt | 18 +++--- .../tracing/SentryTransactionAdviceTest.kt | 8 ++- .../spring/webflux/SentryScheduleHookTest.kt | 18 +++--- .../webflux/SentryWebFluxTracingFilterTest.kt | 4 +- 26 files changed, 159 insertions(+), 178 deletions(-) diff --git a/sentry-spring-jakarta/api/sentry-spring-jakarta.api b/sentry-spring-jakarta/api/sentry-spring-jakarta.api index 13eb6033f9..156ab1aaee 100644 --- a/sentry-spring-jakarta/api/sentry-spring-jakarta.api +++ b/sentry-spring-jakarta/api/sentry-spring-jakarta.api @@ -282,10 +282,10 @@ public final class io/sentry/spring/jakarta/webflux/ReactorUtils { public fun ()V public static fun withSentry (Lreactor/core/publisher/Flux;)Lreactor/core/publisher/Flux; public static fun withSentry (Lreactor/core/publisher/Mono;)Lreactor/core/publisher/Mono; - public static fun withSentryHub (Lreactor/core/publisher/Flux;Lio/sentry/IScopes;)Lreactor/core/publisher/Flux; - public static fun withSentryHub (Lreactor/core/publisher/Mono;Lio/sentry/IScopes;)Lreactor/core/publisher/Mono; - public static fun withSentryNewMainHubClone (Lreactor/core/publisher/Flux;)Lreactor/core/publisher/Flux; - public static fun withSentryNewMainHubClone (Lreactor/core/publisher/Mono;)Lreactor/core/publisher/Mono; + public static fun withSentryForkedRoots (Lreactor/core/publisher/Flux;)Lreactor/core/publisher/Flux; + public static fun withSentryForkedRoots (Lreactor/core/publisher/Mono;)Lreactor/core/publisher/Mono; + public static fun withSentryScopes (Lreactor/core/publisher/Flux;Lio/sentry/IScopes;)Lreactor/core/publisher/Flux; + public static fun withSentryScopes (Lreactor/core/publisher/Mono;Lio/sentry/IScopes;)Lreactor/core/publisher/Mono; } public final class io/sentry/spring/jakarta/webflux/SentryReactorThreadLocalAccessor : io/micrometer/context/ThreadLocalAccessor { diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java index be06d3d253..2e3561a982 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java @@ -9,6 +9,7 @@ import io.sentry.EventProcessor; import io.sentry.Hint; import io.sentry.IScopes; +import io.sentry.ISentryLifecycleToken; import io.sentry.ScopesAdapter; import io.sentry.SentryEvent; import io.sentry.SentryLevel; @@ -60,7 +61,7 @@ protected void doFilterInternal( if (scopes.isEnabled()) { // request may qualify for caching request body, if so resolve cached request final HttpServletRequest request = resolveHttpServletRequest(servletRequest); - scopes.pushScope(); + final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); try { final Hint hint = new Hint(); hint.set(SPRING_REQUEST_FILTER_REQUEST, servletRequest); @@ -70,7 +71,7 @@ protected void doFilterInternal( configureScope(request); filterChain.doFilter(request, response); } finally { - scopes.popScope(); + lifecycleToken.close(); } } else { filterChain.doFilter(servletRequest, response); diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java index c99abf3e21..943a7cc5ff 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java @@ -1,6 +1,7 @@ package io.sentry.spring.jakarta; import io.sentry.IScopes; +import io.sentry.ISentryLifecycleToken; import io.sentry.Sentry; import java.util.concurrent.Callable; import org.jetbrains.annotations.NotNull; @@ -14,18 +15,13 @@ */ public final class SentryTaskDecorator implements TaskDecorator { @Override - @SuppressWarnings("deprecation") + // TODO should there also be a SentryIsolatedTaskDecorator or similar that uses forkedScopes()? public @NotNull Runnable decorate(final @NotNull Runnable runnable) { - // TODO fork - final IScopes newHub = Sentry.getCurrentScopes().clone(); + final IScopes newScopes = Sentry.getCurrentScopes().forkedCurrentScope("spring.taskDecorator"); return () -> { - final IScopes oldState = Sentry.getCurrentScopes(); - Sentry.setCurrentScopes(newHub); - try { + try (final @NotNull ISentryLifecycleToken ignored = newScopes.makeCurrent()) { runnable.run(); - } finally { - Sentry.setCurrentScopes(oldState); } }; } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java index f04b3dd7a6..da781afcd8 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java @@ -2,6 +2,7 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.IScopes; +import io.sentry.ISentryLifecycleToken; import io.sentry.ITransaction; import io.sentry.ScopesAdapter; import io.sentry.SpanStatus; @@ -68,7 +69,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } else { operation = "bean"; } - scopes.pushScope(); + final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); final TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setBindToScope(true); final ITransaction transaction = @@ -86,7 +87,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl throw e; } finally { transaction.finish(); - scopes.popScope(); + lifecycleToken.close(); } } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java index 3321874dd8..5995728785 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java @@ -68,10 +68,6 @@ protected void doFinally( if (transaction != null) { finishTransaction(serverWebExchange, transaction); } - if (requestHub.isEnabled()) { - // TODO close lifecycle token instead of popscope - requestHub.popScope(); - } Sentry.setCurrentScopes(NoOpScopes.getInstance()); } @@ -79,8 +75,6 @@ protected void doFirst( final @NotNull ServerWebExchange serverWebExchange, final @NotNull IScopes requestHub) { if (requestHub.isEnabled()) { serverWebExchange.getAttributes().put(SENTRY_SCOPES_KEY, requestHub); - // TODO fork instead - requestHub.pushScope(); final ServerHttpRequest request = serverWebExchange.getRequest(); final ServerHttpResponse response = serverWebExchange.getResponse(); diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java index 41dd2e4bc0..9755ea0932 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java @@ -10,6 +10,8 @@ // TODO deprecate and replace with "withSentryScopes" etc. @ApiStatus.Experimental +// TODO do we keep old methods around and deprecate them? +// TODO do we need to offer isolated variants? public final class ReactorUtils { /** @@ -20,27 +22,23 @@ public final class ReactorUtils { * enabled - having `io.micrometer:context-propagation:1.0.2+` (provided by Spring Boot 3.0.3+) - * having `io.projectreactor:reactor-core:3.5.3+` (provided by Spring Boot 3.0.3+) */ - @ApiStatus.Experimental - @SuppressWarnings("deprecation") public static Mono withSentry(final @NotNull Mono mono) { - final @NotNull IScopes oldHub = Sentry.getCurrentScopes(); - // TODO fork - final @NotNull IScopes clonedHub = oldHub.clone(); - return withSentryHub(mono, clonedHub); + final @NotNull IScopes oldScopes = Sentry.getCurrentScopes(); + final @NotNull IScopes forkedScopes = oldScopes.forkedCurrentScope("reactor.withSentry"); + return withSentryScopes(mono, forkedScopes); } /** - * Writes a new Sentry {@link IScopes} cloned from the main hub to the {@link Context} and uses + * Writes a new Sentry {@link IScopes} cloned from the main scopes to the {@link Context} and uses * {@link io.micrometer.context.ThreadLocalAccessor} to propagate it. * *

This requires - reactor.core.publisher.Hooks#enableAutomaticContextPropagation() to be * enabled - having `io.micrometer:context-propagation:1.0.2+` (provided by Spring Boot 3.0.3+) - * having `io.projectreactor:reactor-core:3.5.3+` (provided by Spring Boot 3.0.3+) */ - @ApiStatus.Experimental - public static Mono withSentryNewMainHubClone(final @NotNull Mono mono) { - final @NotNull IScopes hub = Sentry.cloneMainHub(); - return withSentryHub(mono, hub); + public static Mono withSentryForkedRoots(final @NotNull Mono mono) { + final @NotNull IScopes scopes = Sentry.forkedRootScopes("reactor"); + return withSentryScopes(mono, scopes); } /** @@ -51,17 +49,16 @@ public static Mono withSentryNewMainHubClone(final @NotNull Mono mono) * enabled - having `io.micrometer:context-propagation:1.0.2+` (provided by Spring Boot 3.0.3+) - * having `io.projectreactor:reactor-core:3.5.3+` (provided by Spring Boot 3.0.3+) */ - @ApiStatus.Experimental - public static Mono withSentryHub(final @NotNull Mono mono, final @NotNull IScopes hub) { + public static Mono withSentryScopes( + final @NotNull Mono mono, final @NotNull IScopes scopes) { /** - * WARNING: Cannot set the hub as current. It would be used by others to clone again causing - * shared hubs and scopes and thus leading to issues like unrelated breadcrumbs showing up in - * events. + * WARNING: Cannot set the scopes as current. It would be used by others to clone again causing + * shared scopes and thus leading to issues like unrelated breadcrumbs showing up in events. */ - // Sentry.setCurrentHub(clonedHub); + // Sentry.setCurrentScopes(forkedScopes); return Mono.deferContextual(ctx -> mono) - .contextWrite(Context.of(SentryReactorThreadLocalAccessor.KEY, hub)); + .contextWrite(Context.of(SentryReactorThreadLocalAccessor.KEY, scopes)); } /** @@ -72,28 +69,24 @@ public static Mono withSentryHub(final @NotNull Mono mono, final @NotN * enabled - having `io.micrometer:context-propagation:1.0.2+` (provided by Spring Boot 3.0.3+) - * having `io.projectreactor:reactor-core:3.5.3+` (provided by Spring Boot 3.0.3+) */ - @ApiStatus.Experimental - @SuppressWarnings("deprecation") public static Flux withSentry(final @NotNull Flux flux) { - final @NotNull IScopes oldHub = Sentry.getCurrentScopes(); - // TODO fork - final @NotNull IScopes clonedHub = oldHub.clone(); + final @NotNull IScopes oldScopes = Sentry.getCurrentScopes(); + final @NotNull IScopes forkedScopes = oldScopes.forkedCurrentScope("reactor.withSentry"); - return withSentryHub(flux, clonedHub); + return withSentryScopes(flux, forkedScopes); } /** - * Writes a new Sentry {@link IScopes} cloned from the main hub to the {@link Context} and uses + * Writes a new Sentry {@link IScopes} cloned from the main scopes to the {@link Context} and uses * {@link io.micrometer.context.ThreadLocalAccessor} to propagate it. * *

This requires - reactor.core.publisher.Hooks#enableAutomaticContextPropagation() to be * enabled - having `io.micrometer:context-propagation:1.0.2+` (provided by Spring Boot 3.0.3+) - * having `io.projectreactor:reactor-core:3.5.3+` (provided by Spring Boot 3.0.3+) */ - @ApiStatus.Experimental - public static Flux withSentryNewMainHubClone(final @NotNull Flux flux) { - final @NotNull IScopes hub = Sentry.cloneMainHub(); - return withSentryHub(flux, hub); + public static Flux withSentryForkedRoots(final @NotNull Flux flux) { + final @NotNull IScopes scopes = Sentry.forkedRootScopes("reactor"); + return withSentryScopes(flux, scopes); } /** @@ -104,16 +97,15 @@ public static Flux withSentryNewMainHubClone(final @NotNull Flux flux) * enabled - having `io.micrometer:context-propagation:1.0.2+` (provided by Spring Boot 3.0.3+) - * having `io.projectreactor:reactor-core:3.5.3+` (provided by Spring Boot 3.0.3+) */ - @ApiStatus.Experimental - public static Flux withSentryHub(final @NotNull Flux flux, final @NotNull IScopes hub) { + public static Flux withSentryScopes( + final @NotNull Flux flux, final @NotNull IScopes scopes) { /** - * WARNING: Cannot set the hub as current. It would be used by others to clone again causing - * shared hubs and scopes and thus leading to issues like unrelated breadcrumbs showing up in - * events. + * WARNING: Cannot set the scopes as current. It would be used by others to clone again causing + * shared scopes and thus leading to issues like unrelated breadcrumbs showing up in events. */ - // Sentry.setCurrentHub(clonedHub); + // Sentry.setCurrentScopes(forkedScopes); return Flux.deferContextual(ctx -> flux) - .contextWrite(Context.of(SentryReactorThreadLocalAccessor.KEY, hub)); + .contextWrite(Context.of(SentryReactorThreadLocalAccessor.KEY, scopes)); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryScheduleHook.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryScheduleHook.java index 882a0b268a..21b35bc60b 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryScheduleHook.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryScheduleHook.java @@ -1,6 +1,7 @@ package io.sentry.spring.jakarta.webflux; import io.sentry.IScopes; +import io.sentry.ISentryLifecycleToken; import io.sentry.Sentry; import java.util.function.Function; import org.jetbrains.annotations.ApiStatus; @@ -8,23 +9,18 @@ /** * Hook meant to used with {@link reactor.core.scheduler.Schedulers#onScheduleHook(String, - * Function)} to configure Reactor to copy correct hub into the operating thread. + * Function)} to configure Reactor to copy correct scopes into the operating thread. */ @ApiStatus.Experimental public final class SentryScheduleHook implements Function { @Override @SuppressWarnings("deprecation") public Runnable apply(final @NotNull Runnable runnable) { - // TODO fork instead - final IScopes newHub = Sentry.getCurrentScopes().clone(); + final IScopes newScopes = Sentry.getCurrentScopes().forkedCurrentScope("spring.scheduleHook"); return () -> { - final IScopes oldState = Sentry.getCurrentScopes(); - Sentry.setCurrentScopes(newHub); - try { + try (final @NotNull ISentryLifecycleToken ignored = newScopes.makeCurrent()) { runnable.run(); - } finally { - Sentry.setCurrentScopes(oldState); } }; } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebExceptionHandler.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebExceptionHandler.java index 40b0ed4e87..15c73ab625 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebExceptionHandler.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebExceptionHandler.java @@ -40,7 +40,7 @@ public SentryWebExceptionHandler(final @NotNull IScopes scopes) { serverWebExchange.getAttributeOrDefault(SentryWebFilter.SENTRY_SCOPES_KEY, null); final @NotNull IScopes scopesToUse = requestScopes != null ? requestScopes : scopes; - return ReactorUtils.withSentryHub( + return ReactorUtils.withSentryScopes( Mono.just(ex) .map( it -> { diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilter.java index dab985eecf..0a6b767ec4 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilter.java @@ -28,7 +28,7 @@ public SentryWebFilter(final @NotNull IScopes scopes) { public Mono filter( final @NotNull ServerWebExchange serverWebExchange, final @NotNull WebFilterChain webFilterChain) { - @NotNull IScopes requestScopes = Sentry.cloneMainHub(); + @NotNull IScopes requestScopes = Sentry.forkedRootScopes("request.webflux"); final ServerHttpRequest request = serverWebExchange.getRequest(); final @Nullable ITransaction transaction = maybeStartTransaction(requestScopes, request); if (transaction != null) { diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor.java index e760ef8f3e..c38e322731 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor.java @@ -26,7 +26,7 @@ public Mono filter( final @NotNull ServerWebExchange serverWebExchange, final @NotNull WebFilterChain webFilterChain) { final @NotNull TransactionContainer transactionContainer = new TransactionContainer(); - return ReactorUtils.withSentryNewMainHubClone( + return ReactorUtils.withSentryForkedRoots( webFilterChain .filter(serverWebExchange) .doFinally( diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentrySpringFilterTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentrySpringFilterTest.kt index b6bce77a0b..ac394deb31 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentrySpringFilterTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentrySpringFilterTest.kt @@ -3,6 +3,7 @@ package io.sentry.spring.jakarta import io.sentry.Breadcrumb import io.sentry.IScope import io.sentry.IScopes +import io.sentry.ISentryLifecycleToken import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -39,6 +40,7 @@ class SentrySpringFilterTest { private class Fixture { val scopes = mock() val response = MockHttpServletResponse() + val lifecycleToken = mock() val chain = mock() lateinit var scope: IScope lateinit var request: HttpServletRequest @@ -47,6 +49,7 @@ class SentrySpringFilterTest { scope = Scope(options) whenever(scopes.options).thenReturn(options) whenever(scopes.isEnabled).thenReturn(true) + whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) this.request = request ?: MockHttpServletRequest().apply { @@ -64,7 +67,7 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).pushScope() + verify(fixture.scopes).pushIsolationScope() } @Test @@ -87,7 +90,7 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).popScope() + verify(fixture.lifecycleToken).close() } @Test @@ -99,7 +102,7 @@ class SentrySpringFilterTest { listener.doFilter(fixture.request, fixture.response, fixture.chain) fail() } catch (e: Exception) { - verify(fixture.scopes).popScope() + verify(fixture.lifecycleToken).close() } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryTaskDecoratorTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryTaskDecoratorTest.kt index e5f8704b49..d44e64f78d 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryTaskDecoratorTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryTaskDecoratorTest.kt @@ -25,35 +25,35 @@ class SentryTaskDecoratorTest { } @Test - fun `hub is reset to its state within the thread after decoration is done`() { + fun `scopes is reset to its state within the thread after decoration is done`() { Sentry.init { it.dsn = dsn } val sut = SentryTaskDecorator() - val mainHub = Sentry.getCurrentScopes() - val threadedHub = Sentry.getCurrentScopes().clone() + val mainScopes = Sentry.getCurrentScopes() + val threadedScopes = Sentry.getCurrentScopes().forkedCurrentScope("test") executor.submit { - Sentry.setCurrentScopes(threadedHub) + Sentry.setCurrentScopes(threadedScopes) }.get() - assertEquals(mainHub, Sentry.getCurrentScopes()) + assertEquals(mainScopes, Sentry.getCurrentScopes()) val callableFuture = executor.submit( sut.decorate { - assertNotEquals(mainHub, Sentry.getCurrentScopes()) - assertNotEquals(threadedHub, Sentry.getCurrentScopes()) + assertNotEquals(mainScopes, Sentry.getCurrentScopes()) + assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) } ) callableFuture.get() executor.submit { - assertNotEquals(mainHub, Sentry.getCurrentScopes()) - assertEquals(threadedHub, Sentry.getCurrentScopes()) + assertNotEquals(mainScopes, Sentry.getCurrentScopes()) + assertEquals(threadedScopes, Sentry.getCurrentScopes()) }.get() } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt index 390b4d8241..b0f83782e0 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt @@ -1,6 +1,7 @@ package io.sentry.spring.jakarta.tracing import io.sentry.IScopes +import io.sentry.ISentryLifecycleToken import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -46,6 +47,8 @@ class SentryTransactionAdviceTest { @Autowired lateinit var scopes: IScopes + val lifecycleToken = mock() + @BeforeTest fun setup() { reset(scopes) @@ -55,6 +58,7 @@ class SentryTransactionAdviceTest { dsn = "https://key@sentry.io/proj" } ) + whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) } @Test @@ -141,13 +145,13 @@ class SentryTransactionAdviceTest { @Test fun `pushes the scope when advice starts`() { classAnnotatedSampleService.hello() - verify(scopes).pushScope() + verify(scopes).pushIsolationScope() } @Test fun `pops the scope when advice finishes`() { classAnnotatedSampleService.hello() - verify(scopes).popScope() + verify(lifecycleToken).close() } @Configuration diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/ReactorUtilsTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/ReactorUtilsTest.kt index 9c851cde11..f3bd5d2653 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/ReactorUtilsTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/ReactorUtilsTest.kt @@ -1,9 +1,9 @@ package io.sentry.spring.jakarta.webflux -import io.sentry.IHub import io.sentry.IScopes import io.sentry.NoOpScopes import io.sentry.Sentry +import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @@ -31,88 +31,88 @@ class ReactorUtilsTest { } @Test - fun `propagates hub inside mono`() { - val hubToUse = mock() - var hubInside: IScopes? = null - val mono = ReactorUtils.withSentryHub( + fun `propagates scopes inside mono`() { + val scopesToUse = mock() + var scopesInside: IScopes? = null + val mono = ReactorUtils.withSentryScopes( Mono.just("hello") .publishOn(Schedulers.boundedElastic()) .map { it -> - hubInside = Sentry.getCurrentScopes() + scopesInside = Sentry.getCurrentScopes() it }, - hubToUse + scopesToUse ) assertEquals("hello", mono.block()) - assertSame(hubToUse, hubInside) + assertSame(scopesToUse, scopesInside) } @Test - fun `propagates hub inside flux`() { - val hubToUse = mock() - var hubInside: IScopes? = null - val flux = ReactorUtils.withSentryHub( + fun `propagates scopes inside flux`() { + val scopesToUse = mock() + var scopesInside: IScopes? = null + val flux = ReactorUtils.withSentryScopes( Flux.just("hello") .publishOn(Schedulers.boundedElastic()) .map { it -> - hubInside = Sentry.getCurrentScopes() + scopesInside = Sentry.getCurrentScopes() it }, - hubToUse + scopesToUse ) assertEquals("hello", flux.blockFirst()) - assertSame(hubToUse, hubInside) + assertSame(scopesToUse, scopesInside) } @Test - fun `without reactive utils hub is not propagated to mono`() { - val hubToUse = mock() - var hubInside: IScopes? = null + fun `without reactive utils scopes is not propagated to mono`() { + val scopesToUse = mock() + var scopesInside: IScopes? = null val mono = Mono.just("hello") .publishOn(Schedulers.boundedElastic()) .map { it -> - hubInside = Sentry.getCurrentScopes() + scopesInside = Sentry.getCurrentScopes() it } assertEquals("hello", mono.block()) - assertNotSame(hubToUse, hubInside) + assertNotSame(scopesToUse, scopesInside) } @Test - fun `without reactive utils hub is not propagated to flux`() { - val hubToUse = mock() - var hubInside: IScopes? = null + fun `without reactive utils scopes is not propagated to flux`() { + val scopesToUse = mock() + var scopesInside: IScopes? = null val flux = Flux.just("hello") .publishOn(Schedulers.boundedElastic()) .map { it -> - hubInside = Sentry.getCurrentScopes() + scopesInside = Sentry.getCurrentScopes() it } assertEquals("hello", flux.blockFirst()) - assertNotSame(hubToUse, hubInside) + assertNotSame(scopesToUse, scopesInside) } @Test - fun `clones hub for mono`() { + fun `clones scopes for mono`() { val mockScopes = mock() - whenever(mockScopes.clone()).thenReturn(mock()) + whenever(mockScopes.forkedCurrentScope(any())).thenReturn(mock()) Sentry.setCurrentScopes(mockScopes) ReactorUtils.withSentry(Mono.just("hello")).block() - verify(mockScopes).clone() + verify(mockScopes).forkedCurrentScope(any()) } @Test - fun `clones hub for flux`() { + fun `clones scopes for flux`() { val mockScopes = mock() - whenever(mockScopes.clone()).thenReturn(mock()) + whenever(mockScopes.forkedCurrentScope(any())).thenReturn(mock()) Sentry.setCurrentScopes(mockScopes) ReactorUtils.withSentry(Flux.just("hello")).blockFirst() - verify(mockScopes).clone() + verify(mockScopes).forkedCurrentScope(any()) } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryScheduleHookTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryScheduleHookTest.kt index 5403caa7e0..4b540da1aa 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryScheduleHookTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryScheduleHookTest.kt @@ -26,35 +26,35 @@ class SentryScheduleHookTest { } @Test - fun `hub is reset to its state within the thread after hook is done`() { + fun `scopes is reset to its state within the thread after hook is done`() { Sentry.init { it.dsn = dsn } val sut = SentryScheduleHook() - val mainHub = Sentry.getCurrentScopes() - val threadedHub = Sentry.getCurrentScopes().clone() + val mainScopes = Sentry.getCurrentScopes() + val threadedScopes = Sentry.getCurrentScopes().forkedCurrentScope("test") executor.submit { - Sentry.setCurrentScopes(threadedHub) + Sentry.setCurrentScopes(threadedScopes) }.get() - assertEquals(mainHub, Sentry.getCurrentScopes()) + assertEquals(mainScopes, Sentry.getCurrentScopes()) val callableFuture = executor.submit( sut.apply { - assertNotEquals(mainHub, Sentry.getCurrentScopes()) - assertNotEquals(threadedHub, Sentry.getCurrentScopes()) + assertNotEquals(mainScopes, Sentry.getCurrentScopes()) + assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) } ) callableFuture.get() executor.submit { - assertNotEquals(mainHub, Sentry.getCurrentScopes()) - assertEquals(threadedHub, Sentry.getCurrentScopes()) + assertNotEquals(mainScopes, Sentry.getCurrentScopes()) + assertEquals(threadedScopes, Sentry.getCurrentScopes()) }.get() } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt index ddbbe75817..44f4925c2d 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt @@ -89,7 +89,7 @@ class SentryWebFluxTracingFilterTest { fun withMockScopes(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { it.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(fixture.scopes)) it.`when` { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes) - it.`when` { Sentry.cloneMainHub() }.thenReturn(fixture.scopes) + it.`when` { Sentry.forkedRootScopes(any()) }.thenReturn(fixture.scopes) closure.invoke() } @@ -210,7 +210,7 @@ class SentryWebFluxTracingFilterTest { verify(fixture.chain).filter(fixture.exchange) - verify(fixture.scopes, times(3)).isEnabled + verify(fixture.scopes, times(2)).isEnabled verifyNoMoreInteractions(fixture.scopes) } } @@ -249,13 +249,11 @@ class SentryWebFluxTracingFilterTest { verify(fixture.chain).filter(fixture.exchange) - verify(fixture.scopes, times(3)).isEnabled + verify(fixture.scopes, times(2)).isEnabled verify(fixture.scopes, times(2)).options verify(fixture.scopes).continueTrace(anyOrNull(), anyOrNull()) - verify(fixture.scopes).pushScope() // TODO don't verify(fixture.scopes).addBreadcrumb(any(), any()) verify(fixture.scopes).configureScope(any()) - verify(fixture.scopes).popScope() // TODO don't verifyNoMoreInteractions(fixture.scopes) } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java b/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java index 7695545f04..252a07910c 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java @@ -9,6 +9,7 @@ import io.sentry.EventProcessor; import io.sentry.Hint; import io.sentry.IScopes; +import io.sentry.ISentryLifecycleToken; import io.sentry.ScopesAdapter; import io.sentry.SentryEvent; import io.sentry.SentryLevel; @@ -60,7 +61,7 @@ protected void doFilterInternal( if (scopes.isEnabled()) { // request may qualify for caching request body, if so resolve cached request final HttpServletRequest request = resolveHttpServletRequest(servletRequest); - scopes.pushScope(); + final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); try { final Hint hint = new Hint(); hint.set(SPRING_REQUEST_FILTER_REQUEST, servletRequest); @@ -70,7 +71,7 @@ protected void doFilterInternal( configureScope(request); filterChain.doFilter(request, response); } finally { - scopes.popScope(); + lifecycleToken.close(); } } else { filterChain.doFilter(servletRequest, response); diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java b/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java index 88d205a57e..761038ece0 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java @@ -1,6 +1,7 @@ package io.sentry.spring; import io.sentry.IScopes; +import io.sentry.ISentryLifecycleToken; import io.sentry.Sentry; import java.util.concurrent.Callable; import org.jetbrains.annotations.NotNull; @@ -14,18 +15,13 @@ */ public final class SentryTaskDecorator implements TaskDecorator { @Override - @SuppressWarnings("deprecation") + // TODO should there also be a SentryIsolatedTaskDecorator or similar that uses forkedScopes()? public @NotNull Runnable decorate(final @NotNull Runnable runnable) { - // TODO fork instead - final IScopes newHub = Sentry.getCurrentScopes().clone(); + final IScopes newHub = Sentry.getCurrentScopes().forkedCurrentScope("spring.taskDecorator"); return () -> { - final IScopes oldState = Sentry.getCurrentScopes(); - Sentry.setCurrentScopes(newHub); - try { + try (final @NotNull ISentryLifecycleToken ignored = newHub.makeCurrent()) { runnable.run(); - } finally { - Sentry.setCurrentScopes(oldState); } }; } diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java index 8f4f5bbdfc..a885510fcd 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java @@ -2,6 +2,7 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.IScopes; +import io.sentry.ISentryLifecycleToken; import io.sentry.ITransaction; import io.sentry.ScopesAdapter; import io.sentry.SpanStatus; @@ -67,7 +68,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } else { operation = "bean"; } - scopes.pushScope(); + final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); final TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setBindToScope(true); final ITransaction transaction = @@ -85,7 +86,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl throw e; } finally { transaction.finish(); - scopes.popScope(); + lifecycleToken.close(); } } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryScheduleHook.java b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryScheduleHook.java index 20f494168d..4f8312835a 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryScheduleHook.java +++ b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryScheduleHook.java @@ -1,6 +1,7 @@ package io.sentry.spring.webflux; import io.sentry.IScopes; +import io.sentry.ISentryLifecycleToken; import io.sentry.Sentry; import java.util.function.Function; import org.jetbrains.annotations.ApiStatus; @@ -8,23 +9,18 @@ /** * Hook meant to used with {@link reactor.core.scheduler.Schedulers#onScheduleHook(String, - * Function)} to configure Reactor to copy correct hub into the operating thread. + * Function)} to configure Reactor to copy correct scopes into the operating thread. */ @ApiStatus.Experimental public final class SentryScheduleHook implements Function { @Override @SuppressWarnings("deprecation") public Runnable apply(final @NotNull Runnable runnable) { - // TODO fork instead - final IScopes newHub = Sentry.getCurrentScopes().clone(); + final IScopes newScopes = Sentry.getCurrentScopes().forkedCurrentScope("spring.scheduleHook"); return () -> { - final IScopes oldState = Sentry.getCurrentScopes(); - Sentry.setCurrentScopes(newHub); - try { + try (final @NotNull ISentryLifecycleToken ignored = newScopes.makeCurrent()) { runnable.run(); - } finally { - Sentry.setCurrentScopes(oldState); } }; } diff --git a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java index 4d39e092bc..10e80ebe8b 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java @@ -50,7 +50,7 @@ public SentryWebFilter(final @NotNull IScopes scopes) { public Mono filter( final @NotNull ServerWebExchange serverWebExchange, final @NotNull WebFilterChain webFilterChain) { - @NotNull IScopes requestHub = Sentry.cloneMainHub(); + @NotNull IScopes requestHub = Sentry.forkedRootScopes("request.webflux"); // TODO do not push / pop, use fork instead if (!requestHub.isEnabled()) { return webFilterChain.filter(serverWebExchange); @@ -81,8 +81,6 @@ isTracingEnabled && shouldTraceRequest(requestHub, request) if (transaction != null) { finishTransaction(serverWebExchange, transaction); } - requestHub.popScope(); // TODO don't - // TODO token based cleanup instead? Sentry.setCurrentScopes(NoOpScopes.getInstance()); }) .doOnError( @@ -96,7 +94,6 @@ isTracingEnabled && shouldTraceRequest(requestHub, request) () -> { serverWebExchange.getAttributes().put(SENTRY_SCOPES_KEY, requestHub); Sentry.setCurrentScopes(requestHub); - requestHub.pushScope(); // TODO don't final ServerHttpResponse response = serverWebExchange.getResponse(); final Hint hint = new Hint(); diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringFilterTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringFilterTest.kt index c6ac952531..6037e253c8 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringFilterTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringFilterTest.kt @@ -3,6 +3,7 @@ package io.sentry.spring import io.sentry.Breadcrumb import io.sentry.IScope import io.sentry.IScopes +import io.sentry.ISentryLifecycleToken import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -39,6 +40,7 @@ class SentrySpringFilterTest { private class Fixture { val scopes = mock() val response = MockHttpServletResponse() + val lifecycleToken = mock() val chain = mock() lateinit var scope: IScope lateinit var request: HttpServletRequest @@ -47,6 +49,7 @@ class SentrySpringFilterTest { scope = Scope(options) whenever(scopes.options).thenReturn(options) whenever(scopes.isEnabled).thenReturn(true) + whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) this.request = request ?: MockHttpServletRequest().apply { @@ -64,7 +67,7 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).pushScope() + verify(fixture.scopes).pushIsolationScope() } @Test @@ -87,7 +90,7 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).popScope() + verify(fixture.lifecycleToken).close() } @Test @@ -99,7 +102,7 @@ class SentrySpringFilterTest { listener.doFilter(fixture.request, fixture.response, fixture.chain) fail() } catch (e: Exception) { - verify(fixture.scopes).popScope() + verify(fixture.lifecycleToken).close() } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryTaskDecoratorTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryTaskDecoratorTest.kt index 3f34ab9d9d..4bbce919eb 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryTaskDecoratorTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryTaskDecoratorTest.kt @@ -25,35 +25,35 @@ class SentryTaskDecoratorTest { } @Test - fun `hub is reset to its state within the thread after decoration is done`() { + fun `scopes is reset to its state within the thread after decoration is done`() { Sentry.init { it.dsn = dsn } val sut = SentryTaskDecorator() - val mainHub = Sentry.getCurrentScopes() - val threadedHub = Sentry.getCurrentScopes().clone() + val mainScopes = Sentry.getCurrentScopes() + val threadedScopes = Sentry.getCurrentScopes().forkedCurrentScope("test") executor.submit { - Sentry.setCurrentScopes(threadedHub) + Sentry.setCurrentScopes(threadedScopes) }.get() - assertEquals(mainHub, Sentry.getCurrentScopes()) + assertEquals(mainScopes, Sentry.getCurrentScopes()) val callableFuture = executor.submit( sut.decorate { - assertNotEquals(mainHub, Sentry.getCurrentScopes()) - assertNotEquals(threadedHub, Sentry.getCurrentScopes()) + assertNotEquals(mainScopes, Sentry.getCurrentScopes()) + assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) } ) callableFuture.get() executor.submit { - assertNotEquals(mainHub, Sentry.getCurrentScopes()) - assertEquals(threadedHub, Sentry.getCurrentScopes()) + assertNotEquals(mainScopes, Sentry.getCurrentScopes()) + assertEquals(threadedScopes, Sentry.getCurrentScopes()) }.get() } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt index f53acde8aa..8a3d8ee46c 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt @@ -1,6 +1,7 @@ package io.sentry.spring.tracing import io.sentry.IScopes +import io.sentry.ISentryLifecycleToken import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -46,6 +47,8 @@ class SentryTransactionAdviceTest { @Autowired lateinit var scopes: IScopes + val lifecycleToken = mock() + @BeforeTest fun setup() { reset(scopes) @@ -55,6 +58,7 @@ class SentryTransactionAdviceTest { dsn = "https://key@sentry.io/proj" } ) + whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) } @Test @@ -141,13 +145,13 @@ class SentryTransactionAdviceTest { @Test fun `pushes the scope when advice starts`() { classAnnotatedSampleService.hello() - verify(scopes).pushScope() + verify(scopes).pushIsolationScope() } @Test fun `pops the scope when advice finishes`() { classAnnotatedSampleService.hello() - verify(scopes).popScope() + verify(lifecycleToken).close() } @Configuration diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryScheduleHookTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryScheduleHookTest.kt index 7a8b2993f9..88c33c3695 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryScheduleHookTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryScheduleHookTest.kt @@ -26,35 +26,35 @@ class SentryScheduleHookTest { } @Test - fun `hub is reset to its state within the thread after hook is done`() { + fun `scopes is reset to its state within the thread after hook is done`() { Sentry.init { it.dsn = dsn } val sut = SentryScheduleHook() - val mainHub = Sentry.getCurrentScopes() - val threadedHub = Sentry.getCurrentScopes().clone() + val mainScopes = Sentry.getCurrentScopes() + val threadedScopes = Sentry.getCurrentScopes().forkedCurrentScope("test") executor.submit { - Sentry.setCurrentHub(threadedHub) + Sentry.setCurrentScopes(threadedScopes) }.get() - assertEquals(mainHub, Sentry.getCurrentScopes()) + assertEquals(mainScopes, Sentry.getCurrentScopes()) val callableFuture = executor.submit( sut.apply { - assertNotEquals(mainHub, Sentry.getCurrentScopes()) - assertNotEquals(threadedHub, Sentry.getCurrentScopes()) + assertNotEquals(mainScopes, Sentry.getCurrentScopes()) + assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) } ) callableFuture.get() executor.submit { - assertNotEquals(mainHub, Sentry.getCurrentScopes()) - assertEquals(threadedHub, Sentry.getCurrentScopes()) + assertNotEquals(mainScopes, Sentry.getCurrentScopes()) + assertEquals(threadedScopes, Sentry.getCurrentScopes()) }.get() } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt index 1a31dcaa10..ff527abd7d 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt @@ -89,7 +89,7 @@ class SentryWebFluxTracingFilterTest { fun withMockScopes(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { it.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(fixture.scopes)) it.`when` { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes) - it.`when` { Sentry.cloneMainHub() }.thenReturn(fixture.scopes) + it.`when` { Sentry.forkedRootScopes(any()) }.thenReturn(fixture.scopes) closure.invoke() } @@ -253,10 +253,8 @@ class SentryWebFluxTracingFilterTest { verify(fixture.scopes).isEnabled verify(fixture.scopes, times(2)).options verify(fixture.scopes).continueTrace(anyOrNull(), anyOrNull()) - verify(fixture.scopes).pushScope() // TODO don't verify(fixture.scopes).addBreadcrumb(any(), any()) verify(fixture.scopes).configureScope(any()) - verify(fixture.scopes).popScope() // TODO don't verifyNoMoreInteractions(fixture.scopes) } } From 153f6781cd26f9be5aa90b1cb183a418dd6cfe09 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 22 Apr 2024 15:26:21 +0200 Subject: [PATCH 25/91] Hubs/Scopes Merge 25 - Use new API in Servlet integrations (#3349) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations --- .../api/sentry-servlet-jakarta.api | 1 + .../jakarta/SentryServletRequestListener.java | 10 ++++++++-- .../jakarta/SentryServletRequestListenerTest.kt | 13 ++++++++++--- sentry-servlet/api/sentry-servlet.api | 1 + .../servlet/SentryServletRequestListener.java | 10 ++++++++-- .../servlet/SentryServletRequestListenerTest.kt | 12 +++++++++--- 6 files changed, 37 insertions(+), 10 deletions(-) diff --git a/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api b/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api index a5421e7453..adde86fda5 100644 --- a/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api +++ b/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api @@ -9,6 +9,7 @@ public class io/sentry/servlet/jakarta/SentryServletContainerInitializer : jakar } public class io/sentry/servlet/jakarta/SentryServletRequestListener : jakarta/servlet/ServletRequestListener { + public static final field SENTRY_LIFECYCLE_TOKEN_KEY Ljava/lang/String; public fun ()V public fun (Lio/sentry/IScopes;)V public fun requestDestroyed (Ljakarta/servlet/ServletRequestEvent;)V diff --git a/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java b/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java index 54775386fd..f5b3be30b9 100644 --- a/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java +++ b/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java @@ -6,7 +6,9 @@ import io.sentry.Breadcrumb; import io.sentry.Hint; import io.sentry.IScopes; +import io.sentry.ISentryLifecycleToken; import io.sentry.ScopesAdapter; +import io.sentry.util.LifecycleHelper; import io.sentry.util.Objects; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletRequestEvent; @@ -21,6 +23,8 @@ @Open public class SentryServletRequestListener implements ServletRequestListener { + public static final String SENTRY_LIFECYCLE_TOKEN_KEY = "sentry-lifecycle"; + private final IScopes scopes; public SentryServletRequestListener(@NotNull IScopes scopes) { @@ -33,14 +37,16 @@ public SentryServletRequestListener() { @Override public void requestDestroyed(@NotNull ServletRequestEvent servletRequestEvent) { - scopes.popScope(); + final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); + LifecycleHelper.close(servletRequest.getAttribute(SENTRY_LIFECYCLE_TOKEN_KEY)); } @Override public void requestInitialized(@NotNull ServletRequestEvent servletRequestEvent) { - scopes.pushScope(); + final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); + servletRequest.setAttribute(SENTRY_LIFECYCLE_TOKEN_KEY, lifecycleToken); if (servletRequest instanceof HttpServletRequest) { final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; diff --git a/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt b/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt index 3be76d1cd2..30ef3da1ed 100644 --- a/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt +++ b/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt @@ -2,10 +2,13 @@ package io.sentry.servlet.jakarta import io.sentry.Breadcrumb import io.sentry.IScopes +import io.sentry.ISentryLifecycleToken import jakarta.servlet.ServletRequestEvent import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.check +import org.mockito.kotlin.eq import org.mockito.kotlin.mock +import org.mockito.kotlin.same import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import kotlin.test.Test @@ -14,6 +17,7 @@ import kotlin.test.assertEquals class SentryServletRequestListenerTest { private class Fixture { val scopes = mock() + val lifecycleToken = mock() val listener = SentryServletRequestListener(scopes) val request = mockRequest( @@ -24,6 +28,7 @@ class SentryServletRequestListenerTest { init { whenever(event.servletRequest).thenReturn(request) + whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) } } @@ -33,7 +38,7 @@ class SentryServletRequestListenerTest { fun `pushes scope when request gets initialized`() { fixture.listener.requestInitialized(fixture.event) - verify(fixture.scopes).pushScope() + verify(fixture.scopes).pushIsolationScope() } @Test @@ -48,12 +53,14 @@ class SentryServletRequestListenerTest { }, anyOrNull() ) + verify(fixture.request).setAttribute(eq("sentry-lifecycle"), same(fixture.lifecycleToken)) } @Test fun `pops scope when request gets destroyed`() { - fixture.listener.requestDestroyed(fixture.event) + whenever(fixture.request.getAttribute(eq("sentry-lifecycle"))).thenReturn(fixture.lifecycleToken) - verify(fixture.scopes).popScope() + fixture.listener.requestDestroyed(fixture.event) + verify(fixture.lifecycleToken).close() } } diff --git a/sentry-servlet/api/sentry-servlet.api b/sentry-servlet/api/sentry-servlet.api index fd7aee819b..63d3cf4b33 100644 --- a/sentry-servlet/api/sentry-servlet.api +++ b/sentry-servlet/api/sentry-servlet.api @@ -9,6 +9,7 @@ public class io/sentry/servlet/SentryServletContainerInitializer : javax/servlet } public class io/sentry/servlet/SentryServletRequestListener : javax/servlet/ServletRequestListener { + public static final field SENTRY_LIFECYCLE_TOKEN_KEY Ljava/lang/String; public fun ()V public fun (Lio/sentry/IScopes;)V public fun requestDestroyed (Ljavax/servlet/ServletRequestEvent;)V diff --git a/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java b/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java index 97c37e1133..4587daa655 100644 --- a/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java +++ b/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java @@ -6,7 +6,9 @@ import io.sentry.Breadcrumb; import io.sentry.Hint; import io.sentry.IScopes; +import io.sentry.ISentryLifecycleToken; import io.sentry.ScopesAdapter; +import io.sentry.util.LifecycleHelper; import io.sentry.util.Objects; import javax.servlet.ServletRequest; import javax.servlet.ServletRequestEvent; @@ -21,6 +23,8 @@ @Open public class SentryServletRequestListener implements ServletRequestListener { + public static final String SENTRY_LIFECYCLE_TOKEN_KEY = "sentry-lifecycle"; + private final IScopes scopes; public SentryServletRequestListener(@NotNull IScopes scopes) { @@ -33,14 +37,16 @@ public SentryServletRequestListener() { @Override public void requestDestroyed(@NotNull ServletRequestEvent servletRequestEvent) { - scopes.popScope(); + final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); + LifecycleHelper.close(servletRequest.getAttribute(SENTRY_LIFECYCLE_TOKEN_KEY)); } @Override public void requestInitialized(@NotNull ServletRequestEvent servletRequestEvent) { - scopes.pushScope(); + final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); + servletRequest.setAttribute(SENTRY_LIFECYCLE_TOKEN_KEY, lifecycleToken); if (servletRequest instanceof HttpServletRequest) { final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; diff --git a/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt b/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt index bfa216f738..d72b93179f 100644 --- a/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt +++ b/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt @@ -2,6 +2,7 @@ package io.sentry.servlet import io.sentry.Breadcrumb import io.sentry.IScopes +import io.sentry.ISentryLifecycleToken import org.assertj.core.api.Assertions.assertThat import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.check @@ -11,10 +12,12 @@ import org.mockito.kotlin.whenever import org.springframework.mock.web.MockHttpServletRequest import javax.servlet.ServletRequestEvent import kotlin.test.Test +import kotlin.test.assertSame class SentryServletRequestListenerTest { private class Fixture { val scopes = mock() + val lifecycleToken = mock() val listener = SentryServletRequestListener(scopes) val request = MockHttpServletRequest() val event = mock() @@ -23,6 +26,7 @@ class SentryServletRequestListenerTest { request.requestURI = "http://localhost:8080/some-uri" request.method = "post" whenever(event.servletRequest).thenReturn(request) + whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) } } @@ -32,7 +36,7 @@ class SentryServletRequestListenerTest { fun `pushes scope when request gets initialized`() { fixture.listener.requestInitialized(fixture.event) - verify(fixture.scopes).pushScope() + verify(fixture.scopes).pushIsolationScope() } @Test @@ -47,12 +51,14 @@ class SentryServletRequestListenerTest { }, anyOrNull() ) + assertSame(fixture.lifecycleToken, fixture.request.getAttribute("sentry-lifecycle")) } @Test fun `pops scope when request gets destroyed`() { - fixture.listener.requestDestroyed(fixture.event) + fixture.request.setAttribute("sentry-lifecycle", fixture.lifecycleToken) - verify(fixture.scopes).popScope() + fixture.listener.requestDestroyed(fixture.event) + verify(fixture.lifecycleToken).close() } } From e0cb935f0d4f2f9c0733b63c19af3c2f28135169 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 22 Apr 2024 16:17:31 +0200 Subject: [PATCH 26/91] Hubs/Scopes Merge 26 - Use new API for Kotlin coroutines and SentryWrapper (#3351) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable --- .../java/io/sentry/kotlin/SentryContext.kt | 10 +- .../io/sentry/kotlin/SentryContextTest.kt | 138 +++++++++++-- .../main/java/io/sentry/SentryWrapper.java | 40 ++-- .../test/java/io/sentry/SentryWrapperTest.kt | 187 +++++++++++++++++- 4 files changed, 329 insertions(+), 46 deletions(-) diff --git a/sentry-kotlin-extensions/src/main/java/io/sentry/kotlin/SentryContext.kt b/sentry-kotlin-extensions/src/main/java/io/sentry/kotlin/SentryContext.kt index 4c814f2805..a77281a033 100644 --- a/sentry-kotlin-extensions/src/main/java/io/sentry/kotlin/SentryContext.kt +++ b/sentry-kotlin-extensions/src/main/java/io/sentry/kotlin/SentryContext.kt @@ -9,23 +9,19 @@ import kotlin.coroutines.CoroutineContext /** * Sentry context element for [CoroutineContext]. */ -@SuppressWarnings("deprecation") -// TODO fork instead -public class SentryContext(private val scopes: IScopes = Sentry.getCurrentScopes().clone()) : +public class SentryContext(private val scopes: IScopes = Sentry.forkedCurrentScope("coroutine")) : CopyableThreadContextElement, AbstractCoroutineContextElement(Key) { private companion object Key : CoroutineContext.Key @SuppressWarnings("deprecation") override fun copyForChild(): CopyableThreadContextElement { - // TODO fork instead - return SentryContext(scopes.clone()) + return SentryContext(scopes.forkedCurrentScope("coroutine.child")) } @SuppressWarnings("deprecation") override fun mergeForChild(overwritingElement: CoroutineContext.Element): CoroutineContext { - // TODO fork instead? - return overwritingElement[Key] ?: SentryContext(scopes.clone()) + return overwritingElement[Key] ?: SentryContext(scopes.forkedCurrentScope("coroutine.child")) } override fun updateThreadContext(context: CoroutineContext): IScopes { diff --git a/sentry-kotlin-extensions/src/test/java/io/sentry/kotlin/SentryContextTest.kt b/sentry-kotlin-extensions/src/test/java/io/sentry/kotlin/SentryContextTest.kt index 578b610267..bd498846dd 100644 --- a/sentry-kotlin-extensions/src/test/java/io/sentry/kotlin/SentryContextTest.kt +++ b/sentry-kotlin-extensions/src/test/java/io/sentry/kotlin/SentryContextTest.kt @@ -1,5 +1,6 @@ package io.sentry.kotlin +import io.sentry.ScopeType import io.sentry.Sentry import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.joinAll @@ -38,11 +39,40 @@ class SentryContextTest { Sentry.setTag("c2", "c2value") assertEquals("c2value", getTag("c2")) assertEquals("parentValue", getTag("parent")) - assertNull(getTag("c1")) + assertNotNull(getTag("c1")) + } + listOf(c1, c2).joinAll() + assertNotNull(getTag("parent")) + assertNotNull(getTag("c1")) + assertNotNull(getTag("c2")) + return@runBlocking + } + + @Test + fun testContextIsNotPassedByDefaultBetweenCoroutinesCurrentScope() = runBlocking { + Sentry.configureScope(ScopeType.CURRENT) { scope -> + scope.setTag("parent", "parentValue") + } + val c1 = launch(SentryContext()) { + Sentry.configureScope(ScopeType.CURRENT) { scope -> + scope.setTag("c1", "c1value") + } + assertEquals("c1value", getTag("c1", ScopeType.CURRENT)) + assertEquals("parentValue", getTag("parent", ScopeType.CURRENT)) + assertNull(getTag("c2", ScopeType.CURRENT)) + } + val c2 = launch(SentryContext()) { + Sentry.configureScope(ScopeType.CURRENT) { scope -> + scope.setTag("c2", "c2value") + } + assertEquals("c2value", getTag("c2", ScopeType.CURRENT)) + assertEquals("parentValue", getTag("parent", ScopeType.CURRENT)) + assertNull(getTag("c1", ScopeType.CURRENT)) } listOf(c1, c2).joinAll() - assertNull(getTag("c1")) - assertNull(getTag("c2")) + assertNotNull(getTag("parent", ScopeType.CURRENT)) + assertNull(getTag("c1", ScopeType.CURRENT)) + assertNull(getTag("c2", ScopeType.CURRENT)) } @Test @@ -84,7 +114,7 @@ class SentryContextTest { } @Test - fun testContextIsClonedWhenPassedToChild() = runBlocking { + fun testContextIsClonedWhenPassedToChildCurrentScope() = runBlocking { Sentry.setTag("parent", "parentValue") launch(SentryContext()) { Sentry.setTag("c1", "c1value") @@ -102,10 +132,44 @@ class SentryContextTest { c2.join() assertNotNull(getTag("c1")) - assertNull(getTag("c2")) + assertNotNull(getTag("c2")) + }.join() + assertNotNull(getTag("parent")) + assertNotNull(getTag("c1")) + assertNotNull(getTag("c2")) + return@runBlocking + } + + @Test + fun testContextIsClonedWhenPassedToChild() = runBlocking { + Sentry.configureScope(ScopeType.CURRENT) { scope -> + scope.setTag("parent", "parentValue") } - assertNull(getTag("c1")) - assertNull(getTag("c2")) + launch(SentryContext()) { + Sentry.configureScope(ScopeType.CURRENT) { scope -> + scope.setTag("c1", "c1value") + } + assertEquals("c1value", getTag("c1", ScopeType.CURRENT)) + assertEquals("parentValue", getTag("parent", ScopeType.CURRENT)) + assertNull(getTag("c2", ScopeType.CURRENT)) + + val c2 = launch() { + Sentry.configureScope(ScopeType.CURRENT) { scope -> + scope.setTag("c2", "c2value") + } + assertEquals("c2value", getTag("c2", ScopeType.CURRENT)) + assertEquals("parentValue", getTag("parent", ScopeType.CURRENT)) + assertNotNull(getTag("c1", ScopeType.CURRENT)) + } + + c2.join() + + assertNotNull(getTag("c1", ScopeType.CURRENT)) + assertNull(getTag("c2", ScopeType.CURRENT)) + }.join() + assertNotNull(getTag("parent", ScopeType.CURRENT)) + assertNull(getTag("c1", ScopeType.CURRENT)) + assertNull(getTag("c2", ScopeType.CURRENT)) } @Test @@ -120,7 +184,7 @@ class SentryContextTest { val c2 = launch( SentryContext( Sentry.getCurrentScopes().clone().also { - it.setTag("cloned", "clonedValue") + Sentry.setTag("cloned", "clonedValue") } ) ) { @@ -134,12 +198,56 @@ class SentryContextTest { c2.join() assertNotNull(getTag("c1")) - assertNull(getTag("c2")) - assertNull(getTag("cloned")) + assertNotNull(getTag("c2")) + assertNotNull(getTag("cloned")) + }.join() + + assertNotNull(getTag("c1")) + assertNotNull(getTag("c2")) + assertNotNull(getTag("cloned")) + return@runBlocking + } + + @Test + fun testExplicitlyPassedContextOverridesPropagatedContextCurrentScope() = runBlocking { + Sentry.configureScope(ScopeType.CURRENT) { scope -> + scope.setTag("parent", "parentValue") + } + launch(SentryContext()) { + Sentry.configureScope(ScopeType.CURRENT) { scope -> + scope.setTag("c1", "c1value") + } + assertEquals("c1value", getTag("c1", ScopeType.CURRENT)) + assertEquals("parentValue", getTag("parent", ScopeType.CURRENT)) + assertNull(getTag("c2", ScopeType.CURRENT)) + + val c2 = launch( + SentryContext( + Sentry.getCurrentScopes().clone().also { + it.configureScope(ScopeType.CURRENT) { scope -> + scope.setTag("cloned", "clonedValue") + } + } + ) + ) { + Sentry.configureScope(ScopeType.CURRENT) { scope -> + scope.setTag("c2", "c2value") + } + assertEquals("c2value", getTag("c2", ScopeType.CURRENT)) + assertEquals("parentValue", getTag("parent", ScopeType.CURRENT)) + assertNotNull(getTag("c1", ScopeType.CURRENT)) + assertNotNull(getTag("cloned", ScopeType.CURRENT)) + } + + c2.join() + + assertNotNull(getTag("c1", ScopeType.CURRENT)) + assertNull(getTag("c2", ScopeType.CURRENT)) + assertNull(getTag("cloned", ScopeType.CURRENT)) } - assertNull(getTag("c1")) - assertNull(getTag("c2")) - assertNull(getTag("cloned")) + assertNull(getTag("c1", ScopeType.CURRENT)) + assertNull(getTag("c2", ScopeType.CURRENT)) + assertNull(getTag("cloned", ScopeType.CURRENT)) } @Test @@ -167,9 +275,9 @@ class SentryContextTest { assertEquals(initialContextElement, mergedContextElement) } - private fun getTag(tag: String): String? { + private fun getTag(tag: String, scopeType: ScopeType = ScopeType.ISOLATION): String? { var value: String? = null - Sentry.configureScope { + Sentry.configureScope(scopeType) { value = it.tags[tag] } return value diff --git a/sentry/src/main/java/io/sentry/SentryWrapper.java b/sentry/src/main/java/io/sentry/SentryWrapper.java index 165ace7c83..d0f2cd8017 100644 --- a/sentry/src/main/java/io/sentry/SentryWrapper.java +++ b/sentry/src/main/java/io/sentry/SentryWrapper.java @@ -27,18 +27,23 @@ public final class SentryWrapper { * @return the wrapped {@link Callable} * @param - the result type of the {@link Callable} */ - @SuppressWarnings("deprecation") + // TODO adapt javadoc public static Callable wrapCallable(final @NotNull Callable callable) { - // TODO replace with forking - final IScopes newHub = Sentry.getCurrentScopes().clone(); + final IScopes newScopes = Sentry.getCurrentScopes().forkedCurrentScope("wrapCallable"); + + return () -> { + try (ISentryLifecycleToken ignored = newScopes.makeCurrent()) { + return callable.call(); + } + }; + } + + public static Callable wrapCallableIsolated(final @NotNull Callable callable) { + final IScopes newScopes = Sentry.getCurrentScopes().forkedScopes("wrapCallable"); return () -> { - final IScopes oldState = Sentry.getCurrentScopes(); - Sentry.setCurrentScopes(newHub); - try { + try (ISentryLifecycleToken ignored = newScopes.makeCurrent()) { return callable.call(); - } finally { - Sentry.setCurrentScopes(oldState); } }; } @@ -55,16 +60,21 @@ public static Callable wrapCallable(final @NotNull Callable callable) */ @SuppressWarnings("deprecation") public static Supplier wrapSupplier(final @NotNull Supplier supplier) { - // TODO replace with forking - final IScopes newHub = Sentry.getCurrentScopes().clone(); + final IScopes newScopes = Sentry.forkedCurrentScope("wrapSupplier"); + + return () -> { + try (ISentryLifecycleToken ignore = newScopes.makeCurrent()) { + return supplier.get(); + } + }; + } + + public static Supplier wrapSupplierIsolated(final @NotNull Supplier supplier) { + final IScopes newScopes = Sentry.forkedScopes("wrapSupplier"); return () -> { - final IScopes oldState = Sentry.getCurrentScopes(); - Sentry.setCurrentScopes(newHub); - try { + try (ISentryLifecycleToken ignore = newScopes.makeCurrent()) { return supplier.get(); - } finally { - Sentry.setCurrentScopes(oldState); } }; } diff --git a/sentry/src/test/java/io/sentry/SentryWrapperTest.kt b/sentry/src/test/java/io/sentry/SentryWrapperTest.kt index 2fb9b38566..a3511450f0 100644 --- a/sentry/src/test/java/io/sentry/SentryWrapperTest.kt +++ b/sentry/src/test/java/io/sentry/SentryWrapperTest.kt @@ -36,7 +36,7 @@ class SentryWrapperTest { } val mainHub = Sentry.getCurrentScopes() - val threadedHub = Sentry.getCurrentScopes().clone() + val threadedHub = mainHub.forkedCurrentScope("test") executor.submit { Sentry.setCurrentScopes(threadedHub) @@ -46,7 +46,7 @@ class SentryWrapperTest { val callableFuture = CompletableFuture.supplyAsync( - SentryWrapper.wrapSupplier { + SentryWrapper.wrapSupplierIsolated { assertNotEquals(mainHub, Sentry.getCurrentScopes()) assertNotEquals(threadedHub, Sentry.getCurrentScopes()) "Result 1" @@ -63,7 +63,7 @@ class SentryWrapperTest { } @Test - fun `wrapped supply async isolates Hubs`() { + fun `wrapped supply async does not isolate Scopes`() { val capturedEvents = mutableListOf() Sentry.init { @@ -110,12 +110,12 @@ class SentryWrapperTest { val clonedEvent2 = capturedEvents.firstOrNull { it.message?.formatted == "ClonedMessage2" } assertEquals(2, mainEvent?.breadcrumbs?.size) - assertEquals(2, clonedEvent?.breadcrumbs?.size) - assertEquals(2, clonedEvent2?.breadcrumbs?.size) + assertEquals(3, clonedEvent?.breadcrumbs?.size) + assertEquals(4, clonedEvent2?.breadcrumbs?.size) } @Test - fun `wrapped callable isolates Hubs`() { + fun `wrapped callable does not isolate Scopes`() { val capturedEvents = mutableListOf() Sentry.init { @@ -159,8 +159,8 @@ class SentryWrapperTest { val clonedEvent2 = capturedEvents.firstOrNull { it.message?.formatted == "ClonedMessage2" } assertEquals(2, mainEvent?.breadcrumbs?.size) - assertEquals(2, clonedEvent?.breadcrumbs?.size) - assertEquals(2, clonedEvent2?.breadcrumbs?.size) + assertEquals(3, clonedEvent?.breadcrumbs?.size) + assertEquals(4, clonedEvent2?.breadcrumbs?.size) } @Test @@ -170,7 +170,7 @@ class SentryWrapperTest { } val mainHub = Sentry.getCurrentScopes() - val threadedHub = Sentry.getCurrentScopes().clone() + val threadedHub = Sentry.getCurrentScopes().forkedCurrentScope("test") executor.submit { Sentry.setCurrentScopes(threadedHub) @@ -194,4 +194,173 @@ class SentryWrapperTest { assertEquals(threadedHub, Sentry.getCurrentScopes()) }.get() } + + @Test + fun `scopes is reset to its state within the thread after isolated supply is done`() { + Sentry.init { + it.dsn = dsn + it.beforeSend = SentryOptions.BeforeSendCallback { event, hint -> + event + } + } + + val mainHub = Sentry.getCurrentScopes() + val threadedHub = Sentry.getCurrentScopes().forkedCurrentScope("test") + + executor.submit { + Sentry.setCurrentScopes(threadedHub) + }.get() + + assertEquals(mainHub, Sentry.getCurrentScopes()) + + val callableFuture = + CompletableFuture.supplyAsync( + SentryWrapper.wrapSupplierIsolated { + assertNotEquals(mainHub, Sentry.getCurrentScopes()) + assertNotEquals(threadedHub, Sentry.getCurrentScopes()) + "Result 1" + }, + executor + ) + + callableFuture.join() + + executor.submit { + assertNotEquals(mainHub, Sentry.getCurrentScopes()) + assertEquals(threadedHub, Sentry.getCurrentScopes()) + }.get() + } + + @Test + fun `wrapped supply async isolates Scopes`() { + val capturedEvents = mutableListOf() + + Sentry.init { + it.dsn = dsn + it.beforeSend = SentryOptions.BeforeSendCallback { event, hint -> + capturedEvents.add(event) + event + } + } + + Sentry.addBreadcrumb("MyOriginalBreadcrumbBefore") + Sentry.captureMessage("OriginalMessageBefore") + + val callableFuture = + CompletableFuture.supplyAsync( + SentryWrapper.wrapSupplierIsolated { + Thread.sleep(20) + Sentry.addBreadcrumb("MyClonedBreadcrumb") + Sentry.captureMessage("ClonedMessage") + "Result 1" + }, + executor + ) + + val callableFuture2 = + CompletableFuture.supplyAsync( + SentryWrapper.wrapSupplierIsolated { + Thread.sleep(10) + Sentry.addBreadcrumb("MyClonedBreadcrumb2") + Sentry.captureMessage("ClonedMessage2") + "Result 2" + }, + executor + ) + + Sentry.addBreadcrumb("MyOriginalBreadcrumb") + Sentry.captureMessage("OriginalMessage") + + callableFuture.join() + callableFuture2.join() + + val mainEvent = capturedEvents.firstOrNull { it.message?.formatted == "OriginalMessage" } + val clonedEvent = capturedEvents.firstOrNull { it.message?.formatted == "ClonedMessage" } + val clonedEvent2 = capturedEvents.firstOrNull { it.message?.formatted == "ClonedMessage2" } + + assertEquals(2, mainEvent?.breadcrumbs?.size) + assertEquals(2, clonedEvent?.breadcrumbs?.size) + assertEquals(2, clonedEvent2?.breadcrumbs?.size) + } + + @Test + fun `wrapped callable isolates Scopes`() { + val capturedEvents = mutableListOf() + + Sentry.init { + it.dsn = dsn + it.beforeSend = SentryOptions.BeforeSendCallback { event, hint -> + capturedEvents.add(event) + event + } + } + + Sentry.addBreadcrumb("MyOriginalBreadcrumbBefore") + Sentry.captureMessage("OriginalMessageBefore") + println(Thread.currentThread().name) + + val future1 = executor.submit( + SentryWrapper.wrapCallableIsolated { + Thread.sleep(20) + Sentry.addBreadcrumb("MyClonedBreadcrumb") + Sentry.captureMessage("ClonedMessage") + "Result 1" + } + ) + + val future2 = executor.submit( + SentryWrapper.wrapCallableIsolated { + Thread.sleep(10) + Sentry.addBreadcrumb("MyClonedBreadcrumb2") + Sentry.captureMessage("ClonedMessage2") + "Result 2" + } + ) + + Sentry.addBreadcrumb("MyOriginalBreadcrumb") + Sentry.captureMessage("OriginalMessage") + + future1.get() + future2.get() + + val mainEvent = capturedEvents.firstOrNull { it.message?.formatted == "OriginalMessage" } + val clonedEvent = capturedEvents.firstOrNull { it.message?.formatted == "ClonedMessage" } + val clonedEvent2 = capturedEvents.firstOrNull { it.message?.formatted == "ClonedMessage2" } + + assertEquals(2, mainEvent?.breadcrumbs?.size) + assertEquals(2, clonedEvent?.breadcrumbs?.size) + assertEquals(2, clonedEvent2?.breadcrumbs?.size) + } + + @Test + fun `scopes is reset to its state within the thread after isolated callable is done`() { + Sentry.init { + it.dsn = dsn + } + + val mainHub = Sentry.getCurrentScopes() + val threadedHub = Sentry.getCurrentScopes().forkedCurrentScope("test") + + executor.submit { + Sentry.setCurrentScopes(threadedHub) + }.get() + + assertEquals(mainHub, Sentry.getCurrentScopes()) + + val callableFuture = + executor.submit( + SentryWrapper.wrapCallableIsolated { + assertNotEquals(mainHub, Sentry.getCurrentScopes()) + assertNotEquals(threadedHub, Sentry.getCurrentScopes()) + "Result 1" + } + ) + + callableFuture.get() + + executor.submit { + assertNotEquals(mainHub, Sentry.getCurrentScopes()) + assertEquals(threadedHub, Sentry.getCurrentScopes()) + }.get() + } } From a3ba20a80772a4f0185188adb4d71f90371c31ee Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 22 Apr 2024 16:24:09 +0200 Subject: [PATCH 27/91] Hubs/Scopes Merge 27 - Discussions (#3352) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs --- build.gradle.kts | 2 +- sentry/api/sentry.api | 179 ++++++++++++++++-- .../src/main/java/io/sentry/Breadcrumb.java | 1 + .../java/io/sentry/CombinedScopeView.java | 4 + sentry/src/main/java/io/sentry/ScopeType.java | 5 +- sentry/src/main/java/io/sentry/Scopes.java | 2 + 6 files changed, 177 insertions(+), 16 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 42acafadb1..acb1fba051 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -99,7 +99,7 @@ allprojects { dependsOn("cleanTest") } withType { - options.compilerArgs.addAll(arrayOf("-Xlint:all", "-Werror", "-Xlint:-classfile", "-Xlint:-processing")) + options.compilerArgs.addAll(arrayOf("-Xlint:all", "-Werror", "-Xlint:-classfile", "-Xlint:-processing", "-Xlint:-try")) } } } diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 8ba2f393d8..e6525cee31 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -93,10 +93,12 @@ public final class io/sentry/BaggageHeader { public fun getValue ()Ljava/lang/String; } -public final class io/sentry/Breadcrumb : io/sentry/JsonSerializable, io/sentry/JsonUnknown { +public final class io/sentry/Breadcrumb : io/sentry/JsonSerializable, io/sentry/JsonUnknown, java/lang/Comparable { public fun ()V public fun (Ljava/lang/String;)V public fun (Ljava/util/Date;)V + public fun compareTo (Lio/sentry/Breadcrumb;)I + public synthetic fun compareTo (Ljava/lang/Object;)I public static fun debug (Ljava/lang/String;)Lio/sentry/Breadcrumb; public fun equals (Ljava/lang/Object;)Z public static fun error (Ljava/lang/String;)Lio/sentry/Breadcrumb; @@ -206,6 +208,90 @@ public final class io/sentry/CheckInStatus : java/lang/Enum { public static fun values ()[Lio/sentry/CheckInStatus; } +public final class io/sentry/CombinedContextsView : io/sentry/protocol/Contexts { + public fun (Lio/sentry/protocol/Contexts;Lio/sentry/protocol/Contexts;Lio/sentry/protocol/Contexts;Lio/sentry/ScopeType;)V + public fun getApp ()Lio/sentry/protocol/App; + public fun getBrowser ()Lio/sentry/protocol/Browser; + public fun getDevice ()Lio/sentry/protocol/Device; + public fun getGpu ()Lio/sentry/protocol/Gpu; + public fun getOperatingSystem ()Lio/sentry/protocol/OperatingSystem; + public fun getResponse ()Lio/sentry/protocol/Response; + public fun getRuntime ()Lio/sentry/protocol/SentryRuntime; + public fun getTrace ()Lio/sentry/SpanContext; + public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V + public fun setApp (Lio/sentry/protocol/App;)V + public fun setBrowser (Lio/sentry/protocol/Browser;)V + public fun setDevice (Lio/sentry/protocol/Device;)V + public fun setGpu (Lio/sentry/protocol/Gpu;)V + public fun setOperatingSystem (Lio/sentry/protocol/OperatingSystem;)V + public fun setResponse (Lio/sentry/protocol/Response;)V + public fun setRuntime (Lio/sentry/protocol/SentryRuntime;)V + public fun setTrace (Lio/sentry/SpanContext;)V + public fun withResponse (Lio/sentry/util/HintUtils$SentryConsumer;)V +} + +public final class io/sentry/CombinedScopeView : io/sentry/IScope { + public fun (Lio/sentry/IScope;Lio/sentry/IScope;Lio/sentry/IScope;)V + public fun addAttachment (Lio/sentry/Attachment;)V + public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V + public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V + public fun addEventProcessor (Lio/sentry/EventProcessor;)V + public fun assignTraceContext (Lio/sentry/SentryEvent;)V + public fun bindClient (Lio/sentry/ISentryClient;)V + public fun clear ()V + public fun clearAttachments ()V + public fun clearBreadcrumbs ()V + public fun clearTransaction ()V + public fun clone ()Lio/sentry/IScope; + public synthetic fun clone ()Ljava/lang/Object; + public fun endSession ()Lio/sentry/Session; + public fun getAttachments ()Ljava/util/List; + public fun getBreadcrumbs ()Ljava/util/Queue; + public fun getClient ()Lio/sentry/ISentryClient; + public fun getContexts ()Lio/sentry/protocol/Contexts; + public fun getEventProcessors ()Ljava/util/List; + public fun getExtras ()Ljava/util/Map; + public fun getFingerprint ()Ljava/util/List; + public fun getLastEventId ()Lio/sentry/protocol/SentryId; + public fun getLevel ()Lio/sentry/SentryLevel; + public fun getOptions ()Lio/sentry/SentryOptions; + public fun getPropagationContext ()Lio/sentry/PropagationContext; + public fun getRequest ()Lio/sentry/protocol/Request; + public fun getScreen ()Ljava/lang/String; + public fun getSession ()Lio/sentry/Session; + public fun getSpan ()Lio/sentry/ISpan; + public fun getTags ()Ljava/util/Map; + public fun getTransaction ()Lio/sentry/ITransaction; + public fun getTransactionName ()Ljava/lang/String; + public fun getUser ()Lio/sentry/protocol/User; + public fun removeContexts (Ljava/lang/String;)V + public fun removeExtra (Ljava/lang/String;)V + public fun removeTag (Ljava/lang/String;)V + public fun setContexts (Ljava/lang/String;Ljava/lang/Boolean;)V + public fun setContexts (Ljava/lang/String;Ljava/lang/Character;)V + public fun setContexts (Ljava/lang/String;Ljava/lang/Number;)V + public fun setContexts (Ljava/lang/String;Ljava/lang/Object;)V + public fun setContexts (Ljava/lang/String;Ljava/lang/String;)V + public fun setContexts (Ljava/lang/String;Ljava/util/Collection;)V + public fun setContexts (Ljava/lang/String;[Ljava/lang/Object;)V + public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V + public fun setFingerprint (Ljava/util/List;)V + public fun setLastEventId (Lio/sentry/protocol/SentryId;)V + public fun setLevel (Lio/sentry/SentryLevel;)V + public fun setPropagationContext (Lio/sentry/PropagationContext;)V + public fun setRequest (Lio/sentry/protocol/Request;)V + public fun setScreen (Ljava/lang/String;)V + public fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V + public fun setTag (Ljava/lang/String;Ljava/lang/String;)V + public fun setTransaction (Lio/sentry/ITransaction;)V + public fun setTransaction (Ljava/lang/String;)V + public fun setUser (Lio/sentry/protocol/User;)V + public fun startSession ()Lio/sentry/Scope$SessionPair; + public fun withPropagationContext (Lio/sentry/Scope$IWithPropagationContext;)Lio/sentry/PropagationContext; + public fun withSession (Lio/sentry/Scope$IWithSession;)Lio/sentry/Session; + public fun withTransaction (Lio/sentry/Scope$IWithTransaction;)V +} + public final class io/sentry/CpuCollectionData { public fun (JD)V public fun getCpuUsagePercentage ()D @@ -437,25 +523,31 @@ public final class io/sentry/Hub : io/sentry/IHub, io/sentry/metrics/MetricsApi$ public synthetic fun clone ()Ljava/lang/Object; public fun close ()V public fun close (Z)V - public fun configureScope (Lio/sentry/ScopeCallback;)V + public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public fun endSession ()V public fun flush (J)V + public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; + public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun getBaggage ()Lio/sentry/BaggageHeader; public fun getDefaultTagsForMetrics ()Ljava/util/Map; + public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; public fun getMetricsAggregator ()Lio/sentry/IMetricsAggregator; public fun getOptions ()Lio/sentry/SentryOptions; public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; + public fun getScope ()Lio/sentry/IScope; public fun getSpan ()Lio/sentry/ISpan; public fun getTraceparent ()Lio/sentry/SentryTraceHeader; public fun getTransaction ()Lio/sentry/ITransaction; public fun isCrashedLastRun ()Ljava/lang/Boolean; public fun isEnabled ()Z public fun isHealthy ()Z + public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; public fun metrics ()Lio/sentry/metrics/MetricsApi; public fun popScope ()V + public fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; public fun pushScope ()Lio/sentry/ISentryLifecycleToken; public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V @@ -493,23 +585,29 @@ public final class io/sentry/HubAdapter : io/sentry/IHub { public synthetic fun clone ()Ljava/lang/Object; public fun close ()V public fun close (Z)V - public fun configureScope (Lio/sentry/ScopeCallback;)V + public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public fun endSession ()V public fun flush (J)V + public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; + public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun getBaggage ()Lio/sentry/BaggageHeader; public static fun getInstance ()Lio/sentry/HubAdapter; + public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getOptions ()Lio/sentry/SentryOptions; public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; + public fun getScope ()Lio/sentry/IScope; public fun getSpan ()Lio/sentry/ISpan; public fun getTraceparent ()Lio/sentry/SentryTraceHeader; public fun getTransaction ()Lio/sentry/ITransaction; public fun isCrashedLastRun ()Ljava/lang/Boolean; public fun isEnabled ()Z public fun isHealthy ()Z + public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; public fun metrics ()Lio/sentry/metrics/MetricsApi; public fun popScope ()V + public fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; public fun pushScope ()Lio/sentry/ISentryLifecycleToken; public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V @@ -547,22 +645,28 @@ public final class io/sentry/HubScopesWrapper : io/sentry/IHub { public synthetic fun clone ()Ljava/lang/Object; public fun close ()V public fun close (Z)V - public fun configureScope (Lio/sentry/ScopeCallback;)V + public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public fun endSession ()V public fun flush (J)V + public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; + public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun getBaggage ()Lio/sentry/BaggageHeader; + public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getOptions ()Lio/sentry/SentryOptions; public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; + public fun getScope ()Lio/sentry/IScope; public fun getSpan ()Lio/sentry/ISpan; public fun getTraceparent ()Lio/sentry/SentryTraceHeader; public fun getTransaction ()Lio/sentry/ITransaction; public fun isCrashedLastRun ()Ljava/lang/Boolean; public fun isEnabled ()Z public fun isHealthy ()Z + public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; public fun metrics ()Lio/sentry/metrics/MetricsApi; public fun popScope ()V + public fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; public fun pushScope ()Lio/sentry/ISentryLifecycleToken; public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V @@ -764,14 +868,19 @@ public abstract interface class io/sentry/IScopes { public abstract fun clone ()Lio/sentry/IHub; public abstract fun close ()V public abstract fun close (Z)V - public abstract fun configureScope (Lio/sentry/ScopeCallback;)V + public fun configureScope (Lio/sentry/ScopeCallback;)V + public abstract fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V public abstract fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public abstract fun endSession ()V public abstract fun flush (J)V + public abstract fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; + public abstract fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public abstract fun getBaggage ()Lio/sentry/BaggageHeader; + public abstract fun getIsolationScope ()Lio/sentry/IScope; public abstract fun getLastEventId ()Lio/sentry/protocol/SentryId; public abstract fun getOptions ()Lio/sentry/SentryOptions; public abstract fun getRateLimiter ()Lio/sentry/transport/RateLimiter; + public abstract fun getScope ()Lio/sentry/IScope; public abstract fun getSpan ()Lio/sentry/ISpan; public abstract fun getTraceparent ()Lio/sentry/SentryTraceHeader; public abstract fun getTransaction ()Lio/sentry/ITransaction; @@ -779,8 +888,10 @@ public abstract interface class io/sentry/IScopes { public abstract fun isEnabled ()Z public abstract fun isHealthy ()Z public fun isNoOp ()Z + public abstract fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; public abstract fun metrics ()Lio/sentry/metrics/MetricsApi; public abstract fun popScope ()V + public abstract fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; public abstract fun pushScope ()Lio/sentry/ISentryLifecycleToken; public abstract fun removeExtra (Ljava/lang/String;)V public abstract fun removeTag (Ljava/lang/String;)V @@ -1253,15 +1364,19 @@ public final class io/sentry/NoOpHub : io/sentry/IHub { public synthetic fun clone ()Ljava/lang/Object; public fun close ()V public fun close (Z)V - public fun configureScope (Lio/sentry/ScopeCallback;)V + public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public fun endSession ()V public fun flush (J)V + public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; + public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun getBaggage ()Lio/sentry/BaggageHeader; public static fun getInstance ()Lio/sentry/NoOpHub; + public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getOptions ()Lio/sentry/SentryOptions; public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; + public fun getScope ()Lio/sentry/IScope; public fun getSpan ()Lio/sentry/ISpan; public fun getTraceparent ()Lio/sentry/SentryTraceHeader; public fun getTransaction ()Lio/sentry/ITransaction; @@ -1269,8 +1384,10 @@ public final class io/sentry/NoOpHub : io/sentry/IHub { public fun isEnabled ()Z public fun isHealthy ()Z public fun isNoOp ()Z + public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; public fun metrics ()Lio/sentry/metrics/MetricsApi; public fun popScope ()V + public fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; public fun pushScope ()Lio/sentry/ISentryLifecycleToken; public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V @@ -1378,15 +1495,19 @@ public final class io/sentry/NoOpScopes : io/sentry/IScopes { public synthetic fun clone ()Ljava/lang/Object; public fun close ()V public fun close (Z)V - public fun configureScope (Lio/sentry/ScopeCallback;)V + public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public fun endSession ()V public fun flush (J)V + public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; + public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun getBaggage ()Lio/sentry/BaggageHeader; public static fun getInstance ()Lio/sentry/NoOpScopes; + public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getOptions ()Lio/sentry/SentryOptions; public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; + public fun getScope ()Lio/sentry/IScope; public fun getSpan ()Lio/sentry/ISpan; public fun getTraceparent ()Lio/sentry/SentryTraceHeader; public fun getTransaction ()Lio/sentry/ITransaction; @@ -1394,8 +1515,10 @@ public final class io/sentry/NoOpScopes : io/sentry/IScopes { public fun isEnabled ()Z public fun isHealthy ()Z public fun isNoOp ()Z + public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; public fun metrics ()Lio/sentry/metrics/MetricsApi; public fun popScope ()V + public fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; public fun pushScope ()Lio/sentry/ISentryLifecycleToken; public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V @@ -1825,6 +1948,14 @@ public abstract class io/sentry/ScopeObserverAdapter : io/sentry/IScopeObserver public fun setUser (Lio/sentry/protocol/User;)V } +public final class io/sentry/ScopeType : java/lang/Enum { + public static final field CURRENT Lio/sentry/ScopeType; + public static final field GLOBAL Lio/sentry/ScopeType; + public static final field ISOLATION Lio/sentry/ScopeType; + public static fun valueOf (Ljava/lang/String;)Lio/sentry/ScopeType; + public static fun values ()[Lio/sentry/ScopeType; +} + public final class io/sentry/Scopes : io/sentry/IScopes, io/sentry/metrics/MetricsApi$IMetricsInterface { public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V @@ -1844,12 +1975,12 @@ public final class io/sentry/Scopes : io/sentry/IScopes, io/sentry/metrics/Metri public synthetic fun clone ()Ljava/lang/Object; public fun close ()V public fun close (Z)V - public fun configureScope (Lio/sentry/ScopeCallback;)V + public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public fun endSession ()V public fun flush (J)V - public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/Scopes; - public fun forkedScopes (Ljava/lang/String;)Lio/sentry/Scopes; + public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; + public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun getBaggage ()Lio/sentry/BaggageHeader; public fun getCreator ()Ljava/lang/String; public fun getDefaultTagsForMetrics ()Ljava/util/Map; @@ -1872,6 +2003,7 @@ public final class io/sentry/Scopes : io/sentry/IScopes, io/sentry/metrics/Metri public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; public fun metrics ()Lio/sentry/metrics/MetricsApi; public fun popScope ()V + public fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; public fun pushScope ()Lio/sentry/ISentryLifecycleToken; public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V @@ -1909,23 +2041,29 @@ public final class io/sentry/ScopesAdapter : io/sentry/IScopes { public synthetic fun clone ()Ljava/lang/Object; public fun close ()V public fun close (Z)V - public fun configureScope (Lio/sentry/ScopeCallback;)V + public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public fun endSession ()V public fun flush (J)V + public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; + public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun getBaggage ()Lio/sentry/BaggageHeader; public static fun getInstance ()Lio/sentry/ScopesAdapter; + public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getOptions ()Lio/sentry/SentryOptions; public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; + public fun getScope ()Lio/sentry/IScope; public fun getSpan ()Lio/sentry/ISpan; public fun getTraceparent ()Lio/sentry/SentryTraceHeader; public fun getTransaction ()Lio/sentry/ITransaction; public fun isCrashedLastRun ()Ljava/lang/Boolean; public fun isEnabled ()Z public fun isHealthy ()Z + public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; public fun metrics ()Lio/sentry/metrics/MetricsApi; public fun popScope ()V + public fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; public fun pushScope ()Lio/sentry/ISentryLifecycleToken; public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V @@ -1996,12 +2134,15 @@ public final class io/sentry/Sentry { public static fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; public static fun captureUserFeedback (Lio/sentry/UserFeedback;)V public static fun clearBreadcrumbs ()V - public static fun cloneMainHub ()Lio/sentry/IScopes; public static fun close ()V public static fun configureScope (Lio/sentry/ScopeCallback;)V + public static fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V public static fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public static fun endSession ()V public static fun flush (J)V + public static fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; + public static fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; + public static fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public static fun getBaggage ()Lio/sentry/BaggageHeader; public static fun getCurrentHub ()Lio/sentry/IHub; public static fun getCurrentScopes ()Lio/sentry/IScopes; @@ -2021,12 +2162,13 @@ public final class io/sentry/Sentry { public static fun isHealthy ()Z public static fun metrics ()Lio/sentry/metrics/MetricsApi; public static fun popScope ()V + public static fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; public static fun pushScope ()Lio/sentry/ISentryLifecycleToken; public static fun removeExtra (Ljava/lang/String;)V public static fun removeTag (Ljava/lang/String;)V public static fun reportFullDisplayed ()V public static fun reportFullyDisplayed ()V - public static fun setCurrentHub (Lio/sentry/IHub;)V + public static fun setCurrentHub (Lio/sentry/IHub;)Lio/sentry/ISentryLifecycleToken; public static fun setCurrentScopes (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; public static fun setExtra (Ljava/lang/String;Ljava/lang/String;)V public static fun setFingerprint (Ljava/util/List;)V @@ -2494,6 +2636,7 @@ public class io/sentry/SentryOptions { public fun getCron ()Lio/sentry/SentryOptions$Cron; public fun getDateProvider ()Lio/sentry/SentryDateProvider; public fun getDebugMetaLoader ()Lio/sentry/internal/debugmeta/IDebugMetaLoader; + public fun getDefaultScopeType ()Lio/sentry/ScopeType; public fun getDiagnosticLevel ()Lio/sentry/SentryLevel; public fun getDist ()Ljava/lang/String; public fun getDistinctId ()Ljava/lang/String; @@ -2604,6 +2747,7 @@ public class io/sentry/SentryOptions { public fun setDateProvider (Lio/sentry/SentryDateProvider;)V public fun setDebug (Z)V public fun setDebugMetaLoader (Lio/sentry/internal/debugmeta/IDebugMetaLoader;)V + public fun setDefaultScopeType (Lio/sentry/ScopeType;)V public fun setDiagnosticLevel (Lio/sentry/SentryLevel;)V public fun setDist (Ljava/lang/String;)V public fun setDistinctId (Ljava/lang/String;)V @@ -2837,7 +2981,9 @@ public final class io/sentry/SentryTracer : io/sentry/ITransaction { public final class io/sentry/SentryWrapper { public fun ()V public static fun wrapCallable (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Callable; + public static fun wrapCallableIsolated (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Callable; public static fun wrapSupplier (Ljava/util/function/Supplier;)Ljava/util/function/Supplier; + public static fun wrapSupplierIsolated (Ljava/util/function/Supplier;)Ljava/util/function/Supplier; } public final class io/sentry/Session : io/sentry/JsonSerializable, io/sentry/JsonUnknown { @@ -3982,7 +4128,7 @@ public final class io/sentry/protocol/Browser$JsonKeys { public fun ()V } -public final class io/sentry/protocol/Contexts : java/util/concurrent/ConcurrentHashMap, io/sentry/JsonSerializable { +public class io/sentry/protocol/Contexts : java/util/concurrent/ConcurrentHashMap, io/sentry/JsonSerializable { public fun ()V public fun (Lio/sentry/protocol/Contexts;)V public fun getApp ()Lio/sentry/protocol/App; @@ -5295,6 +5441,11 @@ public abstract interface class io/sentry/util/LazyEvaluator$Evaluator { public abstract fun evaluate ()Ljava/lang/Object; } +public final class io/sentry/util/LifecycleHelper { + public fun ()V + public static fun close (Ljava/lang/Object;)V +} + public final class io/sentry/util/LogUtils { public fun ()V public static fun logNotInstanceOf (Ljava/lang/Class;Ljava/lang/Object;Lio/sentry/ILogger;)V diff --git a/sentry/src/main/java/io/sentry/Breadcrumb.java b/sentry/src/main/java/io/sentry/Breadcrumb.java index 5f43ab6d29..fcd9407993 100644 --- a/sentry/src/main/java/io/sentry/Breadcrumb.java +++ b/sentry/src/main/java/io/sentry/Breadcrumb.java @@ -663,6 +663,7 @@ public void setUnknown(@Nullable Map unknown) { @Override @SuppressWarnings("JavaUtilDate") public int compareTo(@NotNull Breadcrumb o) { + // TODO also use nano time if equal return timestamp.compareTo(o.timestamp); } diff --git a/sentry/src/main/java/io/sentry/CombinedScopeView.java b/sentry/src/main/java/io/sentry/CombinedScopeView.java index b9f8ab2c70..19253d1f4a 100644 --- a/sentry/src/main/java/io/sentry/CombinedScopeView.java +++ b/sentry/src/main/java/io/sentry/CombinedScopeView.java @@ -237,6 +237,7 @@ public void setTag(@NotNull String key, @NotNull String value) { @Override public void removeTag(@NotNull String key) { + // TODO should this go to all scopes? getDefaultWriteScope().removeTag(key); } @@ -256,6 +257,7 @@ public void setExtra(@NotNull String key, @NotNull String value) { @Override public void removeExtra(@NotNull String key) { + // TODO should this go to all scopes? getDefaultWriteScope().removeExtra(key); } @@ -305,10 +307,12 @@ public void setContexts(@NotNull String key, @NotNull Character value) { @Override public void removeContexts(@NotNull String key) { + // TODO should this go to all scopes? getDefaultWriteScope().removeContexts(key); } private @NotNull IScope getDefaultWriteScope() { + // TODO use Scopes.getSpecificScope? if (ScopeType.CURRENT.equals(getOptions().getDefaultScopeType())) { return scope; } diff --git a/sentry/src/main/java/io/sentry/ScopeType.java b/sentry/src/main/java/io/sentry/ScopeType.java index d54c2b635c..6f35ce6604 100644 --- a/sentry/src/main/java/io/sentry/ScopeType.java +++ b/sentry/src/main/java/io/sentry/ScopeType.java @@ -3,5 +3,8 @@ public enum ScopeType { CURRENT, ISOLATION, - GLOBAL; + GLOBAL, + + // TODO do we need a combined as well so configureScope + COMBINED; } diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index daf143ba66..9319c9b46f 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -363,6 +363,7 @@ public void endSession() { } private IScope getCombinedScopeView() { + // TODO create in ctor? return new CombinedScopeView(getGlobalScope(), isolationScope, scope); } @@ -428,6 +429,7 @@ public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb, final @Nullable } private IScope getSpecificScope(final @Nullable ScopeType scopeType) { + // TODO extract and reuse if (scopeType != null) { switch (scopeType) { case CURRENT: From 2d01626cfdbb920d2c3035d9b733ef51006c17d1 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 23 Apr 2024 15:14:33 +0200 Subject: [PATCH 28/91] Hubs/Scopes Merge 28 - Fix breadcrumb ordering (#3355) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering --- .../io/sentry/logback/SentryAppenderTest.kt | 1 + .../src/main/java/io/sentry/Breadcrumb.java | 11 ++- .../java/io/sentry/CombinedScopeViewTest.kt | 72 +++++++++++++++++++ 3 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt diff --git a/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt b/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt index 4217954be1..96c3dad9f1 100644 --- a/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt +++ b/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt @@ -77,6 +77,7 @@ class SentryAppenderTest { @BeforeTest fun `clear MDC`() { MDC.clear() + Sentry.close() } @Test diff --git a/sentry/src/main/java/io/sentry/Breadcrumb.java b/sentry/src/main/java/io/sentry/Breadcrumb.java index fcd9407993..ddb19e5052 100644 --- a/sentry/src/main/java/io/sentry/Breadcrumb.java +++ b/sentry/src/main/java/io/sentry/Breadcrumb.java @@ -22,6 +22,8 @@ public final class Breadcrumb implements JsonUnknown, JsonSerializable, Comparab /** A timestamp representing when the breadcrumb occurred. */ private final @NotNull Date timestamp; + private final @NotNull Long nanos; + /** If a message is provided, its rendered as text and the whitespace is preserved. */ private @Nullable String message; @@ -46,10 +48,12 @@ public final class Breadcrumb implements JsonUnknown, JsonSerializable, Comparab * @param timestamp the timestamp */ public Breadcrumb(final @NotNull Date timestamp) { + this.nanos = System.nanoTime(); this.timestamp = timestamp; } Breadcrumb(final @NotNull Breadcrumb breadcrumb) { + this.nanos = System.nanoTime(); this.timestamp = breadcrumb.timestamp; this.message = breadcrumb.message; this.type = breadcrumb.type; @@ -663,8 +667,11 @@ public void setUnknown(@Nullable Map unknown) { @Override @SuppressWarnings("JavaUtilDate") public int compareTo(@NotNull Breadcrumb o) { - // TODO also use nano time if equal - return timestamp.compareTo(o.timestamp); + int timestampCompare = timestamp.compareTo(o.timestamp); + if (timestampCompare == 0) { + return nanos.compareTo(o.nanos); + } + return timestampCompare; } public static final class JsonKeys { diff --git a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt new file mode 100644 index 0000000000..38023da18e --- /dev/null +++ b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt @@ -0,0 +1,72 @@ +package io.sentry + +import kotlin.test.Test +import kotlin.test.assertEquals + +class CombinedScopeViewTest { + + @Test + fun `adds breadcrumbs from all scopes in sorted order`() { + val options = SentryOptions() + val globalScope = Scope(options) + val isolationScope = Scope(options) + val scope = Scope(options) + + val combined = CombinedScopeView(globalScope, isolationScope, scope) + + globalScope.addBreadcrumb(Breadcrumb.info("global 1")) + isolationScope.addBreadcrumb(Breadcrumb.info("isolation 1")) + scope.addBreadcrumb(Breadcrumb.info("current 1")) + + globalScope.addBreadcrumb(Breadcrumb.info("global 2")) + isolationScope.addBreadcrumb(Breadcrumb.info("isolation 2")) + scope.addBreadcrumb(Breadcrumb.info("current 2")) + + val breadcrumbs = combined.breadcrumbs + assertEquals("global 1", breadcrumbs.poll().message) + assertEquals("isolation 1", breadcrumbs.poll().message) + assertEquals("current 1", breadcrumbs.poll().message) + assertEquals("global 2", breadcrumbs.poll().message) + assertEquals("isolation 2", breadcrumbs.poll().message) + assertEquals("current 2", breadcrumbs.poll().message) + } + + @Test + fun `oldest breadcrumbs are dropped first`() { + val options = SentryOptions().also { it.maxBreadcrumbs = 5 } + val globalScope = Scope(options) + val isolationScope = Scope(options) + val scope = Scope(options) + + val combined = CombinedScopeView(globalScope, isolationScope, scope) + + globalScope.addBreadcrumb(Breadcrumb.info("global 1")) + isolationScope.addBreadcrumb(Breadcrumb.info("isolation 1")) + scope.addBreadcrumb(Breadcrumb.info("current 1")) + + globalScope.addBreadcrumb(Breadcrumb.info("global 2")) + isolationScope.addBreadcrumb(Breadcrumb.info("isolation 2")) + scope.addBreadcrumb(Breadcrumb.info("current 2")) + + val breadcrumbs = combined.breadcrumbs +// assertEquals("global 1", breadcrumbs.poll().message) <-- was dropped + assertEquals("isolation 1", breadcrumbs.poll().message) + assertEquals("current 1", breadcrumbs.poll().message) + assertEquals("global 2", breadcrumbs.poll().message) + assertEquals("isolation 2", breadcrumbs.poll().message) + assertEquals("current 2", breadcrumbs.poll().message) + + scope.addBreadcrumb(Breadcrumb.info("current 3")) + scope.addBreadcrumb(Breadcrumb.info("current 4")) + + val breadcrumbs2 = combined.breadcrumbs +// assertEquals("global 1", breadcrumbs.poll().message) <-- was dropped +// assertEquals("isolation 1", breadcrumbs2.poll().message) <-- dropped +// assertEquals("current 1", breadcrumbs2.poll().message) <-- dropped + assertEquals("global 2", breadcrumbs2.poll().message) + assertEquals("isolation 2", breadcrumbs2.poll().message) + assertEquals("current 2", breadcrumbs2.poll().message) + assertEquals("current 3", breadcrumbs2.poll().message) + assertEquals("current 4", breadcrumbs2.poll().message) + } +} From 4650d04c664fa9e29cb911cd447b31089be0c483 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 23 Apr 2024 15:17:16 +0200 Subject: [PATCH 29/91] Hubs Scopes Merge 29 - Mark TODOs related to Hubs/Scopes Merge with [HSM] (#3356) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] --- .../android/core/ManifestMetadataReader.java | 6 +-- .../spring/jakarta/SentryTaskDecorator.java | 3 +- .../spring/jakarta/webflux/ReactorUtils.java | 4 +- .../io/sentry/spring/SentryTaskDecorator.java | 3 +- .../spring/webflux/SentryWebFilter.java | 1 - .../java/io/sentry/CombinedScopeView.java | 17 +++--- .../java/io/sentry/DefaultScopesStorage.java | 2 +- .../src/main/java/io/sentry/HubAdapter.java | 2 +- sentry/src/main/java/io/sentry/Scope.java | 6 +-- sentry/src/main/java/io/sentry/ScopeType.java | 2 +- sentry/src/main/java/io/sentry/Scopes.java | 52 ++++++------------- .../main/java/io/sentry/ScopesAdapter.java | 2 +- sentry/src/main/java/io/sentry/Sentry.java | 5 +- .../main/java/io/sentry/SentryOptions.java | 2 +- .../main/java/io/sentry/SentryWrapper.java | 2 +- 15 files changed, 44 insertions(+), 65 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java b/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java index 31e026dd00..b51c4b22a8 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java @@ -37,7 +37,7 @@ final class ManifestMetadataReader { static final String SDK_NAME = "io.sentry.sdk.name"; static final String SDK_VERSION = "io.sentry.sdk.version"; - // TODO: remove on 6.x in favor of SESSION_AUTO_TRACKING_ENABLE + // TODO [MAJOR]: remove on 6.x in favor of SESSION_AUTO_TRACKING_ENABLE static final String SESSION_TRACKING_ENABLE = "io.sentry.session-tracking.enable"; static final String AUTO_SESSION_TRACKING_ENABLE = "io.sentry.auto-session-tracking.enable"; @@ -70,7 +70,7 @@ final class ManifestMetadataReader { @ApiStatus.Experimental static final String TRACE_SAMPLING = "io.sentry.traces.trace-sampling"; - // TODO: remove in favor of TRACE_PROPAGATION_TARGETS + // TODO [MAJOR]: remove in favor of TRACE_PROPAGATION_TARGETS @Deprecated static final String TRACING_ORIGINS = "io.sentry.traces.tracing-origins"; static final String TRACE_PROPAGATION_TARGETS = "io.sentry.traces.trace-propagation-targets"; @@ -323,7 +323,7 @@ static void applyMetadata( List tracePropagationTargets = readList(metadata, logger, TRACE_PROPAGATION_TARGETS); - // TODO remove once TRACING_ORIGINS have been removed + // TODO [MAJOR] remove once TRACING_ORIGINS have been removed if (!metadata.containsKey(TRACE_PROPAGATION_TARGETS) && (tracePropagationTargets == null || tracePropagationTargets.isEmpty())) { tracePropagationTargets = readList(metadata, logger, TRACING_ORIGINS); diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java index 943a7cc5ff..42c35919d7 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java @@ -15,7 +15,8 @@ */ public final class SentryTaskDecorator implements TaskDecorator { @Override - // TODO should there also be a SentryIsolatedTaskDecorator or similar that uses forkedScopes()? + // TODO [HSM] should there also be a SentryIsolatedTaskDecorator or similar that uses + // forkedScopes()? public @NotNull Runnable decorate(final @NotNull Runnable runnable) { final IScopes newScopes = Sentry.getCurrentScopes().forkedCurrentScope("spring.taskDecorator"); diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java index 9755ea0932..0be67c9f5a 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java @@ -10,8 +10,8 @@ // TODO deprecate and replace with "withSentryScopes" etc. @ApiStatus.Experimental -// TODO do we keep old methods around and deprecate them? -// TODO do we need to offer isolated variants? +// TODO [HSM] do we keep old methods around and deprecate them? +// TODO [HSM] do we need to offer isolated variants? public final class ReactorUtils { /** diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java b/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java index 761038ece0..8c3b9ac1f4 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java @@ -15,7 +15,8 @@ */ public final class SentryTaskDecorator implements TaskDecorator { @Override - // TODO should there also be a SentryIsolatedTaskDecorator or similar that uses forkedScopes()? + // TODO [HSM] should there also be a SentryIsolatedTaskDecorator or similar that uses + // forkedScopes()? public @NotNull Runnable decorate(final @NotNull Runnable runnable) { final IScopes newHub = Sentry.getCurrentScopes().forkedCurrentScope("spring.taskDecorator"); diff --git a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java index 10e80ebe8b..e32ede6947 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java @@ -51,7 +51,6 @@ public Mono filter( final @NotNull ServerWebExchange serverWebExchange, final @NotNull WebFilterChain webFilterChain) { @NotNull IScopes requestHub = Sentry.forkedRootScopes("request.webflux"); - // TODO do not push / pop, use fork instead if (!requestHub.isEnabled()) { return webFilterChain.filter(serverWebExchange); } diff --git a/sentry/src/main/java/io/sentry/CombinedScopeView.java b/sentry/src/main/java/io/sentry/CombinedScopeView.java index 19253d1f4a..ee6fdad06e 100644 --- a/sentry/src/main/java/io/sentry/CombinedScopeView.java +++ b/sentry/src/main/java/io/sentry/CombinedScopeView.java @@ -164,7 +164,6 @@ public void setFingerprint(@NotNull List fingerprint) { allBreadcrumbs.addAll(scope.getBreadcrumbs()); Collections.sort(allBreadcrumbs); - // TODO test oldest are removed first final @NotNull Queue breadcrumbs = createBreadcrumbsList(scope.getOptions().getMaxBreadcrumbs()); breadcrumbs.addAll(allBreadcrumbs); @@ -178,7 +177,7 @@ public void setFingerprint(@NotNull List fingerprint) { * @param maxBreadcrumb the max number of breadcrumbs * @return the breadcrumbs queue */ - // TODO copied from Scope, should reuse instead + // TODO [HSM] copied from Scope, should reuse instead private @NotNull Queue createBreadcrumbsList(final int maxBreadcrumb) { return SynchronizedQueue.synchronizedQueue(new CircularFifoQueue<>(maxBreadcrumb)); } @@ -237,7 +236,7 @@ public void setTag(@NotNull String key, @NotNull String value) { @Override public void removeTag(@NotNull String key) { - // TODO should this go to all scopes? + // TODO [HSM] should this go to all scopes? getDefaultWriteScope().removeTag(key); } @@ -257,7 +256,7 @@ public void setExtra(@NotNull String key, @NotNull String value) { @Override public void removeExtra(@NotNull String key) { - // TODO should this go to all scopes? + // TODO [HSM] should this go to all scopes? getDefaultWriteScope().removeExtra(key); } @@ -307,12 +306,12 @@ public void setContexts(@NotNull String key, @NotNull Character value) { @Override public void removeContexts(@NotNull String key) { - // TODO should this go to all scopes? + // TODO [HSM] should this go to all scopes? getDefaultWriteScope().removeContexts(key); } private @NotNull IScope getDefaultWriteScope() { - // TODO use Scopes.getSpecificScope? + // TODO [HSM] use Scopes.getSpecificScope? if (ScopeType.CURRENT.equals(getOptions().getDefaultScopeType())) { return scope; } @@ -343,7 +342,7 @@ public void clearAttachments() { @Override public @NotNull List getEventProcessors() { - // TODO mechanism for ordering event processors + // TODO [HSM] mechanism for ordering event processors final @NotNull List allEventProcessors = new CopyOnWriteArrayList<>(); allEventProcessors.addAll(globalScope.getEventProcessors()); allEventProcessors.addAll(isolationScope.getEventProcessors()); @@ -412,7 +411,7 @@ public void setPropagationContext(@NotNull PropagationContext propagationContext @Override public @NotNull IScope clone() { - // TODO just return a new CombinedScopeView with forked scope? + // TODO [HSM] just return a new CombinedScopeView with forked scope? return getDefaultWriteScope().clone(); } @@ -435,7 +434,7 @@ public void bindClient(@NotNull ISentryClient client) { @Override public @NotNull ISentryClient getClient() { - // TODO checking for noop here doesn't allow disabling via client, is that ok? + // TODO [HSM] checking for noop here doesn't allow disabling via client, is that ok? final @Nullable ISentryClient current = scope.getClient(); if (!(current instanceof NoOpSentryClient)) { return current; diff --git a/sentry/src/main/java/io/sentry/DefaultScopesStorage.java b/sentry/src/main/java/io/sentry/DefaultScopesStorage.java index 12902a1dff..4a054ee7cc 100644 --- a/sentry/src/main/java/io/sentry/DefaultScopesStorage.java +++ b/sentry/src/main/java/io/sentry/DefaultScopesStorage.java @@ -21,7 +21,7 @@ public ISentryLifecycleToken set(@Nullable IScopes scopes) { @Override public void close() { - // TODO prevent further storing? would this cause problems if singleton, closed and + // TODO [HSM] prevent further storing? would this cause problems if singleton, closed and // re-initialized? currentScopes.remove(); } diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index f0d7335a80..8e6966aac5 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -210,7 +210,7 @@ public void flush(long timeoutMillis) { @Override public @NotNull ISentryLifecycleToken makeCurrent() { - // TODO this wouldn't do anything since it replaced the current with the same Scopes + // TODO [HSM] this wouldn't do anything since it replaced the current with the same Scopes return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); } diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index fcbcd74650..665f789158 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -90,8 +90,8 @@ public final class Scope implements IScope { private @NotNull ISentryClient client = NoOpSentryClient.getInstance(); - // TODO intended only for global scope - // TODO test for memory leak + // TODO [HSM] intended only for global scope + // TODO [HSM] test for memory leak private final @NotNull Map, String>> throwableToSpan = Collections.synchronizedMap(new WeakHashMap<>()); @@ -114,7 +114,7 @@ private Scope(final @NotNull Scope scope) { this.options = scope.options; this.level = scope.level; this.client = scope.client; - // TODO should we do this? didn't do it for Hub + // TODO [HSM] should we do this? didn't do it for Hub this.lastEventId = scope.getLastEventId(); final User userRef = scope.user; diff --git a/sentry/src/main/java/io/sentry/ScopeType.java b/sentry/src/main/java/io/sentry/ScopeType.java index 6f35ce6604..3815cf2081 100644 --- a/sentry/src/main/java/io/sentry/ScopeType.java +++ b/sentry/src/main/java/io/sentry/ScopeType.java @@ -5,6 +5,6 @@ public enum ScopeType { ISOLATION, GLOBAL, - // TODO do we need a combined as well so configureScope + // TODO [HSM] do we need a combined as well so configureScope COMBINED; } diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 9319c9b46f..d30a9b6074 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -26,13 +26,13 @@ public final class Scopes implements IScopes, MetricsApi.IMetricsInterface { private final @NotNull IScope scope; private final @NotNull IScope isolationScope; - // TODO just for debugging + @SuppressWarnings("UnusedVariable") private final @Nullable Scopes parentScopes; private final @NotNull String creator; - // TODO should this be set on all scopes (global, isolation, current)? + // TODO [HSM] should this be set on all scopes (global, isolation, current)? private final @NotNull SentryOptions options; private volatile boolean isEnabled; private final @NotNull TracesSampler tracesSampler; @@ -82,12 +82,12 @@ private Scopes( return isolationScope; } - // TODO add to IScopes interface? + // TODO [HSM] add to IScopes interface? public @Nullable Scopes getParent() { return parentScopes; } - // TODO add to IScopes interface? + // TODO [HSM] add to IScopes interface? public boolean isAncestorOf(final @Nullable Scopes otherScopes) { if (otherScopes == null) { return false; @@ -115,7 +115,7 @@ public boolean isAncestorOf(final @Nullable Scopes otherScopes) { return new Scopes(scope.clone(), isolationScope, this, options, creator); } - // TODO always read from root scope? + // TODO [HSM] always read from root scope? @Override public boolean isEnabled() { return isEnabled; @@ -363,7 +363,7 @@ public void endSession() { } private IScope getCombinedScopeView() { - // TODO create in ctor? + // TODO [HSM] create in ctor? return new CombinedScopeView(getGlobalScope(), isolationScope, scope); } @@ -393,7 +393,7 @@ public void close(final boolean isRestarting) { } } - // TODO which scopes do we call this on? isolation and current scope? + // TODO [HSM] which scopes do we call this on? isolation and current scope? configureScope(scope -> scope.clear()); options.getTransactionProfiler().close(); options.getTransactionPerformanceCollector().close(); @@ -429,7 +429,7 @@ public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb, final @Nullable } private IScope getSpecificScope(final @Nullable ScopeType scopeType) { - // TODO extract and reuse + // TODO [HSM] extract and reuse if (scopeType != null) { switch (scopeType) { case CURRENT: @@ -582,11 +582,9 @@ private void updateLastEventId(final @NotNull SentryId lastEventId) { getCombinedScopeView().setLastEventId(lastEventId); } - // TODO add to IScopes interface + // TODO [HSM] add to IScopes interface public @NotNull IScope getGlobalScope() { - // TODO should be: return Sentry.getGlobalScope(); - // return scope; } @Override @@ -627,7 +625,7 @@ public ISentryLifecycleToken pushIsolationScope() { return Sentry.setCurrentScopes(this); } - // TODO needs to be deprecated because there's no more stack + // TODO [HSM] needs to be deprecated because there's no more stack @Override public void popScope() { if (!isEnabled()) { @@ -637,13 +635,13 @@ public void popScope() { } else { final @Nullable Scopes parent = getParent(); if (parent != null) { - // TODO this is never closed + // TODO [HSM] this is never closed parent.makeCurrent(); } } } - // TODO lots of testing required to see how ThreadLocal is affected + // TODO [HSM] lots of testing required to see how ThreadLocal is affected @Override public void withScope(final @NotNull ScopeCallback callback) { if (!isEnabled()) { @@ -655,8 +653,8 @@ public void withScope(final @NotNull ScopeCallback callback) { } else { final @NotNull IScopes forkedScopes = forkedCurrentScope("withScope"); - // TODO should forkedScopes be made current inside callback? - // TODO forkedScopes.makeCurrent()? + // TODO [HSM] should forkedScopes be made current inside callback? + // TODO [HSM] forkedScopes.makeCurrent()? try { callback.run(forkedScopes.getScope()); } catch (Throwable e) { @@ -726,7 +724,7 @@ public void flush(long timeoutMillis) { if (!isEnabled()) { options.getLogger().log(SentryLevel.WARNING, "Disabled Hub cloned."); } - // TODO should this fork isolation scope as well? + // TODO [HSM] should this fork isolation scope as well? return new HubScopesWrapper(forkedCurrentScope("scopes clone")); } @@ -876,24 +874,6 @@ public void setSpanContext( getCombinedScopeView().setSpanContext(throwable, span, transactionName); } - // // TODO this seems unused - // @Nullable - // SpanContext getSpanContext(final @NotNull Throwable throwable) { - // Objects.requireNonNull(throwable, "throwable is required"); - // final Throwable rootCause = ExceptionUtils.findRootCause(throwable); - // final Pair, String> pair = this.throwableToSpan.get(rootCause); - // if (pair != null) { - // final WeakReference spanWeakRef = pair.getFirst(); - // if (spanWeakRef != null) { - // final ISpan span = spanWeakRef.get(); - // if (span != null) { - // return span.getSpanContext(); - // } - // } - // } - // return null; - // } - @Override public @Nullable ISpan getSpan() { ISpan span = null; @@ -947,7 +927,7 @@ public void reportFullyDisplayed() { @NotNull PropagationContext propagationContext = PropagationContext.fromHeaders(getOptions().getLogger(), sentryTrace, baggageHeaders); - // TODO should this go on isolation scope? + // TODO [HSM] should this go on isolation scope? configureScope( (scope) -> { scope.setPropagationContext(propagationContext); diff --git a/sentry/src/main/java/io/sentry/ScopesAdapter.java b/sentry/src/main/java/io/sentry/ScopesAdapter.java index d3b0f43bf2..684ab12113 100644 --- a/sentry/src/main/java/io/sentry/ScopesAdapter.java +++ b/sentry/src/main/java/io/sentry/ScopesAdapter.java @@ -207,7 +207,7 @@ public void flush(long timeoutMillis) { @Override public @NotNull ISentryLifecycleToken makeCurrent() { - // TODO this wouldn't do anything since it replaced the current with the same Scopes + // TODO [HSM] this wouldn't do anything since it replaced the current with the same Scopes return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); } diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 42ccc3cf63..bd1fcee04b 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -47,7 +47,7 @@ private Sentry() {} /** The root Scopes or NoOp if Sentry is disabled. */ private static volatile @NotNull IScopes rootScopes = NoOpScopes.getInstance(); - // TODO cannot pass options here + // TODO [HSM] cannot pass options here private static volatile @NotNull IScope globalScope = new Scope(new SentryOptions()); /** Default value for globalHubMode is false */ @@ -271,7 +271,7 @@ private static synchronized void init( final IScopes scopes = getCurrentScopes(); final IScope rootScope = new Scope(options); final IScope rootIsolationScope = new Scope(options); - // TODO shouldn't replace global scope + // TODO [HSM] shouldn't replace global scope globalScope = new Scope(options); globalScope.bindClient(new SentryClient(options)); rootScopes = new Scopes(rootScope, rootIsolationScope, options, "Sentry.init"); @@ -819,7 +819,6 @@ public static void removeExtra(final @NotNull String key) { public static @NotNull ISentryLifecycleToken pushScope() { // pushScope is no-op in global hub mode if (!globalHubMode) { - // TODO this might have to behave differently from Scopes.pushScope return getCurrentScopes().pushScope(); } return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index c693907121..0523ce7cf0 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -409,7 +409,7 @@ public class SentryOptions { private @NotNull IMainThreadChecker mainThreadChecker = NoOpMainThreadChecker.getInstance(); - // TODO this should default to false on the next major + // TODO [MAJOR] this should default to false on the next major /** Whether OPTIONS requests should be traced. */ private boolean traceOptionsRequests = true; diff --git a/sentry/src/main/java/io/sentry/SentryWrapper.java b/sentry/src/main/java/io/sentry/SentryWrapper.java index d0f2cd8017..4682dac8f5 100644 --- a/sentry/src/main/java/io/sentry/SentryWrapper.java +++ b/sentry/src/main/java/io/sentry/SentryWrapper.java @@ -27,7 +27,7 @@ public final class SentryWrapper { * @return the wrapped {@link Callable} * @param - the result type of the {@link Callable} */ - // TODO adapt javadoc + // TODO [HSM] adapt javadoc public static Callable wrapCallable(final @NotNull Callable callable) { final IScopes newScopes = Sentry.getCurrentScopes().forkedCurrentScope("wrapCallable"); From e296fb6789f1d7421026033da80dd5b394e5c54d Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 23 Apr 2024 15:21:28 +0200 Subject: [PATCH 30/91] Hubs/Scopes Merge 30 - Add `getGlobalScope` and `forkedRootScopes` to `IScopes` (#3359) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes --- sentry/api/sentry.api | 15 +++++++++++++++ sentry/src/main/java/io/sentry/Hub.java | 10 ++++++++++ sentry/src/main/java/io/sentry/HubAdapter.java | 10 ++++++++++ .../main/java/io/sentry/HubScopesWrapper.java | 10 ++++++++++ sentry/src/main/java/io/sentry/IScopes.java | 18 +++++++++++++++++- sentry/src/main/java/io/sentry/NoOpHub.java | 10 ++++++++++ sentry/src/main/java/io/sentry/NoOpScopes.java | 10 ++++++++++ sentry/src/main/java/io/sentry/Scopes.java | 7 ++++++- .../src/main/java/io/sentry/ScopesAdapter.java | 10 ++++++++++ sentry/src/main/java/io/sentry/Sentry.java | 2 -- 10 files changed, 98 insertions(+), 4 deletions(-) diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index e6525cee31..09d83295ce 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -528,9 +528,11 @@ public final class io/sentry/Hub : io/sentry/IHub, io/sentry/metrics/MetricsApi$ public fun endSession ()V public fun flush (J)V public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; + public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun getBaggage ()Lio/sentry/BaggageHeader; public fun getDefaultTagsForMetrics ()Ljava/util/Map; + public fun getGlobalScope ()Lio/sentry/IScope; public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; @@ -590,8 +592,10 @@ public final class io/sentry/HubAdapter : io/sentry/IHub { public fun endSession ()V public fun flush (J)V public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; + public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun getBaggage ()Lio/sentry/BaggageHeader; + public fun getGlobalScope ()Lio/sentry/IScope; public static fun getInstance ()Lio/sentry/HubAdapter; public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; @@ -650,8 +654,10 @@ public final class io/sentry/HubScopesWrapper : io/sentry/IHub { public fun endSession ()V public fun flush (J)V public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; + public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun getBaggage ()Lio/sentry/BaggageHeader; + public fun getGlobalScope ()Lio/sentry/IScope; public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getOptions ()Lio/sentry/SentryOptions; @@ -874,8 +880,10 @@ public abstract interface class io/sentry/IScopes { public abstract fun endSession ()V public abstract fun flush (J)V public abstract fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; + public abstract fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; public abstract fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public abstract fun getBaggage ()Lio/sentry/BaggageHeader; + public abstract fun getGlobalScope ()Lio/sentry/IScope; public abstract fun getIsolationScope ()Lio/sentry/IScope; public abstract fun getLastEventId ()Lio/sentry/protocol/SentryId; public abstract fun getOptions ()Lio/sentry/SentryOptions; @@ -1369,8 +1377,10 @@ public final class io/sentry/NoOpHub : io/sentry/IHub { public fun endSession ()V public fun flush (J)V public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; + public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun getBaggage ()Lio/sentry/BaggageHeader; + public fun getGlobalScope ()Lio/sentry/IScope; public static fun getInstance ()Lio/sentry/NoOpHub; public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; @@ -1500,8 +1510,10 @@ public final class io/sentry/NoOpScopes : io/sentry/IScopes { public fun endSession ()V public fun flush (J)V public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; + public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun getBaggage ()Lio/sentry/BaggageHeader; + public fun getGlobalScope ()Lio/sentry/IScope; public static fun getInstance ()Lio/sentry/NoOpScopes; public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; @@ -1980,6 +1992,7 @@ public final class io/sentry/Scopes : io/sentry/IScopes, io/sentry/metrics/Metri public fun endSession ()V public fun flush (J)V public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; + public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun getBaggage ()Lio/sentry/BaggageHeader; public fun getCreator ()Ljava/lang/String; @@ -2046,8 +2059,10 @@ public final class io/sentry/ScopesAdapter : io/sentry/IScopes { public fun endSession ()V public fun flush (J)V public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; + public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun getBaggage ()Lio/sentry/BaggageHeader; + public fun getGlobalScope ()Lio/sentry/IScope; public static fun getInstance ()Lio/sentry/ScopesAdapter; public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; diff --git a/sentry/src/main/java/io/sentry/Hub.java b/sentry/src/main/java/io/sentry/Hub.java index 35740f4c3e..bcb93c7558 100644 --- a/sentry/src/main/java/io/sentry/Hub.java +++ b/sentry/src/main/java/io/sentry/Hub.java @@ -668,6 +668,11 @@ public void flush(long timeoutMillis) { return Sentry.forkedCurrentScope(creator); } + @Override + public @NotNull IScopes forkedRootScopes(final @NotNull String creator) { + return Sentry.forkedRootScopes(creator); + } + @Override public @NotNull ISentryLifecycleToken makeCurrent() { return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); @@ -683,6 +688,11 @@ public void flush(long timeoutMillis) { return Sentry.getCurrentScopes().getIsolationScope(); } + @Override + public @NotNull IScope getGlobalScope() { + return Sentry.getGlobalScope(); + } + @ApiStatus.Internal @Override public @NotNull SentryId captureTransaction( diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index 8e6966aac5..df1a7aa661 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -208,6 +208,11 @@ public void flush(long timeoutMillis) { return Sentry.forkedCurrentScope(creator); } + @Override + public @NotNull IScopes forkedRootScopes(final @NotNull String creator) { + return Sentry.forkedRootScopes(creator); + } + @Override public @NotNull ISentryLifecycleToken makeCurrent() { // TODO [HSM] this wouldn't do anything since it replaced the current with the same Scopes @@ -224,6 +229,11 @@ public void flush(long timeoutMillis) { return Sentry.getCurrentScopes().getIsolationScope(); } + @Override + public @NotNull IScope getGlobalScope() { + return Sentry.getGlobalScope(); + } + @Override public @NotNull SentryId captureTransaction( @NotNull SentryTransaction transaction, diff --git a/sentry/src/main/java/io/sentry/HubScopesWrapper.java b/sentry/src/main/java/io/sentry/HubScopesWrapper.java index 3309e59671..2a50d8ca48 100644 --- a/sentry/src/main/java/io/sentry/HubScopesWrapper.java +++ b/sentry/src/main/java/io/sentry/HubScopesWrapper.java @@ -203,6 +203,11 @@ public void flush(long timeoutMillis) { return scopes.forkedCurrentScope(creator); } + @Override + public @NotNull IScopes forkedRootScopes(final @NotNull String creator) { + return Sentry.forkedRootScopes(creator); + } + @Override public @NotNull ISentryLifecycleToken makeCurrent() { return scopes.makeCurrent(); @@ -218,6 +223,11 @@ public void flush(long timeoutMillis) { return scopes.getIsolationScope(); } + @Override + public @NotNull IScope getGlobalScope() { + return Sentry.getGlobalScope(); + } + @ApiStatus.Internal @Override public @NotNull SentryId captureTransaction( diff --git a/sentry/src/main/java/io/sentry/IScopes.java b/sentry/src/main/java/io/sentry/IScopes.java index af6f41ae13..6eb82cca32 100644 --- a/sentry/src/main/java/io/sentry/IScopes.java +++ b/sentry/src/main/java/io/sentry/IScopes.java @@ -375,7 +375,7 @@ default void configureScope(@NotNull ScopeCallback callback) { IHub clone(); /** - * Creates a fork of both current and isolation scope. + * Creates a fork of both current and isolation scope from current scopes. * * @param creator debug information to see why scopes where forked * @return forked Scopes @@ -392,6 +392,15 @@ default void configureScope(@NotNull ScopeCallback callback) { @NotNull IScopes forkedCurrentScope(final @NotNull String creator); + /** + * Creates a fork of both current and isolation scope from root scopes. + * + * @param creator debug information to see why scopes where forked + * @return forked Scopes + */ + @NotNull + IScopes forkedRootScopes(final @NotNull String creator); + /** * Stores this Scopes in store, making it the current one that is used by static API. * @@ -414,6 +423,13 @@ default void configureScope(@NotNull ScopeCallback callback) { */ public @NotNull IScope getIsolationScope(); + /** + * Returns the global scope. + * + * @return global scope + */ + public @NotNull IScope getGlobalScope(); + /** * Captures the transaction and enqueues it for sending to Sentry server. * diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index ac1c542a94..06969518b7 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -185,6 +185,16 @@ public void flush(long timeoutMillis) {} return NoOpScope.getInstance(); } + @Override + public @NotNull IScope getGlobalScope() { + return NoOpScope.getInstance(); + } + + @Override + public @NotNull IScopes forkedRootScopes(final @NotNull String creator) { + return NoOpScopes.getInstance(); + } + @Override public @NotNull SentryId captureTransaction( final @NotNull SentryTransaction transaction, diff --git a/sentry/src/main/java/io/sentry/NoOpScopes.java b/sentry/src/main/java/io/sentry/NoOpScopes.java index de75ff8178..74f965f651 100644 --- a/sentry/src/main/java/io/sentry/NoOpScopes.java +++ b/sentry/src/main/java/io/sentry/NoOpScopes.java @@ -169,6 +169,11 @@ public void flush(long timeoutMillis) {} return NoOpScopes.getInstance(); } + @Override + public @NotNull IScopes forkedRootScopes(final @NotNull String creator) { + return NoOpScopes.getInstance(); + } + @Override public @NotNull ISentryLifecycleToken makeCurrent() { return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); @@ -184,6 +189,11 @@ public void flush(long timeoutMillis) {} return NoOpScope.getInstance(); } + @Override + public @NotNull IScope getGlobalScope() { + return NoOpScope.getInstance(); + } + @Override public @NotNull SentryId captureTransaction( final @NotNull SentryTransaction transaction, diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index d30a9b6074..bb83e5060c 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -115,6 +115,11 @@ public boolean isAncestorOf(final @Nullable Scopes otherScopes) { return new Scopes(scope.clone(), isolationScope, this, options, creator); } + @Override + public @NotNull IScopes forkedRootScopes(final @NotNull String creator) { + return Sentry.forkedRootScopes(creator); + } + // TODO [HSM] always read from root scope? @Override public boolean isEnabled() { @@ -582,7 +587,7 @@ private void updateLastEventId(final @NotNull SentryId lastEventId) { getCombinedScopeView().setLastEventId(lastEventId); } - // TODO [HSM] add to IScopes interface + @Override public @NotNull IScope getGlobalScope() { return Sentry.getGlobalScope(); } diff --git a/sentry/src/main/java/io/sentry/ScopesAdapter.java b/sentry/src/main/java/io/sentry/ScopesAdapter.java index 684ab12113..c7f11612e9 100644 --- a/sentry/src/main/java/io/sentry/ScopesAdapter.java +++ b/sentry/src/main/java/io/sentry/ScopesAdapter.java @@ -205,6 +205,11 @@ public void flush(long timeoutMillis) { return Sentry.forkedCurrentScope(creator); } + @Override + public @NotNull IScopes forkedRootScopes(final @NotNull String creator) { + return Sentry.forkedRootScopes(creator); + } + @Override public @NotNull ISentryLifecycleToken makeCurrent() { // TODO [HSM] this wouldn't do anything since it replaced the current with the same Scopes @@ -221,6 +226,11 @@ public void flush(long timeoutMillis) { return Sentry.getCurrentScopes().getIsolationScope(); } + @Override + public @NotNull IScope getGlobalScope() { + return Sentry.getGlobalScope(); + } + @ApiStatus.Internal @Override public @NotNull SentryId captureTransaction( diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index bd1fcee04b..fe5c8b4112 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -102,8 +102,6 @@ private Sentry() {} * @return the hub */ @ApiStatus.Internal - @ApiStatus.Experimental - @SuppressWarnings("deprecation") public static @NotNull IScopes forkedRootScopes(final @NotNull String creator) { if (globalHubMode) { return rootScopes; From d45c72148e9b5d249935f6d73b698dd112fe98f7 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 2 May 2024 14:04:26 +0200 Subject: [PATCH 31/91] Hubs/Scopes Merge 31 - Fix `EventProcessor` ordering on `Scopes` (#3360) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes --- .../api/sentry-android-core.api | 3 ++ .../android/core/AnrV2EventProcessor.java | 5 +++ .../core/DefaultAndroidEventProcessor.java | 5 +++ .../PerformanceAndroidEventProcessor.java | 5 +++ .../core/ScreenshotEventProcessor.java | 5 +++ .../core/ViewHierarchyEventProcessor.java | 5 +++ .../api/sentry-opentelemetry-core.api | 1 + .../OpenTelemetryLinkErrorEventProcessor.java | 5 +++ ...tryRequestHttpServletRequestProcessor.java | 5 +++ ...tryRequestHttpServletRequestProcessor.java | 5 +++ .../api/sentry-spring-jakarta.api | 2 + .../jakarta/ContextTagsEventProcessor.java | 5 +++ ...tryRequestHttpServletRequestProcessor.java | 6 +++ .../spring/jakarta/SentrySpringFilter.java | 6 +++ sentry-spring/api/sentry-spring.api | 2 + .../spring/ContextTagsEventProcessor.java | 5 +++ ...tryRequestHttpServletRequestProcessor.java | 6 +++ .../io/sentry/spring/SentrySpringFilter.java | 6 +++ sentry/api/sentry.api | 22 ++++++++++ .../java/io/sentry/CombinedScopeView.java | 19 ++++++--- ...eduplicateMultithreadedEventProcessor.java | 5 +++ ...DuplicateEventDetectionEventProcessor.java | 5 +++ .../main/java/io/sentry/EventProcessor.java | 11 +++++ sentry/src/main/java/io/sentry/IScope.java | 6 +++ .../java/io/sentry/MainEventProcessor.java | 5 +++ sentry/src/main/java/io/sentry/NoOpScope.java | 7 ++++ sentry/src/main/java/io/sentry/Scope.java | 19 ++++++++- .../src/main/java/io/sentry/SentryClient.java | 1 + .../sentry/SentryRuntimeEventProcessor.java | 5 +++ .../EventProcessorAndOrder.java | 34 +++++++++++++++ .../io/sentry/util/EventProcessorUtils.java | 23 ++++++++++ .../java/io/sentry/CombinedScopeViewTest.kt | 42 +++++++++++++++++++ 32 files changed, 278 insertions(+), 8 deletions(-) create mode 100644 sentry/src/main/java/io/sentry/internal/eventprocessor/EventProcessorAndOrder.java create mode 100644 sentry/src/main/java/io/sentry/util/EventProcessorUtils.java diff --git a/sentry-android-core/api/sentry-android-core.api b/sentry-android-core/api/sentry-android-core.api index 2afe788ef8..9ee8843eea 100644 --- a/sentry-android-core/api/sentry-android-core.api +++ b/sentry-android-core/api/sentry-android-core.api @@ -97,6 +97,7 @@ public final class io/sentry/android/core/AnrIntegrationFactory { public final class io/sentry/android/core/AnrV2EventProcessor : io/sentry/BackfillingEventProcessor { public fun (Landroid/content/Context;Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/android/core/BuildInfoProvider;)V + public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; public fun process (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryTransaction; } @@ -236,6 +237,7 @@ public final class io/sentry/android/core/PhoneStateBreadcrumbsIntegration : io/ public final class io/sentry/android/core/ScreenshotEventProcessor : io/sentry/EventProcessor { public fun (Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/android/core/BuildInfoProvider;)V + public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; public fun process (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryTransaction; } @@ -386,6 +388,7 @@ public final class io/sentry/android/core/UserInteractionIntegration : android/a public final class io/sentry/android/core/ViewHierarchyEventProcessor : io/sentry/EventProcessor { public fun (Lio/sentry/android/core/SentryAndroidOptions;)V + public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; public fun process (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryTransaction; public static fun snapshotViewHierarchy (Landroid/app/Activity;Lio/sentry/ILogger;)Lio/sentry/protocol/ViewHierarchy; diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2EventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2EventProcessor.java index 45f997542b..9ff1294338 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2EventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2EventProcessor.java @@ -429,6 +429,11 @@ private void setOptionsTags(final @NotNull SentryBaseEvent event) { } // endregion + @Override + public @Nullable Long getOrder() { + return 12000L; + } + // region static values private void setStaticValues(final @NotNull SentryEvent event) { mergeUser(event); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java index 45e4b78787..999f187fe5 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java @@ -303,4 +303,9 @@ private void setSideLoadedInfo(final @NotNull SentryBaseEvent event) { return transaction; } + + @Override + public @Nullable Long getOrder() { + return 8000L; + } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java index 2502722f85..6a1a7c67fa 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java @@ -254,4 +254,9 @@ private static SentrySpan timeSpanToSentrySpan( null, defaultSpanData); } + + @Override + public @Nullable Long getOrder() { + return 9000L; + } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ScreenshotEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/ScreenshotEventProcessor.java index 5e07a44078..87a2caf05f 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ScreenshotEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ScreenshotEventProcessor.java @@ -98,4 +98,9 @@ public ScreenshotEventProcessor( hint.set(ANDROID_ACTIVITY, activity); return event; } + + @Override + public @Nullable Long getOrder() { + return 10000L; + } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ViewHierarchyEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/ViewHierarchyEventProcessor.java index 30e9f8de11..f8f42ac145 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ViewHierarchyEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ViewHierarchyEventProcessor.java @@ -284,4 +284,9 @@ private static ViewHierarchyNode viewToNode(@NotNull final View view) { return node; } + + @Override + public @Nullable Long getOrder() { + return 11000L; + } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api index 18c73a9b68..78eee943ed 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api +++ b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api @@ -1,5 +1,6 @@ public final class io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor : io/sentry/EventProcessor { public fun ()V + public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java index bfc4cd05f1..9ed17b06dd 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java @@ -99,4 +99,9 @@ public OpenTelemetryLinkErrorEventProcessor() { return event; } + + @Override + public @Nullable Long getOrder() { + return 6000L; + } } diff --git a/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryRequestHttpServletRequestProcessor.java b/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryRequestHttpServletRequestProcessor.java index 17ac102090..1ee536cb92 100644 --- a/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryRequestHttpServletRequestProcessor.java +++ b/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryRequestHttpServletRequestProcessor.java @@ -56,4 +56,9 @@ public SentryRequestHttpServletRequestProcessor(@NotNull HttpServletRequest http private static @Nullable String toString(final @Nullable Enumeration enumeration) { return enumeration != null ? String.join(",", Collections.list(enumeration)) : null; } + + @Override + public @Nullable Long getOrder() { + return 4000L; + } } diff --git a/sentry-servlet/src/main/java/io/sentry/servlet/SentryRequestHttpServletRequestProcessor.java b/sentry-servlet/src/main/java/io/sentry/servlet/SentryRequestHttpServletRequestProcessor.java index b24c0446b0..a005d50c0a 100644 --- a/sentry-servlet/src/main/java/io/sentry/servlet/SentryRequestHttpServletRequestProcessor.java +++ b/sentry-servlet/src/main/java/io/sentry/servlet/SentryRequestHttpServletRequestProcessor.java @@ -56,4 +56,9 @@ public SentryRequestHttpServletRequestProcessor(@NotNull HttpServletRequest http private static @Nullable String toString(final @Nullable Enumeration enumeration) { return enumeration != null ? String.join(",", Collections.list(enumeration)) : null; } + + @Override + public @Nullable Long getOrder() { + return 4000L; + } } diff --git a/sentry-spring-jakarta/api/sentry-spring-jakarta.api b/sentry-spring-jakarta/api/sentry-spring-jakarta.api index 156ab1aaee..7f414c3495 100644 --- a/sentry-spring-jakarta/api/sentry-spring-jakarta.api +++ b/sentry-spring-jakarta/api/sentry-spring-jakarta.api @@ -5,6 +5,7 @@ public final class io/sentry/spring/jakarta/BuildConfig { public final class io/sentry/spring/jakarta/ContextTagsEventProcessor : io/sentry/EventProcessor { public fun (Lio/sentry/SentryOptions;)V + public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } @@ -43,6 +44,7 @@ public class io/sentry/spring/jakarta/SentryInitBeanPostProcessor : org/springfr public class io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessor : io/sentry/EventProcessor { public fun (Lio/sentry/spring/jakarta/tracing/TransactionNameProvider;Ljakarta/servlet/http/HttpServletRequest;)V + public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/ContextTagsEventProcessor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/ContextTagsEventProcessor.java index 0f00b4e9f7..94f49d8319 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/ContextTagsEventProcessor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/ContextTagsEventProcessor.java @@ -38,4 +38,9 @@ public ContextTagsEventProcessor(final @NotNull SentryOptions options) { } return event; } + + @Override + public @Nullable Long getOrder() { + return 14000L; + } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessor.java index fa782030c0..91b27ddeac 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessor.java @@ -8,6 +8,7 @@ import io.sentry.util.Objects; import jakarta.servlet.http.HttpServletRequest; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** Attaches transaction name from the HTTP request to {@link SentryEvent}. */ @Open @@ -30,4 +31,9 @@ public SentryRequestHttpServletRequestProcessor( } return event; } + + @Override + public @Nullable Long getOrder() { + return 5000L; + } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java index 2e3561a982..de8b5bce65 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java @@ -24,6 +24,7 @@ import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.springframework.http.MediaType; import org.springframework.util.MimeType; import org.springframework.web.filter.OncePerRequestFilter; @@ -157,5 +158,10 @@ public RequestBodyExtractingEventProcessor( } return event; } + + @Override + public @Nullable Long getOrder() { + return 3000L; + } } } diff --git a/sentry-spring/api/sentry-spring.api b/sentry-spring/api/sentry-spring.api index 58de26098f..7c6af0ecf5 100644 --- a/sentry-spring/api/sentry-spring.api +++ b/sentry-spring/api/sentry-spring.api @@ -5,6 +5,7 @@ public final class io/sentry/spring/BuildConfig { public final class io/sentry/spring/ContextTagsEventProcessor : io/sentry/EventProcessor { public fun (Lio/sentry/SentryOptions;)V + public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } @@ -43,6 +44,7 @@ public class io/sentry/spring/SentryInitBeanPostProcessor : org/springframework/ public class io/sentry/spring/SentryRequestHttpServletRequestProcessor : io/sentry/EventProcessor { public fun (Lio/sentry/spring/tracing/TransactionNameProvider;Ljavax/servlet/http/HttpServletRequest;)V + public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } diff --git a/sentry-spring/src/main/java/io/sentry/spring/ContextTagsEventProcessor.java b/sentry-spring/src/main/java/io/sentry/spring/ContextTagsEventProcessor.java index 79330bdf51..41ff04d0c4 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/ContextTagsEventProcessor.java +++ b/sentry-spring/src/main/java/io/sentry/spring/ContextTagsEventProcessor.java @@ -38,4 +38,9 @@ public ContextTagsEventProcessor(final @NotNull SentryOptions options) { } return event; } + + @Override + public @Nullable Long getOrder() { + return 14000L; + } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryRequestHttpServletRequestProcessor.java b/sentry-spring/src/main/java/io/sentry/spring/SentryRequestHttpServletRequestProcessor.java index 38c1067cab..426571ba6e 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryRequestHttpServletRequestProcessor.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryRequestHttpServletRequestProcessor.java @@ -8,6 +8,7 @@ import io.sentry.util.Objects; import javax.servlet.http.HttpServletRequest; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** Attaches transaction name from the HTTP request to {@link SentryEvent}. */ @Open @@ -30,4 +31,9 @@ public SentryRequestHttpServletRequestProcessor( } return event; } + + @Override + public @Nullable Long getOrder() { + return 5000L; + } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java b/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java index 252a07910c..af55ac2ce3 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java @@ -24,6 +24,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.springframework.http.MediaType; import org.springframework.util.MimeType; import org.springframework.web.filter.OncePerRequestFilter; @@ -157,5 +158,10 @@ public RequestBodyExtractingEventProcessor( } return event; } + + @Override + public @Nullable Long getOrder() { + return 3000L; + } } } diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 09d83295ce..675a21fd8b 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -255,6 +255,7 @@ public final class io/sentry/CombinedScopeView : io/sentry/IScope { public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getLevel ()Lio/sentry/SentryLevel; public fun getOptions ()Lio/sentry/SentryOptions; + public fun getOrderedEventProcessors ()Ljava/util/List; public fun getPropagationContext ()Lio/sentry/PropagationContext; public fun getRequest ()Lio/sentry/protocol/Request; public fun getScreen ()Ljava/lang/String; @@ -342,6 +343,7 @@ public final class io/sentry/DateUtils { public final class io/sentry/DeduplicateMultithreadedEventProcessor : io/sentry/EventProcessor { public fun (Lio/sentry/SentryOptions;)V + public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } @@ -377,6 +379,7 @@ public final class io/sentry/DsnUtil { public final class io/sentry/DuplicateEventDetectionEventProcessor : io/sentry/EventProcessor { public fun (Lio/sentry/SentryOptions;)V + public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } @@ -392,6 +395,7 @@ public final class io/sentry/EnvelopeSender : io/sentry/IEnvelopeSender { } public abstract interface class io/sentry/EventProcessor { + public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; public fun process (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryTransaction; } @@ -788,6 +792,7 @@ public abstract interface class io/sentry/IScope { public abstract fun getLastEventId ()Lio/sentry/protocol/SentryId; public abstract fun getLevel ()Lio/sentry/SentryLevel; public abstract fun getOptions ()Lio/sentry/SentryOptions; + public abstract fun getOrderedEventProcessors ()Ljava/util/List; public abstract fun getPropagationContext ()Lio/sentry/PropagationContext; public abstract fun getRequest ()Lio/sentry/protocol/Request; public abstract fun getScreen ()Ljava/lang/String; @@ -1161,6 +1166,7 @@ public abstract interface class io/sentry/JsonUnknown { public final class io/sentry/MainEventProcessor : io/sentry/EventProcessor, java/io/Closeable { public fun (Lio/sentry/SentryOptions;)V public fun close ()V + public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; public fun process (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryTransaction; } @@ -1448,6 +1454,7 @@ public final class io/sentry/NoOpScope : io/sentry/IScope { public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getLevel ()Lio/sentry/SentryLevel; public fun getOptions ()Lio/sentry/SentryOptions; + public fun getOrderedEventProcessors ()Ljava/util/List; public fun getPropagationContext ()Lio/sentry/PropagationContext; public fun getRequest ()Lio/sentry/protocol/Request; public fun getScreen ()Ljava/lang/String; @@ -1891,6 +1898,7 @@ public final class io/sentry/Scope : io/sentry/IScope { public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getLevel ()Lio/sentry/SentryLevel; public fun getOptions ()Lio/sentry/SentryOptions; + public fun getOrderedEventProcessors ()Ljava/util/List; public fun getPropagationContext ()Lio/sentry/PropagationContext; public fun getRequest ()Lio/sentry/protocol/Request; public fun getScreen ()Ljava/lang/String; @@ -1961,6 +1969,7 @@ public abstract class io/sentry/ScopeObserverAdapter : io/sentry/IScopeObserver } public final class io/sentry/ScopeType : java/lang/Enum { + public static final field COMBINED Lio/sentry/ScopeType; public static final field CURRENT Lio/sentry/ScopeType; public static final field GLOBAL Lio/sentry/ScopeType; public static final field ISOLATION Lio/sentry/ScopeType; @@ -3808,6 +3817,14 @@ public final class io/sentry/internal/debugmeta/ResourcesDebugMetaLoader : io/se public fun loadDebugMeta ()Ljava/util/List; } +public final class io/sentry/internal/eventprocessor/EventProcessorAndOrder : java/lang/Comparable { + public fun (Lio/sentry/EventProcessor;Ljava/lang/Long;)V + public fun compareTo (Lio/sentry/internal/eventprocessor/EventProcessorAndOrder;)I + public synthetic fun compareTo (Ljava/lang/Object;)I + public fun getEventProcessor ()Lio/sentry/EventProcessor; + public fun getOrder ()Ljava/lang/Long; +} + public abstract interface class io/sentry/internal/gestures/GestureTargetLocator { public abstract fun locate (Ljava/lang/Object;FFLio/sentry/internal/gestures/UiElement$Type;)Lio/sentry/internal/gestures/UiElement; } @@ -5384,6 +5401,11 @@ public final class io/sentry/util/DebugMetaPropertiesApplier { public static fun getProguardUuid (Ljava/util/Properties;)Ljava/lang/String; } +public final class io/sentry/util/EventProcessorUtils { + public fun ()V + public static fun unwrap (Ljava/util/List;)Ljava/util/List; +} + public final class io/sentry/util/ExceptionUtils { public fun ()V public static fun findRootCause (Ljava/lang/Throwable;)Ljava/lang/Throwable; diff --git a/sentry/src/main/java/io/sentry/CombinedScopeView.java b/sentry/src/main/java/io/sentry/CombinedScopeView.java index ee6fdad06e..332f77f128 100644 --- a/sentry/src/main/java/io/sentry/CombinedScopeView.java +++ b/sentry/src/main/java/io/sentry/CombinedScopeView.java @@ -1,9 +1,11 @@ package io.sentry; +import io.sentry.internal.eventprocessor.EventProcessorAndOrder; import io.sentry.protocol.Contexts; import io.sentry.protocol.Request; import io.sentry.protocol.SentryId; import io.sentry.protocol.User; +import io.sentry.util.EventProcessorUtils; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -341,15 +343,20 @@ public void clearAttachments() { } @Override - public @NotNull List getEventProcessors() { - // TODO [HSM] mechanism for ordering event processors - final @NotNull List allEventProcessors = new CopyOnWriteArrayList<>(); - allEventProcessors.addAll(globalScope.getEventProcessors()); - allEventProcessors.addAll(isolationScope.getEventProcessors()); - allEventProcessors.addAll(scope.getEventProcessors()); + public @NotNull List getOrderedEventProcessors() { + final @NotNull List allEventProcessors = new CopyOnWriteArrayList<>(); + allEventProcessors.addAll(globalScope.getOrderedEventProcessors()); + allEventProcessors.addAll(isolationScope.getOrderedEventProcessors()); + allEventProcessors.addAll(scope.getOrderedEventProcessors()); + Collections.sort(allEventProcessors); return allEventProcessors; } + @Override + public @NotNull List getEventProcessors() { + return EventProcessorUtils.unwrap(getOrderedEventProcessors()); + } + @Override public void addEventProcessor(@NotNull EventProcessor eventProcessor) { getDefaultWriteScope().addEventProcessor(eventProcessor); diff --git a/sentry/src/main/java/io/sentry/DeduplicateMultithreadedEventProcessor.java b/sentry/src/main/java/io/sentry/DeduplicateMultithreadedEventProcessor.java index 924b253db8..b5869a6379 100644 --- a/sentry/src/main/java/io/sentry/DeduplicateMultithreadedEventProcessor.java +++ b/sentry/src/main/java/io/sentry/DeduplicateMultithreadedEventProcessor.java @@ -62,4 +62,9 @@ public DeduplicateMultithreadedEventProcessor(final @NotNull SentryOptions optio processedEvents.put(type, currentEventTid); return event; } + + @Override + public @Nullable Long getOrder() { + return 7000L; + } } diff --git a/sentry/src/main/java/io/sentry/DuplicateEventDetectionEventProcessor.java b/sentry/src/main/java/io/sentry/DuplicateEventDetectionEventProcessor.java index e9b77a8860..5004e6514e 100644 --- a/sentry/src/main/java/io/sentry/DuplicateEventDetectionEventProcessor.java +++ b/sentry/src/main/java/io/sentry/DuplicateEventDetectionEventProcessor.java @@ -62,4 +62,9 @@ private static boolean containsAnyKey( } return causes; } + + @Override + public @Nullable Long getOrder() { + return 1000L; + } } diff --git a/sentry/src/main/java/io/sentry/EventProcessor.java b/sentry/src/main/java/io/sentry/EventProcessor.java index ba67508614..6a8f3c7057 100644 --- a/sentry/src/main/java/io/sentry/EventProcessor.java +++ b/sentry/src/main/java/io/sentry/EventProcessor.java @@ -32,4 +32,15 @@ default SentryEvent process(@NotNull SentryEvent event, @NotNull Hint hint) { default SentryTransaction process(@NotNull SentryTransaction transaction, @NotNull Hint hint) { return transaction; } + + /** + * Controls when this EventProcessor is invoked. + * + * @return order higher number = later, lower number = earlier (negative values may also be + * passed), null = latest (note: multiple event processors using null may lead to random + * ordering) + */ + default @Nullable Long getOrder() { + return null; + } } diff --git a/sentry/src/main/java/io/sentry/IScope.java b/sentry/src/main/java/io/sentry/IScope.java index d761ccc192..c16deae90f 100644 --- a/sentry/src/main/java/io/sentry/IScope.java +++ b/sentry/src/main/java/io/sentry/IScope.java @@ -1,5 +1,6 @@ package io.sentry; +import io.sentry.internal.eventprocessor.EventProcessorAndOrder; import io.sentry.protocol.Contexts; import io.sentry.protocol.Request; import io.sentry.protocol.SentryId; @@ -303,9 +304,14 @@ public interface IScope { * * @return the event processors list */ + @ApiStatus.Internal @NotNull List getEventProcessors(); + @ApiStatus.Internal + @NotNull + List getOrderedEventProcessors(); + /** * Adds an event processor to the Scope's event processors list * diff --git a/sentry/src/main/java/io/sentry/MainEventProcessor.java b/sentry/src/main/java/io/sentry/MainEventProcessor.java index abbf21c84e..23a14eb3a6 100644 --- a/sentry/src/main/java/io/sentry/MainEventProcessor.java +++ b/sentry/src/main/java/io/sentry/MainEventProcessor.java @@ -307,4 +307,9 @@ boolean isClosed() { HostnameCache getHostnameCache() { return hostnameCache; } + + @Override + public @Nullable Long getOrder() { + return 0L; + } } diff --git a/sentry/src/main/java/io/sentry/NoOpScope.java b/sentry/src/main/java/io/sentry/NoOpScope.java index 400a7e739f..c4195ed377 100644 --- a/sentry/src/main/java/io/sentry/NoOpScope.java +++ b/sentry/src/main/java/io/sentry/NoOpScope.java @@ -1,5 +1,6 @@ package io.sentry; +import io.sentry.internal.eventprocessor.EventProcessorAndOrder; import io.sentry.protocol.Contexts; import io.sentry.protocol.Request; import io.sentry.protocol.SentryId; @@ -183,6 +184,12 @@ public void clearAttachments() {} return new ArrayList<>(); } + @ApiStatus.Internal + @Override + public @NotNull List getOrderedEventProcessors() { + return new ArrayList<>(); + } + @Override public void addEventProcessor(@NotNull EventProcessor eventProcessor) {} diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index 665f789158..e0a5d5b257 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -1,5 +1,6 @@ package io.sentry; +import io.sentry.internal.eventprocessor.EventProcessorAndOrder; import io.sentry.protocol.App; import io.sentry.protocol.Contexts; import io.sentry.protocol.Request; @@ -7,6 +8,7 @@ import io.sentry.protocol.TransactionNameSource; import io.sentry.protocol.User; import io.sentry.util.CollectionUtils; +import io.sentry.util.EventProcessorUtils; import io.sentry.util.ExceptionUtils; import io.sentry.util.Objects; import io.sentry.util.Pair; @@ -61,7 +63,7 @@ public final class Scope implements IScope { private @NotNull Map extra = new ConcurrentHashMap<>(); /** Scope's event processor list */ - private @NotNull List eventProcessors = new CopyOnWriteArrayList<>(); + private @NotNull List eventProcessors = new CopyOnWriteArrayList<>(); /** Scope's SentryOptions */ private final @NotNull SentryOptions options; @@ -766,6 +768,19 @@ public void clearAttachments() { @NotNull @Override public List getEventProcessors() { + return EventProcessorUtils.unwrap(eventProcessors); + } + + /** + * Returns the Scope's event processors including their order + * + * @return the event processors list and their order + */ + @ApiStatus.Internal + @NotNull + @Override + public List getOrderedEventProcessors() { + // TODO [HSM] This isn't actually ordered but only gets ordered in CombinedScopeView return eventProcessors; } @@ -776,7 +791,7 @@ public List getEventProcessors() { */ @Override public void addEventProcessor(final @NotNull EventProcessor eventProcessor) { - eventProcessors.add(eventProcessor); + eventProcessors.add(new EventProcessorAndOrder(eventProcessor, eventProcessor.getOrder())); } /** diff --git a/sentry/src/main/java/io/sentry/SentryClient.java b/sentry/src/main/java/io/sentry/SentryClient.java index ea70371722..43db89a75b 100644 --- a/sentry/src/main/java/io/sentry/SentryClient.java +++ b/sentry/src/main/java/io/sentry/SentryClient.java @@ -139,6 +139,7 @@ private boolean shouldApplyScopeData(final @NotNull CheckIn event, final @NotNul } } + // TODO [HSM] EventProcessors from options are always executed after those from scopes event = processEvent(event, hint, options.getEventProcessors()); if (event != null) { diff --git a/sentry/src/main/java/io/sentry/SentryRuntimeEventProcessor.java b/sentry/src/main/java/io/sentry/SentryRuntimeEventProcessor.java index 9d0d6443aa..ca19a9ca74 100644 --- a/sentry/src/main/java/io/sentry/SentryRuntimeEventProcessor.java +++ b/sentry/src/main/java/io/sentry/SentryRuntimeEventProcessor.java @@ -42,4 +42,9 @@ public SentryRuntimeEventProcessor() { } return event; } + + @Override + public @Nullable Long getOrder() { + return 2000L; + } } diff --git a/sentry/src/main/java/io/sentry/internal/eventprocessor/EventProcessorAndOrder.java b/sentry/src/main/java/io/sentry/internal/eventprocessor/EventProcessorAndOrder.java new file mode 100644 index 0000000000..1f504f2555 --- /dev/null +++ b/sentry/src/main/java/io/sentry/internal/eventprocessor/EventProcessorAndOrder.java @@ -0,0 +1,34 @@ +package io.sentry.internal.eventprocessor; + +import io.sentry.EventProcessor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class EventProcessorAndOrder implements Comparable { + + private final @NotNull EventProcessor eventProcessor; + private final @NotNull Long order; + + public EventProcessorAndOrder( + final @NotNull EventProcessor eventProcessor, final @Nullable Long order) { + this.eventProcessor = eventProcessor; + if (order == null) { + this.order = System.nanoTime(); + } else { + this.order = order; + } + } + + public @NotNull EventProcessor getEventProcessor() { + return eventProcessor; + } + + public @NotNull Long getOrder() { + return order; + } + + @Override + public int compareTo(@NotNull EventProcessorAndOrder o) { + return order.compareTo(o.order); + } +} diff --git a/sentry/src/main/java/io/sentry/util/EventProcessorUtils.java b/sentry/src/main/java/io/sentry/util/EventProcessorUtils.java new file mode 100644 index 0000000000..b47d40ecd9 --- /dev/null +++ b/sentry/src/main/java/io/sentry/util/EventProcessorUtils.java @@ -0,0 +1,23 @@ +package io.sentry.util; + +import io.sentry.EventProcessor; +import io.sentry.internal.eventprocessor.EventProcessorAndOrder; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import org.jetbrains.annotations.Nullable; + +public final class EventProcessorUtils { + + public static List unwrap( + final @Nullable List orderedEventProcessor) { + final List eventProcessors = new CopyOnWriteArrayList<>(); + + if (orderedEventProcessor != null) { + for (EventProcessorAndOrder eventProcessorAndOrder : orderedEventProcessor) { + eventProcessors.add(eventProcessorAndOrder.getEventProcessor()); + } + } + + return eventProcessors; + } +} diff --git a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt index 38023da18e..d8e6783c4c 100644 --- a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt +++ b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt @@ -69,4 +69,46 @@ class CombinedScopeViewTest { assertEquals("current 3", breadcrumbs2.poll().message) assertEquals("current 4", breadcrumbs2.poll().message) } + + @Test + fun `event processors from options are not returned`() { + val options = SentryOptions().also { + it.addEventProcessor(MainEventProcessor(it)) + } + + val globalScope = Scope(options) + val isolationScope = Scope(options) + val scope = Scope(options) + + val combined = CombinedScopeView(globalScope, isolationScope, scope) + + assertEquals(0, combined.eventProcessors.size) + } + + @Test + fun `event processors from options and all scopes in order`() { + val options = SentryOptions() + + val globalScope = Scope(options) + val isolationScope = Scope(options) + val scope = Scope(options) + + val first = TestEventProcessor(0).also { scope.addEventProcessor(it) } + val second = TestEventProcessor(1000).also { globalScope.addEventProcessor(it) } + val third = TestEventProcessor(2000).also { isolationScope.addEventProcessor(it) } + val fourth = TestEventProcessor(3000).also { scope.addEventProcessor(it) } + + val combined = CombinedScopeView(globalScope, isolationScope, scope) + + val eventProcessors = combined.eventProcessors + + assertEquals(first, eventProcessors.get(0)) + assertEquals(second, eventProcessors.get(1)) + assertEquals(third, eventProcessors.get(2)) + assertEquals(fourth, eventProcessors.get(3)) + } + + class TestEventProcessor(val orderNumber: Long?) : EventProcessor { + override fun getOrder() = orderNumber + } } From 10f8e4422d0a728a359f52112cf4a41891115ee7 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 2 May 2024 14:05:35 +0200 Subject: [PATCH 32/91] Hubs/Scopes Merge 32 - Reuse code in Scopes (#3361) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes --- .../java/io/sentry/CombinedScopeView.java | 45 ++++++++++++------- sentry/src/main/java/io/sentry/Scope.java | 2 +- sentry/src/main/java/io/sentry/Scopes.java | 36 +++------------ 3 files changed, 34 insertions(+), 49 deletions(-) diff --git a/sentry/src/main/java/io/sentry/CombinedScopeView.java b/sentry/src/main/java/io/sentry/CombinedScopeView.java index 332f77f128..b125ce6c81 100644 --- a/sentry/src/main/java/io/sentry/CombinedScopeView.java +++ b/sentry/src/main/java/io/sentry/CombinedScopeView.java @@ -1,5 +1,7 @@ package io.sentry; +import static io.sentry.Scope.createBreadcrumbsList; + import io.sentry.internal.eventprocessor.EventProcessorAndOrder; import io.sentry.protocol.Contexts; import io.sentry.protocol.Request; @@ -173,17 +175,6 @@ public void setFingerprint(@NotNull List fingerprint) { return breadcrumbs; } - /** - * Creates a breadcrumb list with the max number of breadcrumbs - * - * @param maxBreadcrumb the max number of breadcrumbs - * @return the breadcrumbs queue - */ - // TODO [HSM] copied from Scope, should reuse instead - private @NotNull Queue createBreadcrumbsList(final int maxBreadcrumb) { - return SynchronizedQueue.synchronizedQueue(new CircularFifoQueue<>(maxBreadcrumb)); - } - @Override public void addBreadcrumb(@NotNull Breadcrumb breadcrumb, @Nullable Hint hint) { getDefaultWriteScope().addBreadcrumb(breadcrumb, hint); @@ -313,14 +304,34 @@ public void removeContexts(@NotNull String key) { } private @NotNull IScope getDefaultWriteScope() { - // TODO [HSM] use Scopes.getSpecificScope? - if (ScopeType.CURRENT.equals(getOptions().getDefaultScopeType())) { - return scope; + return getSpecificScope(null); + } + + IScope getSpecificScope(final @Nullable ScopeType scopeType) { + if (scopeType != null) { + switch (scopeType) { + case CURRENT: + return scope; + case ISOLATION: + return isolationScope; + case GLOBAL: + return globalScope; + default: + break; + } } - if (ScopeType.ISOLATION.equals(getOptions().getDefaultScopeType())) { - return isolationScope; + + switch (getOptions().getDefaultScopeType()) { + case CURRENT: + return scope; + case ISOLATION: + return isolationScope; + case GLOBAL: + return globalScope; + default: + // calm the compiler + return scope; } - return globalScope; } @Override diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index e0a5d5b257..521716b141 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -755,7 +755,7 @@ public void clearAttachments() { * @param maxBreadcrumb the max number of breadcrumbs * @return the breadcrumbs queue */ - private @NotNull Queue createBreadcrumbsList(final int maxBreadcrumb) { + static @NotNull Queue createBreadcrumbsList(final int maxBreadcrumb) { return SynchronizedQueue.synchronizedQueue(new CircularFifoQueue<>(maxBreadcrumb)); } diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index bb83e5060c..75300ec818 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -39,6 +39,8 @@ public final class Scopes implements IScopes, MetricsApi.IMetricsInterface { private final @NotNull TransactionPerformanceCollector transactionPerformanceCollector; private final @NotNull MetricsApi metricsApi; + private final @NotNull CombinedScopeView combinedScope; + Scopes( final @NotNull IScope scope, final @NotNull IScope isolationScope, @@ -55,6 +57,7 @@ private Scopes( final @NotNull String creator) { validateOptions(options); + this.combinedScope = new CombinedScopeView(getGlobalScope(), isolationScope, scope); this.scope = scope; this.isolationScope = isolationScope; this.parentScopes = parentScopes; @@ -368,8 +371,7 @@ public void endSession() { } private IScope getCombinedScopeView() { - // TODO [HSM] create in ctor? - return new CombinedScopeView(getGlobalScope(), isolationScope, scope); + return combinedScope; } @Override @@ -433,34 +435,6 @@ public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb, final @Nullable } } - private IScope getSpecificScope(final @Nullable ScopeType scopeType) { - // TODO [HSM] extract and reuse - if (scopeType != null) { - switch (scopeType) { - case CURRENT: - return scope; - case ISOLATION: - return isolationScope; - case GLOBAL: - return getGlobalScope(); - default: - break; - } - } - - switch (getOptions().getDefaultScopeType()) { - case CURRENT: - return scope; - case ISOLATION: - return isolationScope; - case GLOBAL: - return getGlobalScope(); - default: - // calm the compiler - return scope; - } - } - @Override public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb) { addBreadcrumb(breadcrumb, new Hint()); @@ -679,7 +653,7 @@ public void configureScope( "Instance is disabled and this 'configureScope' call is a no-op."); } else { try { - callback.run(getSpecificScope(scopeType)); + callback.run(combinedScope.getSpecificScope(scopeType)); } catch (Throwable e) { options.getLogger().log(SentryLevel.ERROR, "Error in the 'configureScope' callback.", e); } From 2e11284923bd379ee4058521466009c8251af44d Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 2 May 2024 14:15:20 +0200 Subject: [PATCH 33/91] Hubs/Scopes Merge 33 - No longer replace global scope (#3362) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope --- .../android/core/InternalSentrySdk.java | 1 + .../io/sentry/android/core/SentryAndroid.java | 4 + sentry/api/sentry.api | 7 +- .../java/io/sentry/CombinedScopeView.java | 9 +- sentry/src/main/java/io/sentry/IScope.java | 3 + sentry/src/main/java/io/sentry/NoOpScope.java | 3 + sentry/src/main/java/io/sentry/Scope.java | 21 +- sentry/src/main/java/io/sentry/Scopes.java | 206 ++++++++++-------- sentry/src/main/java/io/sentry/Sentry.java | 12 +- 9 files changed, 160 insertions(+), 106 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java index 692d8562f8..3170a4f1ec 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java @@ -44,6 +44,7 @@ public final class InternalSentrySdk { @Nullable public static IScope getCurrentScope() { final @NotNull AtomicReference scopeRef = new AtomicReference<>(); + // TODO [HSM] should this retrieve combined scope? ScopesAdapter.getInstance() .configureScope( scope -> { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java index 424de4d82e..b677cc5e34 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java @@ -24,6 +24,10 @@ /** Sentry initialization class */ public final class SentryAndroid { + static { + Sentry.getGlobalScope().replaceOptions(new SentryAndroidOptions()); + } + // SystemClock.uptimeMillis() isn't affected by phone provider or clock changes. private static final long sdkInitMillis = SystemClock.uptimeMillis(); diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 675a21fd8b..d16d15b44b 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -268,6 +268,7 @@ public final class io/sentry/CombinedScopeView : io/sentry/IScope { public fun removeContexts (Ljava/lang/String;)V public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V + public fun replaceOptions (Lio/sentry/SentryOptions;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Boolean;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Character;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Number;)V @@ -805,7 +806,7 @@ public abstract interface class io/sentry/IScope { public abstract fun removeContexts (Ljava/lang/String;)V public abstract fun removeExtra (Ljava/lang/String;)V public abstract fun removeTag (Ljava/lang/String;)V - public abstract fun setClient (Lio/sentry/ISentryClient;)V + public abstract fun replaceOptions (Lio/sentry/SentryOptions;)V public abstract fun setContexts (Ljava/lang/String;Ljava/lang/Boolean;)V public abstract fun setContexts (Ljava/lang/String;Ljava/lang/Character;)V public abstract fun setContexts (Ljava/lang/String;Ljava/lang/Number;)V @@ -1467,7 +1468,7 @@ public final class io/sentry/NoOpScope : io/sentry/IScope { public fun removeContexts (Ljava/lang/String;)V public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V - public fun setClient (Lio/sentry/ISentryClient;)V + public fun replaceOptions (Lio/sentry/SentryOptions;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Boolean;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Character;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Number;)V @@ -1911,7 +1912,7 @@ public final class io/sentry/Scope : io/sentry/IScope { public fun removeContexts (Ljava/lang/String;)V public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V - public fun setClient (Lio/sentry/ISentryClient;)V + public fun replaceOptions (Lio/sentry/SentryOptions;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Boolean;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Character;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Number;)V diff --git a/sentry/src/main/java/io/sentry/CombinedScopeView.java b/sentry/src/main/java/io/sentry/CombinedScopeView.java index b125ce6c81..38873bb574 100644 --- a/sentry/src/main/java/io/sentry/CombinedScopeView.java +++ b/sentry/src/main/java/io/sentry/CombinedScopeView.java @@ -16,6 +16,7 @@ import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -395,7 +396,7 @@ public void withTransaction(Scope.@NotNull IWithTransaction callback) { @Override public @NotNull SentryOptions getOptions() { - return scope.getOptions(); + return globalScope.getOptions(); } @Override @@ -474,4 +475,10 @@ public void setSpanContext( @NotNull Throwable throwable, @NotNull ISpan span, @NotNull String transactionName) { globalScope.setSpanContext(throwable, span, transactionName); } + + @ApiStatus.Internal + @Override + public void replaceOptions(@NotNull SentryOptions options) { + globalScope.replaceOptions(options); + } } diff --git a/sentry/src/main/java/io/sentry/IScope.java b/sentry/src/main/java/io/sentry/IScope.java index c16deae90f..8259a225ac 100644 --- a/sentry/src/main/java/io/sentry/IScope.java +++ b/sentry/src/main/java/io/sentry/IScope.java @@ -396,4 +396,7 @@ void setSpanContext( final @NotNull Throwable throwable, final @NotNull ISpan span, final @NotNull String transactionName); + + @ApiStatus.Internal + void replaceOptions(final @NotNull SentryOptions options); } diff --git a/sentry/src/main/java/io/sentry/NoOpScope.java b/sentry/src/main/java/io/sentry/NoOpScope.java index c4195ed377..5335f49519 100644 --- a/sentry/src/main/java/io/sentry/NoOpScope.java +++ b/sentry/src/main/java/io/sentry/NoOpScope.java @@ -276,4 +276,7 @@ public void assignTraceContext(@NotNull SentryEvent event) {} @Override public void setSpanContext( @NotNull Throwable throwable, @NotNull ISpan span, @NotNull String transactionName) {} + + @Override + public void replaceOptions(@NotNull SentryOptions options) {} } diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index 521716b141..e894445bd8 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -54,7 +54,7 @@ public final class Scope implements IScope { private @NotNull List fingerprint = new ArrayList<>(); /** Scope's breadcrumb queue */ - private final @NotNull Queue breadcrumbs; + private volatile @NotNull Queue breadcrumbs; /** Scope's tags */ private @NotNull Map tags = new ConcurrentHashMap<>(); @@ -66,7 +66,7 @@ public final class Scope implements IScope { private @NotNull List eventProcessors = new CopyOnWriteArrayList<>(); /** Scope's SentryOptions */ - private final @NotNull SentryOptions options; + private volatile @NotNull SentryOptions options; // TODO Consider: Scope clone doesn't clone sessions @@ -1038,6 +1038,23 @@ public void setSpanContext( } } + @ApiStatus.Internal + @Override + public void replaceOptions(final @NotNull SentryOptions options) { + // TODO [HSM] check if already enabled and noop in that case? + // if (!isEnabled()) {} + this.options = options; + final Queue oldBreadcrumbs = breadcrumbs; + breadcrumbs = createBreadcrumbsList(options.getMaxBreadcrumbs()); + for (Breadcrumb breadcrumb : oldBreadcrumbs) { + /* + this should trigger beforeBreadcrumb + and notify observers for breadcrumbs added before options where customized in Sentry.init + */ + addBreadcrumb(breadcrumb); + } + } + /** The IWithTransaction callback */ @ApiStatus.Internal public interface IWithTransaction { diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 75300ec818..cbeb9ae88f 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -31,9 +31,6 @@ public final class Scopes implements IScopes, MetricsApi.IMetricsInterface { private final @Nullable Scopes parentScopes; private final @NotNull String creator; - - // TODO [HSM] should this be set on all scopes (global, isolation, current)? - private final @NotNull SentryOptions options; private volatile boolean isEnabled; private final @NotNull TracesSampler tracesSampler; private final @NotNull TransactionPerformanceCollector transactionPerformanceCollector; @@ -44,28 +41,27 @@ public final class Scopes implements IScopes, MetricsApi.IMetricsInterface { Scopes( final @NotNull IScope scope, final @NotNull IScope isolationScope, - final @NotNull SentryOptions options, final @NotNull String creator) { - this(scope, isolationScope, null, options, creator); + this(scope, isolationScope, null, creator); } private Scopes( final @NotNull IScope scope, final @NotNull IScope isolationScope, final @Nullable Scopes parentScopes, - final @NotNull SentryOptions options, final @NotNull String creator) { - validateOptions(options); - this.combinedScope = new CombinedScopeView(getGlobalScope(), isolationScope, scope); this.scope = scope; this.isolationScope = isolationScope; this.parentScopes = parentScopes; this.creator = creator; - this.options = options; + + final @NotNull SentryOptions options = getOptions(); + validateOptions(options); this.tracesSampler = new TracesSampler(options); this.transactionPerformanceCollector = options.getTransactionPerformanceCollector(); + // TODO [HSM] Checking isEnabled may not be what we want with global scope anymore this.isEnabled = true; this.metricsApi = new MetricsApi(this); @@ -110,12 +106,12 @@ public boolean isAncestorOf(final @Nullable Scopes otherScopes) { @Override public @NotNull IScopes forkedScopes(final @NotNull String creator) { - return new Scopes(scope.clone(), isolationScope.clone(), this, options, creator); + return new Scopes(scope.clone(), isolationScope.clone(), this, creator); } @Override public @NotNull IScopes forkedCurrentScope(final @NotNull String creator) { - return new Scopes(scope.clone(), isolationScope, this, options, creator); + return new Scopes(scope.clone(), isolationScope, this, creator); } @Override @@ -146,12 +142,12 @@ public boolean isEnabled() { final @Nullable ScopeCallback scopeCallback) { SentryId sentryId = SentryId.EMPTY_ID; if (!isEnabled()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'captureEvent' call is a no-op."); } else if (event == null) { - options.getLogger().log(SentryLevel.WARNING, "captureEvent called with null parameter."); + getOptions().getLogger().log(SentryLevel.WARNING, "captureEvent called with null parameter."); } else { try { assignTraceContext(event); @@ -160,7 +156,7 @@ public boolean isEnabled() { sentryId = getClient().captureEvent(event, localScope, hint); updateLastEventId(sentryId); } catch (Throwable e) { - options + getOptions() .getLogger() .log( SentryLevel.ERROR, "Error while capturing event with id: " + event.getEventId(), e); @@ -185,7 +181,9 @@ private IScope buildLocalScope( callback.run(localScope); return localScope; } catch (Throwable t) { - options.getLogger().log(SentryLevel.ERROR, "Error in the 'ScopeCallback' callback.", t); + getOptions() + .getLogger() + .log(SentryLevel.ERROR, "Error in the 'ScopeCallback' callback.", t); } } return parentScope; @@ -211,20 +209,24 @@ private IScope buildLocalScope( final @Nullable ScopeCallback scopeCallback) { SentryId sentryId = SentryId.EMPTY_ID; if (!isEnabled()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'captureMessage' call is a no-op."); } else if (message == null) { - options.getLogger().log(SentryLevel.WARNING, "captureMessage called with null parameter."); + getOptions() + .getLogger() + .log(SentryLevel.WARNING, "captureMessage called with null parameter."); } else { try { final IScope localScope = buildLocalScope(getCombinedScopeView(), scopeCallback); sentryId = getClient().captureMessage(message, level, localScope); } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error while capturing message: " + message, e); + getOptions() + .getLogger() + .log(SentryLevel.ERROR, "Error while capturing message: " + message, e); } } updateLastEventId(sentryId); @@ -239,7 +241,7 @@ private IScope buildLocalScope( SentryId sentryId = SentryId.EMPTY_ID; if (!isEnabled()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -251,7 +253,7 @@ private IScope buildLocalScope( sentryId = capturedEnvelopeId; } } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error while capturing envelope.", e); + getOptions().getLogger().log(SentryLevel.ERROR, "Error while capturing envelope.", e); } } return sentryId; @@ -278,13 +280,15 @@ private IScope buildLocalScope( final @Nullable ScopeCallback scopeCallback) { SentryId sentryId = SentryId.EMPTY_ID; if (!isEnabled()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'captureException' call is a no-op."); } else if (throwable == null) { - options.getLogger().log(SentryLevel.WARNING, "captureException called with null parameter."); + getOptions() + .getLogger() + .log(SentryLevel.WARNING, "captureException called with null parameter."); } else { try { final SentryEvent event = new SentryEvent(throwable); @@ -294,7 +298,7 @@ private IScope buildLocalScope( sentryId = getClient().captureEvent(event, localScope, hint); } catch (Throwable e) { - options + getOptions() .getLogger() .log( SentryLevel.ERROR, "Error while capturing exception: " + throwable.getMessage(), e); @@ -307,7 +311,7 @@ private IScope buildLocalScope( @Override public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { if (!isEnabled()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -316,7 +320,7 @@ public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { try { getClient().captureUserFeedback(userFeedback); } catch (Throwable e) { - options + getOptions() .getLogger() .log( SentryLevel.ERROR, @@ -329,7 +333,7 @@ public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { @Override public void startSession() { if (!isEnabled()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'startSession' call is a no-op."); @@ -349,7 +353,7 @@ public void startSession() { getClient().captureSession(pair.getCurrent(), hint); } else { - options.getLogger().log(SentryLevel.WARNING, "Session could not be started."); + getOptions().getLogger().log(SentryLevel.WARNING, "Session could not be started."); } } } @@ -357,7 +361,7 @@ public void startSession() { @Override public void endSession() { if (!isEnabled()) { - options + getOptions() .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'endSession' call is a no-op."); } else { @@ -383,17 +387,17 @@ public void close() { @SuppressWarnings("FutureReturnValueIgnored") public void close(final boolean isRestarting) { if (!isEnabled()) { - options + getOptions() .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'close' call is a no-op."); } else { try { - for (Integration integration : options.getIntegrations()) { + for (Integration integration : getOptions().getIntegrations()) { if (integration instanceof Closeable) { try { ((Closeable) integration).close(); } catch (IOException e) { - options + getOptions() .getLogger() .log(SentryLevel.WARNING, "Failed to close the integration {}.", integration, e); } @@ -402,19 +406,20 @@ public void close(final boolean isRestarting) { // TODO [HSM] which scopes do we call this on? isolation and current scope? configureScope(scope -> scope.clear()); - options.getTransactionProfiler().close(); - options.getTransactionPerformanceCollector().close(); - final @NotNull ISentryExecutorService executorService = options.getExecutorService(); + getOptions().getTransactionProfiler().close(); + getOptions().getTransactionPerformanceCollector().close(); + final @NotNull ISentryExecutorService executorService = getOptions().getExecutorService(); if (isRestarting) { - executorService.submit(() -> executorService.close(options.getShutdownTimeoutMillis())); + executorService.submit( + () -> executorService.close(getOptions().getShutdownTimeoutMillis())); } else { - executorService.close(options.getShutdownTimeoutMillis()); + executorService.close(getOptions().getShutdownTimeoutMillis()); } // TODO: should we end session before closing client? getClient().close(isRestarting); } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error while closing the Hub.", e); + getOptions().getLogger().log(SentryLevel.ERROR, "Error while closing the Hub.", e); } isEnabled = false; } @@ -423,13 +428,15 @@ public void close(final boolean isRestarting) { @Override public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb, final @Nullable Hint hint) { if (!isEnabled()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'addBreadcrumb' call is a no-op."); } else if (breadcrumb == null) { - options.getLogger().log(SentryLevel.WARNING, "addBreadcrumb called with null parameter."); + getOptions() + .getLogger() + .log(SentryLevel.WARNING, "addBreadcrumb called with null parameter."); } else { getCombinedScopeView().addBreadcrumb(breadcrumb, hint); } @@ -443,7 +450,7 @@ public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb) { @Override public void setLevel(final @Nullable SentryLevel level) { if (!isEnabled()) { - options + getOptions() .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'setLevel' call is a no-op."); } else { @@ -454,7 +461,7 @@ public void setLevel(final @Nullable SentryLevel level) { @Override public void setTransaction(final @Nullable String transaction) { if (!isEnabled()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -462,14 +469,14 @@ public void setTransaction(final @Nullable String transaction) { } else if (transaction != null) { getCombinedScopeView().setTransaction(transaction); } else { - options.getLogger().log(SentryLevel.WARNING, "Transaction cannot be null"); + getOptions().getLogger().log(SentryLevel.WARNING, "Transaction cannot be null"); } } @Override public void setUser(final @Nullable User user) { if (!isEnabled()) { - options + getOptions() .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'setUser' call is a no-op."); } else { @@ -480,13 +487,15 @@ public void setUser(final @Nullable User user) { @Override public void setFingerprint(final @NotNull List fingerprint) { if (!isEnabled()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'setFingerprint' call is a no-op."); } else if (fingerprint == null) { - options.getLogger().log(SentryLevel.WARNING, "setFingerprint called with null parameter."); + getOptions() + .getLogger() + .log(SentryLevel.WARNING, "setFingerprint called with null parameter."); } else { getCombinedScopeView().setFingerprint(fingerprint); } @@ -495,7 +504,7 @@ public void setFingerprint(final @NotNull List fingerprint) { @Override public void clearBreadcrumbs() { if (!isEnabled()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -508,11 +517,11 @@ public void clearBreadcrumbs() { @Override public void setTag(final @NotNull String key, final @NotNull String value) { if (!isEnabled()) { - options + getOptions() .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'setTag' call is a no-op."); } else if (key == null || value == null) { - options.getLogger().log(SentryLevel.WARNING, "setTag called with null parameter."); + getOptions().getLogger().log(SentryLevel.WARNING, "setTag called with null parameter."); } else { getCombinedScopeView().setTag(key, value); } @@ -521,11 +530,11 @@ public void setTag(final @NotNull String key, final @NotNull String value) { @Override public void removeTag(final @NotNull String key) { if (!isEnabled()) { - options + getOptions() .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'removeTag' call is a no-op."); } else if (key == null) { - options.getLogger().log(SentryLevel.WARNING, "removeTag called with null parameter."); + getOptions().getLogger().log(SentryLevel.WARNING, "removeTag called with null parameter."); } else { getCombinedScopeView().removeTag(key); } @@ -534,11 +543,11 @@ public void removeTag(final @NotNull String key) { @Override public void setExtra(final @NotNull String key, final @NotNull String value) { if (!isEnabled()) { - options + getOptions() .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'setExtra' call is a no-op."); } else if (key == null || value == null) { - options.getLogger().log(SentryLevel.WARNING, "setExtra called with null parameter."); + getOptions().getLogger().log(SentryLevel.WARNING, "setExtra called with null parameter."); } else { getCombinedScopeView().setExtra(key, value); } @@ -547,11 +556,11 @@ public void setExtra(final @NotNull String key, final @NotNull String value) { @Override public void removeExtra(final @NotNull String key) { if (!isEnabled()) { - options + getOptions() .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'removeExtra' call is a no-op."); } else if (key == null) { - options.getLogger().log(SentryLevel.WARNING, "removeExtra called with null parameter."); + getOptions().getLogger().log(SentryLevel.WARNING, "removeExtra called with null parameter."); } else { getCombinedScopeView().removeExtra(key); } @@ -574,7 +583,7 @@ private void updateLastEventId(final @NotNull SentryId lastEventId) { @Override public ISentryLifecycleToken pushScope() { if (!isEnabled()) { - options + getOptions() .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'pushScope' call is a no-op."); return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); @@ -587,7 +596,7 @@ public ISentryLifecycleToken pushScope() { @Override public ISentryLifecycleToken pushIsolationScope() { if (!isEnabled()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -608,7 +617,7 @@ public ISentryLifecycleToken pushIsolationScope() { @Override public void popScope() { if (!isEnabled()) { - options + getOptions() .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'popScope' call is a no-op."); } else { @@ -627,7 +636,7 @@ public void withScope(final @NotNull ScopeCallback callback) { try { callback.run(NoOpScope.getInstance()); } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error in the 'withScope' callback.", e); + getOptions().getLogger().log(SentryLevel.ERROR, "Error in the 'withScope' callback.", e); } } else { @@ -637,7 +646,7 @@ public void withScope(final @NotNull ScopeCallback callback) { try { callback.run(forkedScopes.getScope()); } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error in the 'withScope' callback.", e); + getOptions().getLogger().log(SentryLevel.ERROR, "Error in the 'withScope' callback.", e); } } } @@ -646,7 +655,7 @@ public void withScope(final @NotNull ScopeCallback callback) { public void configureScope( final @Nullable ScopeType scopeType, final @NotNull ScopeCallback callback) { if (!isEnabled()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -655,7 +664,9 @@ public void configureScope( try { callback.run(combinedScope.getSpecificScope(scopeType)); } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error in the 'configureScope' callback.", e); + getOptions() + .getLogger() + .log(SentryLevel.ERROR, "Error in the 'configureScope' callback.", e); } } } @@ -663,15 +674,15 @@ public void configureScope( @Override public void bindClient(final @NotNull ISentryClient client) { if (!isEnabled()) { - options + getOptions() .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'bindClient' call is a no-op."); } else { if (client != null) { - options.getLogger().log(SentryLevel.DEBUG, "New client bound to scope."); + getOptions().getLogger().log(SentryLevel.DEBUG, "New client bound to scope."); getCombinedScopeView().bindClient(client); } else { - options.getLogger().log(SentryLevel.DEBUG, "NoOp client bound to scope."); + getOptions().getLogger().log(SentryLevel.DEBUG, "NoOp client bound to scope."); getCombinedScopeView().bindClient(NoOpSentryClient.getInstance()); } } @@ -685,14 +696,14 @@ public boolean isHealthy() { @Override public void flush(long timeoutMillis) { if (!isEnabled()) { - options + getOptions() .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'flush' call is a no-op."); } else { try { getClient().flush(timeoutMillis); } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error in the 'client.flush'.", e); + getOptions().getLogger().log(SentryLevel.ERROR, "Error in the 'client.flush'.", e); } } } @@ -701,7 +712,7 @@ public void flush(long timeoutMillis) { @SuppressWarnings("deprecation") public @NotNull IHub clone() { if (!isEnabled()) { - options.getLogger().log(SentryLevel.WARNING, "Disabled Hub cloned."); + getOptions().getLogger().log(SentryLevel.WARNING, "Disabled Hub cloned."); } // TODO [HSM] should this fork isolation scope as well? return new HubScopesWrapper(forkedCurrentScope("scopes clone")); @@ -718,14 +729,14 @@ public void flush(long timeoutMillis) { SentryId sentryId = SentryId.EMPTY_ID; if (!isEnabled()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'captureTransaction' call is a no-op."); } else { if (!transaction.isFinished()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -733,18 +744,18 @@ public void flush(long timeoutMillis) { transaction.getEventId()); } else { if (!Boolean.TRUE.equals(transaction.isSampled())) { - options + getOptions() .getLogger() .log( SentryLevel.DEBUG, "Transaction %s was dropped due to sampling decision.", transaction.getEventId()); - if (options.getBackpressureMonitor().getDownsampleFactor() > 0) { - options + if (getOptions().getBackpressureMonitor().getDownsampleFactor() > 0) { + getOptions() .getClientReportRecorder() .recordLostEvent(DiscardReason.BACKPRESSURE, DataCategory.Transaction); } else { - options + getOptions() .getClientReportRecorder() .recordLostEvent(DiscardReason.SAMPLE_RATE, DataCategory.Transaction); } @@ -759,7 +770,7 @@ public void flush(long timeoutMillis) { hint, profilingTraceData); } catch (Throwable e) { - options + getOptions() .getLogger() .log( SentryLevel.ERROR, @@ -786,23 +797,23 @@ public void flush(long timeoutMillis) { ITransaction transaction; if (!isEnabled()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'startTransaction' returns a no-op."); transaction = NoOpTransaction.getInstance(); - } else if (!options.getInstrumenter().equals(transactionContext.getInstrumenter())) { - options + } else if (!getOptions().getInstrumenter().equals(transactionContext.getInstrumenter())) { + getOptions() .getLogger() .log( SentryLevel.DEBUG, "Returning no-op for instrumenter %s as the SDK has been configured to use instrumenter %s", transactionContext.getInstrumenter(), - options.getInstrumenter()); + getOptions().getInstrumenter()); transaction = NoOpTransaction.getInstance(); - } else if (!options.isTracingEnabled()) { - options + } else if (!getOptions().isTracingEnabled()) { + getOptions() .getLogger() .log( SentryLevel.INFO, "Tracing is disabled and this 'startTransaction' returns a no-op."); @@ -820,7 +831,7 @@ public void flush(long timeoutMillis) { // The listener is called only if the transaction exists, as the transaction is needed to // stop it if (samplingDecision.getSampled() && samplingDecision.getProfileSampled()) { - final ITransactionProfiler transactionProfiler = options.getTransactionProfiler(); + final ITransactionProfiler transactionProfiler = getOptions().getTransactionProfiler(); // If the profiler is not running, we start and bind it here. if (!transactionProfiler.isRunning()) { transactionProfiler.start(); @@ -857,7 +868,7 @@ public void setSpanContext( public @Nullable ISpan getSpan() { ISpan span = null; if (!isEnabled()) { - options + getOptions() .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'getSpan' call is a no-op."); } else { @@ -871,7 +882,7 @@ public void setSpanContext( public @Nullable ITransaction getTransaction() { ITransaction span = null; if (!isEnabled()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -884,19 +895,20 @@ public void setSpanContext( @Override public @NotNull SentryOptions getOptions() { - return options; + return combinedScope.getOptions(); } @Override public @Nullable Boolean isCrashedLastRun() { return SentryCrashLastRunState.getInstance() - .isCrashedLastRun(options.getCacheDirPath(), !options.isEnableAutoSessionTracking()); + .isCrashedLastRun( + getOptions().getCacheDirPath(), !getOptions().isEnableAutoSessionTracking()); } @Override public void reportFullyDisplayed() { - if (options.isEnableTimeToFullDisplayTracing()) { - options.getFullyDisplayedReporter().reportFullyDrawn(); + if (getOptions().isEnableTimeToFullDisplayTracing()) { + getOptions().getFullyDisplayedReporter().reportFullyDrawn(); } } @@ -911,7 +923,7 @@ public void reportFullyDisplayed() { (scope) -> { scope.setPropagationContext(propagationContext); }); - if (options.isTracingEnabled()) { + if (getOptions().isTracingEnabled()) { return TransactionContext.fromPropagationContext(propagationContext); } else { return null; @@ -921,7 +933,7 @@ public void reportFullyDisplayed() { @Override public @Nullable SentryTraceHeader getTraceparent() { if (!isEnabled()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -940,7 +952,7 @@ public void reportFullyDisplayed() { @Override public @Nullable BaggageHeader getBaggage() { if (!isEnabled()) { - options + getOptions() .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'getBaggage' call is a no-op."); } else { @@ -959,7 +971,7 @@ public void reportFullyDisplayed() { public @NotNull SentryId captureCheckIn(final @NotNull CheckIn checkIn) { SentryId sentryId = SentryId.EMPTY_ID; if (!isEnabled()) { - options + getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -968,7 +980,9 @@ public void reportFullyDisplayed() { try { sentryId = getClient().captureCheckIn(checkIn, getCombinedScopeView(), null); } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error while capturing check-in for slug", e); + getOptions() + .getLogger() + .log(SentryLevel.ERROR, "Error while capturing check-in for slug", e); } } updateLastEventId(sentryId); @@ -993,17 +1007,17 @@ public void reportFullyDisplayed() { @Override public @NotNull Map getDefaultTagsForMetrics() { - if (!options.isEnableDefaultTagsForMetrics()) { + if (!getOptions().isEnableDefaultTagsForMetrics()) { return Collections.emptyMap(); } final @NotNull Map tags = new HashMap<>(); - final @Nullable String release = options.getRelease(); + final @Nullable String release = getOptions().getRelease(); if (release != null) { tags.put("release", release); } - final @Nullable String environment = options.getEnvironment(); + final @Nullable String environment = getOptions().getEnvironment(); if (environment != null) { tags.put("environment", environment); } @@ -1026,7 +1040,7 @@ public void reportFullyDisplayed() { @Override public @Nullable LocalMetricsAggregator getLocalMetricsAggregator() { - if (!options.isEnableSpanLocalMetricAggregation()) { + if (!getOptions().isEnableSpanLocalMetricAggregation()) { return null; } final @Nullable ISpan span = getSpan(); diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index fe5c8b4112..38d033f28a 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -47,7 +47,12 @@ private Sentry() {} /** The root Scopes or NoOp if Sentry is disabled. */ private static volatile @NotNull IScopes rootScopes = NoOpScopes.getInstance(); - // TODO [HSM] cannot pass options here + /** + * This initializes global scope with default options. Options will later be replaced on + * Sentry.init + * + *

For Android options will also be (temporarily) replaced by SentryAndroid static block. + */ private static volatile @NotNull IScope globalScope = new Scope(new SentryOptions()); /** Default value for globalHubMode is false */ @@ -265,14 +270,13 @@ private static synchronized void init( options.getLogger().log(SentryLevel.INFO, "GlobalHubMode: '%s'", String.valueOf(globalHubMode)); Sentry.globalHubMode = globalHubMode; + globalScope.replaceOptions(options); final IScopes scopes = getCurrentScopes(); final IScope rootScope = new Scope(options); final IScope rootIsolationScope = new Scope(options); - // TODO [HSM] shouldn't replace global scope - globalScope = new Scope(options); globalScope.bindClient(new SentryClient(options)); - rootScopes = new Scopes(rootScope, rootIsolationScope, options, "Sentry.init"); + rootScopes = new Scopes(rootScope, rootIsolationScope, "Sentry.init"); getScopesStorage().set(rootScopes); From dcda5c70e905862380f4037909fd7f338e950b0a Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 2 May 2024 14:21:55 +0200 Subject: [PATCH 34/91] Hubs/Scopes Merge 34 - Replace hub occurrences in comments, var names, tests, etc. (#3366) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. --- .../ActivityBreadcrumbsIntegrationTest.kt | 42 ++-- .../core/ActivityLifecycleIntegrationTest.kt | 212 +++++++++--------- .../EnvelopeFileObserverIntegrationTest.kt | 8 +- .../android/core/InternalSentrySdkTest.kt | 29 ++- .../sentry/android/core/SentryAndroidTest.kt | 6 +- .../core/UserInteractionIntegrationTest.kt | 18 +- .../SentryInstrumentationAnotherTest.kt | 2 - .../graphql/SentryInstrumentationTest.kt | 2 - .../samples/android/ProfilingActivity.kt | 4 +- .../spring/boot/jakarta/SentryProperties.java | 3 +- .../webflux/AbstractSentryWebFilter.java | 23 +- .../webflux/SentryWebFluxTracingFilterTest.kt | 2 - .../java/io/sentry/spring/EnableSentry.java | 2 +- .../io/sentry/spring/SentryTaskDecorator.java | 11 +- .../spring/tracing/SentryTracingFilter.java | 2 +- .../spring/webflux/SentryWebFilter.java | 20 +- .../webflux/SentryWebFluxTracingFilterTest.kt | 2 - sentry/api/sentry.api | 1 + .../main/java/io/sentry/EnvelopeSender.java | 2 +- .../main/java/io/sentry/HubScopesWrapper.java | 2 +- sentry/src/main/java/io/sentry/IScopes.java | 22 +- sentry/src/main/java/io/sentry/NoOpHub.java | 3 + .../src/main/java/io/sentry/OutboxSender.java | 2 +- sentry/src/main/java/io/sentry/Scopes.java | 22 +- ...achedEnvelopeFireAndForgetIntegration.java | 2 +- .../SendFireAndForgetEnvelopeSender.java | 2 +- .../sentry/SendFireAndForgetOutboxSender.java | 2 +- sentry/src/main/java/io/sentry/Sentry.java | 20 +- .../main/java/io/sentry/SentryOptions.java | 4 +- .../main/java/io/sentry/SentryWrapper.java | 12 +- .../io/sentry/ShutdownHookIntegration.java | 2 +- .../UncaughtExceptionHandlerIntegration.java | 2 +- .../test/java/io/sentry/ScopesAdapterTest.kt | 78 +++---- sentry/src/test/java/io/sentry/SentryTest.kt | 32 +-- .../test/java/io/sentry/SentryTracerTest.kt | 92 ++++---- .../test/java/io/sentry/SentryWrapperTest.kt | 68 +++--- ...UncaughtExceptionHandlerIntegrationTest.kt | 2 +- .../java/io/sentry/util/CheckInUtilsTest.kt | 7 - 38 files changed, 382 insertions(+), 385 deletions(-) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityBreadcrumbsIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ActivityBreadcrumbsIntegrationTest.kt index 10dc60e74b..56dabd2fbc 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityBreadcrumbsIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ActivityBreadcrumbsIntegrationTest.kt @@ -4,7 +4,7 @@ import android.app.Activity import android.app.Application import android.os.Bundle import io.sentry.Breadcrumb -import io.sentry.Hub +import io.sentry.Scopes import io.sentry.SentryLevel import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull @@ -20,7 +20,7 @@ class ActivityBreadcrumbsIntegrationTest { private class Fixture { val application = mock() - val hub = mock() + val scopes = mock() val options = SentryAndroidOptions().apply { dsn = "https://key@sentry.io/proj" } @@ -28,7 +28,7 @@ class ActivityBreadcrumbsIntegrationTest { fun getSut(enabled: Boolean = true): ActivityBreadcrumbsIntegration { options.isEnableActivityLifecycleBreadcrumbs = enabled - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) return ActivityBreadcrumbsIntegration( application ) @@ -40,7 +40,7 @@ class ActivityBreadcrumbsIntegrationTest { @Test fun `When ActivityBreadcrumbsIntegration is disabled, it should not register the activity callback`() { val sut = fixture.getSut(false) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.application, never()).registerActivityLifecycleCallbacks(any()) } @@ -48,7 +48,7 @@ class ActivityBreadcrumbsIntegrationTest { @Test fun `When ActivityBreadcrumbsIntegration is enabled, it should register the activity callback`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.application).registerActivityLifecycleCallbacks(any()) @@ -59,12 +59,12 @@ class ActivityBreadcrumbsIntegrationTest { @Test fun `When breadcrumb is added, type and category should be set`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.hub).addBreadcrumb( + verify(fixture.scopes).addBreadcrumb( check { assertEquals("ui.lifecycle", it.category) assertEquals("navigation", it.type) @@ -78,77 +78,77 @@ class ActivityBreadcrumbsIntegrationTest { @Test fun `When activity is created, it should add a breadcrumb`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.hub).addBreadcrumb(any(), anyOrNull()) + verify(fixture.scopes).addBreadcrumb(any(), anyOrNull()) } @Test fun `When activity is started, it should add a breadcrumb`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityStarted(activity) - verify(fixture.hub).addBreadcrumb(any(), anyOrNull()) + verify(fixture.scopes).addBreadcrumb(any(), anyOrNull()) } @Test fun `When activity is resumed, it should add a breadcrumb`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityResumed(activity) - verify(fixture.hub).addBreadcrumb(any(), anyOrNull()) + verify(fixture.scopes).addBreadcrumb(any(), anyOrNull()) } @Test fun `When activity is paused, it should add a breadcrumb`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityPaused(activity) - verify(fixture.hub).addBreadcrumb(any(), anyOrNull()) + verify(fixture.scopes).addBreadcrumb(any(), anyOrNull()) } @Test fun `When activity is stopped, it should add a breadcrumb`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityStopped(activity) - verify(fixture.hub).addBreadcrumb(any(), anyOrNull()) + verify(fixture.scopes).addBreadcrumb(any(), anyOrNull()) } @Test fun `When activity is save instance, it should add a breadcrumb`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivitySaveInstanceState(activity, fixture.bundle) - verify(fixture.hub).addBreadcrumb(any(), anyOrNull()) + verify(fixture.scopes).addBreadcrumb(any(), anyOrNull()) } @Test fun `When activity is destroyed, it should add a breadcrumb`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityDestroyed(activity) - verify(fixture.hub).addBreadcrumb(any(), anyOrNull()) + verify(fixture.scopes).addBreadcrumb(any(), anyOrNull()) } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt index 2d20a16ec5..e21f650162 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt @@ -14,10 +14,10 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.DateUtils import io.sentry.FullyDisplayedReporter -import io.sentry.Hub import io.sentry.IScope import io.sentry.Scope import io.sentry.ScopeCallback +import io.sentry.Scopes import io.sentry.Sentry import io.sentry.SentryDate import io.sentry.SentryDateProvider @@ -71,7 +71,7 @@ class ActivityLifecycleIntegrationTest { private class Fixture { val application = mock() - val hub = mock() + val scopes = mock() val options = SentryAndroidOptions().apply { dsn = "https://key@sentry.io/proj" } @@ -92,13 +92,13 @@ class ActivityLifecycleIntegrationTest { ): ActivityLifecycleIntegration { initializer?.configure(options) - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) // We let the ActivityLifecycleIntegration create the proper transaction here val optionCaptor = argumentCaptor() val contextCaptor = argumentCaptor() - whenever(hub.startTransaction(contextCaptor.capture(), optionCaptor.capture())).thenAnswer { - val t = SentryTracer(contextCaptor.lastValue, hub, optionCaptor.lastValue) + whenever(scopes.startTransaction(contextCaptor.capture(), optionCaptor.capture())).thenAnswer { + val t = SentryTracer(contextCaptor.lastValue, scopes, optionCaptor.lastValue) transaction = t return@thenAnswer t } @@ -145,7 +145,7 @@ class ActivityLifecycleIntegrationTest { @Test fun `When ActivityLifecycleIntegration is registered, it registers activity callback`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.application).registerActivityLifecycleCallbacks(any()) } @@ -153,7 +153,7 @@ class ActivityLifecycleIntegrationTest { @Test fun `When ActivityLifecycleIntegration is closed, it should unregister the callback`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.close() @@ -163,7 +163,7 @@ class ActivityLifecycleIntegrationTest { @Test fun `When ActivityLifecycleIntegration is closed, it should close the ActivityFramesTracker`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.close() @@ -173,39 +173,39 @@ class ActivityLifecycleIntegrationTest { @Test fun `When tracing is disabled, do not start tracing`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.hub, never()).startTransaction(any(), any()) + verify(fixture.scopes, never()).startTransaction(any(), any()) } @Test fun `When tracing is enabled but activity is running, do not start tracing again`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.hub).startTransaction(any(), any()) + verify(fixture.scopes).startTransaction(any(), any()) } @Test fun `Transaction op is ui_load and idle+deadline timeouts are set`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) setAppStartTime() val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("ui.load", it.operation) assertEquals(TransactionNameSource.COMPONENT, it.transactionNameSource) @@ -221,7 +221,7 @@ class ActivityLifecycleIntegrationTest { fun `Activity gets added to ActivityFramesTracker during transaction creation`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityStarted(activity) @@ -233,14 +233,14 @@ class ActivityLifecycleIntegrationTest { fun `Transaction name is the Activity's name`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) setAppStartTime() val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( check { assertEquals("Activity", it.name) assertEquals(TransactionNameSource.COMPONENT, it.transactionNameSource) @@ -254,9 +254,9 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) - whenever(fixture.hub.configureScope(any())).thenAnswer { + whenever(fixture.scopes.configureScope(any())).thenAnswer { val scope = Scope(fixture.options) sut.applyScope(scope, fixture.transaction) @@ -273,11 +273,11 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) - whenever(fixture.hub.configureScope(any())).thenAnswer { + whenever(fixture.scopes.configureScope(any())).thenAnswer { val scope = Scope(fixture.options) - val previousTransaction = SentryTracer(TransactionContext("name", "op"), fixture.hub) + val previousTransaction = SentryTracer(TransactionContext("name", "op"), fixture.scopes) scope.transaction = previousTransaction sut.applyScope(scope, fixture.transaction) @@ -297,14 +297,14 @@ class ActivityLifecycleIntegrationTest { it.isEnableTimeToFullDisplayTracing = true it.idleTimeout = 200 }) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.onActivityCreated(activity, fixture.bundle) sut.ttidSpanMap.values.first().finish() sut.ttfdSpanMap.values.first().finish() // then transaction should not be immediatelly finished - verify(fixture.hub, never()) + verify(fixture.scopes, never()) .captureTransaction( anyOrNull(), anyOrNull(), @@ -316,7 +316,7 @@ class ActivityLifecycleIntegrationTest { Thread.sleep(400) // then the transaction should be finished - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(SpanStatus.OK, it.status) }, @@ -330,13 +330,13 @@ class ActivityLifecycleIntegrationTest { fun `When tracing auto finish is enabled, it doesn't stop the transaction on onActivityPostResumed`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) sut.onActivityPostResumed(activity) - verify(fixture.hub, never()).captureTransaction( + verify(fixture.scopes, never()).captureTransaction( check { assertEquals(SpanStatus.OK, it.status) }, @@ -350,7 +350,7 @@ class ActivityLifecycleIntegrationTest { fun `When tracing has status, do not overwrite it`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) @@ -360,7 +360,7 @@ class ActivityLifecycleIntegrationTest { sut.onActivityPostResumed(activity) sut.onActivityDestroyed(activity) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(SpanStatus.UNKNOWN_ERROR, it.status) }, @@ -376,43 +376,43 @@ class ActivityLifecycleIntegrationTest { it.tracesSampleRate = 1.0 it.isEnableActivityLifecycleTracingAutoFinish = false }) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) sut.onActivityPostResumed(activity) - verify(fixture.hub, never()).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) + verify(fixture.scopes, never()).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) } @Test fun `When tracing is disabled, do not finish transaction`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityPostResumed(activity) - verify(fixture.hub, never()).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) + verify(fixture.scopes, never()).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) } @Test fun `When Activity is destroyed but transaction is running, finish it`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) sut.onActivityDestroyed(activity) - verify(fixture.hub).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) + verify(fixture.scopes).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) } @Test fun `When transaction is started, adds to WeakWef`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) @@ -424,7 +424,7 @@ class ActivityLifecycleIntegrationTest { fun `When Activity is destroyed removes WeakRef`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) @@ -437,7 +437,7 @@ class ActivityLifecycleIntegrationTest { fun `When Activity is destroyed, sets appStartSpan status to cancelled and finish it`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) setAppStartTime() @@ -454,7 +454,7 @@ class ActivityLifecycleIntegrationTest { fun `When Activity is destroyed, sets appStartSpan to null`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) setAppStartTime() @@ -469,7 +469,7 @@ class ActivityLifecycleIntegrationTest { fun `When Activity is destroyed, sets ttidSpan status to deadline_exceeded and finish it`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) setAppStartTime() @@ -486,7 +486,7 @@ class ActivityLifecycleIntegrationTest { fun `When Activity is destroyed, sets ttidSpan to null`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) setAppStartTime() @@ -503,7 +503,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) setAppStartTime() @@ -521,7 +521,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) setAppStartTime() @@ -537,25 +537,25 @@ class ActivityLifecycleIntegrationTest { fun `When new Activity and transaction is created, finish previous ones`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.onActivityCreated(mock(), mock()) sut.onActivityCreated(mock(), fixture.bundle) - verify(fixture.hub).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) + verify(fixture.scopes).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) } @Test fun `do not stop transaction on resumed if API 29`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, mock()) sut.onActivityResumed(activity) - verify(fixture.hub, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) + verify(fixture.scopes, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) } @Test @@ -563,7 +563,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut(Build.VERSION_CODES.P) fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, mock()) @@ -571,21 +571,21 @@ class ActivityLifecycleIntegrationTest { sut.ttfdSpanMap.values.first().finish() sut.onActivityResumed(activity) - verify(fixture.hub, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) + verify(fixture.scopes, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) } @Test fun `start transaction on created if API less than 29`() { val sut = fixture.getSut(Build.VERSION_CODES.P) fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) setAppStartTime() val activity = mock() sut.onActivityCreated(activity, mock()) - verify(fixture.hub).startTransaction(any(), any()) + verify(fixture.scopes).startTransaction(any(), any()) } @Test @@ -593,7 +593,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, mock()) @@ -611,7 +611,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, mock()) @@ -626,7 +626,7 @@ class ActivityLifecycleIntegrationTest { fun `App start is Cold when savedInstanceState is null`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, null) @@ -638,7 +638,7 @@ class ActivityLifecycleIntegrationTest { fun `App start is Warm when savedInstanceState is not null`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() val bundle = Bundle() @@ -651,7 +651,7 @@ class ActivityLifecycleIntegrationTest { fun `Do not overwrite App start type after set`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() val bundle = Bundle() @@ -665,7 +665,7 @@ class ActivityLifecycleIntegrationTest { fun `When firstActivityCreated is true, start transaction with given appStartTime`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val date = SentryNanotimeDate(Date(1), 0) setAppStartTime(date) @@ -674,7 +674,7 @@ class ActivityLifecycleIntegrationTest { sut.onActivityCreated(activity, fixture.bundle) // call only once - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( any(), check { assertEquals(date.nanoTimestamp(), it.startTimestamp!!.nanoTimestamp()) @@ -686,7 +686,7 @@ class ActivityLifecycleIntegrationTest { fun `When firstActivityCreated is true and app start sampling decision is set, start transaction with isAppStart true`() { AppStartMetrics.getInstance().appStartSamplingDecision = mock() val sut = fixture.getSut { it.tracesSampleRate = 1.0 } - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val date = SentryNanotimeDate(Date(1), 0) setAppStartTime(date) @@ -694,7 +694,7 @@ class ActivityLifecycleIntegrationTest { val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( any(), check { assertEquals(date.nanoTimestamp(), it.startTimestamp!!.nanoTimestamp()) @@ -706,7 +706,7 @@ class ActivityLifecycleIntegrationTest { @Test fun `When firstActivityCreated is true and app start sampling decision is not set, start transaction with isAppStart false`() { val sut = fixture.getSut { it.tracesSampleRate = 1.0 } - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val date = SentryNanotimeDate(Date(1), 0) setAppStartTime(date) @@ -714,7 +714,7 @@ class ActivityLifecycleIntegrationTest { val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( any(), check { assertEquals(date.nanoTimestamp(), it.startTimestamp!!.nanoTimestamp()) @@ -727,19 +727,19 @@ class ActivityLifecycleIntegrationTest { fun `When firstActivityCreated is false and app start sampling decision is set, start transaction with isAppStart false`() { AppStartMetrics.getInstance().appStartSamplingDecision = mock() val sut = fixture.getSut { it.tracesSampleRate = 1.0 } - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.hub).startTransaction(any(), check { assertFalse(it.isAppStartTransaction) }) + verify(fixture.scopes).startTransaction(any(), check { assertFalse(it.isAppStartTransaction) }) } @Test fun `When firstActivityCreated is true, do not create app start span if not foregroundImportance`() { val sut = fixture.getSut(importance = RunningAppProcessInfo.IMPORTANCE_BACKGROUND) fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) // usually set by SentryPerformanceProvider val date = SentryNanotimeDate(Date(1), 0) @@ -750,7 +750,7 @@ class ActivityLifecycleIntegrationTest { sut.onActivityCreated(activity, fixture.bundle) // call only once - verify(fixture.hub).startTransaction( + verify(fixture.scopes).startTransaction( any(), check { assertNotEquals(date, it.startTimestamp) } ) @@ -760,7 +760,7 @@ class ActivityLifecycleIntegrationTest { fun `Create and finish app start span immediately in case SDK init is deferred`() { val sut = fixture.getSut(importance = RunningAppProcessInfo.IMPORTANCE_FOREGROUND) fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) // usually set by SentryPerformanceProvider val startDate = SentryNanotimeDate(Date(1), 0) @@ -786,7 +786,7 @@ class ActivityLifecycleIntegrationTest { fun `When SentryPerformanceProvider is disabled, app start time span is still created`() { val sut = fixture.getSut(importance = RunningAppProcessInfo.IMPORTANCE_FOREGROUND) fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) // usually done by SentryPerformanceProvider, if disabled it's done by // SentryAndroid.init @@ -814,7 +814,7 @@ class ActivityLifecycleIntegrationTest { fun `When app-start end time is already set, it should not be overwritten`() { val sut = fixture.getSut(importance = RunningAppProcessInfo.IMPORTANCE_FOREGROUND) fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) // usually done by SentryPerformanceProvider val startDate = SentryNanotimeDate(Date(1), 0) @@ -838,7 +838,7 @@ class ActivityLifecycleIntegrationTest { fun `When activity lifecycle happens multiple times, app-start end time should not be overwritten`() { val sut = fixture.getSut(importance = RunningAppProcessInfo.IMPORTANCE_FOREGROUND) fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) // usually done by SentryPerformanceProvider val startDate = SentryNanotimeDate(Date(1), 0) @@ -876,7 +876,7 @@ class ActivityLifecycleIntegrationTest { fun `When firstActivityCreated is true, start app start warm span with given appStartTime`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val date = SentryNanotimeDate(Date(1), 0) setAppStartTime(date) @@ -893,7 +893,7 @@ class ActivityLifecycleIntegrationTest { fun `When firstActivityCreated is true, start app start cold span with given appStartTime`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val date = SentryNanotimeDate(Date(1), 0) setAppStartTime(date) @@ -910,7 +910,7 @@ class ActivityLifecycleIntegrationTest { fun `When firstActivityCreated is true, start app start span with Warm description`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val date = SentryNanotimeDate(Date(1), 0) setAppStartTime(date) @@ -927,7 +927,7 @@ class ActivityLifecycleIntegrationTest { fun `When firstActivityCreated is true, start app start span with Cold description`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val date = SentryNanotimeDate(Date(1), 0) setAppStartTime(date) @@ -944,7 +944,7 @@ class ActivityLifecycleIntegrationTest { fun `When firstActivityCreated is false, start transaction but not with given appStartTime`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val date = SentryNanotimeDate(Date(1), 0) setAppStartTime() @@ -965,12 +965,12 @@ class ActivityLifecycleIntegrationTest { fun `When transaction is finished, it gets removed from scope`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - whenever(fixture.hub.configureScope(any())).thenAnswer { + whenever(fixture.scopes.configureScope(any())).thenAnswer { val scope = Scope(fixture.options) scope.transaction = fixture.transaction @@ -988,7 +988,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = false - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) @@ -1001,7 +1001,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) @@ -1016,7 +1016,7 @@ class ActivityLifecycleIntegrationTest { fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true fixture.options.executorService = deferredExecutorService - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) val ttfdSpan = sut.ttfdSpanMap[activity] @@ -1032,7 +1032,7 @@ class ActivityLifecycleIntegrationTest { assertEquals(SpanStatus.DEADLINE_EXCEEDED, ttfdSpan.status) sut.onActivityDestroyed(activity) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { // ttfd timed out, so its measurement should not be set val ttfdMeasurement = it.measurements[MeasurementValue.KEY_TIME_TO_FULL_DISPLAY] @@ -1049,7 +1049,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) val ttfdSpan = sut.ttfdSpanMap[activity] @@ -1072,7 +1072,7 @@ class ActivityLifecycleIntegrationTest { assertNull(autoCloseFuture) sut.onActivityDestroyed(activity) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { // ttfd was finished successfully, so its measurement should be set val ttfdMeasurement = it.measurements[MeasurementValue.KEY_TIME_TO_FULL_DISPLAY] @@ -1090,7 +1090,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() val activity2 = mock() sut.onActivityCreated(activity, fixture.bundle) @@ -1128,7 +1128,7 @@ class ActivityLifecycleIntegrationTest { whenever(activity.findViewById(any())).thenReturn(view) // Make the integration create the spans and register to the FirstDrawDoneListener - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.onActivityCreated(activity, fixture.bundle) sut.onActivityResumed(activity) @@ -1143,7 +1143,7 @@ class ActivityLifecycleIntegrationTest { assertTrue(ttidSpan.isFinished) sut.onActivityDestroyed(activity) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { // ttid measurement should be set val ttidMeasurement = it.measurements[MeasurementValue.KEY_TIME_TO_INITIAL_DISPLAY] @@ -1166,7 +1166,7 @@ class ActivityLifecycleIntegrationTest { whenever(activity.findViewById(any())).thenReturn(view) // Make the integration create the spans and register to the FirstDrawDoneListener - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.onActivityCreated(activity, fixture.bundle) sut.onActivityResumed(activity) @@ -1189,7 +1189,7 @@ class ActivityLifecycleIntegrationTest { assertEquals(newEndDate, ttidSpan.finishDate) sut.onActivityDestroyed(activity) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { // ttid and ttfd measurements should be the same val ttidMeasurement = it.measurements[MeasurementValue.KEY_TIME_TO_INITIAL_DISPLAY] @@ -1211,7 +1211,7 @@ class ActivityLifecycleIntegrationTest { fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.onActivityCreated(activity, fixture.bundle) // The ttid span should be running @@ -1233,7 +1233,7 @@ class ActivityLifecycleIntegrationTest { fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true fixture.options.executorService = deferredExecutorService - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.onActivityCreated(activity, fixture.bundle) sut.onActivityResumed(activity) @@ -1268,7 +1268,7 @@ class ActivityLifecycleIntegrationTest { fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.onActivityCreated(activity, fixture.bundle) val ttfdSpan = sut.ttfdSpanMap[activity] assertNotNull(ttfdSpan) @@ -1294,15 +1294,15 @@ class ActivityLifecycleIntegrationTest { val argumentCaptor: ArgumentCaptor = ArgumentCaptor.forClass(ScopeCallback::class.java) val scope = Scope(fixture.options) val propagationContextAtStart = scope.propagationContext - whenever(fixture.hub.configureScope(argumentCaptor.capture())).thenAnswer { + whenever(fixture.scopes.configureScope(argumentCaptor.capture())).thenAnswer { argumentCaptor.value.run(scope) } - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.onActivityCreated(activity, fixture.bundle) // once for the screen, and once for the tracing propagation context - verify(fixture.hub, times(2)).configureScope(any()) + verify(fixture.scopes, times(2)).configureScope(any()) assertNotSame(propagationContextAtStart, scope.propagationContext) } @@ -1314,15 +1314,15 @@ class ActivityLifecycleIntegrationTest { val argumentCaptor: ArgumentCaptor = ArgumentCaptor.forClass(ScopeCallback::class.java) val scope = mock() - whenever(fixture.hub.configureScope(argumentCaptor.capture())).thenAnswer { + whenever(fixture.scopes.configureScope(argumentCaptor.capture())).thenAnswer { argumentCaptor.value.run(scope) } - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.onActivityCreated(activity, fixture.bundle) // once for the screen, and once for the tracing propagation context - verify(fixture.hub, times(2)).configureScope(any()) + verify(fixture.scopes, times(2)).configureScope(any()) verify(scope).setScreen(any()) } @@ -1335,32 +1335,32 @@ class ActivityLifecycleIntegrationTest { val argumentCaptor: ArgumentCaptor = ArgumentCaptor.forClass(ScopeCallback::class.java) val scope = Scope(fixture.options) val propagationContextAtStart = scope.propagationContext - whenever(fixture.hub.configureScope(argumentCaptor.capture())).thenAnswer { + whenever(fixture.scopes.configureScope(argumentCaptor.capture())).thenAnswer { argumentCaptor.value.run(scope) } - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.onActivityCreated(activity, fixture.bundle) // once for the screen, and once for the tracing propagation context - verify(fixture.hub, times(2)).configureScope(any()) + verify(fixture.scopes, times(2)).configureScope(any()) val propagationContextAfterNewTrace = scope.propagationContext assertNotSame(propagationContextAtStart, propagationContextAfterNewTrace) - clearInvocations(fixture.hub) + clearInvocations(fixture.scopes) sut.onActivityCreated(activity, fixture.bundle) // once for the screen, but not for the tracing propagation context - verify(fixture.hub).configureScope(any()) + verify(fixture.scopes).configureScope(any()) assertSame(propagationContextAfterNewTrace, scope.propagationContext) sut.onActivityDestroyed(activity) - clearInvocations(fixture.hub) + clearInvocations(fixture.scopes) sut.onActivityCreated(activity, fixture.bundle) // once for the screen, and once for the tracing propagation context - verify(fixture.hub, times(2)).configureScope(any()) + verify(fixture.scopes, times(2)).configureScope(any()) assertNotSame(propagationContextAfterNewTrace, scope.propagationContext) } @@ -1368,7 +1368,7 @@ class ActivityLifecycleIntegrationTest { fun `when transaction is finished, sets frame metrics`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) @@ -1384,7 +1384,7 @@ class ActivityLifecycleIntegrationTest { fixture.options.tracesSampleRate = 1.0 fixture.options.dateProvider = SentryDateProvider { now } - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) // usually done by SentryPerformanceProvider val startDate = SentryNanotimeDate(Date(5678), 910) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/EnvelopeFileObserverIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/EnvelopeFileObserverIntegrationTest.kt index 192565f101..82d0543833 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/EnvelopeFileObserverIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/EnvelopeFileObserverIntegrationTest.kt @@ -1,9 +1,11 @@ package io.sentry.android.core import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.sentry.Hub import io.sentry.ILogger +import io.sentry.IScope import io.sentry.IScopes +import io.sentry.Scope +import io.sentry.Scopes import io.sentry.SentryLevel import io.sentry.SentryOptions import io.sentry.test.DeferredExecutorService @@ -72,8 +74,8 @@ class EnvelopeFileObserverIntegrationTest { options.cacheDirPath = file.absolutePath options.addIntegration(integrationMock) options.setSerializer(mock()) -// val expected = HubAdapter.getInstance() - val scopes = Hub(options) + val globalScope = Scope(options) + val scopes = Scopes(mock(), mock(), globalScope, "test") // verify(integrationMock).register(expected, options) scopes.close() verify(integrationMock).close() diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt index b34e79991f..e64cbe227e 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt @@ -5,9 +5,9 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.Hub import io.sentry.IScope import io.sentry.Scope +import io.sentry.Scopes import io.sentry.Sentry import io.sentry.SentryEnvelope import io.sentry.SentryEnvelopeHeader @@ -82,7 +82,7 @@ class InternalSentrySdkTest { fun captureEnvelopeWithEvent(event: SentryEvent = SentryEvent()) { // create an envelope with session data - val options = Sentry.getCurrentHub().options + val options = Sentry.getCurrentScopes().options val eventId = SentryId() val header = SentryEnvelopeHeader(eventId) val eventItem = SentryEnvelopeItem.fromEvent(options.serializer, event) @@ -110,20 +110,19 @@ class InternalSentrySdkTest { } @Test - fun `current scope returns null when hub is no-op`() { - Sentry.getCurrentHub().close() + fun `current scope returns null when scopes is no-op`() { + Sentry.getCurrentScopes().close() val scope = InternalSentrySdk.getCurrentScope() assertNull(scope) } @Test - fun `current scope returns obj when hub is active`() { + fun `current scope returns obj when scopes is active`() { + val options = SentryOptions().apply { + dsn = "https://key@uri/1234567" + } Sentry.setCurrentScopes( - Hub( - SentryOptions().apply { - dsn = "https://key@uri/1234567" - } - ) + Scopes(Scope(options), Scope(options), Scope(options), "test") ) val scope = InternalSentrySdk.getCurrentScope() assertNotNull(scope) @@ -131,13 +130,13 @@ class InternalSentrySdkTest { @Test fun `current scope returns a copy of the scope`() { + val options = SentryOptions().apply { + dsn = "https://key@uri/1234567" + } Sentry.setCurrentScopes( - Hub( - SentryOptions().apply { - dsn = "https://key@uri/1234567" - } - ) + Scopes(Scope(options), Scope(options), Scope(options), "test") ) + // TODO [HSM] add breadcrumbs to all scopes and assert they are there Sentry.addBreadcrumb("test") // when the clone is modified diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt index cd0f8ed8c0..3ba130c0c9 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt @@ -330,7 +330,7 @@ class SentryAndroidTest { } var session: Session? = null - Sentry.getCurrentHub().configureScope { scope -> + Sentry.getCurrentScopes().configureScope { scope -> session = scope.session } callback(session) @@ -342,7 +342,7 @@ class SentryAndroidTest { fixture.initSut { options -> options.isEnableAutoSessionTracking = false } - Sentry.getCurrentHub().withScope { scope -> + Sentry.getCurrentScopes().withScope { scope -> assertNull(scope.session) } } @@ -378,7 +378,7 @@ class SentryAndroidTest { it.release = "io.sentry.sample@1.1.0+220" it.environment = "debug" // this is necessary to delay the AnrV2Integration processing to execute the configure - // scope block below (otherwise it won't be possible as hub is no-op before .init) + // scope block below (otherwise it won't be possible as scopes is no-op before .init) it.executorService.submit { Sentry.configureScope { scope -> // make sure the scope values changed to test that we're still using previously diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/UserInteractionIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/UserInteractionIntegrationTest.kt index d43dfe1419..379e3db353 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/UserInteractionIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/UserInteractionIntegrationTest.kt @@ -7,7 +7,7 @@ import android.content.res.Resources import android.util.DisplayMetrics import android.view.Window import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.sentry.Hub +import io.sentry.Scopes import io.sentry.android.core.internal.gestures.NoOpWindowCallback import io.sentry.android.core.internal.gestures.SentryWindowCallback import org.junit.runner.RunWith @@ -26,7 +26,7 @@ class UserInteractionIntegrationTest { private class Fixture { val application = mock() - val hub = mock() + val scopes = mock() val options = SentryAndroidOptions().apply { dsn = "https://key@sentry.io/proj" } @@ -39,7 +39,7 @@ class UserInteractionIntegrationTest { isAndroidXAvailable: Boolean = true ): UserInteractionIntegration { whenever(loadClass.isClassAvailable(any(), anyOrNull())).thenReturn(isAndroidXAvailable) - whenever(hub.options).thenReturn(options) + whenever(scopes.options).thenReturn(options) whenever(window.callback).thenReturn(callback) whenever(activity.window).thenReturn(window) @@ -65,7 +65,7 @@ class UserInteractionIntegrationTest { @Test fun `when user interaction breadcrumb is enabled registers a callback`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.application).registerActivityLifecycleCallbacks(any()) } @@ -75,7 +75,7 @@ class UserInteractionIntegrationTest { val sut = fixture.getSut() fixture.options.isEnableUserInteractionBreadcrumbs = false - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.application, never()).registerActivityLifecycleCallbacks(any()) } @@ -83,7 +83,7 @@ class UserInteractionIntegrationTest { @Test fun `when UserInteractionIntegration is closed unregisters the callback`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.close() @@ -94,7 +94,7 @@ class UserInteractionIntegrationTest { fun `when androidx is unavailable doesn't register a callback`() { val sut = fixture.getSut(isAndroidXAvailable = false) - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) verify(fixture.application, never()).registerActivityLifecycleCallbacks(any()) } @@ -102,7 +102,7 @@ class UserInteractionIntegrationTest { @Test fun `registers window callback on activity resumed`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.onActivityResumed(fixture.activity) @@ -114,7 +114,7 @@ class UserInteractionIntegrationTest { @Test fun `when no original callback delegates to NoOpWindowCallback`() { val sut = fixture.getSut() - sut.register(fixture.hub, fixture.options) + sut.register(fixture.scopes, fixture.options) sut.onActivityResumed(fixture.activity) diff --git a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationAnotherTest.kt b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationAnotherTest.kt index 087a1dfa72..6309155929 100644 --- a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationAnotherTest.kt +++ b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationAnotherTest.kt @@ -28,7 +28,6 @@ import graphql.schema.GraphQLObjectType import graphql.schema.GraphQLScalarType import graphql.schema.GraphQLSchema import io.sentry.Breadcrumb -import io.sentry.HubScopesWrapper import io.sentry.IScopes import io.sentry.Sentry import io.sentry.SentryOptions @@ -358,7 +357,6 @@ class SentryInstrumentationAnotherTest { } fun withMockScopes(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { - it.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(fixture.scopes)) it.`when` { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes) closure.invoke() } diff --git a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationTest.kt b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationTest.kt index 2bb46f79fc..7128b839e3 100644 --- a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationTest.kt +++ b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationTest.kt @@ -18,7 +18,6 @@ import graphql.schema.GraphQLScalarType import graphql.schema.idl.RuntimeWiring import graphql.schema.idl.SchemaGenerator import graphql.schema.idl.SchemaParser -import io.sentry.HubScopesWrapper import io.sentry.IScopes import io.sentry.Sentry import io.sentry.SentryOptions @@ -221,7 +220,6 @@ class SentryInstrumentationTest { } fun withMockScopes(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { - it.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(fixture.scopes)) it.`when` { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes) closure.invoke() } diff --git a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/ProfilingActivity.kt b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/ProfilingActivity.kt index a7004deb35..64e3f48441 100644 --- a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/ProfilingActivity.kt +++ b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/ProfilingActivity.kt @@ -75,7 +75,7 @@ class ProfilingActivity : AppCompatActivity() { private fun finishTransactionAndPrintResults(t: ITransaction) { t.finish() profileFinished = true - val profilesDirPath = Sentry.getCurrentHub().options.profilingTracesDirPath + val profilesDirPath = Sentry.getCurrentScopes().options.profilingTracesDirPath if (profilesDirPath == null) { Toast.makeText(this, R.string.profiling_no_dir_set, Toast.LENGTH_SHORT).show() return @@ -84,7 +84,7 @@ class ProfilingActivity : AppCompatActivity() { // We have concurrent profiling now. We have to wait for all transactions to finish (e.g. button click) // before reading the profile, otherwise it's empty and a crash occurs if (Sentry.getSpan() != null) { - val timeout = Sentry.getCurrentHub().options.idleTimeout ?: 0 + val timeout = Sentry.getCurrentScopes().options.idleTimeout ?: 0 val duration = (getProfileDuration() * 1000).toLong() Thread.sleep((timeout - duration).coerceAtLeast(0)) } diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryProperties.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryProperties.java index 7b3469d7f1..80ea79932c 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryProperties.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryProperties.java @@ -163,7 +163,8 @@ public void setLoggers(final @NotNull List loggers) { @Open public static class Reactive { /** - * Enable/Disable usage of {@link io.micrometer.context.ThreadLocalAccessor} for Hub propagation + * Enable/Disable usage of {@link io.micrometer.context.ThreadLocalAccessor} for Scopes + * propagation */ private boolean threadLocalAccessorEnabled = true; diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java index 5995728785..86e17f27c3 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java @@ -44,17 +44,18 @@ public AbstractSentryWebFilter(final @NotNull IScopes scopes) { } protected @Nullable ITransaction maybeStartTransaction( - final @NotNull IScopes requestHub, final @NotNull ServerHttpRequest request) { - if (requestHub.isEnabled()) { + final @NotNull IScopes requestScopes, final @NotNull ServerHttpRequest request) { + if (requestScopes.isEnabled()) { final @NotNull HttpHeaders headers = request.getHeaders(); final @Nullable String sentryTraceHeader = headers.getFirst(SentryTraceHeader.SENTRY_TRACE_HEADER); final @Nullable List baggageHeaders = headers.get(BaggageHeader.BAGGAGE_HEADER); final @Nullable TransactionContext transactionContext = - requestHub.continueTrace(sentryTraceHeader, baggageHeaders); + requestScopes.continueTrace(sentryTraceHeader, baggageHeaders); - if (requestHub.getOptions().isTracingEnabled() && shouldTraceRequest(requestHub, request)) { - return startTransaction(requestHub, request, transactionContext); + if (requestScopes.getOptions().isTracingEnabled() + && shouldTraceRequest(requestScopes, request)) { + return startTransaction(requestScopes, request, transactionContext); } } @@ -63,7 +64,7 @@ public AbstractSentryWebFilter(final @NotNull IScopes scopes) { protected void doFinally( final @NotNull ServerWebExchange serverWebExchange, - final @NotNull IScopes requestHub, + final @NotNull IScopes requestScopes, final @Nullable ITransaction transaction) { if (transaction != null) { finishTransaction(serverWebExchange, transaction); @@ -72,9 +73,9 @@ protected void doFinally( } protected void doFirst( - final @NotNull ServerWebExchange serverWebExchange, final @NotNull IScopes requestHub) { - if (requestHub.isEnabled()) { - serverWebExchange.getAttributes().put(SENTRY_SCOPES_KEY, requestHub); + final @NotNull ServerWebExchange serverWebExchange, final @NotNull IScopes requestScopes) { + if (requestScopes.isEnabled()) { + serverWebExchange.getAttributes().put(SENTRY_SCOPES_KEY, requestScopes); final ServerHttpRequest request = serverWebExchange.getRequest(); final ServerHttpResponse response = serverWebExchange.getResponse(); @@ -83,8 +84,8 @@ protected void doFirst( hint.set(WEBFLUX_FILTER_RESPONSE, response); final String methodName = request.getMethod() != null ? request.getMethod().name() : "unknown"; - requestHub.addBreadcrumb(Breadcrumb.http(request.getURI().toString(), methodName), hint); - requestHub.configureScope( + requestScopes.addBreadcrumb(Breadcrumb.http(request.getURI().toString(), methodName), hint); + requestScopes.configureScope( scope -> scope.setRequest(sentryRequestResolver.resolveSentryRequest(request))); } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt index 44f4925c2d..76e4e0e2b6 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt @@ -2,7 +2,6 @@ package io.sentry.spring.jakarta.webflux import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.HubScopesWrapper import io.sentry.ILogger import io.sentry.IScopes import io.sentry.PropagationContext @@ -87,7 +86,6 @@ class SentryWebFluxTracingFilterTest { private val fixture = Fixture() fun withMockScopes(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { - it.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(fixture.scopes)) it.`when` { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes) it.`when` { Sentry.forkedRootScopes(any()) }.thenReturn(fixture.scopes) closure.invoke() diff --git a/sentry-spring/src/main/java/io/sentry/spring/EnableSentry.java b/sentry-spring/src/main/java/io/sentry/spring/EnableSentry.java index efb9ce529b..055afd3484 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/EnableSentry.java +++ b/sentry-spring/src/main/java/io/sentry/spring/EnableSentry.java @@ -12,7 +12,7 @@ * *

    *
  • creates bean of type {@link io.sentry.SentryOptions} - *
  • registers {@link io.sentry.IHub} for sending Sentry events + *
  • registers {@link io.sentry.IScopes} for sending Sentry events *
  • registers {@link SentryExceptionResolver} to send Sentry event for any uncaught exception * in Spring MVC flow. *
diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java b/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java index 8c3b9ac1f4..2968eede43 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java @@ -9,19 +9,20 @@ import org.springframework.scheduling.annotation.Async; /** - * Sets a current hub on a thread running a {@link Runnable} given by parameter. Used to propagate - * the current {@link IScopes} on the thread executing async task - like MVC controller methods - * returning a {@link Callable} or Spring beans methods annotated with {@link Async}. + * Forks current scope for the thread running a {@link Runnable} given by parameter. Used to + * propagate the current {@link IScopes} on the thread executing async task - like MVC controller + * methods returning a {@link Callable} or Spring beans methods annotated with {@link Async}. */ public final class SentryTaskDecorator implements TaskDecorator { @Override // TODO [HSM] should there also be a SentryIsolatedTaskDecorator or similar that uses // forkedScopes()? public @NotNull Runnable decorate(final @NotNull Runnable runnable) { - final IScopes newHub = Sentry.getCurrentScopes().forkedCurrentScope("spring.taskDecorator"); + final IScopes forkedScopes = + Sentry.getCurrentScopes().forkedCurrentScope("spring.taskDecorator"); return () -> { - try (final @NotNull ISentryLifecycleToken ignored = newHub.makeCurrent()) { + try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) { runnable.run(); } }; diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java index 50cdb8dc3a..b2519ba14e 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java @@ -59,7 +59,7 @@ public SentryTracingFilter() { public SentryTracingFilter( final @NotNull IScopes scopes, final @NotNull TransactionNameProvider transactionNameProvider) { - this.scopes = Objects.requireNonNull(scopes, "scopes is required"); + this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); this.transactionNameProvider = Objects.requireNonNull(transactionNameProvider, "transactionNameProvider is required"); } diff --git a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java index e32ede6947..0601dcaeb3 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java @@ -50,23 +50,23 @@ public SentryWebFilter(final @NotNull IScopes scopes) { public Mono filter( final @NotNull ServerWebExchange serverWebExchange, final @NotNull WebFilterChain webFilterChain) { - @NotNull IScopes requestHub = Sentry.forkedRootScopes("request.webflux"); - if (!requestHub.isEnabled()) { + @NotNull IScopes requestScopes = Sentry.forkedRootScopes("request.webflux"); + if (!requestScopes.isEnabled()) { return webFilterChain.filter(serverWebExchange); } - final boolean isTracingEnabled = requestHub.getOptions().isTracingEnabled(); + final boolean isTracingEnabled = requestScopes.getOptions().isTracingEnabled(); final @NotNull ServerHttpRequest request = serverWebExchange.getRequest(); final @NotNull HttpHeaders headers = request.getHeaders(); final @Nullable String sentryTraceHeader = headers.getFirst(SentryTraceHeader.SENTRY_TRACE_HEADER); final @Nullable List baggageHeaders = headers.get(BaggageHeader.BAGGAGE_HEADER); final @Nullable TransactionContext transactionContext = - requestHub.continueTrace(sentryTraceHeader, baggageHeaders); + requestScopes.continueTrace(sentryTraceHeader, baggageHeaders); final @Nullable ITransaction transaction = - isTracingEnabled && shouldTraceRequest(requestHub, request) - ? startTransaction(requestHub, request, transactionContext) + isTracingEnabled && shouldTraceRequest(requestScopes, request) + ? startTransaction(requestScopes, request, transactionContext) : null; if (transaction != null) { @@ -91,8 +91,8 @@ isTracingEnabled && shouldTraceRequest(requestHub, request) }) .doFirst( () -> { - serverWebExchange.getAttributes().put(SENTRY_SCOPES_KEY, requestHub); - Sentry.setCurrentScopes(requestHub); + serverWebExchange.getAttributes().put(SENTRY_SCOPES_KEY, requestScopes); + Sentry.setCurrentScopes(requestScopes); final ServerHttpResponse response = serverWebExchange.getResponse(); final Hint hint = new Hint(); @@ -100,9 +100,9 @@ isTracingEnabled && shouldTraceRequest(requestHub, request) hint.set(WEBFLUX_FILTER_RESPONSE, response); final String methodName = request.getMethod() != null ? request.getMethod().name() : "unknown"; - requestHub.addBreadcrumb( + requestScopes.addBreadcrumb( Breadcrumb.http(request.getURI().toString(), methodName), hint); - requestHub.configureScope( + requestScopes.configureScope( scope -> scope.setRequest(sentryRequestResolver.resolveSentryRequest(request))); }); } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt index ff527abd7d..cb764f31e9 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt @@ -2,7 +2,6 @@ package io.sentry.spring.webflux import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.HubScopesWrapper import io.sentry.ILogger import io.sentry.IScopes import io.sentry.PropagationContext @@ -87,7 +86,6 @@ class SentryWebFluxTracingFilterTest { private val fixture = Fixture() fun withMockScopes(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { - it.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(fixture.scopes)) it.`when` { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes) it.`when` { Sentry.forkedRootScopes(any()) }.thenReturn(fixture.scopes) closure.invoke() diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index d16d15b44b..d87ff71ccc 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -1979,6 +1979,7 @@ public final class io/sentry/ScopeType : java/lang/Enum { } public final class io/sentry/Scopes : io/sentry/IScopes, io/sentry/metrics/MetricsApi$IMetricsInterface { + public fun (Lio/sentry/IScope;Lio/sentry/IScope;Lio/sentry/IScope;Ljava/lang/String;)V public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V public fun bindClient (Lio/sentry/ISentryClient;)V diff --git a/sentry/src/main/java/io/sentry/EnvelopeSender.java b/sentry/src/main/java/io/sentry/EnvelopeSender.java index 3a157f59d3..9f630fc260 100644 --- a/sentry/src/main/java/io/sentry/EnvelopeSender.java +++ b/sentry/src/main/java/io/sentry/EnvelopeSender.java @@ -28,7 +28,7 @@ public EnvelopeSender( final long flushTimeoutMillis, final int maxQueueSize) { super(scopes, logger, flushTimeoutMillis, maxQueueSize); - this.scopes = Objects.requireNonNull(scopes, "Hub is required."); + this.scopes = Objects.requireNonNull(scopes, "Scopes are required."); this.serializer = Objects.requireNonNull(serializer, "Serializer is required."); this.logger = Objects.requireNonNull(logger, "Logger is required."); } diff --git a/sentry/src/main/java/io/sentry/HubScopesWrapper.java b/sentry/src/main/java/io/sentry/HubScopesWrapper.java index 2a50d8ca48..14e9a03e25 100644 --- a/sentry/src/main/java/io/sentry/HubScopesWrapper.java +++ b/sentry/src/main/java/io/sentry/HubScopesWrapper.java @@ -14,7 +14,7 @@ @Deprecated public final class HubScopesWrapper implements IHub { - private final IScopes scopes; + private final @NotNull IScopes scopes; public HubScopesWrapper(final @NotNull IScopes scopes) { this.scopes = scopes; diff --git a/sentry/src/main/java/io/sentry/IScopes.java b/sentry/src/main/java/io/sentry/IScopes.java index 6eb82cca32..b639836ca1 100644 --- a/sentry/src/main/java/io/sentry/IScopes.java +++ b/sentry/src/main/java/io/sentry/IScopes.java @@ -13,7 +13,7 @@ public interface IScopes { /** - * Check if the Hub is enabled/active. + * Check if Sentry is enabled/active. * * @return true if its enabled or false otherwise. */ @@ -188,11 +188,11 @@ SentryId captureException( /** Ends the current session */ void endSession(); - /** Flushes out the queue for up to timeout seconds and disable the Hub. */ + /** Flushes out the queue for up to timeout seconds and disable the Scopes. */ void close(); /** - * Flushes out the queue for up to timeout seconds and disable the Hub. + * Flushes out the queue for up to timeout seconds and disable the Scopes. * * @param isRestarting if true, avoids locking the main thread when finishing the queue. */ @@ -320,7 +320,7 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { * SDK in globalHubMode (defaults to true on Android) {@link * Sentry#init(Sentry.OptionsConfiguration, boolean)} calling withScope is discouraged, as scope * changes may be dropped when executed in parallel. Use {@link - * IHub#configureScope(ScopeCallback)} instead. + * IScopes#configureScope(ScopeCallback)} instead. * * @param callback the callback */ @@ -343,7 +343,7 @@ default void configureScope(@NotNull ScopeCallback callback) { void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback); /** - * Binds a different client to the hub + * Binds a different client to the scopes * * @param client the client. */ @@ -357,7 +357,7 @@ default void configureScope(@NotNull ScopeCallback callback) { boolean isHealthy(); /** - * Flushes events queued up, but keeps the Hub enabled. Not implemented yet. + * Flushes events queued up, but keeps the scopes enabled. Not implemented yet. * * @param timeoutMillis time in milliseconds */ @@ -540,9 +540,9 @@ ITransaction startTransaction( /** * Returns the "sentry-trace" header that allows tracing across services. Can also be used in - * <meta> HTML tags. Also see {@link IHub#getBaggage()}. + * <meta> HTML tags. Also see {@link IScopes#getBaggage()}. * - * @deprecated please use {@link IHub#getTraceparent()} instead. + * @deprecated please use {@link IScopes#getTraceparent()} instead. * @return sentry trace header or null */ @Deprecated @@ -610,7 +610,7 @@ void setSpanContext( void reportFullyDisplayed(); /** - * @deprecated See {@link IHub#reportFullyDisplayed()}. + * @deprecated See {@link IScopes#reportFullyDisplayed()}. */ @Deprecated default void reportFullDisplayed() { @@ -631,7 +631,7 @@ TransactionContext continueTrace( /** * Returns the "sentry-trace" header that allows tracing across services. Can also be used in - * <meta> HTML tags. Also see {@link IHub#getBaggage()}. + * <meta> HTML tags. Also see {@link IScopes#getBaggage()}. * * @return sentry trace header or null */ @@ -640,7 +640,7 @@ TransactionContext continueTrace( /** * Returns the "baggage" header that allows tracing across services. Can also be used in - * <meta> HTML tags. Also see {@link IHub#getTraceparent()}. + * <meta> HTML tags. Also see {@link IScopes#getTraceparent()}. * * @return baggage header or null */ diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index 06969518b7..653c8de9f9 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -12,6 +12,9 @@ import org.jetbrains.annotations.Nullable; @Deprecated +/** + * @deprecated use {@link NoOpScopes} instead. + */ public final class NoOpHub implements IHub { private static final NoOpHub instance = new NoOpHub(); diff --git a/sentry/src/main/java/io/sentry/OutboxSender.java b/sentry/src/main/java/io/sentry/OutboxSender.java index f80bf030c8..4e223da03d 100644 --- a/sentry/src/main/java/io/sentry/OutboxSender.java +++ b/sentry/src/main/java/io/sentry/OutboxSender.java @@ -49,7 +49,7 @@ public OutboxSender( final long flushTimeoutMillis, final int maxQueueSize) { super(scopes, logger, flushTimeoutMillis, maxQueueSize); - this.scopes = Objects.requireNonNull(scopes, "Hub is required."); + this.scopes = Objects.requireNonNull(scopes, "Scopes are required."); this.envelopeReader = Objects.requireNonNull(envelopeReader, "Envelope reader is required."); this.serializer = Objects.requireNonNull(serializer, "Serializer is required."); this.logger = Objects.requireNonNull(logger, "Logger is required."); diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index cbeb9ae88f..757a3870a8 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -26,6 +26,7 @@ public final class Scopes implements IScopes, MetricsApi.IMetricsInterface { private final @NotNull IScope scope; private final @NotNull IScope isolationScope; + private final @NotNull IScope globalScope; @SuppressWarnings("UnusedVariable") private final @Nullable Scopes parentScopes; @@ -38,21 +39,24 @@ public final class Scopes implements IScopes, MetricsApi.IMetricsInterface { private final @NotNull CombinedScopeView combinedScope; - Scopes( + public Scopes( final @NotNull IScope scope, final @NotNull IScope isolationScope, + final @NotNull IScope globalScope, final @NotNull String creator) { - this(scope, isolationScope, null, creator); + this(scope, isolationScope, globalScope, null, creator); } private Scopes( final @NotNull IScope scope, final @NotNull IScope isolationScope, + final @NotNull IScope globalScope, final @Nullable Scopes parentScopes, final @NotNull String creator) { - this.combinedScope = new CombinedScopeView(getGlobalScope(), isolationScope, scope); + this.combinedScope = new CombinedScopeView(globalScope, isolationScope, scope); this.scope = scope; this.isolationScope = isolationScope; + this.globalScope = globalScope; this.parentScopes = parentScopes; this.creator = creator; @@ -106,12 +110,12 @@ public boolean isAncestorOf(final @Nullable Scopes otherScopes) { @Override public @NotNull IScopes forkedScopes(final @NotNull String creator) { - return new Scopes(scope.clone(), isolationScope.clone(), this, creator); + return new Scopes(scope.clone(), isolationScope.clone(), globalScope, this, creator); } @Override public @NotNull IScopes forkedCurrentScope(final @NotNull String creator) { - return new Scopes(scope.clone(), isolationScope, this, creator); + return new Scopes(scope.clone(), isolationScope, globalScope, this, creator); } @Override @@ -419,7 +423,7 @@ public void close(final boolean isRestarting) { // TODO: should we end session before closing client? getClient().close(isRestarting); } catch (Throwable e) { - getOptions().getLogger().log(SentryLevel.ERROR, "Error while closing the Hub.", e); + getOptions().getLogger().log(SentryLevel.ERROR, "Error while closing the Scopes.", e); } isEnabled = false; } @@ -572,7 +576,7 @@ private void updateLastEventId(final @NotNull SentryId lastEventId) { @Override public @NotNull IScope getGlobalScope() { - return Sentry.getGlobalScope(); + return globalScope; } @Override @@ -712,7 +716,7 @@ public void flush(long timeoutMillis) { @SuppressWarnings("deprecation") public @NotNull IHub clone() { if (!isEnabled()) { - getOptions().getLogger().log(SentryLevel.WARNING, "Disabled Hub cloned."); + getOptions().getLogger().log(SentryLevel.WARNING, "Disabled Scopes cloned."); } // TODO [HSM] should this fork isolation scope as well? return new HubScopesWrapper(forkedCurrentScope("scopes clone")); @@ -1054,7 +1058,7 @@ private static void validateOptions(final @NotNull SentryOptions options) { Objects.requireNonNull(options, "SentryOptions is required."); if (options.getDsn() == null || options.getDsn().isEmpty()) { throw new IllegalArgumentException( - "Hub requires a DSN to be instantiated. Considering using the NoOpHub if no DSN is available."); + "Scopes requires a DSN to be instantiated. Considering using the NoOpScopes if no DSN is available."); } } } diff --git a/sentry/src/main/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegration.java b/sentry/src/main/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegration.java index 2160d1607e..8234affa05 100644 --- a/sentry/src/main/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegration.java +++ b/sentry/src/main/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegration.java @@ -67,7 +67,7 @@ public SendCachedEnvelopeFireAndForgetIntegration( @Override public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { - this.scopes = Objects.requireNonNull(scopes, "Hub is required"); + this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); this.options = Objects.requireNonNull(options, "SentryOptions is required"); final String cachedDir = options.getCacheDirPath(); diff --git a/sentry/src/main/java/io/sentry/SendFireAndForgetEnvelopeSender.java b/sentry/src/main/java/io/sentry/SendFireAndForgetEnvelopeSender.java index 155946b95a..11c254458a 100644 --- a/sentry/src/main/java/io/sentry/SendFireAndForgetEnvelopeSender.java +++ b/sentry/src/main/java/io/sentry/SendFireAndForgetEnvelopeSender.java @@ -22,7 +22,7 @@ public SendFireAndForgetEnvelopeSender( @Override public @Nullable SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget create( final @NotNull IScopes scopes, final @NotNull SentryOptions options) { - Objects.requireNonNull(scopes, "Hub is required"); + Objects.requireNonNull(scopes, "Scopes are required"); Objects.requireNonNull(options, "SentryOptions is required"); final String dirPath = sendFireAndForgetDirPath.getDirPath(); diff --git a/sentry/src/main/java/io/sentry/SendFireAndForgetOutboxSender.java b/sentry/src/main/java/io/sentry/SendFireAndForgetOutboxSender.java index e7913b5b2f..b14b3b6921 100644 --- a/sentry/src/main/java/io/sentry/SendFireAndForgetOutboxSender.java +++ b/sentry/src/main/java/io/sentry/SendFireAndForgetOutboxSender.java @@ -22,7 +22,7 @@ public SendFireAndForgetOutboxSender( @Override public @Nullable SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget create( final @NotNull IScopes scopes, final @NotNull SentryOptions options) { - Objects.requireNonNull(scopes, "Hub is required"); + Objects.requireNonNull(scopes, "Scopes are required"); Objects.requireNonNull(options, "SentryOptions is required"); final String dirPath = sendFireAndForgetDirPath.getDirPath(); diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 38d033f28a..214a16362a 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -58,7 +58,7 @@ private Sentry() {} /** Default value for globalHubMode is false */ private static final boolean GLOBAL_HUB_DEFAULT_MODE = false; - /** whether to use a single (global) Hub as opposed to one per thread. */ + /** whether to use a single (global) Scopes as opposed to one per thread. */ private static volatile boolean globalHubMode = GLOBAL_HUB_DEFAULT_MODE; @ApiStatus.Internal @@ -104,7 +104,7 @@ private Sentry() {} /** * Returns a new Scopes which is cloned from the rootScopes. * - * @return the hub + * @return the forked scopes */ @ApiStatus.Internal public static @NotNull IScopes forkedRootScopes(final @NotNull String creator) { @@ -139,7 +139,7 @@ private Sentry() {} } /** - * Check if the current Hub is enabled/active. + * Check if Sentry is enabled/active. * * @return true if its enabled or false otherwise. */ @@ -276,7 +276,7 @@ private static synchronized void init( final IScope rootScope = new Scope(options); final IScope rootIsolationScope = new Scope(options); globalScope.bindClient(new SentryClient(options)); - rootScopes = new Scopes(rootScope, rootIsolationScope, "Sentry.init"); + rootScopes = new Scopes(rootScope, rootIsolationScope, globalScope, "Sentry.init"); getScopesStorage().set(rootScopes); @@ -288,10 +288,10 @@ private static synchronized void init( options.setExecutorService(new SentryExecutorService()); } - // when integrations are registered on Hub ctor and async integrations are fired, + // when integrations are registered on Scopes ctor and async integrations are fired, // it might and actually happened that integrations called captureSomething - // and hub was still NoOp. - // Registering integrations here make sure that Hub is already created. + // and Scopes was still NoOp. + // Registering integrations here make sure that Scopes is already created. for (final Integration integration : options.getIntegrations()) { integration.register(ScopesAdapter.getInstance(), options); } @@ -872,7 +872,7 @@ public static void configureScope( } /** - * Binds a different client to the current hub + * Binds a different client to the current Scopes * * @param client the client. */ @@ -885,7 +885,7 @@ public static boolean isHealthy() { } /** - * Flushes events queued up to the current hub. Not implemented yet. + * Flushes events queued up to the current Scopes. Not implemented yet. * * @param timeoutMillis time in milliseconds */ @@ -1037,7 +1037,7 @@ public static void reportFullDisplayed() { reportFullyDisplayed(); } - /** the metrics API for the current hub */ + /** the metrics API for the current Scopes */ @NotNull @ApiStatus.Experimental public static MetricsApi metrics() { diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 0523ce7cf0..34475d2f8c 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -315,7 +315,7 @@ public class SentryOptions { /** Maximum number of spans that can be atteched to single transaction. */ private int maxSpans = 1000; - /** Registers hook that flushes {@link Hub} when main thread shuts down. */ + /** Registers hook that flushes {@link Scopes} when main thread shuts down. */ private boolean enableShutdownHook = true; /** @@ -2496,7 +2496,7 @@ public interface BeforeEmitMetricCallback { /** * Creates SentryOptions instance without initializing any of the internal parts. * - *

Used by {@link NoOpHub}. + *

Used by {@link NoOpScopes}. * * @return SentryOptions */ diff --git a/sentry/src/main/java/io/sentry/SentryWrapper.java b/sentry/src/main/java/io/sentry/SentryWrapper.java index 4682dac8f5..808050e570 100644 --- a/sentry/src/main/java/io/sentry/SentryWrapper.java +++ b/sentry/src/main/java/io/sentry/SentryWrapper.java @@ -12,16 +12,16 @@ *

  • {@link Supplier} * * - * that clones the Hub before execution and restores it afterwards. This prevents reused threads - * (e.g. from thread-pools) from getting an incorrect state. + * that forks the current scope before execution and restores it afterwards. This prevents reused + * threads (e.g. from thread-pools) from getting an incorrect state. */ public final class SentryWrapper { /** * Helper method to wrap {@link Callable} * - *

    Clones the Hub before execution and restores it afterwards. This prevents reused threads - * (e.g. from thread-pools) from getting an incorrect state. + *

    Forks the current scope before execution and restores it afterwards. This prevents reused + * threads (e.g. from thread-pools) from getting an incorrect state. * * @param callable - the {@link Callable} to be wrapped * @return the wrapped {@link Callable} @@ -51,8 +51,8 @@ public static Callable wrapCallableIsolated(final @NotNull Callable ca /** * Helper method to wrap {@link Supplier} * - *

    Clones the Hub before execution and restores it afterwards. This prevents reused threads - * (e.g. from thread-pools) from getting an incorrect state. + *

    Forks the current scope before execution and restores it afterwards. This prevents reused + * threads (e.g. from thread-pools) from getting an incorrect state. * * @param supplier - the {@link Supplier} to be wrapped * @return the wrapped {@link Supplier} diff --git a/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java b/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java index d957a87ccf..c31d31aebb 100644 --- a/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java +++ b/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java @@ -10,7 +10,7 @@ import org.jetbrains.annotations.TestOnly; import org.jetbrains.annotations.VisibleForTesting; -/** Registers hook that flushes {@link Hub} when main thread shuts down. */ +/** Registers hook that flushes {@link Scopes} when main thread shuts down. */ public final class ShutdownHookIntegration implements Integration, Closeable { private final @NotNull Runtime runtime; diff --git a/sentry/src/main/java/io/sentry/UncaughtExceptionHandlerIntegration.java b/sentry/src/main/java/io/sentry/UncaughtExceptionHandlerIntegration.java index 47ceaa084e..98844bd038 100644 --- a/sentry/src/main/java/io/sentry/UncaughtExceptionHandlerIntegration.java +++ b/sentry/src/main/java/io/sentry/UncaughtExceptionHandlerIntegration.java @@ -54,7 +54,7 @@ public final void register(final @NotNull IScopes scopes, final @NotNull SentryO } registered = true; - this.scopes = Objects.requireNonNull(scopes, "Hub is required"); + this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); this.options = Objects.requireNonNull(options, "SentryOptions is required"); this.options diff --git a/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt b/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt index 7637e6c74e..38fc9875b0 100644 --- a/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt +++ b/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt @@ -26,12 +26,12 @@ class ScopesAdapterTest { Sentry.close() } - @Test fun `isEnabled calls Hub`() { + @Test fun `isEnabled calls Scopes`() { ScopesAdapter.getInstance().isEnabled verify(scopes).isEnabled } - @Test fun `captureEvent calls Hub`() { + @Test fun `captureEvent calls Scopes`() { val event = mock() val hint = mock() val scopeCallback = mock() @@ -42,7 +42,7 @@ class ScopesAdapterTest { verify(scopes).captureEvent(eq(event), eq(hint), eq(scopeCallback)) } - @Test fun `captureMessage calls Hub`() { + @Test fun `captureMessage calls Scopes`() { val scopeCallback = mock() val sentryLevel = mock() ScopesAdapter.getInstance().captureMessage("message", sentryLevel) @@ -52,14 +52,14 @@ class ScopesAdapterTest { verify(scopes).captureMessage(eq("message"), eq(sentryLevel), eq(scopeCallback)) } - @Test fun `captureEnvelope calls Hub`() { + @Test fun `captureEnvelope calls Scopes`() { val envelope = mock() val hint = mock() ScopesAdapter.getInstance().captureEnvelope(envelope, hint) verify(scopes).captureEnvelope(eq(envelope), eq(hint)) } - @Test fun `captureException calls Hub`() { + @Test fun `captureException calls Scopes`() { val throwable = mock() val hint = mock() val scopeCallback = mock() @@ -70,142 +70,142 @@ class ScopesAdapterTest { verify(scopes).captureException(eq(throwable), eq(hint), eq(scopeCallback)) } - @Test fun `captureUserFeedback calls Hub`() { + @Test fun `captureUserFeedback calls Scopes`() { val userFeedback = mock() ScopesAdapter.getInstance().captureUserFeedback(userFeedback) verify(scopes).captureUserFeedback(eq(userFeedback)) } - @Test fun `captureCheckIn calls Hub`() { + @Test fun `captureCheckIn calls Scopes`() { val checkIn = mock() ScopesAdapter.getInstance().captureCheckIn(checkIn) verify(scopes).captureCheckIn(eq(checkIn)) } - @Test fun `startSession calls Hub`() { + @Test fun `startSession calls Scopes`() { ScopesAdapter.getInstance().startSession() verify(scopes).startSession() } - @Test fun `endSession calls Hub`() { + @Test fun `endSession calls Scopes`() { ScopesAdapter.getInstance().endSession() verify(scopes).endSession() } - @Test fun `close calls Hub`() { + @Test fun `close calls Scopes`() { ScopesAdapter.getInstance().close() verify(scopes).close(false) } - @Test fun `close with isRestarting true calls Hub with isRestarting false`() { + @Test fun `close with isRestarting true calls Scopes with isRestarting false`() { ScopesAdapter.getInstance().close(true) verify(scopes).close(false) } - @Test fun `close with isRestarting false calls Hub with isRestarting false`() { + @Test fun `close with isRestarting false calls Scopes with isRestarting false`() { ScopesAdapter.getInstance().close(false) verify(scopes).close(false) } - @Test fun `addBreadcrumb calls Hub`() { + @Test fun `addBreadcrumb calls Scopes`() { val breadcrumb = mock() val hint = mock() ScopesAdapter.getInstance().addBreadcrumb(breadcrumb, hint) verify(scopes).addBreadcrumb(eq(breadcrumb), eq(hint)) } - @Test fun `setLevel calls Hub`() { + @Test fun `setLevel calls Scopes`() { val sentryLevel = mock() ScopesAdapter.getInstance().setLevel(sentryLevel) verify(scopes).setLevel(eq(sentryLevel)) } - @Test fun `setTransaction calls Hub`() { + @Test fun `setTransaction calls Scopes`() { ScopesAdapter.getInstance().setTransaction("transaction") verify(scopes).setTransaction(eq("transaction")) } - @Test fun `setUser calls Hub`() { + @Test fun `setUser calls Scopes`() { val user = mock() ScopesAdapter.getInstance().setUser(user) verify(scopes).setUser(eq(user)) } - @Test fun `setFingerprint calls Hub`() { + @Test fun `setFingerprint calls Scopes`() { val fingerprint = ArrayList() ScopesAdapter.getInstance().setFingerprint(fingerprint) verify(scopes).setFingerprint(eq(fingerprint)) } - @Test fun `clearBreadcrumbs calls Hub`() { + @Test fun `clearBreadcrumbs calls Scopes`() { ScopesAdapter.getInstance().clearBreadcrumbs() verify(scopes).clearBreadcrumbs() } - @Test fun `setTag calls Hub`() { + @Test fun `setTag calls Scopes`() { ScopesAdapter.getInstance().setTag("key", "value") verify(scopes).setTag(eq("key"), eq("value")) } - @Test fun `removeTag calls Hub`() { + @Test fun `removeTag calls Scopes`() { ScopesAdapter.getInstance().removeTag("key") verify(scopes).removeTag(eq("key")) } - @Test fun `setExtra calls Hub`() { + @Test fun `setExtra calls Scopes`() { ScopesAdapter.getInstance().setExtra("key", "value") verify(scopes).setExtra(eq("key"), eq("value")) } - @Test fun `removeExtra calls Hub`() { + @Test fun `removeExtra calls Scopes`() { ScopesAdapter.getInstance().removeExtra("key") verify(scopes).removeExtra(eq("key")) } - @Test fun `getLastEventId calls Hub`() { + @Test fun `getLastEventId calls Scopes`() { ScopesAdapter.getInstance().lastEventId verify(scopes).lastEventId } - @Test fun `pushScope calls Hub`() { + @Test fun `pushScope calls Scopes`() { ScopesAdapter.getInstance().pushScope() verify(scopes).pushScope() } - @Test fun `popScope calls Hub`() { + @Test fun `popScope calls Scopes`() { ScopesAdapter.getInstance().popScope() verify(scopes).popScope() } - @Test fun `withScope calls Hub`() { + @Test fun `withScope calls Scopes`() { val scopeCallback = mock() ScopesAdapter.getInstance().withScope(scopeCallback) verify(scopes).withScope(eq(scopeCallback)) } - @Test fun `configureScope calls Hub`() { + @Test fun `configureScope calls Scopes`() { val scopeCallback = mock() ScopesAdapter.getInstance().configureScope(scopeCallback) verify(scopes).configureScope(anyOrNull(), eq(scopeCallback)) } - @Test fun `bindClient calls Hub`() { + @Test fun `bindClient calls Scopes`() { val client = mock() ScopesAdapter.getInstance().bindClient(client) verify(scopes).bindClient(eq(client)) } - @Test fun `flush calls Hub`() { + @Test fun `flush calls Scopes`() { ScopesAdapter.getInstance().flush(1) verify(scopes).flush(eq(1)) } - @Test fun `clone calls Hub`() { + @Test fun `clone calls Scopes`() { ScopesAdapter.getInstance().clone() verify(scopes).clone() } - @Test fun `captureTransaction calls Hub`() { + @Test fun `captureTransaction calls Scopes`() { val transaction = mock() val traceContext = mock() val hint = mock() @@ -214,7 +214,7 @@ class ScopesAdapterTest { verify(scopes).captureTransaction(eq(transaction), eq(traceContext), eq(hint), eq(profilingTraceData)) } - @Test fun `startTransaction calls Hub`() { + @Test fun `startTransaction calls Scopes`() { val transactionContext = mock() val samplingContext = mock() val transactionOptions = mock() @@ -227,39 +227,39 @@ class ScopesAdapterTest { verify(scopes).startTransaction(eq(transactionContext), eq(transactionOptions)) } - @Test fun `traceHeaders calls Hub`() { + @Test fun `traceHeaders calls Scopes`() { ScopesAdapter.getInstance().traceHeaders() verify(scopes).traceHeaders() } - @Test fun `setSpanContext calls Hub`() { + @Test fun `setSpanContext calls Scopes`() { val throwable = mock() val span = mock() ScopesAdapter.getInstance().setSpanContext(throwable, span, "transactionName") verify(scopes).setSpanContext(eq(throwable), eq(span), eq("transactionName")) } - @Test fun `getSpan calls Hub`() { + @Test fun `getSpan calls Scopes`() { ScopesAdapter.getInstance().span verify(scopes).span } - @Test fun `getTransaction calls Hub`() { + @Test fun `getTransaction calls Scopes`() { ScopesAdapter.getInstance().transaction verify(scopes).transaction } - @Test fun `getOptions calls Hub`() { + @Test fun `getOptions calls Scopes`() { ScopesAdapter.getInstance().options verify(scopes).options } - @Test fun `isCrashedLastRun calls Hub`() { + @Test fun `isCrashedLastRun calls Scopes`() { ScopesAdapter.getInstance().isCrashedLastRun verify(scopes).isCrashedLastRun } - @Test fun `reportFullyDisplayed calls Hub`() { + @Test fun `reportFullyDisplayed calls Scopes`() { ScopesAdapter.getInstance().reportFullyDisplayed() verify(scopes).reportFullyDisplayed() } diff --git a/sentry/src/test/java/io/sentry/SentryTest.kt b/sentry/src/test/java/io/sentry/SentryTest.kt index 77d443f909..b1316158b5 100644 --- a/sentry/src/test/java/io/sentry/SentryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTest.kt @@ -422,25 +422,25 @@ class SentryTest { assertNotNull(scopes) assertFalse(Sentry.getCurrentScopes().isNoOp) - val newMainHubClone = Sentry.forkedRootScopes("test") - newMainHubClone.addBreadcrumb("breadcrumbMainClone") + val forkedRootScopes = Sentry.forkedRootScopes("test") + forkedRootScopes.addBreadcrumb("breadcrumbMainClone") scopes.captureMessage("messageCurrent") - newMainHubClone.captureMessage("messageMainClone") + forkedRootScopes.captureMessage("messageMainClone") assertEquals(2, capturedEvents.size) val mainCloneEvent = capturedEvents.firstOrNull { it.message?.formatted == "messageMainClone" } - val currentHubEvent = capturedEvents.firstOrNull { it.message?.formatted == "messageCurrent" } + val currentScopesEvent = capturedEvents.firstOrNull { it.message?.formatted == "messageCurrent" } assertNotNull(mainCloneEvent) assertNotNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbMainClone" }) assertNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbCurrent" }) assertNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbNoOp" }) - assertNotNull(currentHubEvent) - assertNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbMainClone" }) - assertNotNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbCurrent" }) - assertNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbNoOp" }) + assertNotNull(currentScopesEvent) + assertNull(currentScopesEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbMainClone" }) + assertNotNull(currentScopesEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbCurrent" }) + assertNull(currentScopesEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbNoOp" }) } @Test @@ -473,25 +473,25 @@ class SentryTest { assertNotNull(scopes) assertFalse(scopes.isNoOp) - val newMainHubClone = Sentry.forkedRootScopes("test") - newMainHubClone.addBreadcrumb("breadcrumbMainClone") + val forkedRootScopes = Sentry.forkedRootScopes("test") + forkedRootScopes.addBreadcrumb("breadcrumbMainClone") scopes.captureMessage("messageCurrent") - newMainHubClone.captureMessage("messageMainClone") + forkedRootScopes.captureMessage("messageMainClone") assertEquals(2, capturedEvents.size) val mainCloneEvent = capturedEvents.firstOrNull { it.message?.formatted == "messageMainClone" } - val currentHubEvent = capturedEvents.firstOrNull { it.message?.formatted == "messageCurrent" } + val currentScopesEvent = capturedEvents.firstOrNull { it.message?.formatted == "messageCurrent" } assertNotNull(mainCloneEvent) assertNotNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbMainClone" }) assertNotNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbCurrent" }) assertNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbNoOp" }) - assertNotNull(currentHubEvent) - assertNotNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbMainClone" }) - assertNotNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbCurrent" }) - assertNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbNoOp" }) + assertNotNull(currentScopesEvent) + assertNotNull(currentScopesEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbMainClone" }) + assertNotNull(currentScopesEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbCurrent" }) + assertNull(currentScopesEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbNoOp" }) } @Test diff --git a/sentry/src/test/java/io/sentry/SentryTracerTest.kt b/sentry/src/test/java/io/sentry/SentryTracerTest.kt index f92e90799c..ccd96a2543 100644 --- a/sentry/src/test/java/io/sentry/SentryTracerTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTracerTest.kt @@ -29,16 +29,16 @@ class SentryTracerTest { private class Fixture { val options = SentryOptions() - val hub: Hub + val scopes: Scopes val transactionPerformanceCollector: TransactionPerformanceCollector init { options.dsn = "https://key@sentry.io/proj" options.environment = "environment" options.release = "release@3.0.0" - hub = spy(Hub(options)) + scopes = spy(Scopes(Scope(options), Scope(options), Scope(options), "test")) transactionPerformanceCollector = spy(DefaultTransactionPerformanceCollector(options)) - hub.bindClient(mock()) + scopes.bindClient(mock()) } fun getSut( @@ -61,7 +61,7 @@ class SentryTracerTest { transactionOptions.deadlineTimeout = deadlineTimeout transactionOptions.isTrimEnd = trimEnd transactionOptions.transactionFinishedCallback = transactionFinishedCallback - return SentryTracer(TransactionContext("name", "op", samplingDecision), hub, transactionOptions, performanceCollector) + return SentryTracer(TransactionContext("name", "op", samplingDecision), scopes, transactionOptions, performanceCollector) } } @@ -150,7 +150,7 @@ class SentryTracerTest { fun `when transaction is finished, transaction is captured`() { val tracer = fixture.getSut() tracer.finish() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(it.transaction, tracer.name) }, @@ -185,10 +185,10 @@ class SentryTracerTest { @Test fun `when transaction is finished, transaction is cleared from the scope`() { val tracer = fixture.getSut() - fixture.hub.configureScope { it.transaction = tracer } - assertNotNull(fixture.hub.span) + fixture.scopes.configureScope { it.transaction = tracer } + assertNotNull(fixture.scopes.span) tracer.finish() - assertNull(fixture.hub.span) + assertNull(fixture.scopes.span) } @Test @@ -197,7 +197,7 @@ class SentryTracerTest { val ex = RuntimeException() tracer.throwable = ex tracer.finish() - verify(fixture.hub).setSpanContext(ex, tracer.root, "name") + verify(fixture.scopes).setSpanContext(ex, tracer.root, "name") } @Test @@ -206,7 +206,7 @@ class SentryTracerTest { tracer.setTag("tag1", "val1") tracer.setTag("tag2", "val2") tracer.finish() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(mapOf("tag1" to "val1", "tag2" to "val2"), it.tags) assertNotNull(it.contexts.trace) { @@ -226,7 +226,7 @@ class SentryTracerTest { val span = tracer.startChild("op2") span.spanContext.sampled = false tracer.finish() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(1, it.spans.size) assertEquals("op1", it.spans.first().op) @@ -253,7 +253,7 @@ class SentryTracerTest { tracer.setContext("otel", otelContext) tracer.finish() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(otelContext, it.contexts["otel"]) }, @@ -404,8 +404,8 @@ class SentryTracerTest { transaction.finish(SpanStatus.UNKNOWN_ERROR) // call only once - verify(fixture.hub).setSpanContext(ex, transaction.root, "name") - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).setSpanContext(ex, transaction.root, "name") + verify(fixture.scopes).captureTransaction( check { assertNotNull(it.contexts.trace) { assertEquals(SpanStatus.OK, it.status) @@ -486,20 +486,20 @@ class SentryTracerTest { } @Test - fun `when waiting for children, finishing transaction does not call hub if all children are not finished`() { + fun `when waiting for children, finishing transaction does not call scopes if all children are not finished`() { val transaction = fixture.getSut(waitForChildren = true) transaction.startChild("op") transaction.finish() - verify(fixture.hub, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) + verify(fixture.scopes, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) } @Test - fun `when waiting for children, finishing transaction calls hub if all children are finished`() { + fun `when waiting for children, finishing transaction calls scopes if all children are finished`() { val transaction = fixture.getSut(waitForChildren = true) val child = transaction.startChild("op") child.finish() transaction.finish() - verify(fixture.hub).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) + verify(fixture.scopes).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) } @Test @@ -516,21 +516,21 @@ class SentryTracerTest { } @Test - fun `when waiting for children, hub is not called until transaction is finished`() { + fun `when waiting for children, scopes is not called until transaction is finished`() { val transaction = fixture.getSut(waitForChildren = true) val child = transaction.startChild("op") child.finish() - verify(fixture.hub, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) + verify(fixture.scopes, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) } @Test - fun `when waiting for children, finishing last child calls hub if transaction is already finished`() { + fun `when waiting for children, finishing last child calls scopes if transaction is already finished`() { val transaction = fixture.getSut(waitForChildren = true) val child = transaction.startChild("op") transaction.finish(SpanStatus.INVALID_ARGUMENT) - verify(fixture.hub, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) + verify(fixture.scopes, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) child.finish() - verify(fixture.hub, times(1)).captureTransaction( + verify(fixture.scopes, times(1)).captureTransaction( check { assertEquals(SpanStatus.INVALID_ARGUMENT, it.status) }, @@ -552,7 +552,7 @@ class SentryTracerTest { transaction.finish(SpanStatus.INVALID_ARGUMENT) - verify(fixture.hub, times(1)).captureTransaction( + verify(fixture.scopes, times(1)).captureTransaction( check { assertEquals(2, it.spans.size) // span status/timestamp is retained @@ -575,7 +575,7 @@ class SentryTracerTest { it.isTraceSampling = true it.isSendDefaultPii = true }) - fixture.hub.setUser( + fixture.scopes.setUser( User().apply { id = "user-id" others = mapOf("segment" to "pro") @@ -598,7 +598,7 @@ class SentryTracerTest { val transaction = fixture.getSut({ it.isTraceSampling = true }) - fixture.hub.setUser( + fixture.scopes.setUser( User().apply { id = "user-id" others = mapOf("segment" to "pro") @@ -622,7 +622,7 @@ class SentryTracerTest { it.isTraceSampling = true }) val traceBeforeUserSet = transaction.traceContext() - fixture.hub.setUser( + fixture.scopes.setUser( User().apply { id = "user-id" } @@ -652,7 +652,7 @@ class SentryTracerTest { it.isSendDefaultPii = true }) - fixture.hub.setUser( + fixture.scopes.setUser( User().apply { id = "userId12345" others = mapOf("segment" to "pro") @@ -682,7 +682,7 @@ class SentryTracerTest { it.release = "1.0.99-rc.7" }) - fixture.hub.setUser( + fixture.scopes.setUser( User().apply { id = "userId12345" others = mapOf("segment" to "pro") @@ -713,7 +713,7 @@ class SentryTracerTest { it.isSendDefaultPii = true }) - fixture.hub.setUser(null) + fixture.scopes.setUser(null) val header = transaction.toBaggageHeader(null) assertNotNull(header) { @@ -735,7 +735,7 @@ class SentryTracerTest { val transaction = fixture.getSut(samplingDecision = TracesSamplingDecision(true)) transaction.setData("key", "val") transaction.finish() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals("val", it.getExtra("key")) }, @@ -752,7 +752,7 @@ class SentryTracerTest { span.setData("key", "val") span.finish() transaction.finish() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertNotNull(it.spans.first().data) { assertEquals("val", it["key"]) @@ -840,7 +840,7 @@ class SentryTracerTest { await.untilFalse(transaction.isFinishTimerRunning) - verify(fixture.hub, never()).captureTransaction( + verify(fixture.scopes, never()).captureTransaction( anyOrNull(), anyOrNull(), anyOrNull(), @@ -857,7 +857,7 @@ class SentryTracerTest { await.untilFalse(transaction.isFinishTimerRunning) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( anyOrNull(), anyOrNull(), anyOrNull(), @@ -916,7 +916,7 @@ class SentryTracerTest { await.untilFalse(transaction.isFinishTimerRunning) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(2, it.spans.size) assertEquals(transaction.root.finishDate, span2.finishDate) @@ -954,7 +954,7 @@ class SentryTracerTest { transaction.setMeasurement("days", 2, MeasurementUnit.Duration.DAY) transaction.finish() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(1.0f, it.measurements["metric1"]!!.value) assertEquals(null, it.measurements["metric1"]!!.unit) @@ -975,7 +975,7 @@ class SentryTracerTest { transaction.setMeasurement("metric1", 2, MeasurementUnit.Duration.DAY) transaction.finish() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(2, it.measurements["metric1"]!!.value) assertEquals("day", it.measurements["metric1"]!!.unit) @@ -993,7 +993,7 @@ class SentryTracerTest { transaction.setMeasurementFromChild("metric1", 2, MeasurementUnit.Duration.DAY) transaction.finish() - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(1.0f, it.measurements["metric1"]!!.value) assertNull(it.measurements["metric1"]!!.unit) @@ -1063,7 +1063,7 @@ class SentryTracerTest { assertTrue(span.isFinished) // and the transaction should be captured - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(1, it.spans.size) assertEquals(transaction.root.finishDate!!.nanoTimestamp(), span.finishDate!!.nanoTimestamp()) @@ -1093,7 +1093,7 @@ class SentryTracerTest { assertTrue(span.isFinished) // and the transaction should be captured - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(1, it.spans.size) assertEquals(transactionFinishDate, span.finishDate) @@ -1134,7 +1134,7 @@ class SentryTracerTest { assertEquals(expectedParentStartDate, parentSpan.startDate) assertEquals(expectedParentEndDate, parentSpan.finishDate) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(3, it.spans.size) }, @@ -1174,7 +1174,7 @@ class SentryTracerTest { assertEquals(expectedParentStartDate, parentSpan.startDate) assertEquals(expectedParentEndDate, parentSpan.finishDate) - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(3, it.spans.size) }, @@ -1265,7 +1265,7 @@ class SentryTracerTest { assertEquals(transaction.finishDate, span1.finishDate) // and the transaction should be captured with both spans - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(2, it.spans.size) }, @@ -1288,7 +1288,7 @@ class SentryTracerTest { transaction.forceFinish(SpanStatus.ABORTED, false, null) // then a transaction should be captured with 0 spans - verify(fixture.hub).captureTransaction( + verify(fixture.scopes).captureTransaction( check { assertEquals(0, it.spans.size) }, @@ -1311,7 +1311,7 @@ class SentryTracerTest { transaction.forceFinish(SpanStatus.ABORTED, true, null) // then the transaction should be captured with 0 spans - verify(fixture.hub, never()).captureTransaction( + verify(fixture.scopes, never()).captureTransaction( anyOrNull(), anyOrNull(), anyOrNull(), @@ -1335,7 +1335,7 @@ class SentryTracerTest { tracer.scheduleFinish() assertTrue(tracer.isFinished) - verify(fixture.hub).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) + verify(fixture.scopes).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) } @Test diff --git a/sentry/src/test/java/io/sentry/SentryWrapperTest.kt b/sentry/src/test/java/io/sentry/SentryWrapperTest.kt index a3511450f0..a830446428 100644 --- a/sentry/src/test/java/io/sentry/SentryWrapperTest.kt +++ b/sentry/src/test/java/io/sentry/SentryWrapperTest.kt @@ -27,7 +27,7 @@ class SentryWrapperTest { } @Test - fun `hub is reset to its state within the thread after supply is done`() { + fun `scopes are reset to its state within the thread after supply is done`() { Sentry.init { it.dsn = dsn it.beforeSend = SentryOptions.BeforeSendCallback { event, hint -> @@ -35,20 +35,20 @@ class SentryWrapperTest { } } - val mainHub = Sentry.getCurrentScopes() - val threadedHub = mainHub.forkedCurrentScope("test") + val mainScopes = Sentry.getCurrentScopes() + val threadedScopes = mainScopes.forkedCurrentScope("test") executor.submit { - Sentry.setCurrentScopes(threadedHub) + Sentry.setCurrentScopes(threadedScopes) }.get() - assertEquals(mainHub, Sentry.getCurrentScopes()) + assertEquals(mainScopes, Sentry.getCurrentScopes()) val callableFuture = CompletableFuture.supplyAsync( SentryWrapper.wrapSupplierIsolated { - assertNotEquals(mainHub, Sentry.getCurrentScopes()) - assertNotEquals(threadedHub, Sentry.getCurrentScopes()) + assertNotEquals(mainScopes, Sentry.getCurrentScopes()) + assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) "Result 1" }, executor @@ -57,8 +57,8 @@ class SentryWrapperTest { callableFuture.join() executor.submit { - assertNotEquals(mainHub, Sentry.getCurrentScopes()) - assertEquals(threadedHub, Sentry.getCurrentScopes()) + assertNotEquals(mainScopes, Sentry.getCurrentScopes()) + assertEquals(threadedScopes, Sentry.getCurrentScopes()) }.get() } @@ -164,25 +164,25 @@ class SentryWrapperTest { } @Test - fun `hub is reset to its state within the thread after callable is done`() { + fun `scopes are reset to its state within the thread after callable is done`() { Sentry.init { it.dsn = dsn } - val mainHub = Sentry.getCurrentScopes() - val threadedHub = Sentry.getCurrentScopes().forkedCurrentScope("test") + val mainScopes = Sentry.getCurrentScopes() + val threadedScopes = Sentry.getCurrentScopes().forkedCurrentScope("test") executor.submit { - Sentry.setCurrentScopes(threadedHub) + Sentry.setCurrentScopes(threadedScopes) }.get() - assertEquals(mainHub, Sentry.getCurrentScopes()) + assertEquals(mainScopes, Sentry.getCurrentScopes()) val callableFuture = executor.submit( SentryWrapper.wrapCallable { - assertNotEquals(mainHub, Sentry.getCurrentScopes()) - assertNotEquals(threadedHub, Sentry.getCurrentScopes()) + assertNotEquals(mainScopes, Sentry.getCurrentScopes()) + assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) "Result 1" } ) @@ -190,8 +190,8 @@ class SentryWrapperTest { callableFuture.get() executor.submit { - assertNotEquals(mainHub, Sentry.getCurrentScopes()) - assertEquals(threadedHub, Sentry.getCurrentScopes()) + assertNotEquals(mainScopes, Sentry.getCurrentScopes()) + assertEquals(threadedScopes, Sentry.getCurrentScopes()) }.get() } @@ -204,20 +204,20 @@ class SentryWrapperTest { } } - val mainHub = Sentry.getCurrentScopes() - val threadedHub = Sentry.getCurrentScopes().forkedCurrentScope("test") + val mainScopes = Sentry.getCurrentScopes() + val threadedScopes = Sentry.getCurrentScopes().forkedCurrentScope("test") executor.submit { - Sentry.setCurrentScopes(threadedHub) + Sentry.setCurrentScopes(threadedScopes) }.get() - assertEquals(mainHub, Sentry.getCurrentScopes()) + assertEquals(mainScopes, Sentry.getCurrentScopes()) val callableFuture = CompletableFuture.supplyAsync( SentryWrapper.wrapSupplierIsolated { - assertNotEquals(mainHub, Sentry.getCurrentScopes()) - assertNotEquals(threadedHub, Sentry.getCurrentScopes()) + assertNotEquals(mainScopes, Sentry.getCurrentScopes()) + assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) "Result 1" }, executor @@ -226,8 +226,8 @@ class SentryWrapperTest { callableFuture.join() executor.submit { - assertNotEquals(mainHub, Sentry.getCurrentScopes()) - assertEquals(threadedHub, Sentry.getCurrentScopes()) + assertNotEquals(mainScopes, Sentry.getCurrentScopes()) + assertEquals(threadedScopes, Sentry.getCurrentScopes()) }.get() } @@ -338,20 +338,20 @@ class SentryWrapperTest { it.dsn = dsn } - val mainHub = Sentry.getCurrentScopes() - val threadedHub = Sentry.getCurrentScopes().forkedCurrentScope("test") + val mainScopes = Sentry.getCurrentScopes() + val threadedScopes = Sentry.getCurrentScopes().forkedCurrentScope("test") executor.submit { - Sentry.setCurrentScopes(threadedHub) + Sentry.setCurrentScopes(threadedScopes) }.get() - assertEquals(mainHub, Sentry.getCurrentScopes()) + assertEquals(mainScopes, Sentry.getCurrentScopes()) val callableFuture = executor.submit( SentryWrapper.wrapCallableIsolated { - assertNotEquals(mainHub, Sentry.getCurrentScopes()) - assertNotEquals(threadedHub, Sentry.getCurrentScopes()) + assertNotEquals(mainScopes, Sentry.getCurrentScopes()) + assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) "Result 1" } ) @@ -359,8 +359,8 @@ class SentryWrapperTest { callableFuture.get() executor.submit { - assertNotEquals(mainHub, Sentry.getCurrentScopes()) - assertEquals(threadedHub, Sentry.getCurrentScopes()) + assertNotEquals(mainScopes, Sentry.getCurrentScopes()) + assertEquals(threadedScopes, Sentry.getCurrentScopes()) }.get() } } diff --git a/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt b/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt index ec366e1901..51e6d32914 100644 --- a/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt @@ -105,7 +105,7 @@ class UncaughtExceptionHandlerIntegrationTest { options.addIntegration(integrationMock) options.cacheDirPath = fixture.file.absolutePath options.setSerializer(mock()) - val scopes = Hub(options) + val scopes = Scopes(Scope(options), Scope(options), Scope(options), "test") scopes.close() verify(integrationMock).close() } diff --git a/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt b/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt index 7058083ac9..889459dd35 100644 --- a/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt @@ -1,7 +1,6 @@ package io.sentry.util import io.sentry.CheckInStatus -import io.sentry.HubScopesWrapper import io.sentry.IScopes import io.sentry.ISentryLifecycleToken import io.sentry.MonitorConfig @@ -61,7 +60,6 @@ class CheckInUtilsTest { val scopes = mock() val lifecycleToken = mock() sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) - sentry.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(scopes)) sentry.`when` { Sentry.pushIsolationScope() }.then { scopes.pushIsolationScope() lifecycleToken @@ -98,7 +96,6 @@ class CheckInUtilsTest { val scopes = mock() val lifecycleToken = mock() sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) - sentry.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(scopes)) sentry.`when` { Sentry.pushIsolationScope() }.then { scopes.pushIsolationScope() lifecycleToken @@ -139,7 +136,6 @@ class CheckInUtilsTest { val scopes = mock() val lifecycleToken = mock() sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) - sentry.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(scopes)) sentry.`when` { Sentry.pushIsolationScope() }.then { scopes.pushIsolationScope() lifecycleToken @@ -178,7 +174,6 @@ class CheckInUtilsTest { val scopes = mock() val lifecycleToken = mock() sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) - sentry.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(scopes)) sentry.`when` { Sentry.pushIsolationScope() }.then { scopes.pushIsolationScope() lifecycleToken @@ -219,7 +214,6 @@ class CheckInUtilsTest { Mockito.mockStatic(Sentry::class.java).use { sentry -> val scopes = mock() sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) - sentry.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(scopes)) whenever(scopes.options).thenReturn( SentryOptions().apply { cron = SentryOptions.Cron().apply { @@ -247,7 +241,6 @@ class CheckInUtilsTest { Mockito.mockStatic(Sentry::class.java).use { sentry -> val scopes = mock() sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) - sentry.`when` { Sentry.getCurrentHub() }.thenReturn(HubScopesWrapper(scopes)) whenever(scopes.options).thenReturn( SentryOptions().apply { cron = SentryOptions.Cron().apply { From 3a65a07b2451c5a736e69af347d37b1e8c6d31cf Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 2 May 2024 14:22:45 +0200 Subject: [PATCH 35/91] Hubs/Scopes Merge 35 - Implement `ScopesTest` (#3370) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest --- sentry/src/main/java/io/sentry/Scopes.java | 1 + sentry/src/test/java/io/sentry/ScopesTest.kt | 2186 ++++++++++++++++++ 2 files changed, 2187 insertions(+) create mode 100644 sentry/src/test/java/io/sentry/ScopesTest.kt diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 757a3870a8..538c192304 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -421,6 +421,7 @@ public void close(final boolean isRestarting) { } // TODO: should we end session before closing client? + // TODO [HSM] should we go through all clients (global, isolation, current) and close them? getClient().close(isRestarting); } catch (Throwable e) { getOptions().getLogger().log(SentryLevel.ERROR, "Error while closing the Scopes.", e); diff --git a/sentry/src/test/java/io/sentry/ScopesTest.kt b/sentry/src/test/java/io/sentry/ScopesTest.kt new file mode 100644 index 0000000000..1b886aea5b --- /dev/null +++ b/sentry/src/test/java/io/sentry/ScopesTest.kt @@ -0,0 +1,2186 @@ +package io.sentry + +import io.sentry.backpressure.IBackpressureMonitor +import io.sentry.cache.EnvelopeCache +import io.sentry.clientreport.ClientReportTestHelper.Companion.assertClientReport +import io.sentry.clientreport.DiscardReason +import io.sentry.clientreport.DiscardedEvent +import io.sentry.hints.SessionEndHint +import io.sentry.hints.SessionStartHint +import io.sentry.protocol.SentryId +import io.sentry.protocol.SentryTransaction +import io.sentry.protocol.User +import io.sentry.test.DeferredExecutorService +import io.sentry.test.callMethod +import io.sentry.util.HintUtils +import io.sentry.util.StringUtils +import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.argWhere +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.check +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doThrow +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.spy +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.whenever +import java.io.File +import java.nio.file.Files +import java.util.Queue +import java.util.UUID +import java.util.concurrent.atomic.AtomicReference +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertFalse +import kotlin.test.assertNotEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue +import kotlin.test.fail + +class ScopesTest { + + private lateinit var file: File + private lateinit var profilingTraceFile: File + + @BeforeTest + fun `set up`() { + file = Files.createTempDirectory("sentry-disk-cache-test").toAbsolutePath().toFile() + profilingTraceFile = Files.createTempFile("trace", ".trace").toFile() + profilingTraceFile.writeText("sampledProfile") + SentryCrashLastRunState.getInstance().reset() + } + + @AfterTest + fun shutdown() { + file.deleteRecursively() + profilingTraceFile.delete() + Sentry.close() + SentryCrashLastRunState.getInstance().reset() + } + + private fun createScopes(options: SentryOptions): Scopes { + return Scopes(Scope(options), Scope(options), Scope(options), "test") + } + + @Test + fun `when no dsn available, ctor throws illegal arg`() { + val ex = assertFailsWith { createScopes(SentryOptions()) } + assertEquals("Scopes requires a DSN to be instantiated. Considering using the NoOpScopes if no DSN is available.", ex.message) + } + + @Test + fun `when isolation scope is forked, integrations are not registered`() { + val integrationMock = mock() + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + options.addIntegration(integrationMock) +// val expected = HubAdapter.getInstance() + val scopes = createScopes(options) +// verify(integrationMock).register(expected, options) + scopes.forkedScopes("test") + verifyNoMoreInteractions(integrationMock) + } + + @Test + fun `when current scope is forked, integrations are not registered`() { + val integrationMock = mock() + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + options.addIntegration(integrationMock) +// val expected = HubAdapter.getInstance() + val scopes = createScopes(options) +// verify(integrationMock).register(expected, options) + scopes.forkedCurrentScope("test") + verifyNoMoreInteractions(integrationMock) + } + + @Test + fun `when isolation scope is forked, scope changes are isolated`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val scopes = createScopes(options) + var firstScope: IScope? = null + scopes.configureScope { + firstScope = it + it.setTag("scopes", "a") + } + var cloneScope: IScope? = null + val clone = scopes.forkedScopes("test") + clone.configureScope { + cloneScope = it + it.setTag("scopes", "b") + } + assertEquals("a", firstScope!!.tags["scopes"]) + assertEquals("b", cloneScope!!.tags["scopes"]) + } + + @Test + fun `when current scope is forked, scope changes are not isolated`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val scopes = createScopes(options) + var firstScope: IScope? = null + scopes.configureScope { + firstScope = it + it.setTag("scopes", "a") + } + var cloneScope: IScope? = null + val clone = scopes.forkedCurrentScope("test") + clone.configureScope { + cloneScope = it + it.setTag("scopes", "b") + } + assertEquals("b", firstScope!!.tags["scopes"]) + assertEquals("b", cloneScope!!.tags["scopes"]) + } + + @Test + fun `when scopes is initialized, breadcrumbs are capped as per options`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.maxBreadcrumbs = 5 + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + (1..10).forEach { _ -> sut.addBreadcrumb(Breadcrumb(), null) } + var actual = 0 + sut.configureScope { + actual = it.breadcrumbs.size + } + assertEquals(options.maxBreadcrumbs, actual) + } + + @Test + fun `when beforeBreadcrumb returns null, crumb is dropped`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.beforeBreadcrumb = SentryOptions.BeforeBreadcrumbCallback { _: Breadcrumb, _: Any? -> null } + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + sut.addBreadcrumb(Breadcrumb(), null) + var breadcrumbs: Queue? = null + sut.configureScope { breadcrumbs = it.breadcrumbs } + assertEquals(0, breadcrumbs!!.size) + } + + @Test + fun `when beforeBreadcrumb modifies crumb, crumb is stored modified`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + val expected = "expected" + options.beforeBreadcrumb = SentryOptions.BeforeBreadcrumbCallback { breadcrumb: Breadcrumb, _: Any? -> breadcrumb.message = expected; breadcrumb; } + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + val crumb = Breadcrumb() + crumb.message = "original" + sut.addBreadcrumb(crumb) + var breadcrumbs: Queue? = null + sut.configureScope { breadcrumbs = it.breadcrumbs } + assertEquals(expected, breadcrumbs!!.first().message) + } + + @Test + fun `when beforeBreadcrumb is null, crumb is stored`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.beforeBreadcrumb = null + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + val expected = Breadcrumb() + sut.addBreadcrumb(expected) + var breadcrumbs: Queue? = null + sut.configureScope { breadcrumbs = it.breadcrumbs } + assertEquals(expected, breadcrumbs!!.single()) + } + + @Test + fun `when beforeSend throws an exception, breadcrumb adds an entry to the data field with exception message`() { + val exception = Exception("test") + + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.beforeBreadcrumb = SentryOptions.BeforeBreadcrumbCallback { _: Breadcrumb, _: Any? -> throw exception } + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + + val actual = Breadcrumb() + sut.addBreadcrumb(actual) + + assertEquals("test", actual.data["sentry:message"]) + } + + @Test + fun `when initialized, lastEventId is empty`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + assertEquals(SentryId.EMPTY_ID, sut.lastEventId) + } + + @Test + fun `when addBreadcrumb is called on disabled client, no-op`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + var breadcrumbs: Queue? = null + sut.configureScope { breadcrumbs = it.breadcrumbs } + sut.close() + sut.addBreadcrumb(Breadcrumb()) + assertTrue(breadcrumbs!!.isEmpty()) + } + + @Test + fun `when addBreadcrumb is called with message and category, breadcrumb object has values`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + var breadcrumbs: Queue? = null + sut.configureScope { breadcrumbs = it.breadcrumbs } + sut.addBreadcrumb("message", "category") + assertEquals("message", breadcrumbs!!.single().message) + assertEquals("category", breadcrumbs!!.single().category) + } + + @Test + fun `when addBreadcrumb is called with message, breadcrumb object has value`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + var breadcrumbs: Queue? = null + sut.configureScope { breadcrumbs = it.breadcrumbs } + sut.addBreadcrumb("message", "category") + assertEquals("message", breadcrumbs!!.single().message) + assertEquals("category", breadcrumbs!!.single().category) + } + + @Test + fun `when flush is called on disabled client, no-op`() { + val (sut, mockClient) = getEnabledScopes() + sut.close() + + sut.flush(1000) + verify(mockClient, never()).flush(1000) + } + + @Test + fun `when flush is called, client flush gets called`() { + val (sut, mockClient) = getEnabledScopes() + + sut.flush(1000) + verify(mockClient).flush(1000) + } + + //region captureEvent tests + @Test + fun `when captureEvent is called and event is null, lastEventId is empty`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + sut.callMethod("captureEvent", SentryEvent::class.java, null) + assertEquals(SentryId.EMPTY_ID, sut.lastEventId) + } + + @Test + fun `when captureEvent is called on disabled client, do nothing`() { + val (sut, mockClient) = getEnabledScopes() + sut.close() + + sut.captureEvent(SentryEvent()) + verify(mockClient, never()).captureEvent(any(), any()) + } + + @Test + fun `when captureEvent is called with a valid argument, captureEvent on the client should be called`() { + val (sut, mockClient) = getEnabledScopes() + + val event = SentryEvent() + val hints = HintUtils.createWithTypeCheckHint({}) + sut.captureEvent(event, hints) + verify(mockClient).captureEvent(eq(event), any(), eq(hints)) + } + + @Test + fun `when captureEvent is called on disabled scopes, lastEventId does not get overwritten`() { + val (sut, mockClient) = getEnabledScopes() + whenever(mockClient.captureEvent(any(), any(), anyOrNull())).thenReturn(SentryId(UUID.randomUUID())) + val event = SentryEvent() + val hints = HintUtils.createWithTypeCheckHint({}) + sut.captureEvent(event, hints) + val lastEventId = sut.lastEventId + sut.close() + sut.captureEvent(event, hints) + assertEquals(lastEventId, sut.lastEventId) + } + + @Test + fun `when captureEvent is called and session tracking is disabled, it should not capture a session`() { + val (sut, mockClient) = getEnabledScopes() + + val event = SentryEvent() + val hints = HintUtils.createWithTypeCheckHint({}) + sut.captureEvent(event, hints) + verify(mockClient).captureEvent(eq(event), any(), eq(hints)) + verify(mockClient, never()).captureSession(any(), any()) + } + + @Test + fun `when captureEvent is called but no session started, it should not capture a session`() { + val (sut, mockClient) = getEnabledScopes() + + val event = SentryEvent() + val hints = HintUtils.createWithTypeCheckHint({}) + sut.captureEvent(event, hints) + verify(mockClient).captureEvent(eq(event), any(), eq(hints)) + verify(mockClient, never()).captureSession(any(), any()) + } + + @Test + fun `when captureEvent is called and event has exception which has been previously attached with span context, sets span context to the event`() { + val (sut, mockClient) = getEnabledScopes() + val exception = RuntimeException() + val span = mock() + whenever(span.spanContext).thenReturn(SpanContext("op")) + sut.setSpanContext(exception, span, "tx-name") + + val event = SentryEvent(exception) + + val hints = HintUtils.createWithTypeCheckHint({}) + sut.captureEvent(event, hints) + assertEquals(span.spanContext, event.contexts.trace) + verify(mockClient).captureEvent(eq(event), any(), eq(hints)) + } + + @Test + fun `when captureEvent is called and event has exception which root cause has been previously attached with span context, sets span context to the event`() { + val (sut, mockClient) = getEnabledScopes() + val rootCause = RuntimeException() + val span = mock() + whenever(span.spanContext).thenReturn(SpanContext("op")) + sut.setSpanContext(rootCause, span, "tx-name") + + val event = SentryEvent(RuntimeException(rootCause)) + + val hints = HintUtils.createWithTypeCheckHint({}) + sut.captureEvent(event, hints) + assertEquals(span.spanContext, event.contexts.trace) + verify(mockClient).captureEvent(eq(event), any(), eq(hints)) + } + + @Test + fun `when captureEvent is called and event has exception which non-root cause has been previously attached with span context, sets span context to the event`() { + val (sut, mockClient) = getEnabledScopes() + val rootCause = RuntimeException() + val exceptionAssignedToSpan = RuntimeException(rootCause) + val span = mock() + whenever(span.spanContext).thenReturn(SpanContext("op")) + sut.setSpanContext(exceptionAssignedToSpan, span, "tx-name") + + val event = SentryEvent(RuntimeException(exceptionAssignedToSpan)) + + val hints = HintUtils.createWithTypeCheckHint({}) + sut.captureEvent(event, hints) + assertEquals(span.spanContext, event.contexts.trace) + verify(mockClient).captureEvent(eq(event), any(), eq(hints)) + } + + @Test + fun `when captureEvent is called and event has exception which has been previously attached with span context and trace context already set, does not set new span context to the event`() { + val (sut, mockClient) = getEnabledScopes() + val exception = RuntimeException() + val span = mock() + whenever(span.spanContext).thenReturn(SpanContext("op")) + sut.setSpanContext(exception, span, "tx-name") + + val event = SentryEvent(exception) + val originalSpanContext = SpanContext("op") + event.contexts.trace = originalSpanContext + + val hints = HintUtils.createWithTypeCheckHint({}) + sut.captureEvent(event, hints) + assertEquals(originalSpanContext, event.contexts.trace) + verify(mockClient).captureEvent(eq(event), any(), eq(hints)) + } + + @Test + fun `when captureEvent is called and event has exception which has not been previously attached with span context, does not set new span context to the event`() { + val (sut, mockClient) = getEnabledScopes() + + val event = SentryEvent(RuntimeException()) + + val hints = HintUtils.createWithTypeCheckHint({}) + sut.captureEvent(event, hints) + assertNull(event.contexts.trace) + verify(mockClient).captureEvent(eq(event), any(), eq(hints)) + } + + @Test + fun `when captureEvent is called with a ScopeCallback then the modified scope is sent to the client`() { + val (sut, mockClient) = getEnabledScopes() + + sut.captureEvent(SentryEvent(), null) { + it.setTag("test", "testValue") + } + + verify(mockClient).captureEvent( + any(), + check { + assertEquals("testValue", it.tags["test"]) + }, + anyOrNull() + ) + } + + @Test + fun `when captureEvent is called with a ScopeCallback then subsequent calls to captureEvent send the unmodified Scope to the client`() { + val (sut, mockClient) = getEnabledScopes() + val argumentCaptor = argumentCaptor() + + sut.captureEvent(SentryEvent(), null) { + it.setTag("test", "testValue") + } + + sut.captureEvent(SentryEvent()) + + verify(mockClient, times(2)).captureEvent( + any(), + argumentCaptor.capture(), + anyOrNull() + ) + + assertEquals("testValue", argumentCaptor.allValues[0].tags["test"]) + assertNull(argumentCaptor.allValues[1].tags["test"]) + } + + @Test + fun `when captureEvent is called with a ScopeCallback that crashes then the event should still be captured`() { + val (sut, mockClient, logger) = getEnabledScopes() + + val exception = Exception("scope callback exception") + sut.captureEvent(SentryEvent(), null) { + throw exception + } + + verify(mockClient).captureEvent( + any(), + anyOrNull(), + anyOrNull() + ) + + verify(logger).log(eq(SentryLevel.ERROR), any(), eq(exception)) + } + //endregion + + //region captureMessage tests + @Test + fun `when captureMessage is called and event is null, lastEventId is empty`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + sut.callMethod("captureMessage", String::class.java, null) + assertEquals(SentryId.EMPTY_ID, sut.lastEventId) + } + + @Test + fun `when captureMessage is called on disabled client, do nothing`() { + val (sut, mockClient) = getEnabledScopes() + sut.close() + + sut.captureMessage("test") + verify(mockClient, never()).captureMessage(any(), any()) + } + + @Test + fun `when captureMessage is called with a valid message, captureMessage on the client should be called`() { + val (sut, mockClient) = getEnabledScopes() + + sut.captureMessage("test") + verify(mockClient).captureMessage(any(), any(), any()) + } + + @Test + fun `when captureMessage is called, level is INFO by default`() { + val (sut, mockClient) = getEnabledScopes() + sut.captureMessage("test") + verify(mockClient).captureMessage(eq("test"), eq(SentryLevel.INFO), any()) + } + + @Test + fun `when captureMessage is called with a ScopeCallback then the modified scope is sent to the client`() { + val (sut, mockClient) = getEnabledScopes() + + sut.captureMessage("test") { + it.setTag("test", "testValue") + } + + verify(mockClient).captureMessage( + any(), + any(), + check { + assertEquals("testValue", it.tags["test"]) + } + ) + } + + @Test + fun `when captureMessage is called with a ScopeCallback then subsequent calls to captureMessage send the unmodified Scope to the client`() { + val (sut, mockClient) = getEnabledScopes() + val argumentCaptor = argumentCaptor() + + sut.captureMessage("testMessage") { + it.setTag("test", "testValue") + } + + sut.captureMessage("test", SentryLevel.INFO) + + verify(mockClient, times(2)).captureMessage( + any(), + any(), + argumentCaptor.capture() + ) + + assertEquals("testValue", argumentCaptor.allValues[0].tags["test"]) + assertNull(argumentCaptor.allValues[1].tags["test"]) + } + + @Test + fun `when captureMessage is called with a ScopeCallback that crashes then the message should still be captured`() { + val (sut, mockClient, logger) = getEnabledScopes() + + val exception = Exception("scope callback exception") + sut.captureMessage("Hello World") { + throw exception + } + + verify(mockClient).captureMessage( + any(), + anyOrNull(), + anyOrNull() + ) + + verify(logger).log(eq(SentryLevel.ERROR), any(), eq(exception)) + } + + //endregion + + //region captureException tests + @Test + fun `when captureException is called and exception is null, lastEventId is empty`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + sut.callMethod("captureException", Throwable::class.java, null) + assertEquals(SentryId.EMPTY_ID, sut.lastEventId) + } + + @Test + fun `when captureException is called on disabled client, do nothing`() { + val (sut, mockClient) = getEnabledScopes() + sut.close() + + sut.captureException(Throwable()) + verify(mockClient, never()).captureEvent(any(), any(), any()) + } + + @Test + fun `when captureException is called with a valid argument and hint, captureEvent on the client should be called`() { + val (sut, mockClient) = getEnabledScopes() + + val hints = HintUtils.createWithTypeCheckHint({}) + sut.captureException(Throwable(), hints) + verify(mockClient).captureEvent(any(), any(), any()) + } + + @Test + fun `when captureException is called with a valid argument but no hint, captureEvent on the client should be called`() { + val (sut, mockClient) = getEnabledScopes() + + sut.captureException(Throwable()) + verify(mockClient).captureEvent(any(), any(), any()) + } + + @Test + fun `when captureException is called with an exception which has been previously attached with span context, span context should be set on the event before capturing`() { + val (sut, mockClient) = getEnabledScopes() + val throwable = Throwable() + val span = mock() + whenever(span.spanContext).thenReturn(SpanContext("op")) + sut.setSpanContext(throwable, span, "tx-name") + + sut.captureException(throwable) + verify(mockClient).captureEvent( + check { + assertEquals(span.spanContext, it.contexts.trace) + assertEquals("tx-name", it.transaction) + }, + any(), + anyOrNull() + ) + } + + @Test + fun `when captureException is called with an exception which has not been previously attached with span context, span context should not be set on the event before capturing`() { + val (sut, mockClient) = getEnabledScopes() + val span = mock() + whenever(span.spanContext).thenReturn(SpanContext("op")) + sut.setSpanContext(Throwable(), span, "tx-name") + + sut.captureException(Throwable()) + verify(mockClient).captureEvent( + check { + assertNull(it.contexts.trace) + }, + any(), + anyOrNull() + ) + } + + @Test + fun `when captureException is called with a ScopeCallback then the modified scope is sent to the client`() { + val (sut, mockClient) = getEnabledScopes() + + sut.captureException(Throwable(), null) { + it.setTag("test", "testValue") + } + + verify(mockClient).captureEvent( + any(), + check { + assertEquals("testValue", it.tags["test"]) + }, + anyOrNull() + ) + } + + @Test + fun `when captureException is called with a ScopeCallback then subsequent calls to captureException send the unmodified Scope to the client`() { + val (sut, mockClient) = getEnabledScopes() + val argumentCaptor = argumentCaptor() + + sut.captureException(Throwable(), null) { + it.setTag("test", "testValue") + } + + sut.captureException(Throwable()) + + verify(mockClient, times(2)).captureEvent( + any(), + argumentCaptor.capture(), + anyOrNull() + ) + + assertEquals("testValue", argumentCaptor.allValues[0].tags["test"]) + assertNull(argumentCaptor.allValues[1].tags["test"]) + } + + @Test + fun `when captureException is called with a ScopeCallback that crashes then the exception should still be captured`() { + val (sut, mockClient, logger) = getEnabledScopes() + + val exception = Exception("scope callback exception") + sut.captureException(Throwable()) { + throw exception + } + + verify(mockClient).captureEvent( + any(), + anyOrNull(), + anyOrNull() + ) + + verify(logger).log(eq(SentryLevel.ERROR), any(), eq(exception)) + } + + //endregion + + //region captureUserFeedback tests + + @Test + fun `when captureUserFeedback is called it is forwarded to the client`() { + val (sut, mockClient) = getEnabledScopes() + sut.captureUserFeedback(userFeedback) + + verify(mockClient).captureUserFeedback( + check { + assertEquals(userFeedback.eventId, it.eventId) + assertEquals(userFeedback.email, it.email) + assertEquals(userFeedback.name, it.name) + assertEquals(userFeedback.comments, it.comments) + } + ) + } + + @Test + fun `when captureUserFeedback is called on disabled client, do nothing`() { + val (sut, mockClient) = getEnabledScopes() + sut.close() + + sut.captureUserFeedback(userFeedback) + verify(mockClient, never()).captureUserFeedback(any()) + } + + @Test + fun `when captureUserFeedback is called and client throws, don't crash`() { + val (sut, mockClient) = getEnabledScopes() + + whenever(mockClient.captureUserFeedback(any())).doThrow(IllegalArgumentException("")) + + sut.captureUserFeedback(userFeedback) + } + + private val userFeedback: UserFeedback get() { + val eventId = SentryId("c2fb8fee2e2b49758bcb67cda0f713c7") + return UserFeedback(eventId).apply { + name = "John" + email = "john@me.com" + comments = "comment" + } + } + + //region captureCheckIn tests + + @Test + fun `when captureCheckIn is called it is forwarded to the client`() { + val (sut, mockClient) = getEnabledScopes() + sut.captureCheckIn(checkIn) + + verify(mockClient).captureCheckIn( + check { + assertEquals(checkIn.checkInId, it.checkInId) + assertEquals(checkIn.monitorSlug, it.monitorSlug) + assertEquals(checkIn.status, it.status) + }, + any(), + anyOrNull() + ) + } + + @Test + fun `when captureCheckIn is called on disabled client, do nothing`() { + val (sut, mockClient) = getEnabledScopes() + sut.close() + + sut.captureCheckIn(checkIn) + verify(mockClient, never()).captureCheckIn(any(), any(), anyOrNull()) + } + + @Test + fun `when captureCheckIn is called and client throws, don't crash`() { + val (sut, mockClient) = getEnabledScopes() + + whenever(mockClient.captureCheckIn(any(), any(), anyOrNull())).doThrow(IllegalArgumentException("")) + + sut.captureCheckIn(checkIn) + } + + private val checkIn: CheckIn = CheckIn("some_slug", CheckInStatus.OK) + + //endregion + + //region close tests + @Test + fun `when close is called on disabled client, do nothing`() { + val (sut, mockClient) = getEnabledScopes() + sut.close() + + sut.close() + verify(mockClient).close(eq(false)) // 1 to close, but next one wont be recorded + } + + @Test + fun `when close is called and client is alive, close on the client should be called`() { + val (sut, mockClient) = getEnabledScopes() + + sut.close() + verify(mockClient).close(eq(false)) + } + + @Test + fun `when close is called with isRestarting false and client is alive, close on the client should be called with isRestarting false`() { + val (sut, mockClient) = getEnabledScopes() + + sut.close(false) + verify(mockClient).close(eq(false)) + } + + @Test + fun `when close is called with isRestarting true and client is alive, close on the client should be called with isRestarting true`() { + val (sut, mockClient) = getEnabledScopes() + + sut.close(true) + verify(mockClient).close(eq(true)) + } + //endregion + + //region withScope tests + @Test + fun `when withScope is called on disabled client, execute on NoOpScope`() { + val (sut) = getEnabledScopes() + + val scopeCallback = mock() + sut.close() + + sut.withScope(scopeCallback) + verify(scopeCallback).run(NoOpScope.getInstance()) + } + + @Test + fun `when withScope is called with alive client, run should be called`() { + val (sut) = getEnabledScopes() + + val scopeCallback = mock() + + sut.withScope(scopeCallback) + verify(scopeCallback).run(any()) + } + + @Test + fun `when withScope throws an exception then it should be caught`() { + val (scopes, _, logger) = getEnabledScopes() + + val exception = Exception("scope callback exception") + val scopeCallback = ScopeCallback { + throw exception + } + + scopes.withScope(scopeCallback) + + verify(logger).log(eq(SentryLevel.ERROR), any(), eq(exception)) + } + //endregion + + //region configureScope tests + @Test + fun `when configureScope is called on disabled client, do nothing`() { + val (sut) = getEnabledScopes() + + val scopeCallback = mock() + sut.close() + + sut.configureScope(scopeCallback) + verify(scopeCallback, never()).run(any()) + } + + @Test + fun `when configureScope is called with alive client, run should be called`() { + val (sut) = getEnabledScopes() + + val scopeCallback = mock() + + sut.configureScope(scopeCallback) + verify(scopeCallback).run(any()) + } + + @Test + fun `when configureScope throws an exception then it should be caught`() { + val (scopes, _, logger) = getEnabledScopes() + + val exception = Exception("scope callback exception") + val scopeCallback = ScopeCallback { + throw exception + } + + scopes.configureScope(scopeCallback) + + verify(logger).log(eq(SentryLevel.ERROR), any(), eq(exception)) + } + //endregion + + @Test + fun `when integration is registered, scopes is enabled`() { + val mock = mock() + + var options: SentryOptions? = null + // init main scopes and make it enabled + Sentry.init { + it.addIntegration(mock) + it.dsn = "https://key@sentry.io/proj" + it.cacheDirPath = file.absolutePath + it.setSerializer(mock()) + options = it + } + + doAnswer { + val scopes = it.arguments[0] as IScopes + assertTrue(scopes.isEnabled) + }.whenever(mock).register(any(), eq(options!!)) + + verify(mock).register(any(), eq(options!!)) + } + + //region setLevel tests + @Test + fun `when setLevel is called on disabled client, do nothing`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + scopes.close() + + scopes.setLevel(SentryLevel.INFO) + assertNull(scope?.level) + } + + @Test + fun `when setLevel is called, level is set`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + + scopes.setLevel(SentryLevel.INFO) + assertEquals(SentryLevel.INFO, scope?.level) + } + //endregion + + //region setTransaction tests + @Test + fun `when setTransaction is called on disabled client, do nothing`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + scopes.close() + + scopes.setTransaction("test") + assertNull(scope?.transactionName) + } + + @Test + fun `when setTransaction is called, and transaction is not set, transaction name is changed`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + + scopes.setTransaction("test") + assertEquals("test", scope?.transactionName) + } + + @Test + fun `when setTransaction is called, and transaction is set, transaction name is changed`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + + val tx = scopes.startTransaction("test", "op") + scopes.configureScope { it.setTransaction(tx) } + + assertEquals("test", scope?.transactionName) + } + + @Test + fun `when startTransaction is called with different instrumenter, no-op is returned`() { + val scopes = generateScopes() + + val transactionContext = TransactionContext("test", "op").also { it.instrumenter = Instrumenter.OTEL } + val transactionOptions = TransactionOptions() + val tx = scopes.startTransaction(transactionContext, transactionOptions) + + assertTrue(tx is NoOpTransaction) + } + + @Test + fun `when startTransaction is called with different instrumenter, no-op is returned 2`() { + val scopes = generateScopes() { + it.instrumenter = Instrumenter.OTEL + } + + val tx = scopes.startTransaction("test", "op") + + assertTrue(tx is NoOpTransaction) + } + + @Test + fun `when startTransaction is called with configured instrumenter, it works`() { + val scopes = generateScopes() { + it.instrumenter = Instrumenter.OTEL + } + + val transactionContext = TransactionContext("test", "op").also { it.instrumenter = Instrumenter.OTEL } + val transactionOptions = TransactionOptions() + val tx = scopes.startTransaction(transactionContext, transactionOptions) + + assertFalse(tx is NoOpTransaction) + } + //endregion + + //region setUser tests + @Test + fun `when setUser is called on disabled client, do nothing`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + scopes.close() + + scopes.setUser(User()) + assertNull(scope?.user) + } + + @Test + fun `when setUser is called, user is set`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + + val user = User() + scopes.setUser(user) + assertEquals(user, scope?.user) + } + //endregion + + //region setFingerprint tests + @Test + fun `when setFingerprint is called on disabled client, do nothing`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + scopes.close() + + val fingerprint = listOf("abc") + scopes.setFingerprint(fingerprint) + assertEquals(0, scope?.fingerprint?.count()) + } + + @Test + fun `when setFingerprint is called with null parameter, do nothing`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + + scopes.callMethod("setFingerprint", List::class.java, null) + assertEquals(0, scope?.fingerprint?.count()) + } + + @Test + fun `when setFingerprint is called, fingerprint is set`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + + val fingerprint = listOf("abc") + scopes.setFingerprint(fingerprint) + assertEquals(1, scope?.fingerprint?.count()) + } + //endregion + + //region clearBreadcrumbs tests + @Test + fun `when clearBreadcrumbs is called on disabled client, do nothing`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + scopes.addBreadcrumb(Breadcrumb()) + assertEquals(1, scope?.breadcrumbs?.count()) + + scopes.close() + + assertEquals(0, scope?.breadcrumbs?.count()) + } + + @Test + fun `when clearBreadcrumbs is called, clear breadcrumbs`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + + scopes.addBreadcrumb(Breadcrumb()) + assertEquals(1, scope?.breadcrumbs?.count()) + scopes.clearBreadcrumbs() + assertEquals(0, scope?.breadcrumbs?.count()) + } + //endregion + + //region setTag tests + @Test + fun `when setTag is called on disabled client, do nothing`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + scopes.close() + + scopes.setTag("test", "test") + assertEquals(0, scope?.tags?.count()) + } + + @Test + fun `when setTag is called with null parameters, do nothing`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + + scopes.callMethod("setTag", parameterTypes = arrayOf(String::class.java, String::class.java), null, null) + assertEquals(0, scope?.tags?.count()) + } + + @Test + fun `when setTag is called, tag is set`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + + scopes.setTag("test", "test") + assertEquals(1, scope?.tags?.count()) + } + //endregion + + //region setExtra tests + @Test + fun `when setExtra is called on disabled client, do nothing`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + scopes.close() + + scopes.setExtra("test", "test") + assertEquals(0, scope?.extras?.count()) + } + + @Test + fun `when setExtra is called with null parameters, do nothing`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + + scopes.callMethod("setExtra", parameterTypes = arrayOf(String::class.java, String::class.java), null, null) + assertEquals(0, scope?.extras?.count()) + } + + @Test + fun `when setExtra is called, extra is set`() { + val scopes = generateScopes() + var scope: IScope? = null + scopes.configureScope { + scope = it + } + + scopes.setExtra("test", "test") + assertEquals(1, scope?.extras?.count()) + } + //endregion + + //region captureEnvelope tests + @Test + fun `when captureEnvelope is called and envelope is null, throws IllegalArgumentException`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + try { + sut.callMethod("captureEnvelope", SentryEnvelope::class.java, null) + fail() + } catch (e: Exception) { + assertTrue(e.cause is java.lang.IllegalArgumentException) + } + } + + @Test + fun `when captureEnvelope is called on disabled client, do nothing`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + sut.close() + + sut.captureEnvelope(SentryEnvelope(SentryId(UUID.randomUUID()), null, setOf())) + verify(mockClient, never()).captureEnvelope(any(), any()) + } + + @Test + fun `when captureEnvelope is called with a valid envelope, captureEnvelope on the client should be called`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + + val envelope = SentryEnvelope(SentryId(UUID.randomUUID()), null, setOf()) + sut.captureEnvelope(envelope) + verify(mockClient).captureEnvelope(any(), anyOrNull()) + } + + @Test + fun `when captureEnvelope is called, lastEventId is not set`() { + val options = SentryOptions().apply { + dsn = "https://key@sentry.io/proj" + setSerializer(mock()) + } + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + whenever(mockClient.captureEnvelope(any(), anyOrNull())).thenReturn(SentryId()) + val envelope = SentryEnvelope(SentryId(UUID.randomUUID()), null, setOf()) + sut.captureEnvelope(envelope) + assertEquals(SentryId.EMPTY_ID, sut.lastEventId) + } + //endregion + + //region startSession tests + @Test + fun `when startSession is called on disabled client, do nothing`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.release = "0.0.1" + options.setSerializer(mock()) + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + sut.close() + + sut.startSession() + verify(mockClient, never()).captureSession(any(), any()) + } + + @Test + fun `when startSession is called, starts a session`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.release = "0.0.1" + options.setSerializer(mock()) + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + + sut.startSession() + verify(mockClient).captureSession(any(), argWhere { HintUtils.hasType(it, SessionStartHint::class.java) }) + } + + @Test + fun `when startSession is called and there's a session, stops it and starts a new one`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.release = "0.0.1" + options.setSerializer(mock()) + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + + sut.startSession() + sut.startSession() + verify(mockClient).captureSession(any(), argWhere { HintUtils.hasType(it, SessionEndHint::class.java) }) + verify(mockClient, times(2)).captureSession(any(), argWhere { HintUtils.hasType(it, SessionStartHint::class.java) }) + } + //endregion + + //region endSession tests + @Test + fun `when endSession is called on disabled client, do nothing`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.release = "0.0.1" + options.setSerializer(mock()) + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + sut.close() + + sut.endSession() + verify(mockClient, never()).captureSession(any(), any()) + } + + @Test + fun `when endSession is called and session tracking is disabled, do nothing`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.release = "0.0.1" + options.setSerializer(mock()) + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + + sut.endSession() + verify(mockClient, never()).captureSession(any(), any()) + } + + @Test + fun `when endSession is called, end a session`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.release = "0.0.1" + options.setSerializer(mock()) + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + + sut.startSession() + sut.endSession() + verify(mockClient).captureSession(any(), argWhere { HintUtils.hasType(it, SessionStartHint::class.java) }) + verify(mockClient).captureSession(any(), argWhere { HintUtils.hasType(it, SessionEndHint::class.java) }) + } + + @Test + fun `when endSession is called and there's no session, do nothing`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.release = "0.0.1" + options.setSerializer(mock()) + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + + sut.endSession() + verify(mockClient, never()).captureSession(any(), any()) + } + //endregion + + //region captureTransaction tests + @Test + fun `when captureTransaction is called on disabled client, do nothing`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + sut.close() + + val sentryTracer = SentryTracer(TransactionContext("name", "op"), sut) + sentryTracer.finish() + sut.captureTransaction(SentryTransaction(sentryTracer), null as TraceContext?) + verify(mockClient, never()).captureTransaction(any(), any(), any()) + verify(mockClient, never()).captureTransaction(any(), any(), any(), anyOrNull(), anyOrNull()) + } + + @Test + fun `when captureTransaction and transaction is sampled, captureTransaction on the client should be called`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + + val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), sut) + sentryTracer.finish() + val traceContext = sentryTracer.traceContext() + verify(mockClient).captureTransaction(any(), equalTraceContext(traceContext), any(), eq(null), eq(null)) + } + + @Test + fun `when captureTransaction is called, lastEventId is not set`() { + val options = SentryOptions().apply { + dsn = "https://key@sentry.io/proj" + setSerializer(mock()) + } + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + whenever(mockClient.captureTransaction(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())).thenReturn(SentryId()) + + val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), sut) + sentryTracer.finish() + assertEquals(SentryId.EMPTY_ID, sut.lastEventId) + } + + @Test + fun `when captureTransaction and transaction is not finished, captureTransaction on the client should not be called`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + + val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), sut) + sut.captureTransaction(SentryTransaction(sentryTracer), null as TraceContext?) + verify(mockClient, never()).captureTransaction(any(), any(), any(), eq(null), anyOrNull()) + } + + @Test + fun `when captureTransaction and transaction is not sampled, captureTransaction on the client should not be called`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + + val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(false)), sut) + sentryTracer.finish() + val traceContext = sentryTracer.traceContext() + verify(mockClient, never()).captureTransaction(any(), equalTraceContext(traceContext), any(), eq(null), anyOrNull()) + } + + @Test + fun `transactions lost due to sampling are recorded as lost`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + + val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(false)), sut) + sentryTracer.finish() + + assertClientReport( + options.clientReportRecorder, + listOf(DiscardedEvent(DiscardReason.SAMPLE_RATE.reason, DataCategory.Transaction.category, 1)) + ) + } + + @Test + fun `transactions lost due to sampling caused by backpressure are recorded as lost`() { + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + val mockBackpressureMonitor = mock() + options.backpressureMonitor = mockBackpressureMonitor + whenever(mockBackpressureMonitor.downsampleFactor).thenReturn(1) + + val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(false)), sut) + sentryTracer.finish() + + assertClientReport( + options.clientReportRecorder, + listOf(DiscardedEvent(DiscardReason.BACKPRESSURE.reason, DataCategory.Transaction.category, 1)) + ) + } + //endregion + + //region profiling tests + + @Test + fun `when startTransaction and profiling is enabled, transaction is profiled only if sampled`() { + val mockTransactionProfiler = mock() + val mockClient = mock() + whenever(mockTransactionProfiler.onTransactionFinish(any(), anyOrNull(), anyOrNull())).thenAnswer { mockClient.captureEnvelope(mock()) } + val scopes = generateScopes { + it.setTransactionProfiler(mockTransactionProfiler) + } + scopes.bindClient(mockClient) + // Transaction is not sampled, so it should not be profiled + val contexts = TransactionContext("name", "op", TracesSamplingDecision(false, null, true, null)) + val transaction = scopes.startTransaction(contexts) + transaction.finish() + verify(mockClient, never()).captureEnvelope(any()) + + // Transaction is sampled, so it should be profiled + val sampledContexts = TransactionContext("name", "op", TracesSamplingDecision(true, null, true, null)) + val sampledTransaction = scopes.startTransaction(sampledContexts) + sampledTransaction.finish() + verify(mockClient).captureEnvelope(any()) + } + + @Test + fun `when startTransaction and is sampled but profiling is disabled, transaction is not profiled`() { + val mockTransactionProfiler = mock() + val mockClient = mock() + whenever(mockTransactionProfiler.onTransactionFinish(any(), anyOrNull(), anyOrNull())).thenAnswer { mockClient.captureEnvelope(mock()) } + val scopes = generateScopes { + it.profilesSampleRate = 0.0 + it.setTransactionProfiler(mockTransactionProfiler) + } + scopes.bindClient(mockClient) + val contexts = TransactionContext("name", "op") + val transaction = scopes.startTransaction(contexts) + transaction.finish() + verify(mockClient, never()).captureEnvelope(any()) + } + + @Test + fun `when profiler is running and isAppStartTransaction is false, startTransaction does not interact with profiler`() { + val mockTransactionProfiler = mock() + whenever(mockTransactionProfiler.isRunning).thenReturn(true) + val scopes = generateScopes { + it.profilesSampleRate = 1.0 + it.setTransactionProfiler(mockTransactionProfiler) + } + val context = TransactionContext("name", "op") + scopes.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = false }) + verify(mockTransactionProfiler, never()).start() + verify(mockTransactionProfiler, never()).bindTransaction(any()) + } + + @Test + fun `when profiler is running and isAppStartTransaction is true, startTransaction binds current profile`() { + val mockTransactionProfiler = mock() + whenever(mockTransactionProfiler.isRunning).thenReturn(true) + val scopes = generateScopes { + it.profilesSampleRate = 1.0 + it.setTransactionProfiler(mockTransactionProfiler) + } + val context = TransactionContext("name", "op") + val transaction = scopes.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = true }) + verify(mockTransactionProfiler, never()).start() + verify(mockTransactionProfiler).bindTransaction(eq(transaction)) + } + + @Test + fun `when profiler is not running, startTransaction starts and binds current profile`() { + val mockTransactionProfiler = mock() + whenever(mockTransactionProfiler.isRunning).thenReturn(false) + val scopes = generateScopes { + it.profilesSampleRate = 1.0 + it.setTransactionProfiler(mockTransactionProfiler) + } + val context = TransactionContext("name", "op") + val transaction = scopes.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = false }) + verify(mockTransactionProfiler).start() + verify(mockTransactionProfiler).bindTransaction(eq(transaction)) + } + //endregion + + //region startTransaction tests + @Test + fun `when startTransaction, creates transaction`() { + val scopes = generateScopes() + val contexts = TransactionContext("name", "op") + + val transaction = scopes.startTransaction(contexts) + assertTrue(transaction is SentryTracer) + assertEquals(contexts, transaction.root.spanContext) + } + + @Test + fun `when startTransaction with bindToScope set to false, transaction is not attached to the scope`() { + val scopes = generateScopes() + + scopes.startTransaction("name", "op", TransactionOptions()) + + scopes.configureScope { + assertNull(it.span) + } + } + + @Test + fun `when startTransaction without bindToScope set, transaction is not attached to the scope`() { + val scopes = generateScopes() + + scopes.startTransaction("name", "op") + + scopes.configureScope { + assertNull(it.span) + } + } + + @Test + fun `when startTransaction with bindToScope set to true, transaction is attached to the scope`() { + val scopes = generateScopes() + + val transaction = scopes.startTransaction("name", "op", TransactionOptions().also { it.isBindToScope = true }) + + scopes.configureScope { + assertEquals(transaction, it.span) + } + } + + @Test + fun `when startTransaction and no tracing sampling is configured, event is not sampled`() { + val scopes = generateScopes { + it.tracesSampleRate = 0.0 + } + + val transaction = scopes.startTransaction("name", "op") + assertFalse(transaction.isSampled!!) + } + + @Test + fun `when startTransaction and no profile sampling is configured, profile is not sampled`() { + val scopes = generateScopes { + it.tracesSampleRate = 1.0 + it.profilesSampleRate = 0.0 + } + + val transaction = scopes.startTransaction("name", "op") + assertTrue(transaction.isSampled!!) + assertFalse(transaction.isProfileSampled!!) + } + + @Test + fun `when startTransaction with parent sampled and no traces sampler provided, transaction inherits sampling decision`() { + val scopes = generateScopes() + val transactionContext = TransactionContext("name", "op") + transactionContext.parentSampled = true + val transaction = scopes.startTransaction(transactionContext) + assertNotNull(transaction) + assertNotNull(transaction.isSampled) + assertTrue(transaction.isSampled!!) + } + + @Test + fun `when startTransaction with parent profile sampled and no profile sampler provided, transaction inherits profile sampling decision`() { + val scopes = generateScopes() + val transactionContext = TransactionContext("name", "op") + transactionContext.setParentSampled(true, true) + val transaction = scopes.startTransaction(transactionContext) + assertTrue(transaction.isProfileSampled!!) + } + + @Test + fun `Scopes should close the sentry executor processor, profiler and performance collector on close call`() { + val executor = mock() + val profiler = mock() + val performanceCollector = mock() + val options = SentryOptions().apply { + dsn = "https://key@sentry.io/proj" + cacheDirPath = file.absolutePath + executorService = executor + setTransactionProfiler(profiler) + transactionPerformanceCollector = performanceCollector + } + val sut = createScopes(options) + sut.close() + verify(executor).close(any()) + verify(profiler).close() + verify(performanceCollector).close() + } + + @Test + fun `Scopes with isRestarting true should close the sentry executor in the background`() { + val executor = spy(DeferredExecutorService()) + val options = SentryOptions().apply { + dsn = "https://key@sentry.io/proj" + executorService = executor + } + val sut = createScopes(options) + sut.close(true) + verify(executor, never()).close(any()) + executor.runAll() + verify(executor).close(any()) + } + + @Test + fun `Scopes with isRestarting false should close the sentry executor in the background`() { + val executor = mock() + val options = SentryOptions().apply { + dsn = "https://key@sentry.io/proj" + executorService = executor + } + val sut = createScopes(options) + sut.close(false) + verify(executor).close(any()) + } + + @Test + fun `Scopes close should clear the scope`() { + val options = SentryOptions().apply { + dsn = "https://key@sentry.io/proj" + } + + val sut = createScopes(options) + sut.addBreadcrumb("Test") + sut.startTransaction("test", "test.op", TransactionOptions().also { it.isBindToScope = true }) + sut.close() + + // we have to clone the scope, so its isEnabled returns true, but it's still built up from + // the old scope preserving its data + val clone = sut.forkedScopes("test") + var oldScope: IScope? = null + clone.configureScope { scope -> oldScope = scope } + assertNull(oldScope!!.transaction) + assertTrue(oldScope!!.breadcrumbs.isEmpty()) + } + + @Test + fun `when tracesSampleRate and tracesSampler are not set on SentryOptions, startTransaction returns NoOp`() { + val scopes = generateScopes { + it.tracesSampleRate = null + it.tracesSampler = null + } + val transaction = scopes.startTransaction(TransactionContext("name", "op", TracesSamplingDecision(true))) + assertTrue(transaction is NoOpTransaction) + } + //endregion + + //region startTransaction tests + @Test + fun `when traceHeaders and no transaction is active, traceHeaders are generated from scope`() { + val scopes = generateScopes() + + var spanId: SpanId? = null + scopes.configureScope { spanId = it.propagationContext.spanId } + + val traceHeader = scopes.traceHeaders() + assertNotNull(traceHeader) + assertEquals(spanId, traceHeader.spanId) + } + + @Test + fun `when traceHeaders and there is an active transaction, traceHeaders are not null`() { + val scopes = generateScopes() + val tx = scopes.startTransaction("aTransaction", "op") + scopes.configureScope { it.setTransaction(tx) } + + assertNotNull(scopes.traceHeaders()) + } + //endregion + + //region getSpan tests + @Test + fun `when there is no active transaction, getSpan returns null`() { + val scopes = generateScopes() + assertNull(scopes.span) + } + + @Test + fun `when there is no active transaction, getTransaction returns null`() { + val scopes = generateScopes() + assertNull(scopes.transaction) + } + + @Test + fun `when there is active transaction bound to the scope, getTransaction and getSpan return active transaction`() { + val scopes = generateScopes() + val tx = scopes.startTransaction("aTransaction", "op") + scopes.configureScope { it.transaction = tx } + + assertEquals(tx, scopes.transaction) + assertEquals(tx, scopes.span) + } + + @Test + fun `when there is a transaction but the scopes is closed, getTransaction returns null`() { + val scopes = generateScopes() + scopes.startTransaction("name", "op") + scopes.close() + + assertNull(scopes.transaction) + } + + @Test + fun `when there is active span within a transaction bound to the scope, getSpan returns active span`() { + val scopes = generateScopes() + val tx = scopes.startTransaction("aTransaction", "op") + scopes.configureScope { it.setTransaction(tx) } + scopes.configureScope { it.setTransaction(tx) } + val span = tx.startChild("op") + + assertEquals(tx, scopes.transaction) + assertEquals(span, scopes.span) + } + // endregion + + //region setSpanContext + @Test + fun `associates span context with throwable`() { + val (scopes, mockClient) = getEnabledScopes() + val transaction = scopes.startTransaction("aTransaction", "op") + val span = transaction.startChild("op") + val exception = RuntimeException() + + scopes.setSpanContext(exception, span, "tx-name") + scopes.captureEvent(SentryEvent(exception)) + + verify(mockClient).captureEvent( + check { + assertEquals(span.spanContext, it.contexts.trace) + }, + anyOrNull(), + anyOrNull() + ) + } + // endregion + + @Test + fun `isCrashedLastRun does not delete native marker if auto session is enabled`() { + val nativeMarker = File(hashedFolder(), EnvelopeCache.NATIVE_CRASH_MARKER_FILE) + nativeMarker.mkdirs() + nativeMarker.createNewFile() + val scopes = generateScopes() as Scopes + + assertTrue(scopes.isCrashedLastRun!!) + assertTrue(nativeMarker.exists()) + } + + @Test + fun `isCrashedLastRun deletes the native marker if auto session is disabled`() { + val nativeMarker = File(hashedFolder(), EnvelopeCache.NATIVE_CRASH_MARKER_FILE) + nativeMarker.mkdirs() + nativeMarker.createNewFile() + val scopes = generateScopes { + it.isEnableAutoSessionTracking = false + } + + assertTrue(scopes.isCrashedLastRun!!) + assertFalse(nativeMarker.exists()) + } + + @Test + fun `reportFullyDisplayed is ignored if TimeToFullDisplayTracing is disabled`() { + var called = false + val scopes = generateScopes { + it.fullyDisplayedReporter.registerFullyDrawnListener { + called = !called + } + } + scopes.reportFullyDisplayed() + assertFalse(called) + } + + @Test + fun `reportFullyDisplayed calls FullyDisplayedReporter if TimeToFullDisplayTracing is enabled`() { + var called = false + val scopes = generateScopes { + it.isEnableTimeToFullDisplayTracing = true + it.fullyDisplayedReporter.registerFullyDrawnListener { + called = !called + } + } + scopes.reportFullyDisplayed() + assertTrue(called) + } + + @Test + fun `reportFullyDisplayed calls FullyDisplayedReporter only once`() { + var called = false + val scopes = generateScopes { + it.isEnableTimeToFullDisplayTracing = true + it.fullyDisplayedReporter.registerFullyDrawnListener { + called = !called + } + } + scopes.reportFullyDisplayed() + assertTrue(called) + scopes.reportFullyDisplayed() + assertTrue(called) + } + + @Test + fun `reportFullDisplayed calls reportFullyDisplayed`() { + val scopes = spy(generateScopes()) + scopes.reportFullDisplayed() + verify(scopes).reportFullyDisplayed() + } + + @Test + fun `continueTrace creates propagation context from headers and returns transaction context if performance enabled`() { + val scopes = generateScopes() + val traceId = SentryId() + val parentSpanId = SpanId() + val transactionContext = scopes.continueTrace("$traceId-$parentSpanId-1", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) + + scopes.configureScope { scope -> + assertEquals(traceId, scope.propagationContext.traceId) + assertEquals(parentSpanId, scope.propagationContext.parentSpanId) + } + + assertEquals(traceId, transactionContext!!.traceId) + assertEquals(parentSpanId, transactionContext!!.parentSpanId) + } + + @Test + fun `continueTrace creates new propagation context if header invalid and returns transaction context if performance enabled`() { + val scopes = generateScopes() + val traceId = SentryId() + var propagationContextHolder = AtomicReference() + + scopes.configureScope { propagationContextHolder.set(it.propagationContext) } + val propagationContextAtStart = propagationContextHolder.get()!! + + val transactionContext = scopes.continueTrace("invalid", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) + + scopes.configureScope { scope -> + assertNotEquals(propagationContextAtStart.traceId, scope.propagationContext.traceId) + assertNotEquals(propagationContextAtStart.parentSpanId, scope.propagationContext.parentSpanId) + assertNotEquals(propagationContextAtStart.spanId, scope.propagationContext.spanId) + + assertEquals(scope.propagationContext.traceId, transactionContext!!.traceId) + assertEquals(scope.propagationContext.parentSpanId, transactionContext!!.parentSpanId) + assertEquals(scope.propagationContext.spanId, transactionContext!!.spanId) + } + } + + @Test + fun `continueTrace creates propagation context from headers and returns null if performance disabled`() { + val scopes = generateScopes { it.enableTracing = false } + val traceId = SentryId() + val parentSpanId = SpanId() + val transactionContext = scopes.continueTrace("$traceId-$parentSpanId-1", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) + + scopes.configureScope { scope -> + assertEquals(traceId, scope.propagationContext.traceId) + assertEquals(parentSpanId, scope.propagationContext.parentSpanId) + } + + assertNull(transactionContext) + } + + @Test + fun `continueTrace creates new propagation context if header invalid and returns null if performance disabled`() { + val scopes = generateScopes { it.enableTracing = false } + val traceId = SentryId() + var propagationContextHolder = AtomicReference() + + scopes.configureScope { propagationContextHolder.set(it.propagationContext) } + val propagationContextAtStart = propagationContextHolder.get()!! + + val transactionContext = scopes.continueTrace("invalid", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) + + scopes.configureScope { scope -> + assertNotEquals(propagationContextAtStart.traceId, scope.propagationContext.traceId) + assertNotEquals(propagationContextAtStart.parentSpanId, scope.propagationContext.parentSpanId) + assertNotEquals(propagationContextAtStart.spanId, scope.propagationContext.spanId) + } + + assertNull(transactionContext) + } + + @Test + fun `scopes provides no tags for metrics, if metric option is disabled`() { + val scopes = generateScopes { + it.isEnableMetrics = false + it.isEnableDefaultTagsForMetrics = true + } as Scopes + + assertTrue( + scopes.defaultTagsForMetrics.isEmpty() + ) + } + + @Test + fun `scopes provides no tags for metrics, if default tags option is disabled`() { + val scopes = generateScopes { + it.isEnableMetrics = true + it.isEnableDefaultTagsForMetrics = false + } as Scopes + + assertTrue( + scopes.defaultTagsForMetrics.isEmpty() + ) + } + + @Test + fun `scopes provides minimum default tags for metrics, if nothing is set up`() { + val scopes = generateScopes { + it.isEnableMetrics = true + it.isEnableDefaultTagsForMetrics = true + } as Scopes + + assertEquals( + mapOf( + "environment" to "production" + ), + scopes.defaultTagsForMetrics + ) + } + + @Test + fun `scopes provides default tags for metrics, based on options and running transaction`() { + val scopes = generateScopes { + it.isEnableMetrics = true + it.isEnableDefaultTagsForMetrics = true + it.environment = "test" + it.release = "1.0" + } as Scopes + scopes.startTransaction( + "name", + "op", + TransactionOptions().apply { isBindToScope = true } + ) + + assertEquals( + mapOf( + "environment" to "test", + "release" to "1.0", + "transaction" to "name" + ), + scopes.defaultTagsForMetrics + ) + } + + @Test + fun `scopes provides no local metric aggregator if metrics feature is disabled`() { + val scopes = generateScopes { + it.isEnableMetrics = false + it.isEnableSpanLocalMetricAggregation = true + } as Scopes + + scopes.startTransaction( + "name", + "op", + TransactionOptions().apply { isBindToScope = true } + ) + + assertNull(scopes.localMetricsAggregator) + } + + @Test + fun `scopes provides no local metric aggregator if local aggregation feature is disabled`() { + val scopes = generateScopes { + it.isEnableMetrics = true + it.isEnableSpanLocalMetricAggregation = false + } as Scopes + + scopes.startTransaction( + "name", + "op", + TransactionOptions().apply { isBindToScope = true } + ) + + assertNull(scopes.localMetricsAggregator) + } + + @Test + fun `scopes provides local metric aggregator if feature is enabled`() { + val scopes = generateScopes { + it.isEnableMetrics = true + it.isEnableSpanLocalMetricAggregation = true + } as Scopes + + scopes.startTransaction( + "name", + "op", + TransactionOptions().apply { isBindToScope = true } + ) + assertNotNull(scopes.localMetricsAggregator) + } + + @Test + fun `scopes startSpanForMetric starts a child span`() { + val scopes = generateScopes { + it.isEnableMetrics = true + it.isEnableSpanLocalMetricAggregation = true + it.sampleRate = 1.0 + } as Scopes + + val txn = scopes.startTransaction( + "name.txn", + "op.txn", + TransactionOptions().apply { isBindToScope = true } + ) + + val span = scopes.startSpanForMetric("op", "key")!! + + assertEquals("op", span.spanContext.op) + assertEquals("key", span.spanContext.description) + assertEquals(span.spanContext.parentSpanId, txn.spanContext.spanId) + } + + private val dsnTest = "https://key@sentry.io/proj" + + private fun generateScopes(optionsConfiguration: Sentry.OptionsConfiguration? = null): IScopes { + val options = SentryOptions().apply { + dsn = dsnTest + cacheDirPath = file.absolutePath + setSerializer(mock()) + tracesSampleRate = 1.0 + } + optionsConfiguration?.configure(options) + return createScopes(options) + } + + private fun getEnabledScopes(): Triple { + val logger = mock() + + val options = SentryOptions() + options.cacheDirPath = file.absolutePath + options.dsn = "https://key@sentry.io/proj" + options.setSerializer(mock()) + options.tracesSampleRate = 1.0 + options.isDebug = true + options.setLogger(logger) + + val sut = createScopes(options) + val mockClient = mock() + sut.bindClient(mockClient) + return Triple(sut, mockClient, logger) + } + + private fun hashedFolder(): String { + val hash = StringUtils.calculateStringHash(dsnTest, mock()) + val fileHashFolder = File(file.absolutePath, hash!!) + return fileHashFolder.absolutePath + } + + private fun equalTraceContext(expectedContext: TraceContext?): TraceContext? { + expectedContext ?: return eq(null) + + return argWhere { actual -> + expectedContext.traceId == actual.traceId && + expectedContext.transaction == actual.transaction && + expectedContext.environment == actual.environment && + expectedContext.release == actual.release && + expectedContext.publicKey == actual.publicKey && + expectedContext.sampleRate == actual.sampleRate && + expectedContext.userId == actual.userId && + expectedContext.userSegment == actual.userSegment + } + } +} From dcd6d1ef5ac6bedf7cd4e5816dc4bc609d22e18b Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 2 May 2024 14:23:54 +0200 Subject: [PATCH 36/91] Hubs/Scopes Merge 36 - Implement `CombinedScopeViewTest` (#3371) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest --- .../java/io/sentry/CombinedScopeView.java | 1 + .../java/io/sentry/CombinedScopeViewTest.kt | 1023 ++++++++++++++++- 2 files changed, 981 insertions(+), 43 deletions(-) diff --git a/sentry/src/main/java/io/sentry/CombinedScopeView.java b/sentry/src/main/java/io/sentry/CombinedScopeView.java index 38873bb574..44a87da0b1 100644 --- a/sentry/src/main/java/io/sentry/CombinedScopeView.java +++ b/sentry/src/main/java/io/sentry/CombinedScopeView.java @@ -145,6 +145,7 @@ public void setRequest(@Nullable Request request) { @Override public @NotNull List getFingerprint() { + // TODO [HSM] should these be merged? final @Nullable List current = scope.getFingerprint(); if (!current.isEmpty()) { return current; diff --git a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt index d8e6783c4c..11725947cb 100644 --- a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt +++ b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt @@ -1,26 +1,61 @@ package io.sentry +import io.sentry.protocol.Request +import io.sentry.protocol.SentryId +import io.sentry.protocol.User +import junit.framework.TestCase.assertFalse +import junit.framework.TestCase.assertTrue +import org.junit.Assert.assertNotEquals +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.same +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import java.lang.RuntimeException +import java.util.UUID import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertSame class CombinedScopeViewTest { + private class Fixture { + lateinit var globalScope: IScope + lateinit var isolationScope: IScope + lateinit var scope: IScope + lateinit var options: SentryOptions + lateinit var scopes: IScopes + + fun getSut(options: SentryOptions = SentryOptions()): CombinedScopeView { + options.dsn = "https://key@sentry.io/proj" + options.release = "0.1" + this.options = options + globalScope = Scope(options) + isolationScope = Scope(options) + scope = Scope(options) + scopes = Scopes(scope, isolationScope, globalScope, "test") + + return CombinedScopeView(globalScope, isolationScope, scope) + } + } + + private val fixture = Fixture() + @Test fun `adds breadcrumbs from all scopes in sorted order`() { - val options = SentryOptions() - val globalScope = Scope(options) - val isolationScope = Scope(options) - val scope = Scope(options) + val combined = fixture.getSut() - val combined = CombinedScopeView(globalScope, isolationScope, scope) + fixture.globalScope.addBreadcrumb(Breadcrumb.info("global 1")) + fixture.isolationScope.addBreadcrumb(Breadcrumb.info("isolation 1")) + fixture.scope.addBreadcrumb(Breadcrumb.info("current 1")) - globalScope.addBreadcrumb(Breadcrumb.info("global 1")) - isolationScope.addBreadcrumb(Breadcrumb.info("isolation 1")) - scope.addBreadcrumb(Breadcrumb.info("current 1")) - - globalScope.addBreadcrumb(Breadcrumb.info("global 2")) - isolationScope.addBreadcrumb(Breadcrumb.info("isolation 2")) - scope.addBreadcrumb(Breadcrumb.info("current 2")) + fixture.globalScope.addBreadcrumb(Breadcrumb.info("global 2")) + fixture.isolationScope.addBreadcrumb(Breadcrumb.info("isolation 2")) + fixture.scope.addBreadcrumb(Breadcrumb.info("current 2")) val breadcrumbs = combined.breadcrumbs assertEquals("global 1", breadcrumbs.poll().message) @@ -34,19 +69,15 @@ class CombinedScopeViewTest { @Test fun `oldest breadcrumbs are dropped first`() { val options = SentryOptions().also { it.maxBreadcrumbs = 5 } - val globalScope = Scope(options) - val isolationScope = Scope(options) - val scope = Scope(options) - - val combined = CombinedScopeView(globalScope, isolationScope, scope) + val combined = fixture.getSut(options) - globalScope.addBreadcrumb(Breadcrumb.info("global 1")) - isolationScope.addBreadcrumb(Breadcrumb.info("isolation 1")) - scope.addBreadcrumb(Breadcrumb.info("current 1")) + fixture.globalScope.addBreadcrumb(Breadcrumb.info("global 1")) + fixture.isolationScope.addBreadcrumb(Breadcrumb.info("isolation 1")) + fixture.scope.addBreadcrumb(Breadcrumb.info("current 1")) - globalScope.addBreadcrumb(Breadcrumb.info("global 2")) - isolationScope.addBreadcrumb(Breadcrumb.info("isolation 2")) - scope.addBreadcrumb(Breadcrumb.info("current 2")) + fixture.globalScope.addBreadcrumb(Breadcrumb.info("global 2")) + fixture.isolationScope.addBreadcrumb(Breadcrumb.info("isolation 2")) + fixture.scope.addBreadcrumb(Breadcrumb.info("current 2")) val breadcrumbs = combined.breadcrumbs // assertEquals("global 1", breadcrumbs.poll().message) <-- was dropped @@ -56,8 +87,8 @@ class CombinedScopeViewTest { assertEquals("isolation 2", breadcrumbs.poll().message) assertEquals("current 2", breadcrumbs.poll().message) - scope.addBreadcrumb(Breadcrumb.info("current 3")) - scope.addBreadcrumb(Breadcrumb.info("current 4")) + fixture.scope.addBreadcrumb(Breadcrumb.info("current 3")) + fixture.scope.addBreadcrumb(Breadcrumb.info("current 4")) val breadcrumbs2 = combined.breadcrumbs // assertEquals("global 1", breadcrumbs.poll().message) <-- was dropped @@ -70,35 +101,73 @@ class CombinedScopeViewTest { assertEquals("current 4", breadcrumbs2.poll().message) } + @Test + fun `can add breadcrumb with hint`() { + var capturedHint: Hint? = null + val combined = fixture.getSut( + SentryOptions().also { + it.beforeBreadcrumb = + SentryOptions.BeforeBreadcrumbCallback { breadcrumb: Breadcrumb, hint: Hint -> + capturedHint = hint + breadcrumb + } + } + ) + + combined.addBreadcrumb(Breadcrumb.info("aBreadcrumb"), Hint().also { it.set("aTest", "aValue") }) + + assertNotNull(capturedHint) + assertEquals("aValue", capturedHint?.get("aTest")) + + val breadcrumbs = combined.breadcrumbs + assertEquals(1, breadcrumbs.size) + assertEquals("aBreadcrumb", breadcrumbs.first().message) + } + + @Test + fun `adds breadcrumb to default scope`() { + val combined = fixture.getSut() + combined.addBreadcrumb(Breadcrumb.info("aBreadcrumb")) + + assertEquals(ScopeType.ISOLATION, combined.options.defaultScopeType) + assertEquals(0, fixture.scope.breadcrumbs.size) + assertEquals(1, fixture.isolationScope.breadcrumbs.size) + assertEquals(0, fixture.globalScope.breadcrumbs.size) + } + + @Test + fun `clears breadcrumbs only from default scope`() { + val combined = fixture.getSut() + fixture.scope.addBreadcrumb(Breadcrumb.info("scopeBreadcrumb")) + fixture.isolationScope.addBreadcrumb(Breadcrumb.info("isolationBreadcrumb")) + fixture.globalScope.addBreadcrumb(Breadcrumb.info("globalBreadcrumb")) + + combined.clearBreadcrumbs() + + assertEquals(ScopeType.ISOLATION, combined.options.defaultScopeType) + assertEquals(1, fixture.scope.breadcrumbs.size) + assertEquals(0, fixture.isolationScope.breadcrumbs.size) + assertEquals(1, fixture.globalScope.breadcrumbs.size) + } + @Test fun `event processors from options are not returned`() { val options = SentryOptions().also { it.addEventProcessor(MainEventProcessor(it)) } - - val globalScope = Scope(options) - val isolationScope = Scope(options) - val scope = Scope(options) - - val combined = CombinedScopeView(globalScope, isolationScope, scope) + val combined = fixture.getSut(options) assertEquals(0, combined.eventProcessors.size) } @Test - fun `event processors from options and all scopes in order`() { - val options = SentryOptions() - - val globalScope = Scope(options) - val isolationScope = Scope(options) - val scope = Scope(options) - - val first = TestEventProcessor(0).also { scope.addEventProcessor(it) } - val second = TestEventProcessor(1000).also { globalScope.addEventProcessor(it) } - val third = TestEventProcessor(2000).also { isolationScope.addEventProcessor(it) } - val fourth = TestEventProcessor(3000).also { scope.addEventProcessor(it) } + fun `event processors from all scopes are returned in order`() { + val combined = fixture.getSut() - val combined = CombinedScopeView(globalScope, isolationScope, scope) + val first = TestEventProcessor(0).also { fixture.scope.addEventProcessor(it) } + val second = TestEventProcessor(1000).also { fixture.globalScope.addEventProcessor(it) } + val third = TestEventProcessor(2000).also { fixture.isolationScope.addEventProcessor(it) } + val fourth = TestEventProcessor(3000).also { fixture.scope.addEventProcessor(it) } val eventProcessors = combined.eventProcessors @@ -108,6 +177,874 @@ class CombinedScopeViewTest { assertEquals(fourth, eventProcessors.get(3)) } + @Test + fun `adds event processor to default scope`() { + val combined = fixture.getSut() + + val eventProcessor = MainEventProcessor(fixture.options) + combined.addEventProcessor(eventProcessor) + + assertEquals(ScopeType.ISOLATION, combined.options.defaultScopeType) + assertFalse(fixture.scope.eventProcessors.contains(eventProcessor)) + assertTrue(fixture.isolationScope.eventProcessors.contains(eventProcessor)) + assertFalse(fixture.globalScope.eventProcessors.contains(eventProcessor)) + } + + @Test + fun `prefers level from current scope`() { + val combined = fixture.getSut() + fixture.scope.level = SentryLevel.DEBUG + fixture.isolationScope.level = SentryLevel.INFO + fixture.globalScope.level = SentryLevel.WARNING + + assertEquals(SentryLevel.DEBUG, combined.level) + } + + @Test + fun `uses isolation scope level if none in current scope`() { + val combined = fixture.getSut() + fixture.isolationScope.level = SentryLevel.INFO + fixture.globalScope.level = SentryLevel.WARNING + + assertEquals(SentryLevel.INFO, combined.level) + } + + @Test + fun `uses global scope level if none in current or isolation scope`() { + val combined = fixture.getSut() + fixture.globalScope.level = SentryLevel.WARNING + + assertEquals(SentryLevel.WARNING, combined.level) + } + + @Test + fun `returns null level if none in any scope`() { + val combined = fixture.getSut() + + assertNull(combined.level) + } + + @Test + fun `setLevel modifies default scope`() { + val combined = fixture.getSut() + combined.level = SentryLevel.ERROR + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertNull(fixture.scope.level) + assertEquals(SentryLevel.ERROR, fixture.isolationScope.level) + assertNull(fixture.globalScope.level) + } + + @Test + fun `prefers transaction name from current scope`() { + val combined = fixture.getSut() + fixture.scope.setTransaction("scopeTransaction") + fixture.isolationScope.setTransaction("isolationTransaction") + fixture.globalScope.setTransaction("globalTransaction") + + assertEquals("scopeTransaction", combined.transactionName) + } + + @Test + fun `uses isolation transaction name if none in current scope`() { + val combined = fixture.getSut() + fixture.isolationScope.setTransaction("isolationTransaction") + fixture.globalScope.setTransaction("globalTransaction") + + assertEquals("isolationTransaction", combined.transactionName) + } + + @Test + fun `uses global transaction name if none in current or isolation scope`() { + val combined = fixture.getSut() + fixture.globalScope.setTransaction("globalTransaction") + + assertEquals("globalTransaction", combined.transactionName) + } + + @Test + fun `returns null transaction name if none in any scope`() { + val combined = fixture.getSut() + + assertNull(combined.transactionName) + } + + @Test + fun `setTransaction(String) modifies default scope`() { + val combined = fixture.getSut() + combined.setTransaction("aTransaction") + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertNull(fixture.scope.transactionName) + assertEquals("aTransaction", fixture.isolationScope.transactionName) + assertNull(fixture.globalScope.transactionName) + } + + @Test + fun `prefers transaction andspan from current scope`() { + val combined = fixture.getSut() + fixture.scope.setTransaction(createTransaction("scopeTransaction")) + fixture.isolationScope.setTransaction(createTransaction("isolationTransaction")) + fixture.globalScope.setTransaction(createTransaction("globalTransaction")) + + assertEquals("scopeTransaction", combined.transaction!!.name) + assertEquals("scopeTransactionSpan", combined.span!!.operation) + } + + @Test + fun `uses isolation scope transaction andspan if none in current scope`() { + val combined = fixture.getSut() + fixture.isolationScope.setTransaction(createTransaction("isolationTransaction")) + fixture.globalScope.setTransaction(createTransaction("globalTransaction")) + + assertEquals("isolationTransaction", combined.transaction!!.name) + assertEquals("isolationTransactionSpan", combined.span!!.operation) + } + + @Test + fun `uses global transaction andscope span if none in current or isolation scope`() { + val combined = fixture.getSut() + fixture.globalScope.setTransaction(createTransaction("globalTransaction")) + + assertEquals("globalTransaction", combined.transaction!!.name) + assertEquals("globalTransactionSpan", combined.span!!.operation) + } + + @Test + fun `returns null transaction and span if none in any scope`() { + val combined = fixture.getSut() + + assertNull(combined.transaction) + assertNull(combined.span) + } + + @Test + fun `setTransaction(ITransaction) modifies default scope`() { + val combined = fixture.getSut() + val tx = createTransaction("aTransaction") + combined.setTransaction(tx) + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertNull(fixture.scope.transaction) + assertSame(tx, fixture.isolationScope.transaction) + assertNull(fixture.globalScope.transaction) + } + + @Test + fun `clears transaction from default scope`() { + val combined = fixture.getSut() + fixture.scope.setTransaction(createTransaction("scopeTransaction")) + fixture.isolationScope.setTransaction(createTransaction("isolationTransaction")) + fixture.globalScope.setTransaction(createTransaction("globalTransaction")) + + combined.clearTransaction() + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertNotNull(fixture.scope.transaction) + assertNull(fixture.isolationScope.transaction) + assertNotNull(fixture.globalScope.transaction) + } + + @Test + fun `prefers user from current scope`() { + val combined = fixture.getSut() + fixture.scope.user = User().also { it.name = "scopeUser" } + fixture.isolationScope.user = User().also { it.name = "isolationUser" } + fixture.globalScope.user = User().also { it.name = "globalUser" } + + assertEquals("scopeUser", combined.user!!.name) + } + + @Test + fun `uses isolation scope user if none in current scope`() { + val combined = fixture.getSut() + fixture.isolationScope.user = User().also { it.name = "isolationUser" } + fixture.globalScope.user = User().also { it.name = "globalUser" } + + assertEquals("isolationUser", combined.user!!.name) + } + + @Test + fun `uses global scope user if none in current or isolation scope`() { + val combined = fixture.getSut() + fixture.globalScope.user = User().also { it.name = "globalUser" } + + assertEquals("globalUser", combined.user!!.name) + } + + @Test + fun `returns null user if none in any scope`() { + val combined = fixture.getSut() + + assertNull(combined.user) + } + + @Test + fun `set user modifies default scope`() { + val combined = fixture.getSut() + val user = User().also { it.name = "aUser" } + combined.user = user + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertNull(fixture.scope.user) + assertSame(user, fixture.isolationScope.user) + assertNull(fixture.globalScope.user) + } + + @Test + fun `prefers screen from current scope`() { + val combined = fixture.getSut() + fixture.scope.screen = "scopeScreen" + fixture.isolationScope.screen = "isolationScreen" + fixture.globalScope.screen = "globalScreen" + + assertEquals("scopeScreen", combined.screen) + } + + @Test + fun `uses isolation scope screen if none in current scope`() { + val combined = fixture.getSut() + fixture.isolationScope.screen = "isolationScreen" + fixture.globalScope.screen = "globalScreen" + + assertEquals("isolationScreen", combined.screen) + } + + @Test + fun `uses global scope screen if none in current or isolation scope`() { + val combined = fixture.getSut() + fixture.globalScope.screen = "globalScreen" + + assertEquals("globalScreen", combined.screen) + } + + @Test + fun `returns null screen if none in any scope`() { + val combined = fixture.getSut() + + assertNull(combined.screen) + } + + @Test + fun `set screen modifies default scope`() { + val combined = fixture.getSut() + combined.screen = "aScreen" + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertNull(fixture.scope.screen) + assertEquals("aScreen", fixture.isolationScope.screen) + assertNull(fixture.globalScope.screen) + } + + @Test + fun `prefers request from current scope`() { + val combined = fixture.getSut() + fixture.scope.request = Request().also { it.queryString = "scopeRequest" } + fixture.isolationScope.request = Request().also { it.queryString = "isolationRequest" } + fixture.globalScope.request = Request().also { it.queryString = "globalRequest" } + + assertEquals("scopeRequest", combined.request!!.queryString) + } + + @Test + fun `uses isolation scope request if none in current scope`() { + val combined = fixture.getSut() + fixture.isolationScope.request = Request().also { it.queryString = "isolationRequest" } + fixture.globalScope.request = Request().also { it.queryString = "globalRequest" } + + assertEquals("isolationRequest", combined.request!!.queryString) + } + + @Test + fun `uses global scope request if none in current or isolation scope`() { + val combined = fixture.getSut() + fixture.globalScope.request = Request().also { it.queryString = "globalRequest" } + + assertEquals("globalRequest", combined.request!!.queryString) + } + + @Test + fun `returns null request if none in any scope`() { + val combined = fixture.getSut() + + assertNull(combined.request) + } + + @Test + fun `set request modifies default scope`() { + val combined = fixture.getSut() + val request = Request().also { it.queryString = "aRequest" } + combined.request = request + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertNull(fixture.scope.request) + assertSame(request, fixture.isolationScope.request) + assertNull(fixture.globalScope.request) + } + + @Test + fun `clear removes from default scope`() { + val combined = fixture.getSut() + + fixture.scope.level = SentryLevel.DEBUG + fixture.isolationScope.level = SentryLevel.INFO + fixture.globalScope.level = SentryLevel.WARNING + + combined.clear() + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertNotNull(fixture.scope.level) + assertNull(fixture.isolationScope.level) + assertNotNull(fixture.globalScope.level) + } + + @Test + fun `tags are combined from all scopes`() { + val combined = fixture.getSut() + + fixture.scope.setTag("scopeTag", "scopeValue") + fixture.isolationScope.setTag("isolationTag", "isolationValue") + fixture.globalScope.setTag("globalTag", "globalValue") + + val tags = combined.tags + assertEquals("scopeValue", tags["scopeTag"]) + assertEquals("isolationValue", tags["isolationTag"]) + assertEquals("globalValue", tags["globalTag"]) + } + + @Test + fun `setTag writes to default scope`() { + val combined = fixture.getSut() + combined.setTag("aTag", "aValue") + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertNull(fixture.scope.tags["aTag"]) + assertEquals("aValue", fixture.isolationScope.tags["aTag"]) + assertNull(fixture.globalScope.tags["aTag"]) + } + + @Test + fun `prefer scope value for tags with same key`() { + val combined = fixture.getSut() + + fixture.scope.setTag("aTag", "scopeValue") + fixture.isolationScope.setTag("aTag", "isolationValue") + fixture.globalScope.setTag("aTag", "globalValue") + + assertEquals("scopeValue", combined.tags["aTag"]) + } + + @Test + fun `uses isolation scope value for tags with same key if scope does not have it`() { + val combined = fixture.getSut() + + fixture.isolationScope.setTag("aTag", "isolationValue") + fixture.globalScope.setTag("aTag", "globalValue") + + assertEquals("isolationValue", combined.tags["aTag"]) + } + + @Test + fun `uses global scope value for tags with same key if scope and isolation scope do not have it`() { + val combined = fixture.getSut() + + fixture.globalScope.setTag("aTag", "globalValue") + + assertEquals("globalValue", combined.tags["aTag"]) + } + + @Test + fun `removeTag removes from default scope`() { + val combined = fixture.getSut() + + fixture.scope.setTag("aTag", "scopeValue") + fixture.isolationScope.setTag("aTag", "isolationValue") + fixture.globalScope.setTag("aTag", "globalValue") + + combined.removeTag("aTag") + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertEquals("scopeValue", fixture.scope.tags["aTag"]) + assertNull(fixture.isolationScope.tags["aTag"]) + assertEquals("globalValue", fixture.globalScope.tags["aTag"]) + } + + @Test + fun `extras are combined from all scopes`() { + val combined = fixture.getSut() + + fixture.scope.setExtra("scopeExtra", "scopeValue") + fixture.isolationScope.setExtra("isolationExtra", "isolationValue") + fixture.globalScope.setExtra("globalExtra", "globalValue") + + val extras = combined.extras + assertEquals("scopeValue", extras["scopeExtra"]) + assertEquals("isolationValue", extras["isolationExtra"]) + assertEquals("globalValue", extras["globalExtra"]) + } + + @Test + fun `setExtra writes to default scope`() { + val combined = fixture.getSut() + combined.setExtra("someExtra", "aValue") + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertNull(fixture.scope.extras["someExtra"]) + assertEquals("aValue", fixture.isolationScope.extras["someExtra"]) + assertNull(fixture.globalScope.extras["someExtra"]) + } + + @Test + fun `prefer scope value for extras with same key`() { + val combined = fixture.getSut() + + fixture.scope.setExtra("someExtra", "scopeValue") + fixture.isolationScope.setExtra("someExtra", "isolationValue") + fixture.globalScope.setExtra("someExtra", "globalValue") + + assertEquals("scopeValue", combined.extras["someExtra"]) + } + + @Test + fun `uses isolation scope value for extras with same key if scope does not have it`() { + val combined = fixture.getSut() + + fixture.isolationScope.setExtra("someExtra", "isolationValue") + fixture.globalScope.setExtra("someExtra", "globalValue") + + assertEquals("isolationValue", combined.extras["someExtra"]) + } + + @Test + fun `uses global scope value for extras with same key if scope and isolation scope do not have it`() { + val combined = fixture.getSut() + + fixture.globalScope.setExtra("someExtra", "globalValue") + + assertEquals("globalValue", combined.extras["someExtra"]) + } + + @Test + fun `removeExtra removes from default scope`() { + val combined = fixture.getSut() + + fixture.scope.setExtra("someExtra", "scopeValue") + fixture.isolationScope.setExtra("someExtra", "isolationValue") + fixture.globalScope.setExtra("someExtra", "globalValue") + + combined.removeExtra("someExtra") + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertEquals("scopeValue", fixture.scope.extras["someExtra"]) + assertNull(fixture.isolationScope.extras["someExtra"]) + assertEquals("globalValue", fixture.globalScope.extras["someExtra"]) + } + + // TODO [HSM] CombinedContextsView does not override all map methods, leading to unwanted behaviour + // we should probably no longer extend Map but instead keep an internal map + // and offer a toMap function or similar +// @Test +// fun `combines context from all scopes`() { +// val combined = fixture.getSut() +// fixture.scope.setContexts("scopeContext", "scopeValue") +// fixture.isolationScope.setContexts("isolationContext", "isolationValue") +// fixture.globalScope.setContexts("globalContext", "globalValue") +// +// val contexts = combined.contexts +// assertEquals("scopeValue", contexts["scopeContext"]) +// } + + // TODO [HSM] test all setContext methods + + // TODO [HSM] fingerprint tests (discuss how it should behave first) + + @Test + fun `combines attachments Æ’rom all scopes`() { + val combined = fixture.getSut() + + fixture.scope.addAttachment(createAttachment("scopeAttachment.png")) + fixture.isolationScope.addAttachment(createAttachment("isolationAttachment.png")) + fixture.globalScope.addAttachment(createAttachment("globalAttachment.png")) + + val attachments = combined.attachments + assertNotNull(attachments.firstOrNull { it.filename == "scopeAttachment.png" }) + assertNotNull(attachments.firstOrNull { it.filename == "isolationAttachment.png" }) + assertNotNull(attachments.firstOrNull { it.filename == "globalAttachment.png" }) + } + + @Test + fun `adds attachment to default scope`() { + val combined = fixture.getSut() + combined.addAttachment(createAttachment("someAttachment.png")) + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertNull(fixture.scope.attachments.firstOrNull { it.filename == "someAttachment.png" }) + assertNotNull(fixture.isolationScope.attachments.firstOrNull { it.filename == "someAttachment.png" }) + assertNull(fixture.globalScope.attachments.firstOrNull { it.filename == "someAttachment.png" }) + } + + @Test + fun `clears attachments only from default scope`() { + val combined = fixture.getSut() + + fixture.scope.addAttachment(createAttachment("scopeAttachment.png")) + fixture.isolationScope.addAttachment(createAttachment("isolationAttachment.png")) + fixture.globalScope.addAttachment(createAttachment("globalAttachment.png")) + + combined.clearAttachments() + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertNotNull(fixture.scope.attachments.firstOrNull { it.filename == "scopeAttachment.png" }) + assertNull(fixture.isolationScope.attachments.firstOrNull { it.filename == "isolationAttachment.png" }) + assertNotNull(fixture.globalScope.attachments.firstOrNull { it.filename == "globalAttachment.png" }) + } + + @Test + fun `returns options from global scope`() { + val scopeOptions = SentryOptions().also { it.dist = "scopeDist" } + val isolationOptions = SentryOptions().also { it.dist = "isolationDist" } + val globalOptions = SentryOptions().also { it.dist = "globalDist" } + + val combined = CombinedScopeView(Scope(globalOptions), Scope(isolationOptions), Scope(scopeOptions)) + assertEquals("globalDist", combined.options.dist) + } + + @Test + fun `replaces options on global scope`() { + val scopeOptions = SentryOptions().also { it.dist = "scopeDist" } + val isolationOptions = SentryOptions().also { it.dist = "isolationDist" } + val globalOptions = SentryOptions().also { it.dist = "globalDist" } + + val globalScope = Scope(globalOptions) + val isolationScope = Scope(isolationOptions) + val scope = Scope(scopeOptions) + val combined = CombinedScopeView(globalScope, isolationScope, scope) + + val newOptions = SentryOptions().also { it.dist = "newDist" } + combined.replaceOptions(newOptions) + + assertEquals("scopeDist", scope.options.dist) + assertEquals("isolationDist", isolationScope.options.dist) + assertEquals("newDist", globalScope.options.dist) + } + + @Test + fun `prefers client from scope`() { + val combined = fixture.getSut() + + val scopeClient = SentryClient(fixture.options) + fixture.scope.bindClient(scopeClient) + + val isolationClient = SentryClient(fixture.options) + fixture.isolationScope.bindClient(isolationClient) + + val globalClient = SentryClient(fixture.options) + fixture.globalScope.bindClient(globalClient) + + assertSame(scopeClient, combined.client) + } + + @Test + fun `uses isolation scope client if noop on current scope`() { + val combined = fixture.getSut() + + val isolationClient = SentryClient(fixture.options) + fixture.isolationScope.bindClient(isolationClient) + + val globalClient = SentryClient(fixture.options) + fixture.globalScope.bindClient(globalClient) + + assertSame(isolationClient, combined.client) + } + + @Test + fun `uses global scope client if noop on current and isolation scope`() { + val combined = fixture.getSut() + + val globalClient = SentryClient(fixture.options) + fixture.globalScope.bindClient(globalClient) + + assertSame(globalClient, combined.client) + } + + @Test + fun `binds client to default scope`() { + val combined = fixture.getSut() + val client = SentryClient(fixture.options) + combined.bindClient(client) + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertTrue(fixture.scope.client is NoOpSentryClient) + assertSame(client, fixture.isolationScope.client) + assertTrue(fixture.globalScope.client is NoOpSentryClient) + } + + @Test + fun `getSpecificScope(null) returns scope defined in options CURRENT`() { + val combined = fixture.getSut(SentryOptions().also { it.defaultScopeType = ScopeType.CURRENT }) + assertSame(fixture.scope, combined.getSpecificScope(null)) + } + + @Test + fun `getSpecificScope(null) returns scope defined in options ISOLATION`() { + val combined = fixture.getSut(SentryOptions().also { it.defaultScopeType = ScopeType.ISOLATION }) + assertSame(fixture.isolationScope, combined.getSpecificScope(null)) + } + + @Test + fun `getSpecificScope(null) returns scope defined in options GLOBAL`() { + val combined = fixture.getSut(SentryOptions().also { it.defaultScopeType = ScopeType.GLOBAL }) + assertSame(fixture.globalScope, combined.getSpecificScope(null)) + } + + @Test + fun `getSpecificScope(CURRENT) returns scope`() { + val combined = fixture.getSut(SentryOptions().also { it.defaultScopeType = ScopeType.ISOLATION }) + assertSame(fixture.scope, combined.getSpecificScope(ScopeType.CURRENT)) + } + + @Test + fun `getSpecificScope(ISOLATION) returns scope`() { + val combined = fixture.getSut(SentryOptions().also { it.defaultScopeType = ScopeType.CURRENT }) + assertSame(fixture.isolationScope, combined.getSpecificScope(ScopeType.ISOLATION)) + } + + @Test + fun `getSpecificScope(GLOBAL) returns scope`() { + val combined = fixture.getSut(SentryOptions().also { it.defaultScopeType = ScopeType.CURRENT }) + assertSame(fixture.globalScope, combined.getSpecificScope(ScopeType.GLOBAL)) + } + + @Test + fun `forwards setSpanContext to global scope`() { + val scope = mock() + val isolationScope = mock() + val globalScope = mock() + val combined = CombinedScopeView(globalScope, isolationScope, scope) + + val options = SentryOptions().also { it.dsn = "https://key@sentry.io/proj" } + whenever(globalScope.options).thenReturn(options) + + val exception = RuntimeException("someEx") + val transaction = createTransaction("aTransaction", Scopes(scope, isolationScope, globalScope, "test")) + combined.setSpanContext(exception, transaction, "aTransaction") + + verify(scope, never()).setSpanContext(any(), any(), any()) + verify(isolationScope, never()).setSpanContext(any(), any(), any()) + verify(globalScope).setSpanContext(same(exception), same(transaction), eq("aTransaction")) + } + + @Test + fun `withTransaction uses default scope`() { + val combined = fixture.getSut() + fixture.scope.setTransaction(createTransaction("scopeTransaction")) + fixture.isolationScope.setTransaction(createTransaction("isolationTransaction")) + fixture.globalScope.setTransaction(createTransaction("globalTransaction")) + + var capturedTransaction: ITransaction? = null + combined.withTransaction { transaction -> + capturedTransaction = transaction + } + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertEquals("isolationTransaction", capturedTransaction?.name) + } + + @Test + fun `forwards assignTraceContext to global scope`() { + val scope = mock() + val isolationScope = mock() + val globalScope = mock() + val combined = CombinedScopeView(globalScope, isolationScope, scope) + + val event = SentryEvent() + combined.assignTraceContext(event) + + verify(scope, never()).assignTraceContext(any()) + verify(isolationScope, never()).assignTraceContext(any()) + verify(globalScope).assignTraceContext(same(event)) + } + + @Test + fun `retrieves last event id from global scope`() { + val combined = fixture.getSut() + fixture.scope.lastEventId = SentryId(UUID.fromString("c81d4e2e-bcf2-11e6-869b-7df92533d2dc")) + fixture.isolationScope.lastEventId = SentryId(UUID.fromString("d81d4e2e-bcf2-11e6-869b-7df92533d2dd")) + fixture.globalScope.lastEventId = SentryId(UUID.fromString("e81d4e2e-bcf2-11e6-869b-7df92533d2de")) + + assertEquals("e81d4e2ebcf211e6869b7df92533d2de", combined.lastEventId.toString()) + } + + @Test + fun `sets last event id on all scopes`() { + val combined = fixture.getSut() + combined.lastEventId = SentryId(UUID.fromString("c81d4e2e-bcf2-11e6-869b-7df92533d2db")) + + assertEquals("c81d4e2ebcf211e6869b7df92533d2db", fixture.scope.lastEventId.toString()) + assertEquals("c81d4e2ebcf211e6869b7df92533d2db", fixture.isolationScope.lastEventId.toString()) + assertEquals("c81d4e2ebcf211e6869b7df92533d2db", fixture.globalScope.lastEventId.toString()) + } + + @Test + fun `retrieves propagation context from default scope`() { + val combined = fixture.getSut() + fixture.scope.propagationContext = PropagationContext().also { it.traceId = SentryId(UUID.fromString("c81d4e2e-bcf2-11e6-869b-7df92533d2dc")) } + fixture.isolationScope.propagationContext = PropagationContext().also { it.traceId = SentryId(UUID.fromString("d81d4e2e-bcf2-11e6-869b-7df92533d2dd")) } + fixture.globalScope.propagationContext = PropagationContext().also { it.traceId = SentryId(UUID.fromString("e81d4e2e-bcf2-11e6-869b-7df92533d2de")) } + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertEquals("d81d4e2ebcf211e6869b7df92533d2dd", combined.propagationContext.traceId.toString()) + } + + @Test + fun `sets propagation context on default scope`() { + val combined = fixture.getSut() + + combined.propagationContext = PropagationContext().also { it.traceId = SentryId(UUID.fromString("c81d4e2e-bcf2-11e6-869b-7df92533d2db")) } + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertNotEquals("c81d4e2ebcf211e6869b7df92533d2db", fixture.scope.propagationContext.traceId.toString()) + assertEquals("c81d4e2ebcf211e6869b7df92533d2db", fixture.isolationScope.propagationContext.traceId.toString()) + assertNotEquals("c81d4e2ebcf211e6869b7df92533d2db", fixture.globalScope.propagationContext.traceId.toString()) + } + + @Test + fun `withPropagationContext uses default scope`() { + val combined = fixture.getSut() + fixture.scope.propagationContext = PropagationContext().also { it.traceId = SentryId(UUID.fromString("c81d4e2e-bcf2-11e6-869b-7df92533d2dc")) } + fixture.isolationScope.propagationContext = PropagationContext().also { it.traceId = SentryId(UUID.fromString("d81d4e2e-bcf2-11e6-869b-7df92533d2dd")) } + fixture.globalScope.propagationContext = PropagationContext().also { it.traceId = SentryId(UUID.fromString("e81d4e2e-bcf2-11e6-869b-7df92533d2de")) } + + var capturedPropagationContext: PropagationContext? = null + combined.withPropagationContext { propagationContext -> + capturedPropagationContext = propagationContext + } + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertEquals("d81d4e2ebcf211e6869b7df92533d2dd", capturedPropagationContext?.traceId.toString()) + } + + @Test + fun `starts session on default scope`() { + val combined = fixture.getSut() + + combined.startSession() + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertNull(fixture.scope.session) + assertNotNull(fixture.isolationScope.session) + assertNull(fixture.globalScope.session) + } + + @Test + fun `ends session on default scope`() { + val combined = fixture.getSut() + fixture.scope.startSession() + fixture.isolationScope.startSession() + fixture.globalScope.startSession() + + combined.endSession() + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertNotNull(fixture.scope.session) + assertNull(fixture.isolationScope.session) + assertNotNull(fixture.globalScope.session) + } + + @Test + fun `prefers session from current scope`() { + val combined = fixture.getSut() + fixture.scope.startSession() + fixture.isolationScope.startSession() + fixture.globalScope.startSession() + + assertSame(fixture.scope.session, combined.session) + } + + @Test + fun `uses isolation scope session if none on current scope`() { + val combined = fixture.getSut() + fixture.isolationScope.startSession() + fixture.globalScope.startSession() + + assertSame(fixture.isolationScope.session, combined.session) + } + + @Test + fun `uses global scope session if none on current or isolation scope`() { + val combined = fixture.getSut() + fixture.globalScope.startSession() + + assertSame(fixture.globalScope.session, combined.session) + } + + @Test + fun `withSession uses default scope`() { + val combined = fixture.getSut() + fixture.scope.startSession() + fixture.isolationScope.startSession() + fixture.globalScope.startSession() + + var capturedSession: Session? = null + combined.withSession { session -> + capturedSession = session + } + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertSame(fixture.isolationScope.session, capturedSession) + } + + @Test + fun `sets fingerprint on default scope`() { + val combined = fixture.getSut() + combined.fingerprint = listOf("aFingerprint") + + assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) + assertEquals(0, fixture.scope.fingerprint.size) + assertEquals(1, fixture.isolationScope.fingerprint.size) + assertEquals(0, fixture.globalScope.fingerprint.size) + } + + @Test + fun `prefers fingerprint from current scope`() { + val combined = fixture.getSut() + fixture.scope.fingerprint = listOf("scopeFingerprint") + fixture.isolationScope.fingerprint = listOf("isolationFingerprint") + fixture.globalScope.fingerprint = listOf("globalFingerprint") + + assertEquals(listOf("scopeFingerprint"), combined.fingerprint) + } + + @Test + fun `uses isolation scope fingerprint if current scope does not have one`() { + val combined = fixture.getSut() + fixture.isolationScope.fingerprint = listOf("isolationFingerprint") + fixture.globalScope.fingerprint = listOf("globalFingerprint") + + assertEquals(listOf("isolationFingerprint"), combined.fingerprint) + } + + @Test + fun `uses global scope fingerprint if current and isolation scope do not have one`() { + val combined = fixture.getSut() + fixture.globalScope.fingerprint = listOf("globalFingerprint") + + assertEquals(listOf("globalFingerprint"), combined.fingerprint) + } + + // TODO [HSM] test clone + + private fun createTransaction(name: String, scopes: Scopes? = null): ITransaction { + val scopesToUse = scopes ?: fixture.scopes + return SentryTracer(TransactionContext(name, "op", TracesSamplingDecision(true)), scopesToUse).also { + it.startChild("${name}Span") + } + } + + private fun createAttachment(name: String): Attachment { + return Attachment("a".toByteArray(), name, "image/png", false) + } + class TestEventProcessor(val orderNumber: Long?) : EventProcessor { override fun getOrder() = orderNumber } From cac8cb81e6b172d2e2a95f36f1b901071f71c42a Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 2 May 2024 14:25:26 +0200 Subject: [PATCH 37/91] Hubs/Scopes Merge 37 - Fix combined `Contexts` (#3374) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts --- .../android/core/AnrV2EventProcessorTest.kt | 2 +- sentry/api/sentry.api | 25 +- .../java/io/sentry/CombinedContextsView.java | 65 +- .../java/io/sentry/protocol/Contexts.java | 70 ++- .../io/sentry/CombinedContextsViewTest.kt | 568 ++++++++++++++++++ .../java/io/sentry/CombinedScopeViewTest.kt | 88 ++- .../CombinedContextsViewSerializationTest.kt | 89 +++ 7 files changed, 887 insertions(+), 20 deletions(-) create mode 100644 sentry/src/test/java/io/sentry/CombinedContextsViewTest.kt create mode 100644 sentry/src/test/java/io/sentry/protocol/CombinedContextsViewSerializationTest.kt diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2EventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2EventProcessorTest.kt index b581856fe0..2f34b4d2e0 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2EventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2EventProcessorTest.kt @@ -169,7 +169,7 @@ class AnrV2EventProcessorTest { assertNull(processed.platform) assertNull(processed.exceptions) - assertEquals(emptyMap(), processed.contexts) + assertTrue(processed.contexts.isEmpty) } @Test diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index d87ff71ccc..32d530a5a1 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -210,6 +210,9 @@ public final class io/sentry/CheckInStatus : java/lang/Enum { public final class io/sentry/CombinedContextsView : io/sentry/protocol/Contexts { public fun (Lio/sentry/protocol/Contexts;Lio/sentry/protocol/Contexts;Lio/sentry/protocol/Contexts;Lio/sentry/ScopeType;)V + public fun containsKey (Ljava/lang/Object;)Z + public fun entrySet ()Ljava/util/Set; + public fun get (Ljava/lang/Object;)Ljava/lang/Object; public fun getApp ()Lio/sentry/protocol/App; public fun getBrowser ()Lio/sentry/protocol/Browser; public fun getDevice ()Lio/sentry/protocol/Device; @@ -217,7 +220,12 @@ public final class io/sentry/CombinedContextsView : io/sentry/protocol/Contexts public fun getOperatingSystem ()Lio/sentry/protocol/OperatingSystem; public fun getResponse ()Lio/sentry/protocol/Response; public fun getRuntime ()Lio/sentry/protocol/SentryRuntime; + public fun getSize ()I public fun getTrace ()Lio/sentry/SpanContext; + public fun isEmpty ()Z + public fun keys ()Ljava/util/Enumeration; + public fun put (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; + public fun remove (Ljava/lang/Object;)Ljava/lang/Object; public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V public fun setApp (Lio/sentry/protocol/App;)V public fun setBrowser (Lio/sentry/protocol/Browser;)V @@ -227,6 +235,7 @@ public final class io/sentry/CombinedContextsView : io/sentry/protocol/Contexts public fun setResponse (Lio/sentry/protocol/Response;)V public fun setRuntime (Lio/sentry/protocol/SentryRuntime;)V public fun setTrace (Lio/sentry/SpanContext;)V + public fun size ()I public fun withResponse (Lio/sentry/util/HintUtils$SentryConsumer;)V } @@ -4162,9 +4171,13 @@ public final class io/sentry/protocol/Browser$JsonKeys { public fun ()V } -public class io/sentry/protocol/Contexts : java/util/concurrent/ConcurrentHashMap, io/sentry/JsonSerializable { +public class io/sentry/protocol/Contexts : io/sentry/JsonSerializable { public fun ()V public fun (Lio/sentry/protocol/Contexts;)V + public fun containsKey (Ljava/lang/Object;)Z + public fun entrySet ()Ljava/util/Set; + public fun equals (Ljava/lang/Object;)Z + public fun get (Ljava/lang/Object;)Ljava/lang/Object; public fun getApp ()Lio/sentry/protocol/App; public fun getBrowser ()Lio/sentry/protocol/Browser; public fun getDevice ()Lio/sentry/protocol/Device; @@ -4172,8 +4185,17 @@ public class io/sentry/protocol/Contexts : java/util/concurrent/ConcurrentHashMa public fun getOperatingSystem ()Lio/sentry/protocol/OperatingSystem; public fun getResponse ()Lio/sentry/protocol/Response; public fun getRuntime ()Lio/sentry/protocol/SentryRuntime; + public fun getSize ()I public fun getTrace ()Lio/sentry/SpanContext; + public fun hashCode ()I + public fun isEmpty ()Z + public fun keys ()Ljava/util/Enumeration; + public fun put (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; + public fun putAll (Lio/sentry/protocol/Contexts;)V + public fun putAll (Ljava/util/Map;)V + public fun remove (Ljava/lang/Object;)Ljava/lang/Object; public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V + public fun set (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; public fun setApp (Lio/sentry/protocol/App;)V public fun setBrowser (Lio/sentry/protocol/Browser;)V public fun setDevice (Lio/sentry/protocol/Device;)V @@ -4182,6 +4204,7 @@ public class io/sentry/protocol/Contexts : java/util/concurrent/ConcurrentHashMa public fun setResponse (Lio/sentry/protocol/Response;)V public fun setRuntime (Lio/sentry/protocol/SentryRuntime;)V public fun setTrace (Lio/sentry/SpanContext;)V + public fun size ()I public fun withResponse (Lio/sentry/util/HintUtils$SentryConsumer;)V } diff --git a/sentry/src/main/java/io/sentry/CombinedContextsView.java b/sentry/src/main/java/io/sentry/CombinedContextsView.java index 3362a8ea1e..3dd7428975 100644 --- a/sentry/src/main/java/io/sentry/CombinedContextsView.java +++ b/sentry/src/main/java/io/sentry/CombinedContextsView.java @@ -10,6 +10,9 @@ import io.sentry.protocol.SentryRuntime; import io.sentry.util.HintUtils; import java.io.IOException; +import java.util.Enumeration; +import java.util.Map; +import java.util.Set; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -203,12 +206,72 @@ public void setResponse(@NotNull Response response) { getDefaultContexts().setResponse(response); } + @Override + public int size() { + return mergeContexts().size(); + } + + @Override + public int getSize() { + return size(); + } + + @Override + public boolean isEmpty() { + return globalContexts.isEmpty() && isolationContexts.isEmpty() && currentContexts.isEmpty(); + } + + @Override + public boolean containsKey(final @NotNull Object key) { + return globalContexts.containsKey(key) + || isolationContexts.containsKey(key) + || currentContexts.containsKey(key); + } + + @Override + public @Nullable Object get(final @NotNull Object key) { + final @Nullable Object current = currentContexts.get(key); + if (current != null) { + return current; + } + final @Nullable Object isolation = isolationContexts.get(key); + if (isolation != null) { + return isolation; + } + return globalContexts.get(key); + } + + @Override + public @Nullable Object put(final @NotNull String key, final @Nullable Object value) { + return getDefaultContexts().put(key, value); + } + + @Override + public @Nullable Object remove(final @NotNull Object key) { + // TODO [HSM] should this remove from all contexts? + return getDefaultContexts().remove(key); + } + + @Override + public @NotNull Enumeration keys() { + return mergeContexts().keys(); + } + + @Override + public @NotNull Set> entrySet() { + return mergeContexts().entrySet(); + } + @Override public void serialize(@NotNull ObjectWriter writer, @NotNull ILogger logger) throws IOException { + mergeContexts().serialize(writer, logger); + } + + private @NotNull Contexts mergeContexts() { final @NotNull Contexts allContexts = new Contexts(); allContexts.putAll(globalContexts); allContexts.putAll(isolationContexts); allContexts.putAll(currentContexts); - allContexts.serialize(writer, logger); + return allContexts; } } diff --git a/sentry/src/main/java/io/sentry/protocol/Contexts.java b/sentry/src/main/java/io/sentry/protocol/Contexts.java index aa157e665e..4e4c7c5c90 100644 --- a/sentry/src/main/java/io/sentry/protocol/Contexts.java +++ b/sentry/src/main/java/io/sentry/protocol/Contexts.java @@ -12,16 +12,21 @@ import io.sentry.vendor.gson.stream.JsonToken; import java.io.IOException; import java.util.Collections; +import java.util.Enumeration; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @Open -public class Contexts extends ConcurrentHashMap implements JsonSerializable { +public class Contexts implements JsonSerializable { private static final long serialVersionUID = 252445813254943011L; + private final @NotNull ConcurrentHashMap internalStorage = + new ConcurrentHashMap(); + /** Response lock, Ops should be atomic */ private final @NotNull Object responseLock = new Object(); @@ -140,6 +145,69 @@ public void setResponse(final @NotNull Response response) { } } + public int size() { + return internalStorage.size(); + } + + public int getSize() { + return size(); + } + + public boolean isEmpty() { + return internalStorage.isEmpty(); + } + + public boolean containsKey(final @NotNull Object key) { + return internalStorage.containsKey(key); + } + + public @Nullable Object get(final @NotNull Object key) { + return internalStorage.get(key); + } + + public @Nullable Object put(final @NotNull String key, final @Nullable Object value) { + return internalStorage.put(key, value); + } + + public @Nullable Object set(final @NotNull String key, final @Nullable Object value) { + return put(key, value); + } + + public @Nullable Object remove(final @NotNull Object key) { + return internalStorage.remove(key); + } + + public @NotNull Enumeration keys() { + return internalStorage.keys(); + } + + public @NotNull Set> entrySet() { + return internalStorage.entrySet(); + } + + public void putAll(Map m) { + internalStorage.putAll(m); + } + + public void putAll(final @NotNull Contexts contexts) { + internalStorage.putAll(contexts.internalStorage); + } + + @Override + public boolean equals(Object obj) { + if (obj != null && obj instanceof Contexts) { + final @NotNull Contexts otherContexts = (Contexts) obj; + return internalStorage.equals(otherContexts.internalStorage); + } + + return false; + } + + @Override + public int hashCode() { + return internalStorage.hashCode(); + } + // region json @Override diff --git a/sentry/src/test/java/io/sentry/CombinedContextsViewTest.kt b/sentry/src/test/java/io/sentry/CombinedContextsViewTest.kt new file mode 100644 index 0000000000..624ca2417d --- /dev/null +++ b/sentry/src/test/java/io/sentry/CombinedContextsViewTest.kt @@ -0,0 +1,568 @@ +package io.sentry + +import io.sentry.protocol.App +import io.sentry.protocol.Browser +import io.sentry.protocol.Contexts +import io.sentry.protocol.Device +import io.sentry.protocol.Gpu +import io.sentry.protocol.OperatingSystem +import io.sentry.protocol.Response +import io.sentry.protocol.SentryRuntime +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue + +class CombinedContextsViewTest { + + private class Fixture { + lateinit var current: Contexts + lateinit var isolation: Contexts + lateinit var global: Contexts + + fun getSut(): CombinedContextsView { + current = Contexts() + isolation = Contexts() + global = Contexts() + + return CombinedContextsView(global, isolation, current, ScopeType.ISOLATION) + } + } + + private val fixture = Fixture() + + @Test + fun `uses default context CURRENT`() { + fixture.getSut() + val combined = CombinedContextsView(fixture.global, fixture.isolation, fixture.current, ScopeType.CURRENT) + combined.trace = SpanContext("some") + assertEquals("some", fixture.current.trace?.op) + } + + @Test + fun `uses default context ISOLATION`() { + fixture.getSut() + val combined = CombinedContextsView(fixture.global, fixture.isolation, fixture.current, ScopeType.ISOLATION) + combined.trace = SpanContext("some") + assertEquals("some", fixture.isolation.trace?.op) + } + + @Test + fun `uses default context GLOBAL`() { + fixture.getSut() + val combined = CombinedContextsView(fixture.global, fixture.isolation, fixture.current, ScopeType.GLOBAL) + combined.trace = SpanContext("some") + assertEquals("some", fixture.global.trace?.op) + } + + @Test + fun `prefers trace from current context`() { + val combined = fixture.getSut() + fixture.current.trace = SpanContext("current") + fixture.isolation.trace = SpanContext("isolation") + fixture.global.trace = SpanContext("global") + + assertEquals("current", combined.trace?.op) + } + + @Test + fun `uses isolation trace if current context does not have it`() { + val combined = fixture.getSut() + fixture.isolation.trace = SpanContext("isolation") + fixture.global.trace = SpanContext("global") + + assertEquals("isolation", combined.trace?.op) + } + + @Test + fun `uses global trace if current and isolation context do not have it`() { + val combined = fixture.getSut() + fixture.global.trace = SpanContext("global") + + assertEquals("global", combined.trace?.op) + } + + @Test + fun `sets trace on default context`() { + val combined = fixture.getSut() + combined.trace = SpanContext("some") + + assertNull(fixture.current.trace) + assertEquals("some", fixture.isolation.trace?.op) + assertNull(fixture.global.trace) + } + + @Test + fun `prefers app from current context`() { + val combined = fixture.getSut() + fixture.current.setApp(App().also { it.appName = "current" }) + fixture.isolation.setApp(App().also { it.appName = "isolation" }) + fixture.global.setApp(App().also { it.appName = "global" }) + + assertEquals("current", combined.app?.appName) + } + + @Test + fun `uses isolation app if current context does not have it`() { + val combined = fixture.getSut() + fixture.isolation.setApp(App().also { it.appName = "isolation" }) + fixture.global.setApp(App().also { it.appName = "global" }) + + assertEquals("isolation", combined.app?.appName) + } + + @Test + fun `uses global app if current and isolation context do not have it`() { + val combined = fixture.getSut() + fixture.global.setApp(App().also { it.appName = "global" }) + + assertEquals("global", combined.app?.appName) + } + + @Test + fun `sets app on default context`() { + val combined = fixture.getSut() + combined.setApp(App().also { it.appName = "some" }) + + assertNull(fixture.current.app) + assertEquals("some", fixture.isolation.app?.appName) + assertNull(fixture.global.app) + } + + @Test + fun `prefers browser from current context`() { + val combined = fixture.getSut() + fixture.current.setBrowser(Browser().also { it.name = "current" }) + fixture.isolation.setBrowser(Browser().also { it.name = "isolation" }) + fixture.global.setBrowser(Browser().also { it.name = "global" }) + + assertEquals("current", combined.browser?.name) + } + + @Test + fun `uses isolation browser if current context does not have it`() { + val combined = fixture.getSut() + fixture.isolation.setBrowser(Browser().also { it.name = "isolation" }) + fixture.global.setBrowser(Browser().also { it.name = "global" }) + + assertEquals("isolation", combined.browser?.name) + } + + @Test + fun `uses global browser if current and isolation context do not have it`() { + val combined = fixture.getSut() + fixture.global.setBrowser(Browser().also { it.name = "global" }) + + assertEquals("global", combined.browser?.name) + } + + @Test + fun `sets browser on default context`() { + val combined = fixture.getSut() + combined.setBrowser(Browser().also { it.name = "some" }) + + assertNull(fixture.current.browser) + assertEquals("some", fixture.isolation.browser?.name) + assertNull(fixture.global.browser) + } + + @Test + fun `prefers device from current context`() { + val combined = fixture.getSut() + fixture.current.setDevice(Device().also { it.name = "current" }) + fixture.isolation.setDevice(Device().also { it.name = "isolation" }) + fixture.global.setDevice(Device().also { it.name = "global" }) + + assertEquals("current", combined.device?.name) + } + + @Test + fun `uses isolation device if current context does not have it`() { + val combined = fixture.getSut() + fixture.isolation.setDevice(Device().also { it.name = "isolation" }) + fixture.global.setDevice(Device().also { it.name = "global" }) + + assertEquals("isolation", combined.device?.name) + } + + @Test + fun `uses global device if current and isolation context do not have it`() { + val combined = fixture.getSut() + fixture.global.setDevice(Device().also { it.name = "global" }) + + assertEquals("global", combined.device?.name) + } + + @Test + fun `sets device on default context`() { + val combined = fixture.getSut() + combined.setDevice(Device().also { it.name = "some" }) + + assertNull(fixture.current.device) + assertEquals("some", fixture.isolation.device?.name) + assertNull(fixture.global.device) + } + + @Test + fun `prefers operatingSystem from current context`() { + val combined = fixture.getSut() + fixture.current.setOperatingSystem(OperatingSystem().also { it.name = "current" }) + fixture.isolation.setOperatingSystem(OperatingSystem().also { it.name = "isolation" }) + fixture.global.setOperatingSystem(OperatingSystem().also { it.name = "global" }) + + assertEquals("current", combined.operatingSystem?.name) + } + + @Test + fun `uses isolation operatingSystem if current context does not have it`() { + val combined = fixture.getSut() + fixture.isolation.setOperatingSystem(OperatingSystem().also { it.name = "isolation" }) + fixture.global.setOperatingSystem(OperatingSystem().also { it.name = "global" }) + + assertEquals("isolation", combined.operatingSystem?.name) + } + + @Test + fun `uses global operatingSystem if current and isolation context do not have it`() { + val combined = fixture.getSut() + fixture.global.setOperatingSystem(OperatingSystem().also { it.name = "global" }) + + assertEquals("global", combined.operatingSystem?.name) + } + + @Test + fun `sets operatingSystem on default context`() { + val combined = fixture.getSut() + combined.setOperatingSystem(OperatingSystem().also { it.name = "some" }) + + assertNull(fixture.current.operatingSystem) + assertEquals("some", fixture.isolation.operatingSystem?.name) + assertNull(fixture.global.operatingSystem) + } + + @Test + fun `prefers runtime from current context`() { + val combined = fixture.getSut() + fixture.current.setRuntime(SentryRuntime().also { it.name = "current" }) + fixture.isolation.setRuntime(SentryRuntime().also { it.name = "isolation" }) + fixture.global.setRuntime(SentryRuntime().also { it.name = "global" }) + + assertEquals("current", combined.runtime?.name) + } + + @Test + fun `uses isolation runtime if current context does not have it`() { + val combined = fixture.getSut() + fixture.isolation.setRuntime(SentryRuntime().also { it.name = "isolation" }) + fixture.global.setRuntime(SentryRuntime().also { it.name = "global" }) + + assertEquals("isolation", combined.runtime?.name) + } + + @Test + fun `uses global runtime if current and isolation context do not have it`() { + val combined = fixture.getSut() + fixture.global.setRuntime(SentryRuntime().also { it.name = "global" }) + + assertEquals("global", combined.runtime?.name) + } + + @Test + fun `sets runtime on default context`() { + val combined = fixture.getSut() + combined.setRuntime(SentryRuntime().also { it.name = "some" }) + + assertNull(fixture.current.runtime) + assertEquals("some", fixture.isolation.runtime?.name) + assertNull(fixture.global.runtime) + } + + @Test + fun `prefers gpu from current context`() { + val combined = fixture.getSut() + fixture.current.setGpu(Gpu().also { it.name = "current" }) + fixture.isolation.setGpu(Gpu().also { it.name = "isolation" }) + fixture.global.setGpu(Gpu().also { it.name = "global" }) + + assertEquals("current", combined.gpu?.name) + } + + @Test + fun `uses isolation gpu if current context does not have it`() { + val combined = fixture.getSut() + fixture.isolation.setGpu(Gpu().also { it.name = "isolation" }) + fixture.global.setGpu(Gpu().also { it.name = "global" }) + + assertEquals("isolation", combined.gpu?.name) + } + + @Test + fun `uses global gpu if current and isolation context do not have it`() { + val combined = fixture.getSut() + fixture.global.setGpu(Gpu().also { it.name = "global" }) + + assertEquals("global", combined.gpu?.name) + } + + @Test + fun `sets gpu on default context`() { + val combined = fixture.getSut() + combined.setGpu(Gpu().also { it.name = "some" }) + + assertNull(fixture.current.gpu) + assertEquals("some", fixture.isolation.gpu?.name) + assertNull(fixture.global.gpu) + } + + @Test + fun `prefers response from current context`() { + val combined = fixture.getSut() + fixture.current.setResponse(Response().also { it.cookies = "current" }) + fixture.isolation.setResponse(Response().also { it.cookies = "isolation" }) + fixture.global.setResponse(Response().also { it.cookies = "global" }) + + assertEquals("current", combined.response?.cookies) + } + + @Test + fun `uses isolation response if current context does not have it`() { + val combined = fixture.getSut() + fixture.isolation.setResponse(Response().also { it.cookies = "isolation" }) + fixture.global.setResponse(Response().also { it.cookies = "global" }) + + assertEquals("isolation", combined.response?.cookies) + } + + @Test + fun `uses global response if current and isolation context do not have it`() { + val combined = fixture.getSut() + fixture.global.setResponse(Response().also { it.cookies = "global" }) + + assertEquals("global", combined.response?.cookies) + } + + @Test + fun `sets response on default context`() { + val combined = fixture.getSut() + combined.setResponse(Response().also { it.cookies = "some" }) + + assertNull(fixture.current.response) + assertEquals("some", fixture.isolation.response?.cookies) + assertNull(fixture.global.response) + } + + @Test + fun `withResponse is executed on current if present`() { + val combined = fixture.getSut() + fixture.current.setResponse(Response().also { it.cookies = "current" }) + fixture.isolation.setResponse(Response().also { it.cookies = "isolation" }) + fixture.global.setResponse(Response().also { it.cookies = "global" }) + + combined.withResponse { response -> + response.cookies = "updated" + } + + assertEquals("updated", fixture.current.response?.cookies) + assertEquals("isolation", fixture.isolation.response?.cookies) + assertEquals("global", fixture.global.response?.cookies) + } + + @Test + fun `withResponse is executed on isolation if current not present`() { + val combined = fixture.getSut() + fixture.isolation.setResponse(Response().also { it.cookies = "isolation" }) + fixture.global.setResponse(Response().also { it.cookies = "global" }) + + combined.withResponse { response -> + response.cookies = "updated" + } + + assertNull(fixture.current.response) + assertEquals("updated", fixture.isolation.response?.cookies) + assertEquals("global", fixture.global.response?.cookies) + } + + @Test + fun `withResponse is executed on global if current and isoaltion not present`() { + val combined = fixture.getSut() + fixture.global.setResponse(Response().also { it.cookies = "global" }) + + combined.withResponse { response -> + response.cookies = "updated" + } + + assertNull(fixture.current.response) + assertNull(fixture.isolation.response) + assertEquals("updated", fixture.global.response?.cookies) + } + + @Test + fun `withResponse is executed on default if not present anywhere`() { + val combined = fixture.getSut() + + combined.withResponse { response -> + response.cookies = "updated" + } + + assertNull(fixture.current.response) + assertEquals("updated", fixture.isolation.response?.cookies) + assertNull(fixture.global.response) + } + + @Test + fun `size combines contexts`() { + val combined = fixture.getSut() + fixture.current.trace = SpanContext("current") + fixture.isolation.setApp(App().also { it.appName = "isolation" }) + fixture.global.setGpu(Gpu().also { it.name = "global" }) + + assertEquals(3, combined.size) + } + + @Test + fun `size considers overrides`() { + val combined = fixture.getSut() + fixture.current.trace = SpanContext("current") + fixture.isolation.trace = SpanContext("isolation") + fixture.global.trace = SpanContext("global") + + assertEquals(1, combined.size) + } + + @Test + fun `isEmpty`() { + val combined = fixture.getSut() + assertTrue(combined.isEmpty) + } + + @Test + fun `isNotEmpty if current has value`() { + val combined = fixture.getSut() + fixture.current.trace = SpanContext("current") + + assertFalse(combined.isEmpty) + } + + @Test + fun `isNotEmpty if isolation has value`() { + val combined = fixture.getSut() + fixture.isolation.setApp(App().also { it.appName = "isolation" }) + + assertFalse(combined.isEmpty) + } + + @Test + fun `isNotEmpty if global has value`() { + val combined = fixture.getSut() + fixture.global.setGpu(Gpu().also { it.name = "global" }) + + assertFalse(combined.isEmpty) + } + + @Test + fun `containsKey false`() { + val combined = fixture.getSut() + assertFalse(combined.containsKey("trace")) + } + + @Test + fun `containsKey current`() { + val combined = fixture.getSut() + fixture.current.trace = SpanContext("current") + assertTrue(combined.containsKey("trace")) + } + + @Test + fun `containsKey isolation`() { + val combined = fixture.getSut() + fixture.isolation.trace = SpanContext("isolation") + assertTrue(combined.containsKey("trace")) + } + + @Test + fun `containsKey global`() { + val combined = fixture.getSut() + fixture.global.trace = SpanContext("global") + assertTrue(combined.containsKey("trace")) + } + + @Test + fun `keys combines contexts`() { + val combined = fixture.getSut() + fixture.current.trace = SpanContext("current") + fixture.isolation.setApp(App().also { it.appName = "isolation" }) + fixture.global.setGpu(Gpu().also { it.name = "global" }) + + assertEquals(listOf("app", "gpu", "trace"), combined.keys().toList().sorted()) + } + + @Test + fun `entrySet combines contexts`() { + val combined = fixture.getSut() + val trace = SpanContext("current") + fixture.current.trace = trace + val app = App().also { it.appName = "isolation" } + fixture.isolation.setApp(app) + val gpu = Gpu().also { it.name = "global" } + fixture.global.setGpu(gpu) + + val entrySet = combined.entrySet() + assertEquals(3, entrySet.size) + assertNotNull(entrySet.firstOrNull { it.key == "trace" && it.value == trace }) + assertNotNull(entrySet.firstOrNull { it.key == "app" && it.value == app }) + assertNotNull(entrySet.firstOrNull { it.key == "gpu" && it.value == gpu }) + } + + @Test + fun `get prefers current`() { + val combined = fixture.getSut() + fixture.current.put("test", "current") + fixture.isolation.put("test", "isolation") + fixture.global.put("test", "global") + + assertEquals("current", combined.get("test")) + } + + @Test + fun `get uses isolation if not in current`() { + val combined = fixture.getSut() + fixture.isolation.put("test", "isolation") + fixture.global.put("test", "global") + + assertEquals("isolation", combined.get("test")) + } + + @Test + fun `get uses global if not in current or isolation`() { + val combined = fixture.getSut() + fixture.global.put("test", "global") + + assertEquals("global", combined.get("test")) + } + + @Test + fun `put stores in default context`() { + val combined = fixture.getSut() + combined.put("test", "aValue") + + assertNull(fixture.current.get("test")) + assertEquals("aValue", fixture.isolation.get("test")) + assertNull(fixture.global.get("test")) + } + + @Test + fun `remove removes from default context`() { + val combined = fixture.getSut() + fixture.current.put("test", "current") + fixture.isolation.put("test", "isolation") + fixture.global.put("test", "global") + + combined.remove("test") + + assertEquals("current", fixture.current.get("test")) + assertNull(fixture.isolation.get("test")) + assertEquals("global", fixture.global.get("test")) + } +} diff --git a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt index 11725947cb..65a1bc49f7 100644 --- a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt +++ b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt @@ -1,5 +1,6 @@ package io.sentry +import io.sentry.protocol.Device import io.sentry.protocol.Request import io.sentry.protocol.SentryId import io.sentry.protocol.User @@ -640,26 +641,81 @@ class CombinedScopeViewTest { assertEquals("globalValue", fixture.globalScope.extras["someExtra"]) } - // TODO [HSM] CombinedContextsView does not override all map methods, leading to unwanted behaviour - // we should probably no longer extend Map but instead keep an internal map - // and offer a toMap function or similar -// @Test -// fun `combines context from all scopes`() { -// val combined = fixture.getSut() -// fixture.scope.setContexts("scopeContext", "scopeValue") -// fixture.isolationScope.setContexts("isolationContext", "isolationValue") -// fixture.globalScope.setContexts("globalContext", "globalValue") -// -// val contexts = combined.contexts -// assertEquals("scopeValue", contexts["scopeContext"]) -// } + @Test + fun `combines context from all scopes`() { + val combined = fixture.getSut() + fixture.scope.setContexts("scopeContext", "scopeValue") + fixture.isolationScope.setContexts("isolationContext", "isolationValue") + fixture.globalScope.setContexts("globalContext", "globalValue") - // TODO [HSM] test all setContext methods + val contexts = combined.contexts + assertEquals(mapOf("value" to "scopeValue"), contexts["scopeContext"]) + } + + @Test + fun `current scope context overrides context of other scopes`() { + val combined = fixture.getSut() + fixture.scope.setContexts("someContext", "scopeValue") + fixture.isolationScope.setContexts("someContext", "isolationValue") + fixture.globalScope.setContexts("someContext", "globalValue") + + val contexts = combined.contexts + assertEquals(mapOf("value" to "scopeValue"), contexts["someContext"]) + } + + @Test + fun `isolation scope context overrides global context`() { + val combined = fixture.getSut() + fixture.isolationScope.setContexts("someContext", "isolationValue") + fixture.globalScope.setContexts("someContext", "globalValue") + + val contexts = combined.contexts + assertEquals(mapOf("value" to "isolationValue"), contexts["someContext"]) + } + + @Test + fun `setContexts writes to default scope`() { + val combined = fixture.getSut() + combined.setContexts("aString", "stringValue") + combined.setContexts("aChar", 'c') + combined.setContexts("aNumber", 1) + combined.setContexts("someObject", Device().also { it.brand = "someDeviceBrand" }) + combined.setContexts("someArray", arrayOf("a", "b")) + combined.setContexts("someList", listOf("c", "d", "e")) - // TODO [HSM] fingerprint tests (discuss how it should behave first) + assertNull(fixture.scope.contexts["aString"]) + assertNull(fixture.scope.contexts["aChar"]) + assertNull(fixture.scope.contexts["aNumber"]) + assertNull(fixture.scope.contexts["someObject"]) + assertNull(fixture.scope.contexts["someArray"]) + assertNull(fixture.scope.contexts["someList"]) + + assertEquals(mapOf("value" to "stringValue"), fixture.isolationScope.contexts["aString"]) + assertEquals(mapOf("value" to 'c'), fixture.isolationScope.contexts["aChar"]) + assertEquals(mapOf("value" to 1), fixture.isolationScope.contexts["aNumber"]) + assertEquals("someDeviceBrand", (fixture.isolationScope.contexts["someObject"] as? Device)?.brand) + val arrayValue = (fixture.isolationScope.contexts["someArray"] as? Map)?.get("value") as? Array + assertEquals(2, arrayValue?.size) + assertEquals("a", arrayValue?.get(0)) + assertEquals("b", arrayValue?.get(1)) + val listValue = (fixture.isolationScope.contexts["someList"] as? Map)?.get("value") as? List + assertEquals(3, listValue?.size) + assertEquals("c", listValue?.get(0)) + assertEquals("d", listValue?.get(1)) + assertEquals("e", listValue?.get(2)) + + assertNull(fixture.globalScope.contexts["aString"]) + assertNull(fixture.globalScope.contexts["aChar"]) + assertNull(fixture.globalScope.contexts["aNumber"]) + assertNull(fixture.globalScope.contexts["someObject"]) + assertNull(fixture.globalScope.contexts["someArray"]) + assertNull(fixture.globalScope.contexts["someList"]) + } + + // TODO [HSM] test all setContext methods @Test - fun `combines attachments Æ’rom all scopes`() { + fun `combines attachments from all scopes`() { val combined = fixture.getSut() fixture.scope.addAttachment(createAttachment("scopeAttachment.png")) diff --git a/sentry/src/test/java/io/sentry/protocol/CombinedContextsViewSerializationTest.kt b/sentry/src/test/java/io/sentry/protocol/CombinedContextsViewSerializationTest.kt new file mode 100644 index 0000000000..87cb226abc --- /dev/null +++ b/sentry/src/test/java/io/sentry/protocol/CombinedContextsViewSerializationTest.kt @@ -0,0 +1,89 @@ +package io.sentry.protocol + +import io.sentry.CombinedContextsView +import io.sentry.ILogger +import io.sentry.JsonObjectWriter +import io.sentry.ScopeType +import org.junit.Test +import org.mockito.kotlin.any +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import kotlin.test.assertEquals + +class CombinedContextsViewSerializationTest { + + class Fixture { + val logger = mock() + + fun getSut(): CombinedContextsView { + val current = Contexts() + val isolation = Contexts() + val global = Contexts() + val combined = CombinedContextsView(global, isolation, current, ScopeType.ISOLATION) + + current.setApp(AppSerializationTest.Fixture().getSut()) + current.setBrowser(BrowserSerializationTest.Fixture().getSut()) + current.trace = SpanContextSerializationTest.Fixture().getSut() + + isolation.setDevice(DeviceSerializationTest.Fixture().getSut()) + isolation.setOperatingSystem(OperatingSystemSerializationTest.Fixture().getSut()) + isolation.setResponse(ResponseSerializationTest.Fixture().getSut()) + + global.setRuntime(SentryRuntimeSerializationTest.Fixture().getSut()) + global.setGpu(GpuSerializationTest.Fixture().getSut()) + + return combined + } + } + private val fixture = Fixture() + + @Test + fun serialize() { + val expected = SerializationUtils.sanitizedFile("json/contexts.json") + val actual = SerializationUtils.serializeToString(fixture.getSut(), fixture.logger) + + assertEquals(expected, actual) + } + + @Test + fun serializeUnknownEntry() { + val sut = fixture.getSut() + sut["fixture-key"] = "fixture-value" + + val writer = mock().apply { + whenever(name(any())).thenReturn(this) + } + sut.serialize(writer, fixture.logger) + + verify(writer).name("fixture-key") + verify(writer).value(fixture.logger, "fixture-value") + } + + @Test + fun deserialize() { + val expectedJson = SerializationUtils.sanitizedFile("json/contexts.json") + val actual = SerializationUtils.deserializeJson( + expectedJson, + Contexts.Deserializer(), + fixture.logger + ) + val actualJson = SerializationUtils.serializeToString(actual, fixture.logger) + + assertEquals(expectedJson, actualJson) + } + + @Test + fun deserializeUnknownEntry() { + val sut = fixture.getSut() + sut["fixture-key"] = "fixture-value" + val serialized = SerializationUtils.serializeToString(sut, fixture.logger) + val deserialized = SerializationUtils.deserializeJson( + serialized, + Contexts.Deserializer(), + fixture.logger + ) + + assertEquals("fixture-value", deserialized["fixture-key"]) + } +} From 934930370a43287d59fced08f73d3fbd6c743cfb Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 2 May 2024 14:26:42 +0200 Subject: [PATCH 38/91] Hubs/Scopes Merge 38 - Use `ScopeType.COMBINED` for cross platform (`InternalSentrySdk`) (#3375) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform --- .../main/java/io/sentry/android/core/InternalSentrySdk.java | 3 ++- .../java/io/sentry/android/core/InternalSentrySdkTest.kt | 6 +++++- sentry/src/main/java/io/sentry/CombinedScopeView.java | 6 +++--- sentry/src/main/java/io/sentry/HubAdapter.java | 1 - sentry/src/main/java/io/sentry/ScopeType.java | 2 -- sentry/src/main/java/io/sentry/Scopes.java | 4 +--- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java index 3170a4f1ec..84a2ec4d38 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java @@ -9,6 +9,7 @@ import io.sentry.IScopes; import io.sentry.ISerializer; import io.sentry.ObjectWriter; +import io.sentry.ScopeType; import io.sentry.ScopesAdapter; import io.sentry.SentryEnvelope; import io.sentry.SentryEnvelopeItem; @@ -44,9 +45,9 @@ public final class InternalSentrySdk { @Nullable public static IScope getCurrentScope() { final @NotNull AtomicReference scopeRef = new AtomicReference<>(); - // TODO [HSM] should this retrieve combined scope? ScopesAdapter.getInstance() .configureScope( + ScopeType.COMBINED, scope -> { scopeRef.set(scope.clone()); }); diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt index e64cbe227e..f4f2c696d3 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt @@ -7,6 +7,7 @@ import io.sentry.Breadcrumb import io.sentry.Hint import io.sentry.IScope import io.sentry.Scope +import io.sentry.ScopeType import io.sentry.Scopes import io.sentry.Sentry import io.sentry.SentryEnvelope @@ -138,6 +139,9 @@ class InternalSentrySdkTest { ) // TODO [HSM] add breadcrumbs to all scopes and assert they are there Sentry.addBreadcrumb("test") + Sentry.configureScope(ScopeType.CURRENT) { scope -> scope.addBreadcrumb(Breadcrumb("currentBreadcrumb")) } + Sentry.configureScope(ScopeType.ISOLATION) { scope -> scope.addBreadcrumb(Breadcrumb("isolationBreadcrumb")) } + Sentry.configureScope(ScopeType.GLOBAL) { scope -> scope.addBreadcrumb(Breadcrumb("globalBreadcrumb")) } // when the clone is modified val clonedScope = InternalSentrySdk.getCurrentScope()!! @@ -145,7 +149,7 @@ class InternalSentrySdkTest { // then modifications should not be reflected Sentry.configureScope { scope -> - assertEquals(1, scope.breadcrumbs.size) + assertEquals(3, scope.breadcrumbs.size) } } diff --git a/sentry/src/main/java/io/sentry/CombinedScopeView.java b/sentry/src/main/java/io/sentry/CombinedScopeView.java index 44a87da0b1..66e557e16d 100644 --- a/sentry/src/main/java/io/sentry/CombinedScopeView.java +++ b/sentry/src/main/java/io/sentry/CombinedScopeView.java @@ -318,6 +318,8 @@ IScope getSpecificScope(final @Nullable ScopeType scopeType) { return isolationScope; case GLOBAL: return globalScope; + case COMBINED: + return this; default: break; } @@ -431,8 +433,7 @@ public void setPropagationContext(@NotNull PropagationContext propagationContext @Override public @NotNull IScope clone() { - // TODO [HSM] just return a new CombinedScopeView with forked scope? - return getDefaultWriteScope().clone(); + return new CombinedScopeView(globalScope, isolationScope.clone(), scope.clone()); } @Override @@ -454,7 +455,6 @@ public void bindClient(@NotNull ISentryClient client) { @Override public @NotNull ISentryClient getClient() { - // TODO [HSM] checking for noop here doesn't allow disabling via client, is that ok? final @Nullable ISentryClient current = scope.getClient(); if (!(current instanceof NoOpSentryClient)) { return current; diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index df1a7aa661..266770ddce 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -215,7 +215,6 @@ public void flush(long timeoutMillis) { @Override public @NotNull ISentryLifecycleToken makeCurrent() { - // TODO [HSM] this wouldn't do anything since it replaced the current with the same Scopes return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); } diff --git a/sentry/src/main/java/io/sentry/ScopeType.java b/sentry/src/main/java/io/sentry/ScopeType.java index 3815cf2081..513b92115d 100644 --- a/sentry/src/main/java/io/sentry/ScopeType.java +++ b/sentry/src/main/java/io/sentry/ScopeType.java @@ -4,7 +4,5 @@ public enum ScopeType { CURRENT, ISOLATION, GLOBAL, - - // TODO [HSM] do we need a combined as well so configureScope COMBINED; } diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 538c192304..5b3d0e672b 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -646,9 +646,7 @@ public void withScope(final @NotNull ScopeCallback callback) { } else { final @NotNull IScopes forkedScopes = forkedCurrentScope("withScope"); - // TODO [HSM] should forkedScopes be made current inside callback? - // TODO [HSM] forkedScopes.makeCurrent()? - try { + try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) { callback.run(forkedScopes.getScope()); } catch (Throwable e) { getOptions().getLogger().log(SentryLevel.ERROR, "Error in the 'withScope' callback.", e); From 0cc4e7316bb9d3e7e31306dd0fcd971fd931919f Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 2 May 2024 14:32:56 +0200 Subject: [PATCH 39/91] Hubs/Scopes Merge 39 - Review Changes (#3381) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more --- .../io/sentry/kotlin/SentryContextTest.kt | 22 ++++--- sentry-quartz/api/sentry-quartz.api | 2 +- .../io/sentry/quartz/SentryJobListener.java | 9 +-- .../api/sentry-servlet-jakarta.api | 2 +- .../jakarta/SentryServletRequestListener.java | 9 +-- .../SentryServletRequestListenerTest.kt | 11 ++-- sentry-servlet/api/sentry-servlet.api | 2 +- .../servlet/SentryServletRequestListener.java | 9 +-- .../SentryServletRequestListenerTest.kt | 11 ++-- .../spring/jakarta/SentrySpringFilter.java | 25 ++++---- .../spring/jakarta/SentryTaskDecorator.java | 10 ++-- .../jakarta/checkin/SentryCheckInAdvice.java | 37 ++++++------ .../tracing/SentryTransactionAdvice.java | 48 +++++++-------- .../spring/jakarta/webflux/ReactorUtils.java | 9 +-- .../spring/jakarta/SentryCheckInAdviceTest.kt | 29 ++++++---- .../spring/jakarta/SentrySpringFilterTest.kt | 11 +++- .../tracing/SentryTransactionAdviceTest.kt | 6 +- .../io/sentry/spring/SentrySpringFilter.java | 25 ++++---- .../io/sentry/spring/SentryTaskDecorator.java | 12 ++-- .../spring/checkin/SentryCheckInAdvice.java | 37 ++++++------ .../tracing/SentryTransactionAdvice.java | 48 +++++++-------- .../sentry/spring/SentryCheckInAdviceTest.kt | 27 ++++++--- .../sentry/spring/SentrySpringFilterTest.kt | 11 +++- .../tracing/SentryTransactionAdviceTest.kt | 6 +- sentry/api/sentry.api | 18 ++++-- .../src/main/java/io/sentry/Breadcrumb.java | 6 +- .../java/io/sentry/CombinedContextsView.java | 2 +- .../java/io/sentry/CombinedScopeView.java | 10 ++-- sentry/src/main/java/io/sentry/Hub.java | 25 ++++++++ .../src/main/java/io/sentry/HubAdapter.java | 9 +++ .../main/java/io/sentry/HubScopesWrapper.java | 9 +++ sentry/src/main/java/io/sentry/IScope.java | 2 +- sentry/src/main/java/io/sentry/IScopes.java | 38 ++++++++++-- .../main/java/io/sentry/IScopesStorage.java | 2 + sentry/src/main/java/io/sentry/NoOpHub.java | 9 +++ sentry/src/main/java/io/sentry/NoOpScope.java | 2 +- .../src/main/java/io/sentry/NoOpScopes.java | 9 +++ sentry/src/main/java/io/sentry/Scope.java | 2 +- sentry/src/main/java/io/sentry/Scopes.java | 58 ++++++++++++------- .../main/java/io/sentry/ScopesAdapter.java | 10 +++- sentry/src/main/java/io/sentry/Sentry.java | 24 +++++++- .../main/java/io/sentry/SentryWrapper.java | 45 ++++++++++---- .../java/io/sentry/protocol/Contexts.java | 4 +- .../java/io/sentry/util/CheckInUtils.java | 43 +++++++------- .../java/io/sentry/CombinedScopeViewTest.kt | 26 ++++----- sentry/src/test/java/io/sentry/ScopesTest.kt | 41 +++++++++++-- .../java/io/sentry/util/CheckInUtilsTest.kt | 40 ++++++++----- 47 files changed, 549 insertions(+), 303 deletions(-) diff --git a/sentry-kotlin-extensions/src/test/java/io/sentry/kotlin/SentryContextTest.kt b/sentry-kotlin-extensions/src/test/java/io/sentry/kotlin/SentryContextTest.kt index bd498846dd..9cffd744d8 100644 --- a/sentry-kotlin-extensions/src/test/java/io/sentry/kotlin/SentryContextTest.kt +++ b/sentry-kotlin-extensions/src/test/java/io/sentry/kotlin/SentryContextTest.kt @@ -16,6 +16,10 @@ import kotlin.test.assertNull class SentryContextTest { + // TODO [HSM] In global hub mode SentryContext behaves differently + // because Sentry.getCurrentScopes always returns rootScopes + // What's the desired behaviour? + @BeforeTest fun init() { Sentry.init("https://key@sentry.io/123") @@ -183,8 +187,8 @@ class SentryContextTest { val c2 = launch( SentryContext( - Sentry.getCurrentScopes().clone().also { - Sentry.setTag("cloned", "clonedValue") + Sentry.getCurrentScopes().forkedScopes("test").also { + it.setTag("cloned", "clonedValue") } ) ) { @@ -198,13 +202,13 @@ class SentryContextTest { c2.join() assertNotNull(getTag("c1")) - assertNotNull(getTag("c2")) - assertNotNull(getTag("cloned")) + assertNull(getTag("c2")) + assertNull(getTag("cloned")) }.join() assertNotNull(getTag("c1")) - assertNotNull(getTag("c2")) - assertNotNull(getTag("cloned")) + assertNull(getTag("c2")) + assertNull(getTag("cloned")) return@runBlocking } @@ -223,7 +227,7 @@ class SentryContextTest { val c2 = launch( SentryContext( - Sentry.getCurrentScopes().clone().also { + Sentry.getCurrentScopes().forkedCurrentScope("test").also { it.configureScope(ScopeType.CURRENT) { scope -> scope.setTag("cloned", "clonedValue") } @@ -253,7 +257,7 @@ class SentryContextTest { @Test fun `mergeForChild returns copy of initial context if Key not present`() { val initialContextElement = SentryContext( - Sentry.getCurrentScopes().clone().also { + Sentry.getCurrentScopes().forkedScopes("test").also { it.setTag("cloned", "clonedValue") } ) @@ -266,7 +270,7 @@ class SentryContextTest { @Test fun `mergeForChild returns passed context`() { val initialContextElement = SentryContext( - Sentry.getCurrentScopes().clone().also { + Sentry.getCurrentScopes().forkedScopes("test").also { it.setTag("cloned", "clonedValue") } ) diff --git a/sentry-quartz/api/sentry-quartz.api b/sentry-quartz/api/sentry-quartz.api index 21ca2abca6..bb8b142a91 100644 --- a/sentry-quartz/api/sentry-quartz.api +++ b/sentry-quartz/api/sentry-quartz.api @@ -5,7 +5,7 @@ public final class io/sentry/quartz/BuildConfig { public final class io/sentry/quartz/SentryJobListener : org/quartz/JobListener { public static final field SENTRY_CHECK_IN_ID_KEY Ljava/lang/String; - public static final field SENTRY_LIFECYCLE_TOKEN_KEY Ljava/lang/String; + public static final field SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY Ljava/lang/String; public static final field SENTRY_SLUG_KEY Ljava/lang/String; public fun ()V public fun (Lio/sentry/IScopes;)V diff --git a/sentry-quartz/src/main/java/io/sentry/quartz/SentryJobListener.java b/sentry-quartz/src/main/java/io/sentry/quartz/SentryJobListener.java index 03f1acbbed..38dbffdc8e 100644 --- a/sentry-quartz/src/main/java/io/sentry/quartz/SentryJobListener.java +++ b/sentry-quartz/src/main/java/io/sentry/quartz/SentryJobListener.java @@ -25,7 +25,7 @@ public final class SentryJobListener implements JobListener { public static final String SENTRY_CHECK_IN_ID_KEY = "sentry-checkin-id"; public static final String SENTRY_SLUG_KEY = "sentry-slug"; - public static final String SENTRY_LIFECYCLE_TOKEN_KEY = "sentry-lifecycle"; + public static final String SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY = "sentry-scope-lifecycle"; private final @NotNull IScopes scopes; @@ -52,14 +52,15 @@ public void jobToBeExecuted(final @NotNull JobExecutionContext context) { if (maybeSlug == null) { return; } - final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); + final @NotNull ISentryLifecycleToken lifecycleToken = + scopes.forkedScopes("SentryJobListener").makeCurrent(); TracingUtils.startNewTrace(scopes); final @NotNull String slug = maybeSlug; final @NotNull CheckIn checkIn = new CheckIn(slug, CheckInStatus.IN_PROGRESS); final @NotNull SentryId checkInId = scopes.captureCheckIn(checkIn); context.put(SENTRY_CHECK_IN_ID_KEY, checkInId); context.put(SENTRY_SLUG_KEY, slug); - context.put(SENTRY_LIFECYCLE_TOKEN_KEY, lifecycleToken); + context.put(SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY, lifecycleToken); } catch (Throwable t) { scopes .getOptions() @@ -107,7 +108,7 @@ public void jobWasExecuted(JobExecutionContext context, JobExecutionException jo .getLogger() .log(SentryLevel.ERROR, "Unable to capture check-in in jobWasExecuted.", t); } finally { - LifecycleHelper.close(context.get(SENTRY_LIFECYCLE_TOKEN_KEY)); + LifecycleHelper.close(context.get(SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY)); } } } diff --git a/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api b/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api index adde86fda5..d89edeec60 100644 --- a/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api +++ b/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api @@ -9,7 +9,7 @@ public class io/sentry/servlet/jakarta/SentryServletContainerInitializer : jakar } public class io/sentry/servlet/jakarta/SentryServletRequestListener : jakarta/servlet/ServletRequestListener { - public static final field SENTRY_LIFECYCLE_TOKEN_KEY Ljava/lang/String; + public static final field SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY Ljava/lang/String; public fun ()V public fun (Lio/sentry/IScopes;)V public fun requestDestroyed (Ljakarta/servlet/ServletRequestEvent;)V diff --git a/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java b/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java index f5b3be30b9..9c8edeaf71 100644 --- a/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java +++ b/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java @@ -23,7 +23,7 @@ @Open public class SentryServletRequestListener implements ServletRequestListener { - public static final String SENTRY_LIFECYCLE_TOKEN_KEY = "sentry-lifecycle"; + public static final String SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY = "sentry-scope-lifecycle"; private final IScopes scopes; @@ -38,15 +38,16 @@ public SentryServletRequestListener() { @Override public void requestDestroyed(@NotNull ServletRequestEvent servletRequestEvent) { final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); - LifecycleHelper.close(servletRequest.getAttribute(SENTRY_LIFECYCLE_TOKEN_KEY)); + LifecycleHelper.close(servletRequest.getAttribute(SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY)); } @Override public void requestInitialized(@NotNull ServletRequestEvent servletRequestEvent) { - final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); + final @NotNull ISentryLifecycleToken lifecycleToken = + scopes.forkedScopes("SentryServletRequestListener").makeCurrent(); final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); - servletRequest.setAttribute(SENTRY_LIFECYCLE_TOKEN_KEY, lifecycleToken); + servletRequest.setAttribute(SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY, lifecycleToken); if (servletRequest instanceof HttpServletRequest) { final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; diff --git a/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt b/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt index 30ef3da1ed..322c410117 100644 --- a/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt +++ b/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt @@ -4,6 +4,7 @@ import io.sentry.Breadcrumb import io.sentry.IScopes import io.sentry.ISentryLifecycleToken import jakarta.servlet.ServletRequestEvent +import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.check import org.mockito.kotlin.eq @@ -28,7 +29,8 @@ class SentryServletRequestListenerTest { init { whenever(event.servletRequest).thenReturn(request) - whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) + whenever(scopes.forkedScopes(any())).thenReturn(scopes) + whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) } } @@ -38,7 +40,8 @@ class SentryServletRequestListenerTest { fun `pushes scope when request gets initialized`() { fixture.listener.requestInitialized(fixture.event) - verify(fixture.scopes).pushIsolationScope() + verify(fixture.scopes).forkedScopes(any()) + verify(fixture.scopes).makeCurrent() } @Test @@ -53,12 +56,12 @@ class SentryServletRequestListenerTest { }, anyOrNull() ) - verify(fixture.request).setAttribute(eq("sentry-lifecycle"), same(fixture.lifecycleToken)) + verify(fixture.request).setAttribute(eq("sentry-scope-lifecycle"), same(fixture.lifecycleToken)) } @Test fun `pops scope when request gets destroyed`() { - whenever(fixture.request.getAttribute(eq("sentry-lifecycle"))).thenReturn(fixture.lifecycleToken) + whenever(fixture.request.getAttribute(eq("sentry-scope-lifecycle"))).thenReturn(fixture.lifecycleToken) fixture.listener.requestDestroyed(fixture.event) verify(fixture.lifecycleToken).close() diff --git a/sentry-servlet/api/sentry-servlet.api b/sentry-servlet/api/sentry-servlet.api index 63d3cf4b33..3bbffa1b5d 100644 --- a/sentry-servlet/api/sentry-servlet.api +++ b/sentry-servlet/api/sentry-servlet.api @@ -9,7 +9,7 @@ public class io/sentry/servlet/SentryServletContainerInitializer : javax/servlet } public class io/sentry/servlet/SentryServletRequestListener : javax/servlet/ServletRequestListener { - public static final field SENTRY_LIFECYCLE_TOKEN_KEY Ljava/lang/String; + public static final field SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY Ljava/lang/String; public fun ()V public fun (Lio/sentry/IScopes;)V public fun requestDestroyed (Ljavax/servlet/ServletRequestEvent;)V diff --git a/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java b/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java index 4587daa655..0a2a2f5d23 100644 --- a/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java +++ b/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java @@ -23,7 +23,7 @@ @Open public class SentryServletRequestListener implements ServletRequestListener { - public static final String SENTRY_LIFECYCLE_TOKEN_KEY = "sentry-lifecycle"; + public static final String SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY = "sentry-scope-lifecycle"; private final IScopes scopes; @@ -38,15 +38,16 @@ public SentryServletRequestListener() { @Override public void requestDestroyed(@NotNull ServletRequestEvent servletRequestEvent) { final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); - LifecycleHelper.close(servletRequest.getAttribute(SENTRY_LIFECYCLE_TOKEN_KEY)); + LifecycleHelper.close(servletRequest.getAttribute(SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY)); } @Override public void requestInitialized(@NotNull ServletRequestEvent servletRequestEvent) { - final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); + final @NotNull ISentryLifecycleToken lifecycleToken = + scopes.forkedScopes("SentryServletRequestListener").makeCurrent(); final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); - servletRequest.setAttribute(SENTRY_LIFECYCLE_TOKEN_KEY, lifecycleToken); + servletRequest.setAttribute(SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY, lifecycleToken); if (servletRequest instanceof HttpServletRequest) { final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; diff --git a/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt b/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt index d72b93179f..07327599d3 100644 --- a/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt +++ b/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt @@ -4,6 +4,7 @@ import io.sentry.Breadcrumb import io.sentry.IScopes import io.sentry.ISentryLifecycleToken import org.assertj.core.api.Assertions.assertThat +import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.check import org.mockito.kotlin.mock @@ -26,7 +27,8 @@ class SentryServletRequestListenerTest { request.requestURI = "http://localhost:8080/some-uri" request.method = "post" whenever(event.servletRequest).thenReturn(request) - whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) + whenever(scopes.forkedScopes(any())).thenReturn(scopes) + whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) } } @@ -36,7 +38,8 @@ class SentryServletRequestListenerTest { fun `pushes scope when request gets initialized`() { fixture.listener.requestInitialized(fixture.event) - verify(fixture.scopes).pushIsolationScope() + verify(fixture.scopes).forkedScopes(any()) + verify(fixture.scopes).makeCurrent() } @Test @@ -51,12 +54,12 @@ class SentryServletRequestListenerTest { }, anyOrNull() ) - assertSame(fixture.lifecycleToken, fixture.request.getAttribute("sentry-lifecycle")) + assertSame(fixture.lifecycleToken, fixture.request.getAttribute("sentry-scope-lifecycle")) } @Test fun `pops scope when request gets destroyed`() { - fixture.request.setAttribute("sentry-lifecycle", fixture.lifecycleToken) + fixture.request.setAttribute("sentry-scope-lifecycle", fixture.lifecycleToken) fixture.listener.requestDestroyed(fixture.event) verify(fixture.lifecycleToken).close() diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java index de8b5bce65..c7573701ec 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java @@ -31,7 +31,7 @@ @Open public class SentrySpringFilter extends OncePerRequestFilter { - private final @NotNull IScopes scopes; + private final @NotNull IScopes scopesBeforeForking; private final @NotNull SentryRequestResolver requestResolver; private final @NotNull TransactionNameProvider transactionNameProvider; @@ -39,7 +39,7 @@ public SentrySpringFilter( final @NotNull IScopes scopes, final @NotNull SentryRequestResolver requestResolver, final @NotNull TransactionNameProvider transactionNameProvider) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + this.scopesBeforeForking = Objects.requireNonNull(scopes, "scopes are required"); this.requestResolver = Objects.requireNonNull(requestResolver, "requestResolver is required"); this.transactionNameProvider = Objects.requireNonNull(transactionNameProvider, "transactionNameProvider is required"); @@ -59,27 +59,28 @@ protected void doFilterInternal( final @NotNull HttpServletResponse response, final @NotNull FilterChain filterChain) throws ServletException, IOException { - if (scopes.isEnabled()) { + if (scopesBeforeForking.isEnabled()) { // request may qualify for caching request body, if so resolve cached request - final HttpServletRequest request = resolveHttpServletRequest(servletRequest); - final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); - try { + final HttpServletRequest request = + resolveHttpServletRequest(scopesBeforeForking, servletRequest); + final @NotNull IScopes forkedScopes = scopesBeforeForking.forkedScopes("SentrySpringFilter"); + try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) { final Hint hint = new Hint(); hint.set(SPRING_REQUEST_FILTER_REQUEST, servletRequest); hint.set(SPRING_REQUEST_FILTER_RESPONSE, response); - scopes.addBreadcrumb(Breadcrumb.http(request.getRequestURI(), request.getMethod()), hint); - configureScope(request); + forkedScopes.addBreadcrumb( + Breadcrumb.http(request.getRequestURI(), request.getMethod()), hint); + configureScope(forkedScopes, request); filterChain.doFilter(request, response); - } finally { - lifecycleToken.close(); } } else { filterChain.doFilter(servletRequest, response); } } - private void configureScope(HttpServletRequest request) { + private void configureScope( + final @NotNull IScopes scopes, final @NotNull HttpServletRequest request) { try { scopes.configureScope( scope -> { @@ -106,7 +107,7 @@ private void configureScope(HttpServletRequest request) { } private @NotNull HttpServletRequest resolveHttpServletRequest( - final @NotNull HttpServletRequest request) { + final @NotNull IScopes scopes, final @NotNull HttpServletRequest request) { if (scopes.getOptions().isSendDefaultPii() && qualifiesForCaching(request, scopes.getOptions().getMaxRequestBodySize())) { try { diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java index 42c35919d7..ba75795260 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java @@ -9,16 +9,14 @@ import org.springframework.scheduling.annotation.Async; /** - * Sets a current scopes on a thread running a {@link Runnable} given by parameter. Used to - * propagate the current {@link IScopes} on the thread executing async task - like MVC controller - * methods returning a {@link Callable} or Spring beans methods annotated with {@link Async}. + * Forks scopes for a thread running a {@link Runnable} given by parameter. Used to propagate the + * current {@link IScopes} on the thread executing async task - like MVC controller methods + * returning a {@link Callable} or Spring beans methods annotated with {@link Async}. */ public final class SentryTaskDecorator implements TaskDecorator { @Override - // TODO [HSM] should there also be a SentryIsolatedTaskDecorator or similar that uses - // forkedScopes()? public @NotNull Runnable decorate(final @NotNull Runnable runnable) { - final IScopes newScopes = Sentry.getCurrentScopes().forkedCurrentScope("spring.taskDecorator"); + final IScopes newScopes = Sentry.getCurrentScopes().forkedScopes("SentryTaskDecorator"); return () -> { try (final @NotNull ISentryLifecycleToken ignored = newScopes.makeCurrent()) { diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/checkin/SentryCheckInAdvice.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/checkin/SentryCheckInAdvice.java index e51647cba8..dd22f4dc5d 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/checkin/SentryCheckInAdvice.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/checkin/SentryCheckInAdvice.java @@ -87,27 +87,28 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl return invocation.proceed(); } - final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); - TracingUtils.startNewTrace(scopes); + try (final @NotNull ISentryLifecycleToken ignored = + scopes.forkedScopes("SentryCheckInAdvice").makeCurrent()) { + TracingUtils.startNewTrace(scopes); - @Nullable SentryId checkInId = null; - final long startTime = System.currentTimeMillis(); - boolean didError = false; + @Nullable SentryId checkInId = null; + final long startTime = System.currentTimeMillis(); + boolean didError = false; - try { - if (!isHeartbeatOnly) { - checkInId = scopes.captureCheckIn(new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS)); + try { + if (!isHeartbeatOnly) { + checkInId = scopes.captureCheckIn(new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS)); + } + return invocation.proceed(); + } catch (Throwable e) { + didError = true; + throw e; + } finally { + final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK; + CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); + checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); + scopes.captureCheckIn(checkIn); } - return invocation.proceed(); - } catch (Throwable e) { - didError = true; - throw e; - } finally { - final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK; - CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); - checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); - scopes.captureCheckIn(checkIn); - lifecycleToken.close(); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java index da781afcd8..c85831ae8f 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java @@ -29,14 +29,14 @@ public class SentryTransactionAdvice implements MethodInterceptor { private static final String TRACE_ORIGIN = "auto.function.spring_jakarta.advice"; - private final @NotNull IScopes scopes; + private final @NotNull IScopes scopesBeforeForking; public SentryTransactionAdvice() { this(ScopesAdapter.getInstance()); } public SentryTransactionAdvice(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + this.scopesBeforeForking = Objects.requireNonNull(scopes, "scopes are required"); } @SuppressWarnings("deprecation") @@ -57,7 +57,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl final TransactionNameAndSource nameAndSource = resolveTransactionName(invocation, sentryTransaction); - final boolean isTransactionActive = isTransactionActive(); + final boolean isTransactionActive = isTransactionActive(scopesBeforeForking); if (isTransactionActive) { // transaction is already active, we do not start new transaction @@ -69,25 +69,27 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } else { operation = "bean"; } - final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); - final TransactionOptions transactionOptions = new TransactionOptions(); - transactionOptions.setBindToScope(true); - final ITransaction transaction = - scopes.startTransaction( - new TransactionContext(nameAndSource.name, nameAndSource.source, operation), - transactionOptions); - transaction.getSpanContext().setOrigin(TRACE_ORIGIN); - try { - final Object result = invocation.proceed(); - transaction.setStatus(SpanStatus.OK); - return result; - } catch (Throwable e) { - transaction.setStatus(SpanStatus.INTERNAL_ERROR); - transaction.setThrowable(e); - throw e; - } finally { - transaction.finish(); - lifecycleToken.close(); + final @NotNull IScopes forkedScopes = + scopesBeforeForking.forkedScopes("SentryTransactionAdvice"); + try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) { + final TransactionOptions transactionOptions = new TransactionOptions(); + transactionOptions.setBindToScope(true); + final ITransaction transaction = + forkedScopes.startTransaction( + new TransactionContext(nameAndSource.name, nameAndSource.source, operation), + transactionOptions); + transaction.getSpanContext().setOrigin(TRACE_ORIGIN); + try { + final Object result = invocation.proceed(); + transaction.setStatus(SpanStatus.OK); + return result; + } catch (Throwable e) { + transaction.setStatus(SpanStatus.INTERNAL_ERROR); + transaction.setThrowable(e); + throw e; + } finally { + transaction.finish(); + } } } } @@ -106,7 +108,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } } - private boolean isTransactionActive() { + private boolean isTransactionActive(final @NotNull IScopes scopes) { return scopes.getSpan() != null; } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java index 0be67c9f5a..1c2bb0afcf 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java @@ -8,10 +8,7 @@ import reactor.core.publisher.Mono; import reactor.util.context.Context; -// TODO deprecate and replace with "withSentryScopes" etc. @ApiStatus.Experimental -// TODO [HSM] do we keep old methods around and deprecate them? -// TODO [HSM] do we need to offer isolated variants? public final class ReactorUtils { /** @@ -29,7 +26,7 @@ public static Mono withSentry(final @NotNull Mono mono) { } /** - * Writes a new Sentry {@link IScopes} cloned from the main scopes to the {@link Context} and uses + * Writes a new Sentry {@link IScopes} forked from the main scopes to the {@link Context} and uses * {@link io.micrometer.context.ThreadLocalAccessor} to propagate it. * *

    This requires - reactor.core.publisher.Hooks#enableAutomaticContextPropagation() to be @@ -77,7 +74,7 @@ public static Flux withSentry(final @NotNull Flux flux) { } /** - * Writes a new Sentry {@link IScopes} cloned from the main scopes to the {@link Context} and uses + * Writes a new Sentry {@link IScopes} forked from the main scopes to the {@link Context} and uses * {@link io.micrometer.context.ThreadLocalAccessor} to propagate it. * *

    This requires - reactor.core.publisher.Hooks#enableAutomaticContextPropagation() to be @@ -100,7 +97,7 @@ public static Flux withSentryForkedRoots(final @NotNull Flux flux) { public static Flux withSentryScopes( final @NotNull Flux flux, final @NotNull IScopes scopes) { /** - * WARNING: Cannot set the scopes as current. It would be used by others to clone again causing + * WARNING: Cannot set the scopes as current. It would be used by others to fork again causing * shared scopes and thus leading to issues like unrelated breadcrumbs showing up in events. */ // Sentry.setCurrentScopes(forkedScopes); diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryCheckInAdviceTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryCheckInAdviceTest.kt index e87b5f5b26..e02f1cb62a 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryCheckInAdviceTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryCheckInAdviceTest.kt @@ -63,7 +63,8 @@ class SentryCheckInAdviceTest { fun setup() { reset(scopes) whenever(scopes.options).thenReturn(SentryOptions()) - whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) + whenever(scopes.forkedScopes(any())).thenReturn(scopes) + whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) } @Test @@ -84,7 +85,8 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).pushIsolationScope() + order.verify(scopes).forkedScopes(any()) + order.verify(scopes).makeCurrent() order.verify(scopes, times(2)).captureCheckIn(any()) order.verify(lifecycleToken).close() } @@ -108,7 +110,8 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.ERROR.apiName(), doneCheckIn.status) val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).pushIsolationScope() + order.verify(scopes).forkedScopes(any()) + order.verify(scopes).makeCurrent() order.verify(scopes, times(2)).captureCheckIn(any()) order.verify(lifecycleToken).close() } @@ -128,7 +131,8 @@ class SentryCheckInAdviceTest { assertNotNull(doneCheckIn.duration) val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).pushIsolationScope() + order.verify(scopes).forkedScopes(any()) + order.verify(scopes).makeCurrent() order.verify(scopes).captureCheckIn(any()) order.verify(lifecycleToken).close() } @@ -149,7 +153,8 @@ class SentryCheckInAdviceTest { assertNotNull(doneCheckIn.duration) val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).pushIsolationScope() + order.verify(scopes).forkedScopes(any()) + order.verify(scopes).makeCurrent() order.verify(scopes).captureCheckIn(any()) order.verify(lifecycleToken).close() } @@ -163,9 +168,10 @@ class SentryCheckInAdviceTest { assertEquals(1, result) assertEquals(0, checkInCaptor.allValues.size) - verify(scopes, never()).pushScope() + verify(scopes, never()).forkedScopes(any()) + verify(scopes, never()).makeCurrent() verify(scopes, never()).captureCheckIn(any()) - verify(scopes, never()).popScope() + verify(lifecycleToken, never()).close() } @Test @@ -183,7 +189,8 @@ class SentryCheckInAdviceTest { assertNotNull(doneCheckIn.duration) val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).pushIsolationScope() + order.verify(scopes).forkedScopes(any()) + order.verify(scopes).makeCurrent() order.verify(scopes).captureCheckIn(any()) order.verify(lifecycleToken).close() } @@ -203,7 +210,8 @@ class SentryCheckInAdviceTest { assertNotNull(doneCheckIn.duration) val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).pushIsolationScope() + order.verify(scopes).forkedScopes(any()) + order.verify(scopes).makeCurrent() order.verify(scopes).captureCheckIn(any()) order.verify(lifecycleToken).close() } @@ -223,7 +231,8 @@ class SentryCheckInAdviceTest { assertNotNull(doneCheckIn.duration) val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).pushIsolationScope() + order.verify(scopes).forkedScopes(any()) + order.verify(scopes).makeCurrent() order.verify(scopes).captureCheckIn(any()) order.verify(lifecycleToken).close() } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentrySpringFilterTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentrySpringFilterTest.kt index ac394deb31..1c1f2b5c13 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentrySpringFilterTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentrySpringFilterTest.kt @@ -39,6 +39,7 @@ import kotlin.test.fail class SentrySpringFilterTest { private class Fixture { val scopes = mock() + val scopesBeforeForking = mock() val response = MockHttpServletResponse() val lifecycleToken = mock() val chain = mock() @@ -47,16 +48,19 @@ class SentrySpringFilterTest { fun getSut(request: HttpServletRequest? = null, options: SentryOptions = SentryOptions()): SentrySpringFilter { scope = Scope(options) + whenever(scopesBeforeForking.options).thenReturn(options) + whenever(scopesBeforeForking.isEnabled).thenReturn(true) whenever(scopes.options).thenReturn(options) whenever(scopes.isEnabled).thenReturn(true) - whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) + whenever(scopesBeforeForking.forkedScopes(any())).thenReturn(scopes) + whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) this.request = request ?: MockHttpServletRequest().apply { this.requestURI = "http://localhost:8080/some-uri" this.method = "post" } - return SentrySpringFilter(scopes) + return SentrySpringFilter(scopesBeforeForking) } } @@ -67,7 +71,8 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).pushIsolationScope() + verify(fixture.scopesBeforeForking).forkedScopes(any()) + verify(fixture.scopes).makeCurrent() } @Test diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt index b0f83782e0..978c5baa63 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt @@ -58,7 +58,8 @@ class SentryTransactionAdviceTest { dsn = "https://key@sentry.io/proj" } ) - whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) + whenever(scopes.forkedScopes(any())).thenReturn(scopes) + whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) } @Test @@ -145,7 +146,8 @@ class SentryTransactionAdviceTest { @Test fun `pushes the scope when advice starts`() { classAnnotatedSampleService.hello() - verify(scopes).pushIsolationScope() + verify(scopes).forkedScopes(any()) + verify(scopes).makeCurrent() } @Test diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java b/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java index af55ac2ce3..d450e1451a 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java @@ -31,7 +31,7 @@ @Open public class SentrySpringFilter extends OncePerRequestFilter { - private final @NotNull IScopes scopes; + private final @NotNull IScopes scopesBeforeForking; private final @NotNull SentryRequestResolver requestResolver; private final @NotNull TransactionNameProvider transactionNameProvider; @@ -39,7 +39,7 @@ public SentrySpringFilter( final @NotNull IScopes scopes, final @NotNull SentryRequestResolver requestResolver, final @NotNull TransactionNameProvider transactionNameProvider) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + this.scopesBeforeForking = Objects.requireNonNull(scopes, "scopes are required"); this.requestResolver = Objects.requireNonNull(requestResolver, "requestResolver is required"); this.transactionNameProvider = Objects.requireNonNull(transactionNameProvider, "transactionNameProvider is required"); @@ -59,27 +59,28 @@ protected void doFilterInternal( final @NotNull HttpServletResponse response, final @NotNull FilterChain filterChain) throws ServletException, IOException { - if (scopes.isEnabled()) { + if (scopesBeforeForking.isEnabled()) { // request may qualify for caching request body, if so resolve cached request - final HttpServletRequest request = resolveHttpServletRequest(servletRequest); - final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); - try { + final HttpServletRequest request = + resolveHttpServletRequest(scopesBeforeForking, servletRequest); + final @NotNull IScopes forkedScopes = scopesBeforeForking.forkedScopes("SentrySpringFilter"); + try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) { final Hint hint = new Hint(); hint.set(SPRING_REQUEST_FILTER_REQUEST, servletRequest); hint.set(SPRING_REQUEST_FILTER_RESPONSE, response); - scopes.addBreadcrumb(Breadcrumb.http(request.getRequestURI(), request.getMethod()), hint); - configureScope(request); + forkedScopes.addBreadcrumb( + Breadcrumb.http(request.getRequestURI(), request.getMethod()), hint); + configureScope(forkedScopes, request); filterChain.doFilter(request, response); - } finally { - lifecycleToken.close(); } } else { filterChain.doFilter(servletRequest, response); } } - private void configureScope(HttpServletRequest request) { + private void configureScope( + final @NotNull IScopes scopes, final @NotNull HttpServletRequest request) { try { scopes.configureScope( scope -> { @@ -106,7 +107,7 @@ private void configureScope(HttpServletRequest request) { } private @NotNull HttpServletRequest resolveHttpServletRequest( - final @NotNull HttpServletRequest request) { + final @NotNull IScopes scopes, final @NotNull HttpServletRequest request) { if (scopes.getOptions().isSendDefaultPii() && qualifiesForCaching(request, scopes.getOptions().getMaxRequestBodySize())) { try { diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java b/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java index 2968eede43..3b0960cb44 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java @@ -9,17 +9,15 @@ import org.springframework.scheduling.annotation.Async; /** - * Forks current scope for the thread running a {@link Runnable} given by parameter. Used to - * propagate the current {@link IScopes} on the thread executing async task - like MVC controller - * methods returning a {@link Callable} or Spring beans methods annotated with {@link Async}. + * Forks scopes for the thread running a {@link Runnable} given by parameter. Used to propagate the + * current {@link IScopes} on the thread executing async task - like MVC controller methods + * returning a {@link Callable} or Spring beans methods annotated with {@link Async}. */ public final class SentryTaskDecorator implements TaskDecorator { @Override - // TODO [HSM] should there also be a SentryIsolatedTaskDecorator or similar that uses - // forkedScopes()? public @NotNull Runnable decorate(final @NotNull Runnable runnable) { - final IScopes forkedScopes = - Sentry.getCurrentScopes().forkedCurrentScope("spring.taskDecorator"); + final @NotNull IScopes forkedScopes = + Sentry.getCurrentScopes().forkedScopes("SentryTaskDecorator"); return () -> { try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) { diff --git a/sentry-spring/src/main/java/io/sentry/spring/checkin/SentryCheckInAdvice.java b/sentry-spring/src/main/java/io/sentry/spring/checkin/SentryCheckInAdvice.java index 9e59093b16..58e7e9430e 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/checkin/SentryCheckInAdvice.java +++ b/sentry-spring/src/main/java/io/sentry/spring/checkin/SentryCheckInAdvice.java @@ -90,27 +90,28 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl return invocation.proceed(); } - final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); - TracingUtils.startNewTrace(scopes); + try (final @NotNull ISentryLifecycleToken ignored = + scopes.forkedScopes("SentryCheckInAdvice").makeCurrent()) { + TracingUtils.startNewTrace(scopes); - @Nullable SentryId checkInId = null; - final long startTime = System.currentTimeMillis(); - boolean didError = false; + @Nullable SentryId checkInId = null; + final long startTime = System.currentTimeMillis(); + boolean didError = false; - try { - if (!isHeartbeatOnly) { - checkInId = scopes.captureCheckIn(new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS)); + try { + if (!isHeartbeatOnly) { + checkInId = scopes.captureCheckIn(new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS)); + } + return invocation.proceed(); + } catch (Throwable e) { + didError = true; + throw e; + } finally { + final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK; + CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); + checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); + scopes.captureCheckIn(checkIn); } - return invocation.proceed(); - } catch (Throwable e) { - didError = true; - throw e; - } finally { - final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK; - CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); - checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); - scopes.captureCheckIn(checkIn); - lifecycleToken.close(); } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java index a885510fcd..e293eb0b9c 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java @@ -28,14 +28,14 @@ @Open public class SentryTransactionAdvice implements MethodInterceptor { private static final String TRACE_ORIGIN = "auto.function.spring.advice"; - private final @NotNull IScopes scopes; + private final @NotNull IScopes scopesBeforeForking; public SentryTransactionAdvice() { this(ScopesAdapter.getInstance()); } public SentryTransactionAdvice(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + this.scopesBeforeForking = Objects.requireNonNull(scopes, "scopes are required"); } @SuppressWarnings("deprecation") @@ -56,7 +56,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl final TransactionNameAndSource nameAndSource = resolveTransactionName(invocation, sentryTransaction); - final boolean isTransactionActive = isTransactionActive(); + final boolean isTransactionActive = isTransactionActive(scopesBeforeForking); if (isTransactionActive) { // transaction is already active, we do not start new transaction @@ -68,25 +68,27 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } else { operation = "bean"; } - final @NotNull ISentryLifecycleToken lifecycleToken = scopes.pushIsolationScope(); - final TransactionOptions transactionOptions = new TransactionOptions(); - transactionOptions.setBindToScope(true); - final ITransaction transaction = - scopes.startTransaction( - new TransactionContext(nameAndSource.name, nameAndSource.source, operation), - transactionOptions); - transaction.getSpanContext().setOrigin(TRACE_ORIGIN); - try { - final Object result = invocation.proceed(); - transaction.setStatus(SpanStatus.OK); - return result; - } catch (Throwable e) { - transaction.setStatus(SpanStatus.INTERNAL_ERROR); - transaction.setThrowable(e); - throw e; - } finally { - transaction.finish(); - lifecycleToken.close(); + final @NotNull IScopes forkedScopes = + scopesBeforeForking.forkedScopes("SentryTransactionAdvice"); + try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) { + final TransactionOptions transactionOptions = new TransactionOptions(); + transactionOptions.setBindToScope(true); + final ITransaction transaction = + forkedScopes.startTransaction( + new TransactionContext(nameAndSource.name, nameAndSource.source, operation), + transactionOptions); + transaction.getSpanContext().setOrigin(TRACE_ORIGIN); + try { + final Object result = invocation.proceed(); + transaction.setStatus(SpanStatus.OK); + return result; + } catch (Throwable e) { + transaction.setStatus(SpanStatus.INTERNAL_ERROR); + transaction.setThrowable(e); + throw e; + } finally { + transaction.finish(); + } } } } @@ -105,7 +107,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } } - private boolean isTransactionActive() { + private boolean isTransactionActive(final @NotNull IScopes scopes) { return scopes.getSpan() != null; } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryCheckInAdviceTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryCheckInAdviceTest.kt index 57bd293756..6e18ef64f9 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryCheckInAdviceTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryCheckInAdviceTest.kt @@ -64,7 +64,8 @@ class SentryCheckInAdviceTest { fun setup() { reset(scopes) whenever(scopes.options).thenReturn(SentryOptions()) - whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) + whenever(scopes.forkedScopes(any())).thenReturn(scopes) + whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) } @Test @@ -85,7 +86,8 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).pushIsolationScope() + order.verify(scopes).forkedScopes(any()) + order.verify(scopes).makeCurrent() order.verify(scopes, times(2)).captureCheckIn(any()) order.verify(lifecycleToken).close() } @@ -109,7 +111,8 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.ERROR.apiName(), doneCheckIn.status) val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).pushIsolationScope() + order.verify(scopes).forkedScopes(any()) + order.verify(scopes).makeCurrent() order.verify(scopes, times(2)).captureCheckIn(any()) order.verify(lifecycleToken).close() } @@ -129,7 +132,8 @@ class SentryCheckInAdviceTest { assertNotNull(doneCheckIn.duration) val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).pushIsolationScope() + order.verify(scopes).forkedScopes(any()) + order.verify(scopes).makeCurrent() order.verify(scopes).captureCheckIn(any()) order.verify(lifecycleToken).close() } @@ -150,7 +154,8 @@ class SentryCheckInAdviceTest { assertNotNull(doneCheckIn.duration) val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).pushIsolationScope() + order.verify(scopes).forkedScopes(any()) + order.verify(scopes).makeCurrent() order.verify(scopes).captureCheckIn(any()) order.verify(lifecycleToken).close() } @@ -164,7 +169,8 @@ class SentryCheckInAdviceTest { assertEquals(1, result) assertEquals(0, checkInCaptor.allValues.size) - verify(scopes, never()).pushIsolationScope() + verify(scopes, never()).forkedScopes(any()) + verify(scopes, never()).makeCurrent() verify(scopes, never()).captureCheckIn(any()) verify(lifecycleToken, never()).close() } @@ -184,7 +190,8 @@ class SentryCheckInAdviceTest { assertNotNull(doneCheckIn.duration) val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).pushIsolationScope() + order.verify(scopes).forkedScopes(any()) + order.verify(scopes).makeCurrent() order.verify(scopes).captureCheckIn(any()) order.verify(lifecycleToken).close() } @@ -204,7 +211,8 @@ class SentryCheckInAdviceTest { assertNotNull(doneCheckIn.duration) val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).pushIsolationScope() + order.verify(scopes).forkedScopes(any()) + order.verify(scopes).makeCurrent() order.verify(scopes).captureCheckIn(any()) order.verify(lifecycleToken).close() } @@ -224,7 +232,8 @@ class SentryCheckInAdviceTest { assertNotNull(doneCheckIn.duration) val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).pushIsolationScope() + order.verify(scopes).forkedScopes(any()) + order.verify(scopes).makeCurrent() order.verify(scopes).captureCheckIn(any()) order.verify(lifecycleToken).close() } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringFilterTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringFilterTest.kt index 6037e253c8..f4dce4cad0 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringFilterTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringFilterTest.kt @@ -39,6 +39,7 @@ import kotlin.test.fail class SentrySpringFilterTest { private class Fixture { val scopes = mock() + val scopesBeforeForking = mock() val response = MockHttpServletResponse() val lifecycleToken = mock() val chain = mock() @@ -47,16 +48,19 @@ class SentrySpringFilterTest { fun getSut(request: HttpServletRequest? = null, options: SentryOptions = SentryOptions()): SentrySpringFilter { scope = Scope(options) + whenever(scopesBeforeForking.options).thenReturn(options) + whenever(scopesBeforeForking.isEnabled).thenReturn(true) whenever(scopes.options).thenReturn(options) whenever(scopes.isEnabled).thenReturn(true) - whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) + whenever(scopesBeforeForking.forkedScopes(any())).thenReturn(scopes) + whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) this.request = request ?: MockHttpServletRequest().apply { this.requestURI = "http://localhost:8080/some-uri" this.method = "post" } - return SentrySpringFilter(scopes) + return SentrySpringFilter(scopesBeforeForking) } } @@ -67,7 +71,8 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).pushIsolationScope() + verify(fixture.scopesBeforeForking).forkedScopes(any()) + verify(fixture.scopes).makeCurrent() } @Test diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt index 8a3d8ee46c..3c35bad8e4 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt @@ -58,7 +58,8 @@ class SentryTransactionAdviceTest { dsn = "https://key@sentry.io/proj" } ) - whenever(scopes.pushIsolationScope()).thenReturn(lifecycleToken) + whenever(scopes.forkedScopes(any())).thenReturn(scopes) + whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) } @Test @@ -145,7 +146,8 @@ class SentryTransactionAdviceTest { @Test fun `pushes the scope when advice starts`() { classAnnotatedSampleService.hello() - verify(scopes).pushIsolationScope() + verify(scopes).forkedScopes(any()) + verify(scopes).makeCurrent() } @Test diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 32d530a5a1..42446f8e58 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -259,12 +259,12 @@ public final class io/sentry/CombinedScopeView : io/sentry/IScope { public fun getClient ()Lio/sentry/ISentryClient; public fun getContexts ()Lio/sentry/protocol/Contexts; public fun getEventProcessors ()Ljava/util/List; + public fun getEventProcessorsWithOrder ()Ljava/util/List; public fun getExtras ()Ljava/util/Map; public fun getFingerprint ()Ljava/util/List; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getLevel ()Lio/sentry/SentryLevel; public fun getOptions ()Lio/sentry/SentryOptions; - public fun getOrderedEventProcessors ()Ljava/util/List; public fun getPropagationContext ()Lio/sentry/PropagationContext; public fun getRequest ()Lio/sentry/protocol/Request; public fun getScreen ()Ljava/lang/String; @@ -579,6 +579,7 @@ public final class io/sentry/Hub : io/sentry/IHub, io/sentry/metrics/MetricsApi$ public fun startSpanForMetric (Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ISpan; public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; public fun traceHeaders ()Lio/sentry/SentryTraceHeader; + public fun withIsolationScope (Lio/sentry/ScopeCallback;)V public fun withScope (Lio/sentry/ScopeCallback;)V } @@ -640,6 +641,7 @@ public final class io/sentry/HubAdapter : io/sentry/IHub { public fun startSession ()V public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; public fun traceHeaders ()Lio/sentry/SentryTraceHeader; + public fun withIsolationScope (Lio/sentry/ScopeCallback;)V public fun withScope (Lio/sentry/ScopeCallback;)V } @@ -701,6 +703,7 @@ public final class io/sentry/HubScopesWrapper : io/sentry/IHub { public fun startSession ()V public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; public fun traceHeaders ()Lio/sentry/SentryTraceHeader; + public fun withIsolationScope (Lio/sentry/ScopeCallback;)V public fun withScope (Lio/sentry/ScopeCallback;)V } @@ -797,12 +800,12 @@ public abstract interface class io/sentry/IScope { public abstract fun getClient ()Lio/sentry/ISentryClient; public abstract fun getContexts ()Lio/sentry/protocol/Contexts; public abstract fun getEventProcessors ()Ljava/util/List; + public abstract fun getEventProcessorsWithOrder ()Ljava/util/List; public abstract fun getExtras ()Ljava/util/Map; public abstract fun getFingerprint ()Ljava/util/List; public abstract fun getLastEventId ()Lio/sentry/protocol/SentryId; public abstract fun getLevel ()Lio/sentry/SentryLevel; public abstract fun getOptions ()Lio/sentry/SentryOptions; - public abstract fun getOrderedEventProcessors ()Ljava/util/List; public abstract fun getPropagationContext ()Lio/sentry/PropagationContext; public abstract fun getRequest ()Lio/sentry/protocol/Request; public abstract fun getScreen ()Ljava/lang/String; @@ -933,6 +936,7 @@ public abstract interface class io/sentry/IScopes { public fun startTransaction (Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ITransaction; public fun startTransaction (Ljava/lang/String;Ljava/lang/String;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; public abstract fun traceHeaders ()Lio/sentry/SentryTraceHeader; + public abstract fun withIsolationScope (Lio/sentry/ScopeCallback;)V public abstract fun withScope (Lio/sentry/ScopeCallback;)V } @@ -1428,6 +1432,7 @@ public final class io/sentry/NoOpHub : io/sentry/IHub { public fun startSession ()V public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; public fun traceHeaders ()Lio/sentry/SentryTraceHeader; + public fun withIsolationScope (Lio/sentry/ScopeCallback;)V public fun withScope (Lio/sentry/ScopeCallback;)V } @@ -1458,13 +1463,13 @@ public final class io/sentry/NoOpScope : io/sentry/IScope { public fun getClient ()Lio/sentry/ISentryClient; public fun getContexts ()Lio/sentry/protocol/Contexts; public fun getEventProcessors ()Ljava/util/List; + public fun getEventProcessorsWithOrder ()Ljava/util/List; public fun getExtras ()Ljava/util/Map; public fun getFingerprint ()Ljava/util/List; public static fun getInstance ()Lio/sentry/NoOpScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getLevel ()Lio/sentry/SentryLevel; public fun getOptions ()Lio/sentry/SentryOptions; - public fun getOrderedEventProcessors ()Ljava/util/List; public fun getPropagationContext ()Lio/sentry/PropagationContext; public fun getRequest ()Lio/sentry/protocol/Request; public fun getScreen ()Ljava/lang/String; @@ -1562,6 +1567,7 @@ public final class io/sentry/NoOpScopes : io/sentry/IScopes { public fun startSession ()V public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; public fun traceHeaders ()Lio/sentry/SentryTraceHeader; + public fun withIsolationScope (Lio/sentry/ScopeCallback;)V public fun withScope (Lio/sentry/ScopeCallback;)V } @@ -1903,12 +1909,12 @@ public final class io/sentry/Scope : io/sentry/IScope { public fun getClient ()Lio/sentry/ISentryClient; public fun getContexts ()Lio/sentry/protocol/Contexts; public fun getEventProcessors ()Ljava/util/List; + public fun getEventProcessorsWithOrder ()Ljava/util/List; public fun getExtras ()Ljava/util/Map; public fun getFingerprint ()Ljava/util/List; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getLevel ()Lio/sentry/SentryLevel; public fun getOptions ()Lio/sentry/SentryOptions; - public fun getOrderedEventProcessors ()Ljava/util/List; public fun getPropagationContext ()Lio/sentry/PropagationContext; public fun getRequest ()Lio/sentry/protocol/Request; public fun getScreen ()Ljava/lang/String; @@ -2023,7 +2029,6 @@ public final class io/sentry/Scopes : io/sentry/IScopes, io/sentry/metrics/Metri public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; public fun getMetricsAggregator ()Lio/sentry/IMetricsAggregator; public fun getOptions ()Lio/sentry/SentryOptions; - public fun getParent ()Lio/sentry/Scopes; public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; public fun getScope ()Lio/sentry/IScope; public fun getSpan ()Lio/sentry/ISpan; @@ -2052,6 +2057,7 @@ public final class io/sentry/Scopes : io/sentry/IScopes, io/sentry/metrics/Metri public fun startSpanForMetric (Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ISpan; public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; public fun traceHeaders ()Lio/sentry/SentryTraceHeader; + public fun withIsolationScope (Lio/sentry/ScopeCallback;)V public fun withScope (Lio/sentry/ScopeCallback;)V } @@ -2113,6 +2119,7 @@ public final class io/sentry/ScopesAdapter : io/sentry/IScopes { public fun startSession ()V public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; public fun traceHeaders ()Lio/sentry/SentryTraceHeader; + public fun withIsolationScope (Lio/sentry/ScopeCallback;)V public fun withScope (Lio/sentry/ScopeCallback;)V } @@ -2218,6 +2225,7 @@ public final class io/sentry/Sentry { public static fun startTransaction (Ljava/lang/String;Ljava/lang/String;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; public static fun startTransaction (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; public static fun traceHeaders ()Lio/sentry/SentryTraceHeader; + public static fun withIsolationScope (Lio/sentry/ScopeCallback;)V public static fun withScope (Lio/sentry/ScopeCallback;)V } diff --git a/sentry/src/main/java/io/sentry/Breadcrumb.java b/sentry/src/main/java/io/sentry/Breadcrumb.java index ddb19e5052..b4e2fadd71 100644 --- a/sentry/src/main/java/io/sentry/Breadcrumb.java +++ b/sentry/src/main/java/io/sentry/Breadcrumb.java @@ -667,11 +667,7 @@ public void setUnknown(@Nullable Map unknown) { @Override @SuppressWarnings("JavaUtilDate") public int compareTo(@NotNull Breadcrumb o) { - int timestampCompare = timestamp.compareTo(o.timestamp); - if (timestampCompare == 0) { - return nanos.compareTo(o.nanos); - } - return timestampCompare; + return nanos.compareTo(o.nanos); } public static final class JsonKeys { diff --git a/sentry/src/main/java/io/sentry/CombinedContextsView.java b/sentry/src/main/java/io/sentry/CombinedContextsView.java index 3dd7428975..40c220bb31 100644 --- a/sentry/src/main/java/io/sentry/CombinedContextsView.java +++ b/sentry/src/main/java/io/sentry/CombinedContextsView.java @@ -54,7 +54,7 @@ public void setTrace(@Nullable SpanContext traceContext) { getDefaultContexts().setTrace(traceContext); } - private Contexts getDefaultContexts() { + private @NotNull Contexts getDefaultContexts() { switch (defaultScopeType) { case CURRENT: return currentContexts; diff --git a/sentry/src/main/java/io/sentry/CombinedScopeView.java b/sentry/src/main/java/io/sentry/CombinedScopeView.java index 66e557e16d..2ed33d56ab 100644 --- a/sentry/src/main/java/io/sentry/CombinedScopeView.java +++ b/sentry/src/main/java/io/sentry/CombinedScopeView.java @@ -358,18 +358,18 @@ public void clearAttachments() { } @Override - public @NotNull List getOrderedEventProcessors() { + public @NotNull List getEventProcessorsWithOrder() { final @NotNull List allEventProcessors = new CopyOnWriteArrayList<>(); - allEventProcessors.addAll(globalScope.getOrderedEventProcessors()); - allEventProcessors.addAll(isolationScope.getOrderedEventProcessors()); - allEventProcessors.addAll(scope.getOrderedEventProcessors()); + allEventProcessors.addAll(globalScope.getEventProcessorsWithOrder()); + allEventProcessors.addAll(isolationScope.getEventProcessorsWithOrder()); + allEventProcessors.addAll(scope.getEventProcessorsWithOrder()); Collections.sort(allEventProcessors); return allEventProcessors; } @Override public @NotNull List getEventProcessors() { - return EventProcessorUtils.unwrap(getOrderedEventProcessors()); + return EventProcessorUtils.unwrap(getEventProcessorsWithOrder()); } @Override diff --git a/sentry/src/main/java/io/sentry/Hub.java b/sentry/src/main/java/io/sentry/Hub.java index bcb93c7558..63081e6cd2 100644 --- a/sentry/src/main/java/io/sentry/Hub.java +++ b/sentry/src/main/java/io/sentry/Hub.java @@ -27,6 +27,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +// TODO [HSM] remove Hub class @Deprecated public final class Hub implements IHub, MetricsApi.IMetricsInterface { @@ -563,6 +564,7 @@ public void reportFullyDisplayed() { } @Override + @Deprecated public void popScope() { if (!isEnabled()) { options @@ -593,6 +595,26 @@ public void withScope(final @NotNull ScopeCallback callback) { } } + @Override + public void withIsolationScope(final @NotNull ScopeCallback callback) { + if (!isEnabled()) { + try { + callback.run(NoOpScope.getInstance()); + } catch (Throwable e) { + options.getLogger().log(SentryLevel.ERROR, "Error in the 'withScope' callback.", e); + } + + } else { + pushScope(); + try { + callback.run(stack.peek().getScope()); + } catch (Throwable e) { + options.getLogger().log(SentryLevel.ERROR, "Error in the 'withScope' callback.", e); + } + popScope(); + } + } + @Override public void configureScope( final @Nullable ScopeType scopeType, final @NotNull ScopeCallback callback) { @@ -679,16 +701,19 @@ public void flush(long timeoutMillis) { } @Override + @ApiStatus.Internal public @NotNull IScope getScope() { return Sentry.getCurrentScopes().getScope(); } @Override + @ApiStatus.Internal public @NotNull IScope getIsolationScope() { return Sentry.getCurrentScopes().getIsolationScope(); } @Override + @ApiStatus.Internal public @NotNull IScope getGlobalScope() { return Sentry.getGlobalScope(); } diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index 266770ddce..df0669504a 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -164,6 +164,7 @@ public void removeExtra(@NotNull String key) { } @Override + @Deprecated public void popScope() { Sentry.popScope(); } @@ -173,6 +174,11 @@ public void withScope(@NotNull ScopeCallback callback) { Sentry.withScope(callback); } + @Override + public void withIsolationScope(@NotNull ScopeCallback callback) { + Sentry.withIsolationScope(callback); + } + @Override public void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback) { Sentry.configureScope(scopeType, callback); @@ -219,16 +225,19 @@ public void flush(long timeoutMillis) { } @Override + @ApiStatus.Internal public @NotNull IScope getScope() { return Sentry.getCurrentScopes().getScope(); } @Override + @ApiStatus.Internal public @NotNull IScope getIsolationScope() { return Sentry.getCurrentScopes().getIsolationScope(); } @Override + @ApiStatus.Internal public @NotNull IScope getGlobalScope() { return Sentry.getGlobalScope(); } diff --git a/sentry/src/main/java/io/sentry/HubScopesWrapper.java b/sentry/src/main/java/io/sentry/HubScopesWrapper.java index 14e9a03e25..4909f6b193 100644 --- a/sentry/src/main/java/io/sentry/HubScopesWrapper.java +++ b/sentry/src/main/java/io/sentry/HubScopesWrapper.java @@ -159,6 +159,7 @@ public void removeExtra(@NotNull String key) { } @Override + @Deprecated public void popScope() { scopes.popScope(); } @@ -168,6 +169,11 @@ public void withScope(@NotNull ScopeCallback callback) { scopes.withScope(callback); } + @Override + public void withIsolationScope(@NotNull ScopeCallback callback) { + scopes.withIsolationScope(callback); + } + @Override public void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback) { scopes.configureScope(scopeType, callback); @@ -214,16 +220,19 @@ public void flush(long timeoutMillis) { } @Override + @ApiStatus.Internal public @NotNull IScope getScope() { return scopes.getScope(); } @Override + @ApiStatus.Internal public @NotNull IScope getIsolationScope() { return scopes.getIsolationScope(); } @Override + @ApiStatus.Internal public @NotNull IScope getGlobalScope() { return Sentry.getGlobalScope(); } diff --git a/sentry/src/main/java/io/sentry/IScope.java b/sentry/src/main/java/io/sentry/IScope.java index 8259a225ac..e7bfd55909 100644 --- a/sentry/src/main/java/io/sentry/IScope.java +++ b/sentry/src/main/java/io/sentry/IScope.java @@ -310,7 +310,7 @@ public interface IScope { @ApiStatus.Internal @NotNull - List getOrderedEventProcessors(); + List getEventProcessorsWithOrder(); /** * Adds an event processor to the Scope's event processors list diff --git a/sentry/src/main/java/io/sentry/IScopes.java b/sentry/src/main/java/io/sentry/IScopes.java index b639836ca1..d6b95574d7 100644 --- a/sentry/src/main/java/io/sentry/IScopes.java +++ b/sentry/src/main/java/io/sentry/IScopes.java @@ -312,12 +312,19 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { @NotNull ISentryLifecycleToken pushIsolationScope(); - /** Removes the first scope */ + /** + * Removes the first scope and restores its parent. + * + * @deprecated please call {@link ISentryLifecycleToken#close()} on the token returned by {@link + * IScopes#pushScope()} or {@link IScopes#pushIsolationScope()} instead. + */ + @Deprecated void popScope(); /** - * Runs the callback with a new scope which gets dropped at the end. If you're using the Sentry - * SDK in globalHubMode (defaults to true on Android) {@link + * Runs the callback with a new current scope which gets dropped at the end. + * + *

    If you're using the Sentry SDK in globalHubMode (defaults to true on Android) {@link * Sentry#init(Sentry.OptionsConfiguration, boolean)} calling withScope is discouraged, as scope * changes may be dropped when executed in parallel. Use {@link * IScopes#configureScope(ScopeCallback)} instead. @@ -326,6 +333,19 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { */ void withScope(@NotNull ScopeCallback callback); + /** + * Runs the callback with a new isolation scope which gets dropped at the end. Current scope is + * also forked. + * + *

    If you're using the Sentry SDK in globalHubMode (defaults to true on Android) {@link + * Sentry#init(Sentry.OptionsConfiguration, boolean)} calling withScope is discouraged, as scope + * changes may be dropped when executed in parallel. Use {@link IScopes#configureScope(ScopeType, + * ScopeCallback)} instead. + * + * @param callback the callback + */ + void withIsolationScope(@NotNull ScopeCallback callback); + /** * Configures the scope through the callback. * @@ -414,21 +434,27 @@ default void configureScope(@NotNull ScopeCallback callback) { * * @return scope */ - public @NotNull IScope getScope(); + @ApiStatus.Internal + @NotNull + IScope getScope(); /** * Returns the isolation scope of this Scopes. * * @return isolation scope */ - public @NotNull IScope getIsolationScope(); + @ApiStatus.Internal + @NotNull + IScope getIsolationScope(); /** * Returns the global scope. * * @return global scope */ - public @NotNull IScope getGlobalScope(); + @ApiStatus.Internal + @NotNull + IScope getGlobalScope(); /** * Captures the transaction and enqueues it for sending to Sentry server. diff --git a/sentry/src/main/java/io/sentry/IScopesStorage.java b/sentry/src/main/java/io/sentry/IScopesStorage.java index 92f6b587c4..d067d6bafd 100644 --- a/sentry/src/main/java/io/sentry/IScopesStorage.java +++ b/sentry/src/main/java/io/sentry/IScopesStorage.java @@ -1,9 +1,11 @@ package io.sentry; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public interface IScopesStorage { + @NotNull ISentryLifecycleToken set(final @Nullable IScopes scopes); @Nullable diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index 653c8de9f9..3625eb7c06 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -137,6 +137,7 @@ public void removeExtra(@NotNull String key) {} } @Override + @Deprecated public void popScope() {} @Override @@ -144,6 +145,11 @@ public void withScope(@NotNull ScopeCallback callback) { callback.run(NoOpScope.getInstance()); } + @Override + public void withIsolationScope(@NotNull ScopeCallback callback) { + callback.run(NoOpScope.getInstance()); + } + @Override public void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback) {} @@ -179,16 +185,19 @@ public void flush(long timeoutMillis) {} } @Override + @ApiStatus.Internal public @NotNull IScope getScope() { return NoOpScope.getInstance(); } @Override + @ApiStatus.Internal public @NotNull IScope getIsolationScope() { return NoOpScope.getInstance(); } @Override + @ApiStatus.Internal public @NotNull IScope getGlobalScope() { return NoOpScope.getInstance(); } diff --git a/sentry/src/main/java/io/sentry/NoOpScope.java b/sentry/src/main/java/io/sentry/NoOpScope.java index 5335f49519..0226310b4b 100644 --- a/sentry/src/main/java/io/sentry/NoOpScope.java +++ b/sentry/src/main/java/io/sentry/NoOpScope.java @@ -186,7 +186,7 @@ public void clearAttachments() {} @ApiStatus.Internal @Override - public @NotNull List getOrderedEventProcessors() { + public @NotNull List getEventProcessorsWithOrder() { return new ArrayList<>(); } diff --git a/sentry/src/main/java/io/sentry/NoOpScopes.java b/sentry/src/main/java/io/sentry/NoOpScopes.java index 74f965f651..945203066b 100644 --- a/sentry/src/main/java/io/sentry/NoOpScopes.java +++ b/sentry/src/main/java/io/sentry/NoOpScopes.java @@ -132,6 +132,7 @@ public void removeExtra(@NotNull String key) {} } @Override + @Deprecated public void popScope() {} @Override @@ -139,6 +140,11 @@ public void withScope(@NotNull ScopeCallback callback) { callback.run(NoOpScope.getInstance()); } + @Override + public void withIsolationScope(@NotNull ScopeCallback callback) { + callback.run(NoOpScope.getInstance()); + } + @Override public void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback) {} @@ -180,16 +186,19 @@ public void flush(long timeoutMillis) {} } @Override + @ApiStatus.Internal public @NotNull IScope getScope() { return NoOpScope.getInstance(); } @Override + @ApiStatus.Internal public @NotNull IScope getIsolationScope() { return NoOpScope.getInstance(); } @Override + @ApiStatus.Internal public @NotNull IScope getGlobalScope() { return NoOpScope.getInstance(); } diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index e894445bd8..009feeb1b2 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -779,7 +779,7 @@ public List getEventProcessors() { @ApiStatus.Internal @NotNull @Override - public List getOrderedEventProcessors() { + public List getEventProcessorsWithOrder() { // TODO [HSM] This isn't actually ordered but only gets ordered in CombinedScopeView return eventProcessors; } diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 5b3d0e672b..3d9b39a936 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -28,7 +28,6 @@ public final class Scopes implements IScopes, MetricsApi.IMetricsInterface { private final @NotNull IScope isolationScope; private final @NotNull IScope globalScope; - @SuppressWarnings("UnusedVariable") private final @Nullable Scopes parentScopes; private final @NotNull String creator; @@ -76,18 +75,21 @@ private Scopes( } @Override + @ApiStatus.Internal public @NotNull IScope getScope() { return scope; } @Override + @ApiStatus.Internal public @NotNull IScope getIsolationScope() { return isolationScope; } - // TODO [HSM] add to IScopes interface? - public @Nullable Scopes getParent() { - return parentScopes; + @Override + @ApiStatus.Internal + public @NotNull IScope getGlobalScope() { + return globalScope; } // TODO [HSM] add to IScopes interface? @@ -100,9 +102,8 @@ public boolean isAncestorOf(final @Nullable Scopes otherScopes) { return true; } - final @Nullable Scopes parent = otherScopes.getParent(); - if (parent != null) { - return isAncestorOf(parent); + if (otherScopes.parentScopes != null) { + return isAncestorOf(otherScopes.parentScopes); } return false; @@ -408,8 +409,8 @@ public void close(final boolean isRestarting) { } } - // TODO [HSM] which scopes do we call this on? isolation and current scope? configureScope(scope -> scope.clear()); + configureScope(ScopeType.ISOLATION, scope -> scope.clear()); getOptions().getTransactionProfiler().close(); getOptions().getTransactionPerformanceCollector().close(); final @NotNull ISentryExecutorService executorService = getOptions().getExecutorService(); @@ -421,8 +422,9 @@ public void close(final boolean isRestarting) { } // TODO: should we end session before closing client? - // TODO [HSM] should we go through all clients (global, isolation, current) and close them? - getClient().close(isRestarting); + configureScope(ScopeType.CURRENT, scope -> scope.getClient().close(isRestarting)); + configureScope(ScopeType.ISOLATION, scope -> scope.getClient().close(isRestarting)); + configureScope(ScopeType.GLOBAL, scope -> scope.getClient().close(isRestarting)); } catch (Throwable e) { getOptions().getLogger().log(SentryLevel.ERROR, "Error while closing the Scopes.", e); } @@ -575,11 +577,6 @@ private void updateLastEventId(final @NotNull SentryId lastEventId) { getCombinedScopeView().setLastEventId(lastEventId); } - @Override - public @NotNull IScope getGlobalScope() { - return globalScope; - } - @Override public @NotNull SentryId getLastEventId() { return getCombinedScopeView().getLastEventId(); @@ -618,17 +615,16 @@ public ISentryLifecycleToken pushIsolationScope() { return Sentry.setCurrentScopes(this); } - // TODO [HSM] needs to be deprecated because there's no more stack @Override + @Deprecated public void popScope() { if (!isEnabled()) { getOptions() .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'popScope' call is a no-op."); } else { - final @Nullable Scopes parent = getParent(); + final @Nullable Scopes parent = parentScopes; if (parent != null) { - // TODO [HSM] this is never closed parent.makeCurrent(); } } @@ -654,6 +650,29 @@ public void withScope(final @NotNull ScopeCallback callback) { } } + @Override + public void withIsolationScope(final @NotNull ScopeCallback callback) { + if (!isEnabled()) { + try { + callback.run(NoOpScope.getInstance()); + } catch (Throwable e) { + getOptions() + .getLogger() + .log(SentryLevel.ERROR, "Error in the 'withIsolationScope' callback.", e); + } + + } else { + final @NotNull IScopes forkedScopes = forkedScopes("withIsolationScope"); + try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) { + callback.run(forkedScopes.getIsolationScope()); + } catch (Throwable e) { + getOptions() + .getLogger() + .log(SentryLevel.ERROR, "Error in the 'withIsolationScope' callback.", e); + } + } + } + @Override public void configureScope( final @Nullable ScopeType scopeType, final @NotNull ScopeCallback callback) { @@ -717,8 +736,7 @@ public void flush(long timeoutMillis) { if (!isEnabled()) { getOptions().getLogger().log(SentryLevel.WARNING, "Disabled Scopes cloned."); } - // TODO [HSM] should this fork isolation scope as well? - return new HubScopesWrapper(forkedCurrentScope("scopes clone")); + return new HubScopesWrapper(forkedScopes("scopes clone")); } @ApiStatus.Internal diff --git a/sentry/src/main/java/io/sentry/ScopesAdapter.java b/sentry/src/main/java/io/sentry/ScopesAdapter.java index c7f11612e9..005480ccf5 100644 --- a/sentry/src/main/java/io/sentry/ScopesAdapter.java +++ b/sentry/src/main/java/io/sentry/ScopesAdapter.java @@ -160,6 +160,7 @@ public void removeExtra(@NotNull String key) { } @Override + @Deprecated public void popScope() { Sentry.popScope(); } @@ -169,6 +170,11 @@ public void withScope(@NotNull ScopeCallback callback) { Sentry.withScope(callback); } + @Override + public void withIsolationScope(@NotNull ScopeCallback callback) { + Sentry.withIsolationScope(callback); + } + @Override public void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback) { Sentry.configureScope(scopeType, callback); @@ -212,21 +218,23 @@ public void flush(long timeoutMillis) { @Override public @NotNull ISentryLifecycleToken makeCurrent() { - // TODO [HSM] this wouldn't do anything since it replaced the current with the same Scopes return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); } @Override + @ApiStatus.Internal public @NotNull IScope getScope() { return Sentry.getCurrentScopes().getScope(); } @Override + @ApiStatus.Internal public @NotNull IScope getIsolationScope() { return Sentry.getCurrentScopes().getIsolationScope(); } @Override + @ApiStatus.Internal public @NotNull IScope getGlobalScope() { return Sentry.getGlobalScope(); } diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 214a16362a..4c751f68f1 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -53,6 +53,8 @@ private Sentry() {} * *

    For Android options will also be (temporarily) replaced by SentryAndroid static block. */ + // TODO [HSM] use SentryOptions.empty and address + // https://github.com/getsentry/sentry-java/issues/2541 private static volatile @NotNull IScope globalScope = new Scope(new SentryOptions()); /** Default value for globalHubMode is false */ @@ -89,7 +91,7 @@ private Sentry() {} if (globalHubMode) { return rootScopes; } - IScopes scopes = getScopesStorage().get(); + @Nullable IScopes scopes = getScopesStorage().get(); if (scopes == null || scopes.isNoOp()) { scopes = rootScopes.forkedScopes("getCurrentScopes"); getScopesStorage().set(scopes); @@ -835,7 +837,13 @@ public static void removeExtra(final @NotNull String key) { return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); } - /** Removes the first scope */ + /** + * Removes the first scope and restores its parent. + * + * @deprecated please call {@link ISentryLifecycleToken#close()} on the token returned by {@link + * Sentry#pushScope()} or {@link Sentry#pushIsolationScope()} instead. + */ + @Deprecated public static void popScope() { // popScope is no-op in global hub mode if (!globalHubMode) { @@ -844,7 +852,7 @@ public static void popScope() { } /** - * Runs the callback with a new scope which gets dropped at the end + * Runs the callback with a new current scope which gets dropped at the end * * @param callback the callback */ @@ -852,6 +860,16 @@ public static void withScope(final @NotNull ScopeCallback callback) { getCurrentScopes().withScope(callback); } + /** + * Runs the callback with a new isolation scope which gets dropped at the end. Current scope is + * also forked. + * + * @param callback the callback + */ + public static void withIsolationScope(final @NotNull ScopeCallback callback) { + getCurrentScopes().withIsolationScope(callback); + } + /** * Configures the scope through the callback. * diff --git a/sentry/src/main/java/io/sentry/SentryWrapper.java b/sentry/src/main/java/io/sentry/SentryWrapper.java index 808050e570..78505cf297 100644 --- a/sentry/src/main/java/io/sentry/SentryWrapper.java +++ b/sentry/src/main/java/io/sentry/SentryWrapper.java @@ -12,24 +12,26 @@ *

  • {@link Supplier} * * - * that forks the current scope before execution and restores it afterwards. This prevents reused - * threads (e.g. from thread-pools) from getting an incorrect state. + * that forks the current scope(s) before execution and restores previous state afterwards. Which + * scope(s) are forked, depends on the method used here. This prevents reused threads (e.g. from + * thread-pools) from getting an incorrect state. */ +// TODO [HSM] only deliver isolated variant as default for now public final class SentryWrapper { /** * Helper method to wrap {@link Callable} * - *

    Forks the current scope before execution and restores it afterwards. This prevents reused - * threads (e.g. from thread-pools) from getting an incorrect state. + *

    Forks current scope before execution and restores previous state afterwards. This prevents + * reused threads (e.g. from thread-pools) from getting an incorrect state. * * @param callable - the {@link Callable} to be wrapped * @return the wrapped {@link Callable} * @param - the result type of the {@link Callable} */ - // TODO [HSM] adapt javadoc public static Callable wrapCallable(final @NotNull Callable callable) { - final IScopes newScopes = Sentry.getCurrentScopes().forkedCurrentScope("wrapCallable"); + final IScopes newScopes = + Sentry.getCurrentScopes().forkedCurrentScope("SentryWrapper.wrapCallable"); return () -> { try (ISentryLifecycleToken ignored = newScopes.makeCurrent()) { @@ -38,8 +40,18 @@ public static Callable wrapCallable(final @NotNull Callable callable) }; } + /** + * Helper method to wrap {@link Callable} + * + *

    Forks current and isolation scope before execution and restores previous state afterwards. + * This prevents reused threads (e.g. from thread-pools) from getting an incorrect state. + * + * @param callable - the {@link Callable} to be wrapped + * @return the wrapped {@link Callable} + * @param - the result type of the {@link Callable} + */ public static Callable wrapCallableIsolated(final @NotNull Callable callable) { - final IScopes newScopes = Sentry.getCurrentScopes().forkedScopes("wrapCallable"); + final IScopes newScopes = Sentry.getCurrentScopes().forkedScopes("SentryWrapper.wrapCallable"); return () -> { try (ISentryLifecycleToken ignored = newScopes.makeCurrent()) { @@ -51,16 +63,15 @@ public static Callable wrapCallableIsolated(final @NotNull Callable ca /** * Helper method to wrap {@link Supplier} * - *

    Forks the current scope before execution and restores it afterwards. This prevents reused - * threads (e.g. from thread-pools) from getting an incorrect state. + *

    Forks current scope before execution and restores previous state afterwards. This prevents + * reused threads (e.g. from thread-pools) from getting an incorrect state. * * @param supplier - the {@link Supplier} to be wrapped * @return the wrapped {@link Supplier} * @param - the result type of the {@link Supplier} */ - @SuppressWarnings("deprecation") public static Supplier wrapSupplier(final @NotNull Supplier supplier) { - final IScopes newScopes = Sentry.forkedCurrentScope("wrapSupplier"); + final IScopes newScopes = Sentry.forkedCurrentScope("SentryWrapper.wrapSupplier"); return () -> { try (ISentryLifecycleToken ignore = newScopes.makeCurrent()) { @@ -69,8 +80,18 @@ public static Supplier wrapSupplier(final @NotNull Supplier supplier) }; } + /** + * Helper method to wrap {@link Supplier} + * + *

    Forks current and isolation scope before execution and restores previous state afterwards. + * This prevents reused threads (e.g. from thread-pools) from getting an incorrect state. + * + * @param supplier - the {@link Supplier} to be wrapped + * @return the wrapped {@link Supplier} + * @param - the result type of the {@link Supplier} + */ public static Supplier wrapSupplierIsolated(final @NotNull Supplier supplier) { - final IScopes newScopes = Sentry.forkedScopes("wrapSupplier"); + final IScopes newScopes = Sentry.forkedScopes("SentryWrapper.wrapSupplier"); return () -> { try (ISentryLifecycleToken ignore = newScopes.makeCurrent()) { diff --git a/sentry/src/main/java/io/sentry/protocol/Contexts.java b/sentry/src/main/java/io/sentry/protocol/Contexts.java index 4e4c7c5c90..40a5914151 100644 --- a/sentry/src/main/java/io/sentry/protocol/Contexts.java +++ b/sentry/src/main/java/io/sentry/protocol/Contexts.java @@ -25,7 +25,7 @@ public class Contexts implements JsonSerializable { private static final long serialVersionUID = 252445813254943011L; private final @NotNull ConcurrentHashMap internalStorage = - new ConcurrentHashMap(); + new ConcurrentHashMap<>(); /** Response lock, Ops should be atomic */ private final @NotNull Object responseLock = new Object(); @@ -146,10 +146,12 @@ public void setResponse(final @NotNull Response response) { } public int size() { + // since this used to extend map return internalStorage.size(); } public int getSize() { + // for kotlin .size return size(); } diff --git a/sentry/src/main/java/io/sentry/util/CheckInUtils.java b/sentry/src/main/java/io/sentry/util/CheckInUtils.java index d42cf4edf4..3f13064cbb 100644 --- a/sentry/src/main/java/io/sentry/util/CheckInUtils.java +++ b/sentry/src/main/java/io/sentry/util/CheckInUtils.java @@ -31,29 +31,30 @@ public static U withCheckIn( final @Nullable MonitorConfig monitorConfig, final @NotNull Callable callable) throws Exception { - final @NotNull ISentryLifecycleToken lifecycleToken = Sentry.pushIsolationScope(); - final @NotNull IScopes scopes = Sentry.getCurrentScopes(); - final long startTime = System.currentTimeMillis(); - boolean didError = false; + try (final @NotNull ISentryLifecycleToken ignored = + Sentry.forkedScopes("CheckInUtils").makeCurrent()) { + final @NotNull IScopes scopes = Sentry.getCurrentScopes(); + final long startTime = System.currentTimeMillis(); + boolean didError = false; - TracingUtils.startNewTrace(scopes); + TracingUtils.startNewTrace(scopes); - CheckIn inProgressCheckIn = new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS); - if (monitorConfig != null) { - inProgressCheckIn.setMonitorConfig(monitorConfig); - } - @Nullable SentryId checkInId = scopes.captureCheckIn(inProgressCheckIn); - try { - return callable.call(); - } catch (Throwable t) { - didError = true; - throw t; - } finally { - final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK; - CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); - checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); - scopes.captureCheckIn(checkIn); - lifecycleToken.close(); + CheckIn inProgressCheckIn = new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS); + if (monitorConfig != null) { + inProgressCheckIn.setMonitorConfig(monitorConfig); + } + @Nullable SentryId checkInId = scopes.captureCheckIn(inProgressCheckIn); + try { + return callable.call(); + } catch (Throwable t) { + didError = true; + throw t; + } finally { + final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK; + CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); + checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); + scopes.captureCheckIn(checkIn); + } } } diff --git a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt index 65a1bc49f7..a18e708f86 100644 --- a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt +++ b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt @@ -172,10 +172,10 @@ class CombinedScopeViewTest { val eventProcessors = combined.eventProcessors - assertEquals(first, eventProcessors.get(0)) - assertEquals(second, eventProcessors.get(1)) - assertEquals(third, eventProcessors.get(2)) - assertEquals(fourth, eventProcessors.get(3)) + assertEquals(first, eventProcessors[0]) + assertEquals(second, eventProcessors[1]) + assertEquals(third, eventProcessors[2]) + assertEquals(fourth, eventProcessors[3]) } @Test @@ -282,7 +282,7 @@ class CombinedScopeViewTest { } @Test - fun `prefers transaction andspan from current scope`() { + fun `prefers transaction and span from current scope`() { val combined = fixture.getSut() fixture.scope.setTransaction(createTransaction("scopeTransaction")) fixture.isolationScope.setTransaction(createTransaction("isolationTransaction")) @@ -293,7 +293,7 @@ class CombinedScopeViewTest { } @Test - fun `uses isolation scope transaction andspan if none in current scope`() { + fun `uses isolation scope transaction and span if none in current scope`() { val combined = fixture.getSut() fixture.isolationScope.setTransaction(createTransaction("isolationTransaction")) fixture.globalScope.setTransaction(createTransaction("globalTransaction")) @@ -303,7 +303,7 @@ class CombinedScopeViewTest { } @Test - fun `uses global transaction andscope span if none in current or isolation scope`() { + fun `uses global transaction and scope span if none in current or isolation scope`() { val combined = fixture.getSut() fixture.globalScope.setTransaction(createTransaction("globalTransaction")) @@ -525,7 +525,7 @@ class CombinedScopeViewTest { } @Test - fun `prefer scope value for tags with same key`() { + fun `prefer current scope value for tags with same key`() { val combined = fixture.getSut() fixture.scope.setTag("aTag", "scopeValue") @@ -596,7 +596,7 @@ class CombinedScopeViewTest { } @Test - fun `prefer scope value for extras with same key`() { + fun `prefer current scope value for extras with same key`() { val combined = fixture.getSut() fixture.scope.setExtra("someExtra", "scopeValue") @@ -712,8 +712,6 @@ class CombinedScopeViewTest { assertNull(fixture.globalScope.contexts["someList"]) } - // TODO [HSM] test all setContext methods - @Test fun `combines attachments from all scopes`() { val combined = fixture.getSut() @@ -854,19 +852,19 @@ class CombinedScopeViewTest { } @Test - fun `getSpecificScope(CURRENT) returns scope`() { + fun `getSpecificScope(CURRENT) returns current scope`() { val combined = fixture.getSut(SentryOptions().also { it.defaultScopeType = ScopeType.ISOLATION }) assertSame(fixture.scope, combined.getSpecificScope(ScopeType.CURRENT)) } @Test - fun `getSpecificScope(ISOLATION) returns scope`() { + fun `getSpecificScope(ISOLATION) returns isolation scope`() { val combined = fixture.getSut(SentryOptions().also { it.defaultScopeType = ScopeType.CURRENT }) assertSame(fixture.isolationScope, combined.getSpecificScope(ScopeType.ISOLATION)) } @Test - fun `getSpecificScope(GLOBAL) returns scope`() { + fun `getSpecificScope(GLOBAL) returns global scope`() { val combined = fixture.getSut(SentryOptions().also { it.defaultScopeType = ScopeType.CURRENT }) assertSame(fixture.globalScope, combined.getSpecificScope(ScopeType.GLOBAL)) } diff --git a/sentry/src/test/java/io/sentry/ScopesTest.kt b/sentry/src/test/java/io/sentry/ScopesTest.kt index 1b886aea5b..5bcecc31f6 100644 --- a/sentry/src/test/java/io/sentry/ScopesTest.kt +++ b/sentry/src/test/java/io/sentry/ScopesTest.kt @@ -85,9 +85,7 @@ class ScopesTest { options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) options.addIntegration(integrationMock) -// val expected = HubAdapter.getInstance() val scopes = createScopes(options) -// verify(integrationMock).register(expected, options) scopes.forkedScopes("test") verifyNoMoreInteractions(integrationMock) } @@ -100,9 +98,7 @@ class ScopesTest { options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) options.addIntegration(integrationMock) -// val expected = HubAdapter.getInstance() val scopes = createScopes(options) -// verify(integrationMock).register(expected, options) scopes.forkedCurrentScope("test") verifyNoMoreInteractions(integrationMock) } @@ -883,6 +879,43 @@ class ScopesTest { } //endregion + //region withIsolationScope tests + @Test + fun `when withIsolationScope is called on disabled client, execute on NoOpScope`() { + val (sut) = getEnabledScopes() + + val scopeCallback = mock() + sut.close() + + sut.withIsolationScope(scopeCallback) + verify(scopeCallback).run(NoOpScope.getInstance()) + } + + @Test + fun `when withIsolationScope is called with alive client, run should be called`() { + val (sut) = getEnabledScopes() + + val scopeCallback = mock() + + sut.withIsolationScope(scopeCallback) + verify(scopeCallback).run(any()) + } + + @Test + fun `when withIsolationScope throws an exception then it should be caught`() { + val (scopes, _, logger) = getEnabledScopes() + + val exception = Exception("scope callback exception") + val scopeCallback = ScopeCallback { + throw exception + } + + scopes.withIsolationScope(scopeCallback) + + verify(logger).log(eq(SentryLevel.ERROR), any(), eq(exception)) + } + //endregion + //region configureScope tests @Test fun `when configureScope is called on disabled client, do nothing`() { diff --git a/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt b/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt index 889459dd35..943837d697 100644 --- a/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt @@ -60,10 +60,11 @@ class CheckInUtilsTest { val scopes = mock() val lifecycleToken = mock() sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) - sentry.`when` { Sentry.pushIsolationScope() }.then { - scopes.pushIsolationScope() - lifecycleToken + sentry.`when` { Sentry.forkedScopes(any()) }.then { + scopes.forkedScopes("test") } + whenever(scopes.forkedScopes(any())).thenReturn(scopes) + whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) whenever(scopes.options).thenReturn(SentryOptions()) val returnValue = CheckInUtils.withCheckIn("monitor-1") { return@withCheckIn "test1" @@ -71,7 +72,8 @@ class CheckInUtilsTest { assertEquals("test1", returnValue) inOrder(scopes, lifecycleToken) { - verify(scopes).pushIsolationScope() + verify(scopes).forkedScopes(any()) + verify(scopes).makeCurrent() verify(scopes).configureScope(any()) verify(scopes).captureCheckIn( check { @@ -96,10 +98,11 @@ class CheckInUtilsTest { val scopes = mock() val lifecycleToken = mock() sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) - sentry.`when` { Sentry.pushIsolationScope() }.then { - scopes.pushIsolationScope() - lifecycleToken + sentry.`when` { Sentry.forkedScopes(any()) }.then { + scopes.forkedScopes("test") } + whenever(scopes.forkedScopes(any())).thenReturn(scopes) + whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) try { CheckInUtils.withCheckIn("monitor-1") { @@ -111,7 +114,8 @@ class CheckInUtilsTest { } inOrder(scopes, lifecycleToken) { - verify(scopes).pushIsolationScope() + verify(scopes).forkedScopes(any()) + verify(scopes).makeCurrent() verify(scopes).configureScope(any()) verify(scopes).captureCheckIn( check { @@ -136,10 +140,11 @@ class CheckInUtilsTest { val scopes = mock() val lifecycleToken = mock() sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) - sentry.`when` { Sentry.pushIsolationScope() }.then { - scopes.pushIsolationScope() - lifecycleToken + sentry.`when` { Sentry.forkedScopes(any()) }.then { + scopes.forkedScopes("test") } + whenever(scopes.forkedScopes(any())).thenReturn(scopes) + whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) whenever(scopes.options).thenReturn(SentryOptions()) val monitorConfig = MonitorConfig(MonitorSchedule.interval(7, MonitorScheduleUnit.DAY)) val returnValue = CheckInUtils.withCheckIn("monitor-1", monitorConfig) { @@ -148,7 +153,8 @@ class CheckInUtilsTest { assertEquals("test1", returnValue) inOrder(scopes, lifecycleToken) { - verify(scopes).pushIsolationScope() + verify(scopes).forkedScopes(any()) + verify(scopes).makeCurrent() verify(scopes).configureScope(any()) verify(scopes).captureCheckIn( check { @@ -174,10 +180,11 @@ class CheckInUtilsTest { val scopes = mock() val lifecycleToken = mock() sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) - sentry.`when` { Sentry.pushIsolationScope() }.then { - scopes.pushIsolationScope() - lifecycleToken + sentry.`when` { Sentry.forkedScopes(any()) }.then { + scopes.forkedScopes("test") } + whenever(scopes.forkedScopes(any())).thenReturn(scopes) + whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) whenever(scopes.options).thenReturn(SentryOptions()) val monitorConfig = MonitorConfig(MonitorSchedule.interval(7, MonitorScheduleUnit.DAY)).apply { failureIssueThreshold = 10 @@ -189,7 +196,8 @@ class CheckInUtilsTest { assertEquals("test1", returnValue) inOrder(scopes, lifecycleToken) { - verify(scopes).pushIsolationScope() + verify(scopes).forkedScopes(any()) + verify(scopes).makeCurrent() verify(scopes).configureScope(any()) verify(scopes).captureCheckIn( check { From aa3cd3edda0c0dc6ac01928808dafcf4ad1fbe54 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 2 May 2024 14:38:34 +0200 Subject: [PATCH 40/91] Hubs/Scopes Merge 40 - `Scopes.isEnabled` now checks `getClient().isEnabled()` (#3385) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes --- .../EnvelopeFileObserverIntegrationTest.kt | 8 +-- .../android/core/InternalSentrySdkTest.kt | 14 ++-- .../io/sentry/logback/SentryAppenderTest.kt | 25 +++++-- .../api/sentry-test-support.api | 7 ++ .../src/main/kotlin/io/sentry/test/Mocks.kt | 27 +++++++ .../java/io/sentry/CombinedContextsView.java | 1 - .../java/io/sentry/CombinedScopeView.java | 3 - sentry/src/main/java/io/sentry/Scope.java | 23 +++--- sentry/src/main/java/io/sentry/Scopes.java | 26 ++----- .../io/sentry/util/EventProcessorUtils.java | 5 +- .../java/io/sentry/CombinedScopeViewTest.kt | 15 +++- .../src/test/java/io/sentry/HubAdapterTest.kt | 3 +- .../test/java/io/sentry/ScopesAdapterTest.kt | 3 +- sentry/src/test/java/io/sentry/ScopesTest.kt | 71 +++++++++++++------ sentry/src/test/java/io/sentry/SentryTest.kt | 5 +- .../test/java/io/sentry/SentryTracerTest.kt | 4 +- sentry/src/test/java/io/sentry/StackTest.kt | 3 +- ...UncaughtExceptionHandlerIntegrationTest.kt | 3 +- .../sentry/metrics/MetricsIntegrationTest.kt | 1 + 19 files changed, 157 insertions(+), 90 deletions(-) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/EnvelopeFileObserverIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/EnvelopeFileObserverIntegrationTest.kt index 82d0543833..69b2eee2ec 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/EnvelopeFileObserverIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/EnvelopeFileObserverIntegrationTest.kt @@ -2,14 +2,12 @@ package io.sentry.android.core import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.ILogger -import io.sentry.IScope import io.sentry.IScopes -import io.sentry.Scope -import io.sentry.Scopes import io.sentry.SentryLevel import io.sentry.SentryOptions import io.sentry.test.DeferredExecutorService import io.sentry.test.ImmediateExecutorService +import io.sentry.test.createTestScopes import org.junit.runner.RunWith import org.mockito.kotlin.eq import org.mockito.kotlin.mock @@ -74,9 +72,7 @@ class EnvelopeFileObserverIntegrationTest { options.cacheDirPath = file.absolutePath options.addIntegration(integrationMock) options.setSerializer(mock()) - val globalScope = Scope(options) - val scopes = Scopes(mock(), mock(), globalScope, "test") -// verify(integrationMock).register(expected, options) + val scopes = createTestScopes(options) scopes.close() verify(integrationMock).close() } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt index f4f2c696d3..07a120d527 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt @@ -8,7 +8,6 @@ import io.sentry.Hint import io.sentry.IScope import io.sentry.Scope import io.sentry.ScopeType -import io.sentry.Scopes import io.sentry.Sentry import io.sentry.SentryEnvelope import io.sentry.SentryEnvelopeHeader @@ -24,6 +23,7 @@ import io.sentry.protocol.Contexts import io.sentry.protocol.Mechanism import io.sentry.protocol.SentryId import io.sentry.protocol.User +import io.sentry.test.createTestScopes import io.sentry.transport.ITransport import io.sentry.transport.RateLimiter import org.junit.runner.RunWith @@ -106,13 +106,14 @@ class InternalSentrySdkTest { @BeforeTest fun `set up`() { + Sentry.close() context = ApplicationProvider.getApplicationContext() DeviceInfoUtil.resetInstance() } @Test fun `current scope returns null when scopes is no-op`() { - Sentry.getCurrentScopes().close() + Sentry.setCurrentScopes(createTestScopes(enabled = false)) val scope = InternalSentrySdk.getCurrentScope() assertNull(scope) } @@ -122,9 +123,7 @@ class InternalSentrySdkTest { val options = SentryOptions().apply { dsn = "https://key@uri/1234567" } - Sentry.setCurrentScopes( - Scopes(Scope(options), Scope(options), Scope(options), "test") - ) + Sentry.setCurrentScopes(createTestScopes(options)) val scope = InternalSentrySdk.getCurrentScope() assertNotNull(scope) } @@ -134,10 +133,7 @@ class InternalSentrySdkTest { val options = SentryOptions().apply { dsn = "https://key@uri/1234567" } - Sentry.setCurrentScopes( - Scopes(Scope(options), Scope(options), Scope(options), "test") - ) - // TODO [HSM] add breadcrumbs to all scopes and assert they are there + Sentry.setCurrentScopes(createTestScopes(options)) Sentry.addBreadcrumb("test") Sentry.configureScope(ScopeType.CURRENT) { scope -> scope.addBreadcrumb(Breadcrumb("currentBreadcrumb")) } Sentry.configureScope(ScopeType.ISOLATION) { scope -> scope.addBreadcrumb(Breadcrumb("isolationBreadcrumb")) } diff --git a/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt b/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt index 96c3dad9f1..3abc2cdd11 100644 --- a/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt +++ b/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt @@ -35,17 +35,18 @@ import kotlin.test.assertNull import kotlin.test.assertTrue class SentryAppenderTest { - private class Fixture(dsn: String? = "http://key@localhost/proj", minimumBreadcrumbLevel: Level? = null, minimumEventLevel: Level? = null, contextTags: List? = null, encoder: Encoder? = null, sendDefaultPii: Boolean = false) { + private class Fixture(dsn: String? = "http://key@localhost/proj", minimumBreadcrumbLevel: Level? = null, minimumEventLevel: Level? = null, contextTags: List? = null, encoder: Encoder? = null, sendDefaultPii: Boolean = false, options: SentryOptions = SentryOptions(), startLater: Boolean = false) { val logger: Logger = LoggerFactory.getLogger(SentryAppenderTest::class.java) val loggerContext = LoggerFactory.getILoggerFactory() as LoggerContext val transportFactory = mock() val transport = mock() val utcTimeZone: ZoneId = ZoneId.of("UTC") + val appender = SentryAppender() + var encoder: Encoder? = null init { whenever(this.transportFactory.create(any(), any())).thenReturn(transport) - val appender = SentryAppender() - val options = SentryOptions() + this.encoder = encoder options.dsn = dsn options.isSendDefaultPii = sendDefaultPii contextTags?.forEach { options.addContextTag(it) } @@ -59,6 +60,12 @@ class SentryAppenderTest { val rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME) rootLogger.level = Level.TRACE rootLogger.addAppender(appender) + if (!startLater) { + start() + } + } + + fun start() { appender.start() encoder?.start() loggerContext.start() @@ -82,17 +89,25 @@ class SentryAppenderTest { @Test fun `does not initialize Sentry if Sentry is already enabled`() { - fixture = Fixture() + fixture = Fixture( + startLater = true, + options = SentryOptions().also { + it.setTag("only-present-if-logger-init-was-run", "another-value") + } + ) Sentry.init { it.dsn = "http://key@localhost/proj" it.environment = "manual-environment" it.setTransportFactory(fixture.transportFactory) + it.setTag("tag-from-first-init", "some-value") } + fixture.start() + fixture.logger.error("testing environment field") verify(fixture.transport).send( checkEvent { event -> - assertEquals("manual-environment", event.environment) + assertNull(event.tags?.get("only-present-if-logger-init-was-run")) }, anyOrNull() ) diff --git a/sentry-test-support/api/sentry-test-support.api b/sentry-test-support/api/sentry-test-support.api index dd1a4b69d3..27d7034690 100644 --- a/sentry-test-support/api/sentry-test-support.api +++ b/sentry-test-support/api/sentry-test-support.api @@ -31,6 +31,13 @@ public final class io/sentry/test/ImmediateExecutorService : io/sentry/ISentryEx public fun submit (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Future; } +public final class io/sentry/test/MocksKt { + public static final fun createSentryClientMock (Z)Lio/sentry/ISentryClient; + public static synthetic fun createSentryClientMock$default (ZILjava/lang/Object;)Lio/sentry/ISentryClient; + public static final fun createTestScopes (Lio/sentry/SentryOptions;ZLio/sentry/IScope;Lio/sentry/IScope;Lio/sentry/IScope;)Lio/sentry/Scopes; + public static synthetic fun createTestScopes$default (Lio/sentry/SentryOptions;ZLio/sentry/IScope;Lio/sentry/IScope;Lio/sentry/IScope;ILjava/lang/Object;)Lio/sentry/Scopes; +} + public final class io/sentry/test/ReflectionKt { public static final fun collectInterfaceHierarchy (Ljava/lang/Class;)Ljava/util/List; public static final fun containsMethod (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Z diff --git a/sentry-test-support/src/main/kotlin/io/sentry/test/Mocks.kt b/sentry-test-support/src/main/kotlin/io/sentry/test/Mocks.kt index 168f3e6372..26f0066beb 100644 --- a/sentry-test-support/src/main/kotlin/io/sentry/test/Mocks.kt +++ b/sentry-test-support/src/main/kotlin/io/sentry/test/Mocks.kt @@ -1,11 +1,19 @@ // ktlint-disable filename package io.sentry.test +import io.sentry.IScope +import io.sentry.ISentryClient import io.sentry.ISentryExecutorService +import io.sentry.Scope +import io.sentry.Scopes +import io.sentry.SentryOptions import io.sentry.backpressure.IBackpressureMonitor +import org.mockito.kotlin.any import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever import java.util.concurrent.Callable import java.util.concurrent.Future +import java.util.concurrent.atomic.AtomicBoolean class ImmediateExecutorService : ISentryExecutorService { override fun submit(runnable: Runnable): Future<*> { @@ -65,3 +73,22 @@ class DeferredExecutorService : ISentryExecutorService { fun hasScheduledRunnables(): Boolean = scheduledRunnables.isNotEmpty() } + +fun createSentryClientMock(enabled: Boolean = true) = mock().also { + val isEnabled = AtomicBoolean(enabled) + whenever(it.isEnabled).then { isEnabled.get() } + whenever(it.close()).then { isEnabled.set(false) } + whenever(it.close(any())).then { isEnabled.set(false) } +} + +fun createTestScopes(options: SentryOptions? = null, enabled: Boolean = true, scope: IScope? = null, isolationScope: IScope? = null, globalScope: IScope? = null): Scopes { + val optionsToUse = options ?: SentryOptions().also { it.dsn = "https://key@sentry.io/proj" } + val scopeToUse = scope ?: Scope(optionsToUse) + val isolationScopeToUse = isolationScope ?: Scope(optionsToUse) + val globalScopeToUse = globalScope ?: Scope(optionsToUse) + return Scopes(scopeToUse, isolationScopeToUse, globalScopeToUse, "test").also { + if (enabled) { + it.bindClient(createSentryClientMock()) + } + } +} diff --git a/sentry/src/main/java/io/sentry/CombinedContextsView.java b/sentry/src/main/java/io/sentry/CombinedContextsView.java index 40c220bb31..3720fa5b93 100644 --- a/sentry/src/main/java/io/sentry/CombinedContextsView.java +++ b/sentry/src/main/java/io/sentry/CombinedContextsView.java @@ -248,7 +248,6 @@ public boolean containsKey(final @NotNull Object key) { @Override public @Nullable Object remove(final @NotNull Object key) { - // TODO [HSM] should this remove from all contexts? return getDefaultContexts().remove(key); } diff --git a/sentry/src/main/java/io/sentry/CombinedScopeView.java b/sentry/src/main/java/io/sentry/CombinedScopeView.java index 2ed33d56ab..86b90379ad 100644 --- a/sentry/src/main/java/io/sentry/CombinedScopeView.java +++ b/sentry/src/main/java/io/sentry/CombinedScopeView.java @@ -231,7 +231,6 @@ public void setTag(@NotNull String key, @NotNull String value) { @Override public void removeTag(@NotNull String key) { - // TODO [HSM] should this go to all scopes? getDefaultWriteScope().removeTag(key); } @@ -251,7 +250,6 @@ public void setExtra(@NotNull String key, @NotNull String value) { @Override public void removeExtra(@NotNull String key) { - // TODO [HSM] should this go to all scopes? getDefaultWriteScope().removeExtra(key); } @@ -301,7 +299,6 @@ public void setContexts(@NotNull String key, @NotNull Character value) { @Override public void removeContexts(@NotNull String key) { - // TODO [HSM] should this go to all scopes? getDefaultWriteScope().removeContexts(key); } diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index 009feeb1b2..3df40a2b01 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -780,7 +780,6 @@ public List getEventProcessors() { @NotNull @Override public List getEventProcessorsWithOrder() { - // TODO [HSM] This isn't actually ordered but only gets ordered in CombinedScopeView return eventProcessors; } @@ -1041,17 +1040,17 @@ public void setSpanContext( @ApiStatus.Internal @Override public void replaceOptions(final @NotNull SentryOptions options) { - // TODO [HSM] check if already enabled and noop in that case? - // if (!isEnabled()) {} - this.options = options; - final Queue oldBreadcrumbs = breadcrumbs; - breadcrumbs = createBreadcrumbsList(options.getMaxBreadcrumbs()); - for (Breadcrumb breadcrumb : oldBreadcrumbs) { - /* - this should trigger beforeBreadcrumb - and notify observers for breadcrumbs added before options where customized in Sentry.init - */ - addBreadcrumb(breadcrumb); + if (!getClient().isEnabled()) { + this.options = options; + final Queue oldBreadcrumbs = breadcrumbs; + breadcrumbs = createBreadcrumbsList(options.getMaxBreadcrumbs()); + for (Breadcrumb breadcrumb : oldBreadcrumbs) { + /* + this should trigger beforeBreadcrumb + and notify observers for breadcrumbs added before options where customized in Sentry.init + */ + addBreadcrumb(breadcrumb); + } } } diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 3d9b39a936..59540d105b 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -31,7 +31,6 @@ public final class Scopes implements IScopes, MetricsApi.IMetricsInterface { private final @Nullable Scopes parentScopes; private final @NotNull String creator; - private volatile boolean isEnabled; private final @NotNull TracesSampler tracesSampler; private final @NotNull TransactionPerformanceCollector transactionPerformanceCollector; private final @NotNull MetricsApi metricsApi; @@ -63,10 +62,6 @@ private Scopes( validateOptions(options); this.tracesSampler = new TracesSampler(options); this.transactionPerformanceCollector = options.getTransactionPerformanceCollector(); - - // TODO [HSM] Checking isEnabled may not be what we want with global scope anymore - this.isEnabled = true; - this.metricsApi = new MetricsApi(this); } @@ -124,10 +119,9 @@ public boolean isAncestorOf(final @Nullable Scopes otherScopes) { return Sentry.forkedRootScopes(creator); } - // TODO [HSM] always read from root scope? @Override public boolean isEnabled() { - return isEnabled; + return getClient().isEnabled(); } @Override @@ -428,7 +422,6 @@ public void close(final boolean isRestarting) { } catch (Throwable e) { getOptions().getLogger().log(SentryLevel.ERROR, "Error while closing the Scopes.", e); } - isEnabled = false; } } @@ -695,18 +688,12 @@ public void configureScope( @Override public void bindClient(final @NotNull ISentryClient client) { - if (!isEnabled()) { - getOptions() - .getLogger() - .log(SentryLevel.WARNING, "Instance is disabled and this 'bindClient' call is a no-op."); + if (client != null) { + getOptions().getLogger().log(SentryLevel.DEBUG, "New client bound to scope."); + getCombinedScopeView().bindClient(client); } else { - if (client != null) { - getOptions().getLogger().log(SentryLevel.DEBUG, "New client bound to scope."); - getCombinedScopeView().bindClient(client); - } else { - getOptions().getLogger().log(SentryLevel.DEBUG, "NoOp client bound to scope."); - getCombinedScopeView().bindClient(NoOpSentryClient.getInstance()); - } + getOptions().getLogger().log(SentryLevel.DEBUG, "NoOp client bound to scope."); + getCombinedScopeView().bindClient(NoOpSentryClient.getInstance()); } } @@ -939,7 +926,6 @@ public void reportFullyDisplayed() { @NotNull PropagationContext propagationContext = PropagationContext.fromHeaders(getOptions().getLogger(), sentryTrace, baggageHeaders); - // TODO [HSM] should this go on isolation scope? configureScope( (scope) -> { scope.setPropagationContext(propagationContext); diff --git a/sentry/src/main/java/io/sentry/util/EventProcessorUtils.java b/sentry/src/main/java/io/sentry/util/EventProcessorUtils.java index b47d40ecd9..8fb73982c0 100644 --- a/sentry/src/main/java/io/sentry/util/EventProcessorUtils.java +++ b/sentry/src/main/java/io/sentry/util/EventProcessorUtils.java @@ -2,6 +2,7 @@ import io.sentry.EventProcessor; import io.sentry.internal.eventprocessor.EventProcessorAndOrder; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.jetbrains.annotations.Nullable; @@ -10,7 +11,7 @@ public final class EventProcessorUtils { public static List unwrap( final @Nullable List orderedEventProcessor) { - final List eventProcessors = new CopyOnWriteArrayList<>(); + final List eventProcessors = new ArrayList<>(); if (orderedEventProcessor != null) { for (EventProcessorAndOrder eventProcessorAndOrder : orderedEventProcessor) { @@ -18,6 +19,6 @@ public static List unwrap( } } - return eventProcessors; + return new CopyOnWriteArrayList<>(eventProcessors); } } diff --git a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt index a18e708f86..b73a7adcc8 100644 --- a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt +++ b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt @@ -4,6 +4,7 @@ import io.sentry.protocol.Device import io.sentry.protocol.Request import io.sentry.protocol.SentryId import io.sentry.protocol.User +import io.sentry.test.createTestScopes import junit.framework.TestCase.assertFalse import junit.framework.TestCase.assertTrue import org.junit.Assert.assertNotEquals @@ -38,7 +39,7 @@ class CombinedScopeViewTest { globalScope = Scope(options) isolationScope = Scope(options) scope = Scope(options) - scopes = Scopes(scope, isolationScope, globalScope, "test") + scopes = createTestScopes(options, scope = scope, isolationScope = isolationScope, globalScope = globalScope) return CombinedScopeView(globalScope, isolationScope, scope) } @@ -801,6 +802,9 @@ class CombinedScopeViewTest { @Test fun `uses isolation scope client if noop on current scope`() { val combined = fixture.getSut() + fixture.scope.bindClient(NoOpSentryClient.getInstance()) + fixture.isolationScope.bindClient(NoOpSentryClient.getInstance()) + fixture.globalScope.bindClient(NoOpSentryClient.getInstance()) val isolationClient = SentryClient(fixture.options) fixture.isolationScope.bindClient(isolationClient) @@ -814,6 +818,9 @@ class CombinedScopeViewTest { @Test fun `uses global scope client if noop on current and isolation scope`() { val combined = fixture.getSut() + fixture.scope.bindClient(NoOpSentryClient.getInstance()) + fixture.isolationScope.bindClient(NoOpSentryClient.getInstance()) + fixture.globalScope.bindClient(NoOpSentryClient.getInstance()) val globalClient = SentryClient(fixture.options) fixture.globalScope.bindClient(globalClient) @@ -824,6 +831,10 @@ class CombinedScopeViewTest { @Test fun `binds client to default scope`() { val combined = fixture.getSut() + fixture.scope.bindClient(NoOpSentryClient.getInstance()) + fixture.isolationScope.bindClient(NoOpSentryClient.getInstance()) + fixture.globalScope.bindClient(NoOpSentryClient.getInstance()) + val client = SentryClient(fixture.options) combined.bindClient(client) @@ -880,7 +891,7 @@ class CombinedScopeViewTest { whenever(globalScope.options).thenReturn(options) val exception = RuntimeException("someEx") - val transaction = createTransaction("aTransaction", Scopes(scope, isolationScope, globalScope, "test")) + val transaction = createTransaction("aTransaction", createTestScopes(options = options, scope = scope, isolationScope = isolationScope, globalScope = globalScope)) combined.setSpanContext(exception, transaction, "aTransaction") verify(scope, never()).setSpanContext(any(), any(), any()) diff --git a/sentry/src/test/java/io/sentry/HubAdapterTest.kt b/sentry/src/test/java/io/sentry/HubAdapterTest.kt index c8e17bfb2b..9c6ff6ddf1 100644 --- a/sentry/src/test/java/io/sentry/HubAdapterTest.kt +++ b/sentry/src/test/java/io/sentry/HubAdapterTest.kt @@ -2,6 +2,7 @@ package io.sentry import io.sentry.protocol.SentryTransaction import io.sentry.protocol.User +import io.sentry.test.createSentryClientMock import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.eq @@ -190,7 +191,7 @@ class HubAdapterTest { } @Test fun `bindClient calls Hub`() { - val client = mock() + val client = createSentryClientMock() HubAdapter.getInstance().bindClient(client) verify(scopes).bindClient(eq(client)) } diff --git a/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt b/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt index 38fc9875b0..19123a23ed 100644 --- a/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt +++ b/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt @@ -2,6 +2,7 @@ package io.sentry import io.sentry.protocol.SentryTransaction import io.sentry.protocol.User +import io.sentry.test.createSentryClientMock import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.eq @@ -190,7 +191,7 @@ class ScopesAdapterTest { } @Test fun `bindClient calls Scopes`() { - val client = mock() + val client = createSentryClientMock() ScopesAdapter.getInstance().bindClient(client) verify(scopes).bindClient(eq(client)) } diff --git a/sentry/src/test/java/io/sentry/ScopesTest.kt b/sentry/src/test/java/io/sentry/ScopesTest.kt index 5bcecc31f6..33a0ce90a8 100644 --- a/sentry/src/test/java/io/sentry/ScopesTest.kt +++ b/sentry/src/test/java/io/sentry/ScopesTest.kt @@ -12,6 +12,8 @@ import io.sentry.protocol.SentryTransaction import io.sentry.protocol.User import io.sentry.test.DeferredExecutorService import io.sentry.test.callMethod +import io.sentry.test.createSentryClientMock +import io.sentry.test.createTestScopes import io.sentry.util.HintUtils import io.sentry.util.StringUtils import org.mockito.kotlin.any @@ -68,7 +70,9 @@ class ScopesTest { } private fun createScopes(options: SentryOptions): Scopes { - return Scopes(Scope(options), Scope(options), Scope(options), "test") + return createTestScopes(options).also { + it.bindClient(SentryClient(options)) + } } @Test @@ -244,7 +248,7 @@ class ScopesTest { options.setSerializer(mock()) val sut = createScopes(options) var breadcrumbs: Queue? = null - sut.configureScope { breadcrumbs = it.breadcrumbs } + sut.configureScope(ScopeType.COMBINED) { breadcrumbs = it.breadcrumbs } sut.close() sut.addBreadcrumb(Breadcrumb()) assertTrue(breadcrumbs!!.isEmpty()) @@ -1279,7 +1283,7 @@ class ScopesTest { options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock(enabled = false) sut.bindClient(mockClient) sut.close() @@ -1294,7 +1298,7 @@ class ScopesTest { options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock() sut.bindClient(mockClient) val envelope = SentryEnvelope(SentryId(UUID.randomUUID()), null, setOf()) @@ -1309,7 +1313,7 @@ class ScopesTest { setSerializer(mock()) } val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock() sut.bindClient(mockClient) whenever(mockClient.captureEnvelope(any(), anyOrNull())).thenReturn(SentryId()) val envelope = SentryEnvelope(SentryId(UUID.randomUUID()), null, setOf()) @@ -1327,10 +1331,14 @@ class ScopesTest { options.release = "0.0.1" options.setSerializer(mock()) val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock() sut.bindClient(mockClient) sut.close() + sut.configureScope(ScopeType.ISOLATION) { scope -> + scope.client.isEnabled + } + sut.startSession() verify(mockClient, never()).captureSession(any(), any()) } @@ -1343,7 +1351,7 @@ class ScopesTest { options.release = "0.0.1" options.setSerializer(mock()) val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock() sut.bindClient(mockClient) sut.startSession() @@ -1358,7 +1366,7 @@ class ScopesTest { options.release = "0.0.1" options.setSerializer(mock()) val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock() sut.bindClient(mockClient) sut.startSession() @@ -1377,7 +1385,7 @@ class ScopesTest { options.release = "0.0.1" options.setSerializer(mock()) val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock(enabled = false) sut.bindClient(mockClient) sut.close() @@ -1393,7 +1401,7 @@ class ScopesTest { options.release = "0.0.1" options.setSerializer(mock()) val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock() sut.bindClient(mockClient) sut.endSession() @@ -1408,7 +1416,7 @@ class ScopesTest { options.release = "0.0.1" options.setSerializer(mock()) val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock() sut.bindClient(mockClient) sut.startSession() @@ -1425,7 +1433,7 @@ class ScopesTest { options.release = "0.0.1" options.setSerializer(mock()) val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock() sut.bindClient(mockClient) sut.endSession() @@ -1441,7 +1449,7 @@ class ScopesTest { options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock() sut.bindClient(mockClient) sut.close() @@ -1459,7 +1467,7 @@ class ScopesTest { options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock() sut.bindClient(mockClient) val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), sut) @@ -1475,7 +1483,7 @@ class ScopesTest { setSerializer(mock()) } val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock() sut.bindClient(mockClient) whenever(mockClient.captureTransaction(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())).thenReturn(SentryId()) @@ -1491,7 +1499,7 @@ class ScopesTest { options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock() sut.bindClient(mockClient) val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), sut) @@ -1506,7 +1514,7 @@ class ScopesTest { options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock() sut.bindClient(mockClient) val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(false)), sut) @@ -1522,7 +1530,7 @@ class ScopesTest { options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock() sut.bindClient(mockClient) val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(false)), sut) @@ -1541,7 +1549,7 @@ class ScopesTest { options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock() sut.bindClient(mockClient) val mockBackpressureMonitor = mock() options.backpressureMonitor = mockBackpressureMonitor @@ -1562,7 +1570,7 @@ class ScopesTest { @Test fun `when startTransaction and profiling is enabled, transaction is profiled only if sampled`() { val mockTransactionProfiler = mock() - val mockClient = mock() + val mockClient = createSentryClientMock() whenever(mockTransactionProfiler.onTransactionFinish(any(), anyOrNull(), anyOrNull())).thenAnswer { mockClient.captureEnvelope(mock()) } val scopes = generateScopes { it.setTransactionProfiler(mockTransactionProfiler) @@ -1584,7 +1592,7 @@ class ScopesTest { @Test fun `when startTransaction and is sampled but profiling is disabled, transaction is not profiled`() { val mockTransactionProfiler = mock() - val mockClient = mock() + val mockClient = createSentryClientMock() whenever(mockTransactionProfiler.onTransactionFinish(any(), anyOrNull(), anyOrNull())).thenAnswer { mockClient.captureEnvelope(mock()) } val scopes = generateScopes { it.profilesSampleRate = 0.0 @@ -1785,6 +1793,7 @@ class ScopesTest { // we have to clone the scope, so its isEnabled returns true, but it's still built up from // the old scope preserving its data val clone = sut.forkedScopes("test") + clone.bindClient(createSentryClientMock(enabled = true)) var oldScope: IScope? = null clone.configureScope { scope -> oldScope = scope } assertNull(oldScope!!.transaction) @@ -2166,6 +2175,24 @@ class ScopesTest { assertEquals(span.spanContext.parentSpanId, txn.spanContext.spanId) } + @Test + fun `is considered enabled if client is enabled()`() { + val scopes = generateScopes() as Scopes + val client = mock() + whenever(client.isEnabled).thenReturn(true) + scopes.bindClient(client) + assertTrue(scopes.isEnabled) + } + + @Test + fun `is considered disabled if client is disabled()`() { + val scopes = generateScopes() as Scopes + val client = mock() + whenever(client.isEnabled).thenReturn(false) + scopes.bindClient(client) + assertFalse(scopes.isEnabled) + } + private val dsnTest = "https://key@sentry.io/proj" private fun generateScopes(optionsConfiguration: Sentry.OptionsConfiguration? = null): IScopes { @@ -2191,7 +2218,7 @@ class ScopesTest { options.setLogger(logger) val sut = createScopes(options) - val mockClient = mock() + val mockClient = createSentryClientMock() sut.bindClient(mockClient) return Triple(sut, mockClient, logger) } diff --git a/sentry/src/test/java/io/sentry/SentryTest.kt b/sentry/src/test/java/io/sentry/SentryTest.kt index b1316158b5..28305b8eec 100644 --- a/sentry/src/test/java/io/sentry/SentryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTest.kt @@ -15,6 +15,7 @@ import io.sentry.protocol.SdkVersion import io.sentry.protocol.SentryId import io.sentry.protocol.SentryThread import io.sentry.test.ImmediateExecutorService +import io.sentry.test.createSentryClientMock import io.sentry.util.PlatformTestManipulator import io.sentry.util.thread.IMainThreadChecker import io.sentry.util.thread.MainThreadChecker @@ -266,7 +267,7 @@ class SentryTest { fun `captureUserFeedback gets forwarded to client`() { Sentry.init { it.dsn = dsn } - val client = mock() + val client = createSentryClientMock() Sentry.getCurrentScopes().bindClient(client) val userFeedback = UserFeedback(SentryId.EMPTY_ID) @@ -860,7 +861,7 @@ class SentryTest { fun `captureCheckIn gets forwarded to client`() { Sentry.init { it.dsn = dsn } - val client = mock() + val client = createSentryClientMock() Sentry.getCurrentScopes().bindClient(client) val checkIn = CheckIn("some_slug", CheckInStatus.OK) diff --git a/sentry/src/test/java/io/sentry/SentryTracerTest.kt b/sentry/src/test/java/io/sentry/SentryTracerTest.kt index ccd96a2543..d3404a853c 100644 --- a/sentry/src/test/java/io/sentry/SentryTracerTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTracerTest.kt @@ -2,6 +2,7 @@ package io.sentry import io.sentry.protocol.TransactionNameSource import io.sentry.protocol.User +import io.sentry.test.createTestScopes import io.sentry.util.thread.IMainThreadChecker import org.awaitility.kotlin.await import org.mockito.kotlin.any @@ -36,9 +37,8 @@ class SentryTracerTest { options.dsn = "https://key@sentry.io/proj" options.environment = "environment" options.release = "release@3.0.0" - scopes = spy(Scopes(Scope(options), Scope(options), Scope(options), "test")) + scopes = spy(createTestScopes(options)) transactionPerformanceCollector = spy(DefaultTransactionPerformanceCollector(options)) - scopes.bindClient(mock()) } fun getSut( diff --git a/sentry/src/test/java/io/sentry/StackTest.kt b/sentry/src/test/java/io/sentry/StackTest.kt index 13089ab6a4..c8b0aa8a9f 100644 --- a/sentry/src/test/java/io/sentry/StackTest.kt +++ b/sentry/src/test/java/io/sentry/StackTest.kt @@ -1,6 +1,7 @@ package io.sentry import io.sentry.Stack.StackItem +import io.sentry.test.createSentryClientMock import org.mockito.kotlin.mock import kotlin.test.Test import kotlin.test.assertEquals @@ -10,7 +11,7 @@ class StackTest { private class Fixture { val options = SentryOptions() - val client = mock() + val client = createSentryClientMock() val scope = Scope(options) lateinit var rootItem: StackItem diff --git a/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt b/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt index 51e6d32914..93b84c9892 100644 --- a/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt @@ -5,6 +5,7 @@ import io.sentry.exception.ExceptionMechanismException import io.sentry.hints.DiskFlushNotification import io.sentry.hints.EventDropReason.MULTITHREADED_DEDUPLICATION import io.sentry.protocol.SentryId +import io.sentry.test.createTestScopes import io.sentry.util.HintUtils import org.mockito.kotlin.any import org.mockito.kotlin.argThat @@ -105,7 +106,7 @@ class UncaughtExceptionHandlerIntegrationTest { options.addIntegration(integrationMock) options.cacheDirPath = fixture.file.absolutePath options.setSerializer(mock()) - val scopes = Scopes(Scope(options), Scope(options), Scope(options), "test") + val scopes = createTestScopes(options) scopes.close() verify(integrationMock).close() } diff --git a/sentry/src/test/java/io/sentry/metrics/MetricsIntegrationTest.kt b/sentry/src/test/java/io/sentry/metrics/MetricsIntegrationTest.kt index 56d40f5b86..bf7a1f0f43 100644 --- a/sentry/src/test/java/io/sentry/metrics/MetricsIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/metrics/MetricsIntegrationTest.kt @@ -70,6 +70,7 @@ class MetricsIntegrationTest { Sentry.init(options) val client = mock() + whenever(client.isEnabled).thenReturn(true) val aggregator = MetricsAggregator(options, client) whenever(client.metricsAggregator).thenReturn(aggregator) Sentry.bindClient(client) From 9b6175897ac17bf9918b3f33785b5fef058df30f Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 2 May 2024 14:39:44 +0200 Subject: [PATCH 41/91] Hubs/Scopes Merge 41 - Use `SentryOptions.empty()` (#3387) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty --- sentry/src/main/java/io/sentry/Sentry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 4c751f68f1..05cba51dc0 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -55,7 +55,7 @@ private Sentry() {} */ // TODO [HSM] use SentryOptions.empty and address // https://github.com/getsentry/sentry-java/issues/2541 - private static volatile @NotNull IScope globalScope = new Scope(new SentryOptions()); + private static volatile @NotNull IScope globalScope = new Scope(SentryOptions.empty()); /** Default value for globalHubMode is false */ private static final boolean GLOBAL_HUB_DEFAULT_MODE = false; From 05ff87832274bd6598a949d54861b1b966edbdd1 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Thu, 2 May 2024 15:17:07 +0200 Subject: [PATCH 42/91] Hubs/Scopes Merge 42 - Remove `Hub` (#3389) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub --- .../io/sentry/android/core/SentryAndroid.java | 4 - .../jakarta/webflux/SentryScheduleHook.java | 1 - .../spring/webflux/SentryScheduleHook.java | 1 - sentry/api/sentry.api | 66 - sentry/src/main/java/io/sentry/Hub.java | 1069 -------- sentry/src/main/java/io/sentry/Scope.java | 3 +- sentry/src/test/java/io/sentry/HubTest.kt | 2149 ----------------- 7 files changed, 1 insertion(+), 3292 deletions(-) delete mode 100644 sentry/src/main/java/io/sentry/Hub.java delete mode 100644 sentry/src/test/java/io/sentry/HubTest.kt diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java index b677cc5e34..424de4d82e 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java @@ -24,10 +24,6 @@ /** Sentry initialization class */ public final class SentryAndroid { - static { - Sentry.getGlobalScope().replaceOptions(new SentryAndroidOptions()); - } - // SystemClock.uptimeMillis() isn't affected by phone provider or clock changes. private static final long sdkInitMillis = SystemClock.uptimeMillis(); diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryScheduleHook.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryScheduleHook.java index 21b35bc60b..57a74732ea 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryScheduleHook.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryScheduleHook.java @@ -14,7 +14,6 @@ @ApiStatus.Experimental public final class SentryScheduleHook implements Function { @Override - @SuppressWarnings("deprecation") public Runnable apply(final @NotNull Runnable runnable) { final IScopes newScopes = Sentry.getCurrentScopes().forkedCurrentScope("spring.scheduleHook"); diff --git a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryScheduleHook.java b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryScheduleHook.java index 4f8312835a..50839e653e 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryScheduleHook.java +++ b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryScheduleHook.java @@ -14,7 +14,6 @@ @ApiStatus.Experimental public final class SentryScheduleHook implements Function { @Override - @SuppressWarnings("deprecation") public Runnable apply(final @NotNull Runnable runnable) { final IScopes newScopes = Sentry.getCurrentScopes().forkedCurrentScope("spring.scheduleHook"); diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 42446f8e58..b201a47d0c 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -517,72 +517,6 @@ public final class io/sentry/HttpStatusCodeRange { public fun isInRange (I)Z } -public final class io/sentry/Hub : io/sentry/IHub, io/sentry/metrics/MetricsApi$IMetricsInterface { - public fun (Lio/sentry/SentryOptions;)V - public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V - public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V - public fun bindClient (Lio/sentry/ISentryClient;)V - public fun captureCheckIn (Lio/sentry/CheckIn;)Lio/sentry/protocol/SentryId; - public fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; - public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; - public fun captureUserFeedback (Lio/sentry/UserFeedback;)V - public fun clearBreadcrumbs ()V - public fun clone ()Lio/sentry/IHub; - public synthetic fun clone ()Ljava/lang/Object; - public fun close ()V - public fun close (Z)V - public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V - public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; - public fun endSession ()V - public fun flush (J)V - public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; - public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; - public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; - public fun getBaggage ()Lio/sentry/BaggageHeader; - public fun getDefaultTagsForMetrics ()Ljava/util/Map; - public fun getGlobalScope ()Lio/sentry/IScope; - public fun getIsolationScope ()Lio/sentry/IScope; - public fun getLastEventId ()Lio/sentry/protocol/SentryId; - public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; - public fun getMetricsAggregator ()Lio/sentry/IMetricsAggregator; - public fun getOptions ()Lio/sentry/SentryOptions; - public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; - public fun getScope ()Lio/sentry/IScope; - public fun getSpan ()Lio/sentry/ISpan; - public fun getTraceparent ()Lio/sentry/SentryTraceHeader; - public fun getTransaction ()Lio/sentry/ITransaction; - public fun isCrashedLastRun ()Ljava/lang/Boolean; - public fun isEnabled ()Z - public fun isHealthy ()Z - public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; - public fun metrics ()Lio/sentry/metrics/MetricsApi; - public fun popScope ()V - public fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; - public fun pushScope ()Lio/sentry/ISentryLifecycleToken; - public fun removeExtra (Ljava/lang/String;)V - public fun removeTag (Ljava/lang/String;)V - public fun reportFullyDisplayed ()V - public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V - public fun setFingerprint (Ljava/util/List;)V - public fun setLevel (Lio/sentry/SentryLevel;)V - public fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V - public fun setTag (Ljava/lang/String;Ljava/lang/String;)V - public fun setTransaction (Ljava/lang/String;)V - public fun setUser (Lio/sentry/protocol/User;)V - public fun startSession ()V - public fun startSpanForMetric (Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ISpan; - public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; - public fun traceHeaders ()Lio/sentry/SentryTraceHeader; - public fun withIsolationScope (Lio/sentry/ScopeCallback;)V - public fun withScope (Lio/sentry/ScopeCallback;)V -} - public final class io/sentry/HubAdapter : io/sentry/IHub { public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V diff --git a/sentry/src/main/java/io/sentry/Hub.java b/sentry/src/main/java/io/sentry/Hub.java deleted file mode 100644 index 63081e6cd2..0000000000 --- a/sentry/src/main/java/io/sentry/Hub.java +++ /dev/null @@ -1,1069 +0,0 @@ -package io.sentry; - -import io.sentry.Stack.StackItem; -import io.sentry.clientreport.DiscardReason; -import io.sentry.hints.SessionEndHint; -import io.sentry.hints.SessionStartHint; -import io.sentry.metrics.LocalMetricsAggregator; -import io.sentry.metrics.MetricsApi; -import io.sentry.protocol.SentryId; -import io.sentry.protocol.SentryTransaction; -import io.sentry.protocol.User; -import io.sentry.transport.RateLimiter; -import io.sentry.util.ExceptionUtils; -import io.sentry.util.HintUtils; -import io.sentry.util.Objects; -import io.sentry.util.Pair; -import io.sentry.util.TracingUtils; -import java.io.Closeable; -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -// TODO [HSM] remove Hub class -@Deprecated -public final class Hub implements IHub, MetricsApi.IMetricsInterface { - - private volatile @NotNull SentryId lastEventId; - private final @NotNull SentryOptions options; - private volatile boolean isEnabled; - private final @NotNull Stack stack; - private final @NotNull TracesSampler tracesSampler; - private final @NotNull Map, String>> throwableToSpan = - Collections.synchronizedMap(new WeakHashMap<>()); - private final @NotNull TransactionPerformanceCollector transactionPerformanceCollector; - private final @NotNull MetricsApi metricsApi; - - public Hub(final @NotNull SentryOptions options) { - this(options, createRootStackItem(options)); - // Integrations are no longer registered on Hub ctor, but on Sentry.init - } - - private Hub(final @NotNull SentryOptions options, final @NotNull Stack stack) { - validateOptions(options); - - this.options = options; - this.tracesSampler = new TracesSampler(options); - this.stack = stack; - this.lastEventId = SentryId.EMPTY_ID; - this.transactionPerformanceCollector = options.getTransactionPerformanceCollector(); - - // Integrations will use this Hub instance once registered. - // Make sure Hub ready to be used then. - this.isEnabled = true; - - this.metricsApi = new MetricsApi(this); - } - - private Hub(final @NotNull SentryOptions options, final @NotNull StackItem rootStackItem) { - this(options, new Stack(options.getLogger(), rootStackItem)); - } - - private static void validateOptions(final @NotNull SentryOptions options) { - Objects.requireNonNull(options, "SentryOptions is required."); - if (options.getDsn() == null || options.getDsn().isEmpty()) { - throw new IllegalArgumentException( - "Hub requires a DSN to be instantiated. Considering using the NoOpHub if no DSN is available."); - } - } - - private static StackItem createRootStackItem(final @NotNull SentryOptions options) { - validateOptions(options); - final IScope scope = new Scope(options); - final ISentryClient client = new SentryClient(options); - return new StackItem(options, client, scope); - } - - @Override - public boolean isEnabled() { - return isEnabled; - } - - @Override - public @NotNull SentryId captureEvent( - final @NotNull SentryEvent event, final @Nullable Hint hint) { - return captureEventInternal(event, hint, null); - } - - @Override - public @NotNull SentryId captureEvent( - final @NotNull SentryEvent event, - final @Nullable Hint hint, - final @NotNull ScopeCallback callback) { - return captureEventInternal(event, hint, callback); - } - - private @NotNull SentryId captureEventInternal( - final @NotNull SentryEvent event, - final @Nullable Hint hint, - final @Nullable ScopeCallback scopeCallback) { - SentryId sentryId = SentryId.EMPTY_ID; - if (!isEnabled()) { - options - .getLogger() - .log( - SentryLevel.WARNING, "Instance is disabled and this 'captureEvent' call is a no-op."); - } else if (event == null) { - options.getLogger().log(SentryLevel.WARNING, "captureEvent called with null parameter."); - } else { - try { - assignTraceContext(event); - final StackItem item = stack.peek(); - - final IScope scope = buildLocalScope(item.getScope(), scopeCallback); - - sentryId = item.getClient().captureEvent(event, scope, hint); - this.lastEventId = sentryId; - } catch (Throwable e) { - options - .getLogger() - .log( - SentryLevel.ERROR, "Error while capturing event with id: " + event.getEventId(), e); - } - } - return sentryId; - } - - @Override - public @NotNull SentryId captureMessage( - final @NotNull String message, final @NotNull SentryLevel level) { - return captureMessageInternal(message, level, null); - } - - @Override - public @NotNull SentryId captureMessage( - final @NotNull String message, - final @NotNull SentryLevel level, - final @NotNull ScopeCallback callback) { - return captureMessageInternal(message, level, callback); - } - - private @NotNull SentryId captureMessageInternal( - final @NotNull String message, - final @NotNull SentryLevel level, - final @Nullable ScopeCallback scopeCallback) { - SentryId sentryId = SentryId.EMPTY_ID; - if (!isEnabled()) { - options - .getLogger() - .log( - SentryLevel.WARNING, - "Instance is disabled and this 'captureMessage' call is a no-op."); - } else if (message == null) { - options.getLogger().log(SentryLevel.WARNING, "captureMessage called with null parameter."); - } else { - try { - final StackItem item = stack.peek(); - - final IScope scope = buildLocalScope(item.getScope(), scopeCallback); - - sentryId = item.getClient().captureMessage(message, level, scope); - } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error while capturing message: " + message, e); - } - } - this.lastEventId = sentryId; - return sentryId; - } - - @ApiStatus.Internal - @Override - public @NotNull SentryId captureEnvelope( - final @NotNull SentryEnvelope envelope, final @Nullable Hint hint) { - Objects.requireNonNull(envelope, "SentryEnvelope is required."); - - SentryId sentryId = SentryId.EMPTY_ID; - if (!isEnabled()) { - options - .getLogger() - .log( - SentryLevel.WARNING, - "Instance is disabled and this 'captureEnvelope' call is a no-op."); - } else { - try { - final SentryId capturedEnvelopeId = - stack.peek().getClient().captureEnvelope(envelope, hint); - if (capturedEnvelopeId != null) { - sentryId = capturedEnvelopeId; - } - } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error while capturing envelope.", e); - } - } - return sentryId; - } - - @Override - public @NotNull SentryId captureException( - final @NotNull Throwable throwable, final @Nullable Hint hint) { - return captureExceptionInternal(throwable, hint, null); - } - - @Override - public @NotNull SentryId captureException( - final @NotNull Throwable throwable, - final @Nullable Hint hint, - final @NotNull ScopeCallback callback) { - - return captureExceptionInternal(throwable, hint, callback); - } - - private @NotNull SentryId captureExceptionInternal( - final @NotNull Throwable throwable, - final @Nullable Hint hint, - final @Nullable ScopeCallback scopeCallback) { - SentryId sentryId = SentryId.EMPTY_ID; - if (!isEnabled()) { - options - .getLogger() - .log( - SentryLevel.WARNING, - "Instance is disabled and this 'captureException' call is a no-op."); - } else if (throwable == null) { - options.getLogger().log(SentryLevel.WARNING, "captureException called with null parameter."); - } else { - try { - final StackItem item = stack.peek(); - final SentryEvent event = new SentryEvent(throwable); - assignTraceContext(event); - - final IScope scope = buildLocalScope(item.getScope(), scopeCallback); - - sentryId = item.getClient().captureEvent(event, scope, hint); - } catch (Throwable e) { - options - .getLogger() - .log( - SentryLevel.ERROR, "Error while capturing exception: " + throwable.getMessage(), e); - } - } - this.lastEventId = sentryId; - return sentryId; - } - - private void assignTraceContext(final @NotNull SentryEvent event) { - if (options.isTracingEnabled() && event.getThrowable() != null) { - final Pair, String> pair = - throwableToSpan.get(ExceptionUtils.findRootCause(event.getThrowable())); - if (pair != null) { - final WeakReference spanWeakRef = pair.getFirst(); - if (event.getContexts().getTrace() == null && spanWeakRef != null) { - final ISpan span = spanWeakRef.get(); - if (span != null) { - event.getContexts().setTrace(span.getSpanContext()); - } - } - final String transactionName = pair.getSecond(); - if (event.getTransaction() == null && transactionName != null) { - event.setTransaction(transactionName); - } - } - } - } - - @Override - public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { - if (!isEnabled()) { - options - .getLogger() - .log( - SentryLevel.WARNING, - "Instance is disabled and this 'captureUserFeedback' call is a no-op."); - } else { - try { - final StackItem item = stack.peek(); - item.getClient().captureUserFeedback(userFeedback); - } catch (Throwable e) { - options - .getLogger() - .log( - SentryLevel.ERROR, - "Error while capturing captureUserFeedback: " + userFeedback.toString(), - e); - } - } - } - - @Override - public void startSession() { - if (!isEnabled()) { - options - .getLogger() - .log( - SentryLevel.WARNING, "Instance is disabled and this 'startSession' call is a no-op."); - } else { - final StackItem item = this.stack.peek(); - final Scope.SessionPair pair = item.getScope().startSession(); - if (pair != null) { - // TODO: add helper overload `captureSessions` to pass a list of sessions and submit a - // single envelope - // Or create the envelope here with both items and call `captureEnvelope` - if (pair.getPrevious() != null) { - final Hint hint = HintUtils.createWithTypeCheckHint(new SessionEndHint()); - - item.getClient().captureSession(pair.getPrevious(), hint); - } - - final Hint hint = HintUtils.createWithTypeCheckHint(new SessionStartHint()); - - item.getClient().captureSession(pair.getCurrent(), hint); - } else { - options.getLogger().log(SentryLevel.WARNING, "Session could not be started."); - } - } - } - - @Override - public void endSession() { - if (!isEnabled()) { - options - .getLogger() - .log(SentryLevel.WARNING, "Instance is disabled and this 'endSession' call is a no-op."); - } else { - final StackItem item = this.stack.peek(); - final Session previousSession = item.getScope().endSession(); - if (previousSession != null) { - final Hint hint = HintUtils.createWithTypeCheckHint(new SessionEndHint()); - - item.getClient().captureSession(previousSession, hint); - } - } - } - - @Override - public void close() { - close(false); - } - - @Override - @SuppressWarnings("FutureReturnValueIgnored") - public void close(final boolean isRestarting) { - if (!isEnabled()) { - options - .getLogger() - .log(SentryLevel.WARNING, "Instance is disabled and this 'close' call is a no-op."); - } else { - try { - for (Integration integration : options.getIntegrations()) { - if (integration instanceof Closeable) { - try { - ((Closeable) integration).close(); - } catch (IOException e) { - options - .getLogger() - .log(SentryLevel.WARNING, "Failed to close the integration {}.", integration, e); - } - } - } - - configureScope(scope -> scope.clear()); - options.getTransactionProfiler().close(); - options.getTransactionPerformanceCollector().close(); - final @NotNull ISentryExecutorService executorService = options.getExecutorService(); - if (isRestarting) { - executorService.submit(() -> executorService.close(options.getShutdownTimeoutMillis())); - } else { - executorService.close(options.getShutdownTimeoutMillis()); - } - - // Close the top-most client - final StackItem item = stack.peek(); - // TODO: should we end session before closing client? - item.getClient().close(isRestarting); - } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error while closing the Hub.", e); - } - isEnabled = false; - } - } - - @Override - public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb, final @Nullable Hint hint) { - if (!isEnabled()) { - options - .getLogger() - .log( - SentryLevel.WARNING, - "Instance is disabled and this 'addBreadcrumb' call is a no-op."); - } else if (breadcrumb == null) { - options.getLogger().log(SentryLevel.WARNING, "addBreadcrumb called with null parameter."); - } else { - stack.peek().getScope().addBreadcrumb(breadcrumb, hint); - } - } - - @Override - public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb) { - addBreadcrumb(breadcrumb, new Hint()); - } - - @Override - public void setLevel(final @Nullable SentryLevel level) { - if (!isEnabled()) { - options - .getLogger() - .log(SentryLevel.WARNING, "Instance is disabled and this 'setLevel' call is a no-op."); - } else { - stack.peek().getScope().setLevel(level); - } - } - - @Override - public void setTransaction(final @Nullable String transaction) { - if (!isEnabled()) { - options - .getLogger() - .log( - SentryLevel.WARNING, - "Instance is disabled and this 'setTransaction' call is a no-op."); - } else if (transaction != null) { - stack.peek().getScope().setTransaction(transaction); - } else { - options.getLogger().log(SentryLevel.WARNING, "Transaction cannot be null"); - } - } - - @Override - public void setUser(final @Nullable User user) { - if (!isEnabled()) { - options - .getLogger() - .log(SentryLevel.WARNING, "Instance is disabled and this 'setUser' call is a no-op."); - } else { - stack.peek().getScope().setUser(user); - } - } - - @Override - public void setFingerprint(final @NotNull List fingerprint) { - if (!isEnabled()) { - options - .getLogger() - .log( - SentryLevel.WARNING, - "Instance is disabled and this 'setFingerprint' call is a no-op."); - } else if (fingerprint == null) { - options.getLogger().log(SentryLevel.WARNING, "setFingerprint called with null parameter."); - } else { - stack.peek().getScope().setFingerprint(fingerprint); - } - } - - @Override - public void clearBreadcrumbs() { - if (!isEnabled()) { - options - .getLogger() - .log( - SentryLevel.WARNING, - "Instance is disabled and this 'clearBreadcrumbs' call is a no-op."); - } else { - stack.peek().getScope().clearBreadcrumbs(); - } - } - - @Override - public void setTag(final @NotNull String key, final @NotNull String value) { - if (!isEnabled()) { - options - .getLogger() - .log(SentryLevel.WARNING, "Instance is disabled and this 'setTag' call is a no-op."); - } else if (key == null || value == null) { - options.getLogger().log(SentryLevel.WARNING, "setTag called with null parameter."); - } else { - stack.peek().getScope().setTag(key, value); - } - } - - @Override - public void removeTag(final @NotNull String key) { - if (!isEnabled()) { - options - .getLogger() - .log(SentryLevel.WARNING, "Instance is disabled and this 'removeTag' call is a no-op."); - } else if (key == null) { - options.getLogger().log(SentryLevel.WARNING, "removeTag called with null parameter."); - } else { - stack.peek().getScope().removeTag(key); - } - } - - @Override - public void setExtra(final @NotNull String key, final @NotNull String value) { - if (!isEnabled()) { - options - .getLogger() - .log(SentryLevel.WARNING, "Instance is disabled and this 'setExtra' call is a no-op."); - } else if (key == null || value == null) { - options.getLogger().log(SentryLevel.WARNING, "setExtra called with null parameter."); - } else { - stack.peek().getScope().setExtra(key, value); - } - } - - @Override - public void removeExtra(final @NotNull String key) { - if (!isEnabled()) { - options - .getLogger() - .log(SentryLevel.WARNING, "Instance is disabled and this 'removeExtra' call is a no-op."); - } else if (key == null) { - options.getLogger().log(SentryLevel.WARNING, "removeExtra called with null parameter."); - } else { - stack.peek().getScope().removeExtra(key); - } - } - - @Override - public @NotNull SentryId getLastEventId() { - return lastEventId; - } - - @Override - public @NotNull ISentryLifecycleToken pushScope() { - if (!isEnabled()) { - options - .getLogger() - .log(SentryLevel.WARNING, "Instance is disabled and this 'pushScope' call is a no-op."); - } else { - final StackItem item = stack.peek(); - final StackItem newItem = new StackItem(options, item.getClient(), item.getScope().clone()); - stack.push(newItem); - } - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); - } - - @Override - public @NotNull ISentryLifecycleToken pushIsolationScope() { - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); - } - - @Override - public @NotNull SentryOptions getOptions() { - return this.stack.peek().getOptions(); - } - - @Override - public @Nullable Boolean isCrashedLastRun() { - return SentryCrashLastRunState.getInstance() - .isCrashedLastRun(options.getCacheDirPath(), !options.isEnableAutoSessionTracking()); - } - - @Override - public void reportFullyDisplayed() { - if (options.isEnableTimeToFullDisplayTracing()) { - options.getFullyDisplayedReporter().reportFullyDrawn(); - } - } - - @Override - @Deprecated - public void popScope() { - if (!isEnabled()) { - options - .getLogger() - .log(SentryLevel.WARNING, "Instance is disabled and this 'popScope' call is a no-op."); - } else { - stack.pop(); - } - } - - @Override - public void withScope(final @NotNull ScopeCallback callback) { - if (!isEnabled()) { - try { - callback.run(NoOpScope.getInstance()); - } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error in the 'withScope' callback.", e); - } - - } else { - pushScope(); - try { - callback.run(stack.peek().getScope()); - } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error in the 'withScope' callback.", e); - } - popScope(); - } - } - - @Override - public void withIsolationScope(final @NotNull ScopeCallback callback) { - if (!isEnabled()) { - try { - callback.run(NoOpScope.getInstance()); - } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error in the 'withScope' callback.", e); - } - - } else { - pushScope(); - try { - callback.run(stack.peek().getScope()); - } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error in the 'withScope' callback.", e); - } - popScope(); - } - } - - @Override - public void configureScope( - final @Nullable ScopeType scopeType, final @NotNull ScopeCallback callback) { - if (!isEnabled()) { - options - .getLogger() - .log( - SentryLevel.WARNING, - "Instance is disabled and this 'configureScope' call is a no-op."); - } else { - try { - callback.run(stack.peek().getScope()); - } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error in the 'configureScope' callback.", e); - } - } - } - - @Override - public void bindClient(final @NotNull ISentryClient client) { - if (!isEnabled()) { - options - .getLogger() - .log(SentryLevel.WARNING, "Instance is disabled and this 'bindClient' call is a no-op."); - } else { - final StackItem item = stack.peek(); - if (client != null) { - options.getLogger().log(SentryLevel.DEBUG, "New client bound to scope."); - item.setClient(client); - } else { - options.getLogger().log(SentryLevel.DEBUG, "NoOp client bound to scope."); - item.setClient(NoOpSentryClient.getInstance()); - } - } - } - - @Override - public boolean isHealthy() { - return stack.peek().getClient().isHealthy(); - } - - @Override - public void flush(long timeoutMillis) { - if (!isEnabled()) { - options - .getLogger() - .log(SentryLevel.WARNING, "Instance is disabled and this 'flush' call is a no-op."); - } else { - try { - stack.peek().getClient().flush(timeoutMillis); - } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error in the 'client.flush'.", e); - } - } - } - - @Override - public @NotNull IHub clone() { - if (!isEnabled()) { - options.getLogger().log(SentryLevel.WARNING, "Disabled Hub cloned."); - } - // Clone will be invoked in parallel - return new Hub(this.options, new Stack(this.stack)); - } - - @Override - public @NotNull IScopes forkedScopes(@NotNull String creator) { - return Sentry.forkedScopes(creator); - } - - @Override - public @NotNull IScopes forkedCurrentScope(@NotNull String creator) { - return Sentry.forkedCurrentScope(creator); - } - - @Override - public @NotNull IScopes forkedRootScopes(final @NotNull String creator) { - return Sentry.forkedRootScopes(creator); - } - - @Override - public @NotNull ISentryLifecycleToken makeCurrent() { - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getScope() { - return Sentry.getCurrentScopes().getScope(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getIsolationScope() { - return Sentry.getCurrentScopes().getIsolationScope(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getGlobalScope() { - return Sentry.getGlobalScope(); - } - - @ApiStatus.Internal - @Override - public @NotNull SentryId captureTransaction( - final @NotNull SentryTransaction transaction, - final @Nullable TraceContext traceContext, - final @Nullable Hint hint, - final @Nullable ProfilingTraceData profilingTraceData) { - Objects.requireNonNull(transaction, "transaction is required"); - - SentryId sentryId = SentryId.EMPTY_ID; - if (!isEnabled()) { - options - .getLogger() - .log( - SentryLevel.WARNING, - "Instance is disabled and this 'captureTransaction' call is a no-op."); - } else { - if (!transaction.isFinished()) { - options - .getLogger() - .log( - SentryLevel.WARNING, - "Transaction: %s is not finished and this 'captureTransaction' call is a no-op.", - transaction.getEventId()); - } else { - if (!Boolean.TRUE.equals(transaction.isSampled())) { - options - .getLogger() - .log( - SentryLevel.DEBUG, - "Transaction %s was dropped due to sampling decision.", - transaction.getEventId()); - if (options.getBackpressureMonitor().getDownsampleFactor() > 0) { - options - .getClientReportRecorder() - .recordLostEvent(DiscardReason.BACKPRESSURE, DataCategory.Transaction); - } else { - options - .getClientReportRecorder() - .recordLostEvent(DiscardReason.SAMPLE_RATE, DataCategory.Transaction); - } - } else { - StackItem item = null; - try { - item = stack.peek(); - sentryId = - item.getClient() - .captureTransaction( - transaction, traceContext, item.getScope(), hint, profilingTraceData); - } catch (Throwable e) { - options - .getLogger() - .log( - SentryLevel.ERROR, - "Error while capturing transaction with id: " + transaction.getEventId(), - e); - } - } - } - } - return sentryId; - } - - @Override - public @NotNull ITransaction startTransaction( - final @NotNull TransactionContext transactionContext, - final @NotNull TransactionOptions transactionOptions) { - return createTransaction(transactionContext, transactionOptions); - } - - private @NotNull ITransaction createTransaction( - final @NotNull TransactionContext transactionContext, - final @NotNull TransactionOptions transactionOptions) { - Objects.requireNonNull(transactionContext, "transactionContext is required"); - - ITransaction transaction; - if (!isEnabled()) { - options - .getLogger() - .log( - SentryLevel.WARNING, - "Instance is disabled and this 'startTransaction' returns a no-op."); - transaction = NoOpTransaction.getInstance(); - } else if (!options.getInstrumenter().equals(transactionContext.getInstrumenter())) { - options - .getLogger() - .log( - SentryLevel.DEBUG, - "Returning no-op for instrumenter %s as the SDK has been configured to use instrumenter %s", - transactionContext.getInstrumenter(), - options.getInstrumenter()); - transaction = NoOpTransaction.getInstance(); - } else if (!options.isTracingEnabled()) { - options - .getLogger() - .log( - SentryLevel.INFO, "Tracing is disabled and this 'startTransaction' returns a no-op."); - transaction = NoOpTransaction.getInstance(); - } else { - final SamplingContext samplingContext = - new SamplingContext(transactionContext, transactionOptions.getCustomSamplingContext()); - @NotNull TracesSamplingDecision samplingDecision = tracesSampler.sample(samplingContext); - transactionContext.setSamplingDecision(samplingDecision); - - transaction = - new SentryTracer( - transactionContext, this, transactionOptions, transactionPerformanceCollector); - - // The listener is called only if the transaction exists, as the transaction is needed to - // stop it - if (samplingDecision.getSampled() && samplingDecision.getProfileSampled()) { - final ITransactionProfiler transactionProfiler = options.getTransactionProfiler(); - // If the profiler is not running, we start and bind it here. - if (!transactionProfiler.isRunning()) { - transactionProfiler.start(); - transactionProfiler.bindTransaction(transaction); - } else if (transactionOptions.isAppStartTransaction()) { - // If the profiler is running and the current transaction is the app start, we bind it. - transactionProfiler.bindTransaction(transaction); - } - } - } - if (transactionOptions.isBindToScope()) { - configureScope(scope -> scope.setTransaction(transaction)); - } - return transaction; - } - - @Deprecated - @SuppressWarnings("InlineMeSuggester") - @Override - public @Nullable SentryTraceHeader traceHeaders() { - return getTraceparent(); - } - - @Override - public @Nullable ISpan getSpan() { - ISpan span = null; - if (!isEnabled()) { - options - .getLogger() - .log(SentryLevel.WARNING, "Instance is disabled and this 'getSpan' call is a no-op."); - } else { - span = stack.peek().getScope().getSpan(); - } - return span; - } - - @Override - @ApiStatus.Internal - public @Nullable ITransaction getTransaction() { - ITransaction span = null; - if (!isEnabled()) { - options - .getLogger() - .log( - SentryLevel.WARNING, - "Instance is disabled and this 'getTransaction' call is a no-op."); - } else { - span = stack.peek().getScope().getTransaction(); - } - return span; - } - - @Override - @ApiStatus.Internal - public void setSpanContext( - final @NotNull Throwable throwable, - final @NotNull ISpan span, - final @NotNull String transactionName) { - Objects.requireNonNull(throwable, "throwable is required"); - Objects.requireNonNull(span, "span is required"); - Objects.requireNonNull(transactionName, "transactionName is required"); - // to match any cause, span context is always attached to the root cause of the exception - final Throwable rootCause = ExceptionUtils.findRootCause(throwable); - // the most inner span should be assigned to a throwable - if (!throwableToSpan.containsKey(rootCause)) { - throwableToSpan.put(rootCause, new Pair<>(new WeakReference<>(span), transactionName)); - } - } - - @Nullable - SpanContext getSpanContext(final @NotNull Throwable throwable) { - Objects.requireNonNull(throwable, "throwable is required"); - final Throwable rootCause = ExceptionUtils.findRootCause(throwable); - final Pair, String> pair = this.throwableToSpan.get(rootCause); - if (pair != null) { - final WeakReference spanWeakRef = pair.getFirst(); - if (spanWeakRef != null) { - final ISpan span = spanWeakRef.get(); - if (span != null) { - return span.getSpanContext(); - } - } - } - return null; - } - - private IScope buildLocalScope( - final @NotNull IScope scope, final @Nullable ScopeCallback callback) { - if (callback != null) { - try { - final IScope localScope = scope.clone(); - callback.run(localScope); - return localScope; - } catch (Throwable t) { - options.getLogger().log(SentryLevel.ERROR, "Error in the 'ScopeCallback' callback.", t); - } - } - return scope; - } - - @Override - public @Nullable TransactionContext continueTrace( - final @Nullable String sentryTrace, final @Nullable List baggageHeaders) { - @NotNull - PropagationContext propagationContext = - PropagationContext.fromHeaders(getOptions().getLogger(), sentryTrace, baggageHeaders); - configureScope( - (scope) -> { - scope.setPropagationContext(propagationContext); - }); - if (options.isTracingEnabled()) { - return TransactionContext.fromPropagationContext(propagationContext); - } else { - return null; - } - } - - @Override - public @Nullable SentryTraceHeader getTraceparent() { - if (!isEnabled()) { - options - .getLogger() - .log( - SentryLevel.WARNING, - "Instance is disabled and this 'getTraceparent' call is a no-op."); - } else { - final @Nullable TracingUtils.TracingHeaders headers = - TracingUtils.trace(this, null, getSpan()); - if (headers != null) { - return headers.getSentryTraceHeader(); - } - } - - return null; - } - - @Override - public @Nullable BaggageHeader getBaggage() { - if (!isEnabled()) { - options - .getLogger() - .log(SentryLevel.WARNING, "Instance is disabled and this 'getBaggage' call is a no-op."); - } else { - final @Nullable TracingUtils.TracingHeaders headers = - TracingUtils.trace(this, null, getSpan()); - if (headers != null) { - return headers.getBaggageHeader(); - } - } - - return null; - } - - @Override - @ApiStatus.Experimental - public @NotNull SentryId captureCheckIn(final @NotNull CheckIn checkIn) { - SentryId sentryId = SentryId.EMPTY_ID; - if (!isEnabled()) { - options - .getLogger() - .log( - SentryLevel.WARNING, - "Instance is disabled and this 'captureCheckIn' call is a no-op."); - } else { - try { - StackItem item = stack.peek(); - sentryId = item.getClient().captureCheckIn(checkIn, item.getScope(), null); - } catch (Throwable e) { - options.getLogger().log(SentryLevel.ERROR, "Error while capturing check-in for slug", e); - } - } - this.lastEventId = sentryId; - return sentryId; - } - - @ApiStatus.Internal - @Override - public @Nullable RateLimiter getRateLimiter() { - final StackItem item = stack.peek(); - return item.getClient().getRateLimiter(); - } - - @Override - public @NotNull MetricsApi metrics() { - return metricsApi; - } - - @Override - public @NotNull IMetricsAggregator getMetricsAggregator() { - return stack.peek().getClient().getMetricsAggregator(); - } - - @Override - public @NotNull Map getDefaultTagsForMetrics() { - if (!options.isEnableDefaultTagsForMetrics()) { - return Collections.emptyMap(); - } - - final @NotNull Map tags = new HashMap<>(); - final @Nullable String release = options.getRelease(); - if (release != null) { - tags.put("release", release); - } - - final @Nullable String environment = options.getEnvironment(); - if (environment != null) { - tags.put("environment", environment); - } - - final @Nullable String txnName = stack.peek().getScope().getTransactionName(); - if (txnName != null) { - tags.put("transaction", txnName); - } - return Collections.unmodifiableMap(tags); - } - - @Override - public @Nullable ISpan startSpanForMetric(@NotNull String op, @NotNull String description) { - final @Nullable ISpan span = getSpan(); - if (span != null) { - return span.startChild(op, description); - } - return null; - } - - @Override - public @Nullable LocalMetricsAggregator getLocalMetricsAggregator() { - if (!options.isEnableSpanLocalMetricAggregation()) { - return null; - } - final @Nullable ISpan span = getSpan(); - if (span != null) { - return span.getLocalMetricsAggregator(); - } - return null; - } -} diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index 3df40a2b01..0d42326e13 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -116,7 +116,6 @@ private Scope(final @NotNull Scope scope) { this.options = scope.options; this.level = scope.level; this.client = scope.client; - // TODO [HSM] should we do this? didn't do it for Hub this.lastEventId = scope.getLastEventId(); final User userRef = scope.user; @@ -838,7 +837,7 @@ public SessionPair startSession() { SessionPair pair = null; synchronized (sessionLock) { if (session != null) { - // Assumes session will NOT flush itself (Not passing any hub to it) + // Assumes session will NOT flush itself (Not passing any scopes to it) session.end(); } previousSession = session; diff --git a/sentry/src/test/java/io/sentry/HubTest.kt b/sentry/src/test/java/io/sentry/HubTest.kt deleted file mode 100644 index f30fd0a966..0000000000 --- a/sentry/src/test/java/io/sentry/HubTest.kt +++ /dev/null @@ -1,2149 +0,0 @@ -package io.sentry - -import io.sentry.backpressure.IBackpressureMonitor -import io.sentry.cache.EnvelopeCache -import io.sentry.clientreport.ClientReportTestHelper.Companion.assertClientReport -import io.sentry.clientreport.DiscardReason -import io.sentry.clientreport.DiscardedEvent -import io.sentry.hints.SessionEndHint -import io.sentry.hints.SessionStartHint -import io.sentry.protocol.SentryId -import io.sentry.protocol.SentryTransaction -import io.sentry.protocol.User -import io.sentry.test.DeferredExecutorService -import io.sentry.test.callMethod -import io.sentry.util.HintUtils -import io.sentry.util.StringUtils -import org.mockito.kotlin.any -import org.mockito.kotlin.anyOrNull -import org.mockito.kotlin.argWhere -import org.mockito.kotlin.argumentCaptor -import org.mockito.kotlin.check -import org.mockito.kotlin.doAnswer -import org.mockito.kotlin.doThrow -import org.mockito.kotlin.eq -import org.mockito.kotlin.mock -import org.mockito.kotlin.never -import org.mockito.kotlin.spy -import org.mockito.kotlin.times -import org.mockito.kotlin.verify -import org.mockito.kotlin.verifyNoMoreInteractions -import org.mockito.kotlin.whenever -import java.io.File -import java.nio.file.Files -import java.util.Queue -import java.util.UUID -import java.util.concurrent.atomic.AtomicReference -import kotlin.test.AfterTest -import kotlin.test.BeforeTest -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertFalse -import kotlin.test.assertNotEquals -import kotlin.test.assertNotNull -import kotlin.test.assertNull -import kotlin.test.assertTrue -import kotlin.test.fail - -class HubTest { - - private lateinit var file: File - private lateinit var profilingTraceFile: File - - @BeforeTest - fun `set up`() { - file = Files.createTempDirectory("sentry-disk-cache-test").toAbsolutePath().toFile() - profilingTraceFile = Files.createTempFile("trace", ".trace").toFile() - profilingTraceFile.writeText("sampledProfile") - } - - @AfterTest - fun shutdown() { - file.deleteRecursively() - profilingTraceFile.delete() - Sentry.close() - } - - @Test - fun `when no dsn available, ctor throws illegal arg`() { - val ex = assertFailsWith { Hub(SentryOptions()) } - assertEquals("Hub requires a DSN to be instantiated. Considering using the NoOpHub if no DSN is available.", ex.message) - } - - @Test - fun `when scopes is cloned, integrations are not registered`() { - val integrationMock = mock() - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - options.addIntegration(integrationMock) -// val expected = HubAdapter.getInstance() - val scopes = Hub(options) -// verify(integrationMock).register(expected, options) - scopes.clone() - verifyNoMoreInteractions(integrationMock) - } - - @Test - fun `when scopes is cloned, scope changes are isolated`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val scopes = Hub(options) - var firstScope: IScope? = null - scopes.configureScope { - firstScope = it - it.setTag("scopes", "a") - } - var cloneScope: IScope? = null - val clone = scopes.clone() - clone.configureScope { - cloneScope = it - it.setTag("scopes", "b") - } - assertEquals("a", firstScope!!.tags["scopes"]) - assertEquals("b", cloneScope!!.tags["scopes"]) - } - - @Test - fun `when scopes is initialized, breadcrumbs are capped as per options`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.maxBreadcrumbs = 5 - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - (1..10).forEach { _ -> sut.addBreadcrumb(Breadcrumb(), null) } - var actual = 0 - sut.configureScope { - actual = it.breadcrumbs.size - } - assertEquals(options.maxBreadcrumbs, actual) - } - - @Test - fun `when beforeBreadcrumb returns null, crumb is dropped`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.beforeBreadcrumb = SentryOptions.BeforeBreadcrumbCallback { _: Breadcrumb, _: Any? -> null } - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - sut.addBreadcrumb(Breadcrumb(), null) - var breadcrumbs: Queue? = null - sut.configureScope { breadcrumbs = it.breadcrumbs } - assertEquals(0, breadcrumbs!!.size) - } - - @Test - fun `when beforeBreadcrumb modifies crumb, crumb is stored modified`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - val expected = "expected" - options.beforeBreadcrumb = SentryOptions.BeforeBreadcrumbCallback { breadcrumb: Breadcrumb, _: Any? -> breadcrumb.message = expected; breadcrumb; } - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - val crumb = Breadcrumb() - crumb.message = "original" - sut.addBreadcrumb(crumb) - var breadcrumbs: Queue? = null - sut.configureScope { breadcrumbs = it.breadcrumbs } - assertEquals(expected, breadcrumbs!!.first().message) - } - - @Test - fun `when beforeBreadcrumb is null, crumb is stored`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.beforeBreadcrumb = null - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - val expected = Breadcrumb() - sut.addBreadcrumb(expected) - var breadcrumbs: Queue? = null - sut.configureScope { breadcrumbs = it.breadcrumbs } - assertEquals(expected, breadcrumbs!!.single()) - } - - @Test - fun `when beforeSend throws an exception, breadcrumb adds an entry to the data field with exception message`() { - val exception = Exception("test") - - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.beforeBreadcrumb = SentryOptions.BeforeBreadcrumbCallback { _: Breadcrumb, _: Any? -> throw exception } - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - - val actual = Breadcrumb() - sut.addBreadcrumb(actual) - - assertEquals("test", actual.data["sentry:message"]) - } - - @Test - fun `when initialized, lastEventId is empty`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - assertEquals(SentryId.EMPTY_ID, sut.lastEventId) - } - - @Test - fun `when addBreadcrumb is called on disabled client, no-op`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - var breadcrumbs: Queue? = null - sut.configureScope { breadcrumbs = it.breadcrumbs } - sut.close() - sut.addBreadcrumb(Breadcrumb()) - assertTrue(breadcrumbs!!.isEmpty()) - } - - @Test - fun `when addBreadcrumb is called with message and category, breadcrumb object has values`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - var breadcrumbs: Queue? = null - sut.configureScope { breadcrumbs = it.breadcrumbs } - sut.addBreadcrumb("message", "category") - assertEquals("message", breadcrumbs!!.single().message) - assertEquals("category", breadcrumbs!!.single().category) - } - - @Test - fun `when addBreadcrumb is called with message, breadcrumb object has value`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - var breadcrumbs: Queue? = null - sut.configureScope { breadcrumbs = it.breadcrumbs } - sut.addBreadcrumb("message", "category") - assertEquals("message", breadcrumbs!!.single().message) - assertEquals("category", breadcrumbs!!.single().category) - } - - @Test - fun `when flush is called on disabled client, no-op`() { - val (sut, mockClient) = getEnabledHub() - sut.close() - - sut.flush(1000) - verify(mockClient, never()).flush(1000) - } - - @Test - fun `when flush is called, client flush gets called`() { - val (sut, mockClient) = getEnabledHub() - - sut.flush(1000) - verify(mockClient).flush(1000) - } - - //region captureEvent tests - @Test - fun `when captureEvent is called and event is null, lastEventId is empty`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - sut.callMethod("captureEvent", SentryEvent::class.java, null) - assertEquals(SentryId.EMPTY_ID, sut.lastEventId) - } - - @Test - fun `when captureEvent is called on disabled client, do nothing`() { - val (sut, mockClient) = getEnabledHub() - sut.close() - - sut.captureEvent(SentryEvent()) - verify(mockClient, never()).captureEvent(any(), any()) - } - - @Test - fun `when captureEvent is called with a valid argument, captureEvent on the client should be called`() { - val (sut, mockClient) = getEnabledHub() - - val event = SentryEvent() - val hints = HintUtils.createWithTypeCheckHint({}) - sut.captureEvent(event, hints) - verify(mockClient).captureEvent(eq(event), any(), eq(hints)) - } - - @Test - fun `when captureEvent is called on disabled scopes, lastEventId does not get overwritten`() { - val (sut, mockClient) = getEnabledHub() - whenever(mockClient.captureEvent(any(), any(), anyOrNull())).thenReturn(SentryId(UUID.randomUUID())) - val event = SentryEvent() - val hints = HintUtils.createWithTypeCheckHint({}) - sut.captureEvent(event, hints) - val lastEventId = sut.lastEventId - sut.close() - sut.captureEvent(event, hints) - assertEquals(lastEventId, sut.lastEventId) - } - - @Test - fun `when captureEvent is called and session tracking is disabled, it should not capture a session`() { - val (sut, mockClient) = getEnabledHub() - - val event = SentryEvent() - val hints = HintUtils.createWithTypeCheckHint({}) - sut.captureEvent(event, hints) - verify(mockClient).captureEvent(eq(event), any(), eq(hints)) - verify(mockClient, never()).captureSession(any(), any()) - } - - @Test - fun `when captureEvent is called but no session started, it should not capture a session`() { - val (sut, mockClient) = getEnabledHub() - - val event = SentryEvent() - val hints = HintUtils.createWithTypeCheckHint({}) - sut.captureEvent(event, hints) - verify(mockClient).captureEvent(eq(event), any(), eq(hints)) - verify(mockClient, never()).captureSession(any(), any()) - } - - @Test - fun `when captureEvent is called and event has exception which has been previously attached with span context, sets span context to the event`() { - val (sut, mockClient) = getEnabledHub() - val exception = RuntimeException() - val span = mock() - whenever(span.spanContext).thenReturn(SpanContext("op")) - sut.setSpanContext(exception, span, "tx-name") - - val event = SentryEvent(exception) - - val hints = HintUtils.createWithTypeCheckHint({}) - sut.captureEvent(event, hints) - assertEquals(span.spanContext, event.contexts.trace) - verify(mockClient).captureEvent(eq(event), any(), eq(hints)) - } - - @Test - fun `when captureEvent is called and event has exception which root cause has been previously attached with span context, sets span context to the event`() { - val (sut, mockClient) = getEnabledHub() - val rootCause = RuntimeException() - val span = mock() - whenever(span.spanContext).thenReturn(SpanContext("op")) - sut.setSpanContext(rootCause, span, "tx-name") - - val event = SentryEvent(RuntimeException(rootCause)) - - val hints = HintUtils.createWithTypeCheckHint({}) - sut.captureEvent(event, hints) - assertEquals(span.spanContext, event.contexts.trace) - verify(mockClient).captureEvent(eq(event), any(), eq(hints)) - } - - @Test - fun `when captureEvent is called and event has exception which non-root cause has been previously attached with span context, sets span context to the event`() { - val (sut, mockClient) = getEnabledHub() - val rootCause = RuntimeException() - val exceptionAssignedToSpan = RuntimeException(rootCause) - val span = mock() - whenever(span.spanContext).thenReturn(SpanContext("op")) - sut.setSpanContext(exceptionAssignedToSpan, span, "tx-name") - - val event = SentryEvent(RuntimeException(exceptionAssignedToSpan)) - - val hints = HintUtils.createWithTypeCheckHint({}) - sut.captureEvent(event, hints) - assertEquals(span.spanContext, event.contexts.trace) - verify(mockClient).captureEvent(eq(event), any(), eq(hints)) - } - - @Test - fun `when captureEvent is called and event has exception which has been previously attached with span context and trace context already set, does not set new span context to the event`() { - val (sut, mockClient) = getEnabledHub() - val exception = RuntimeException() - val span = mock() - whenever(span.spanContext).thenReturn(SpanContext("op")) - sut.setSpanContext(exception, span, "tx-name") - - val event = SentryEvent(exception) - val originalSpanContext = SpanContext("op") - event.contexts.trace = originalSpanContext - - val hints = HintUtils.createWithTypeCheckHint({}) - sut.captureEvent(event, hints) - assertEquals(originalSpanContext, event.contexts.trace) - verify(mockClient).captureEvent(eq(event), any(), eq(hints)) - } - - @Test - fun `when captureEvent is called and event has exception which has not been previously attached with span context, does not set new span context to the event`() { - val (sut, mockClient) = getEnabledHub() - - val event = SentryEvent(RuntimeException()) - - val hints = HintUtils.createWithTypeCheckHint({}) - sut.captureEvent(event, hints) - assertNull(event.contexts.trace) - verify(mockClient).captureEvent(eq(event), any(), eq(hints)) - } - - @Test - fun `when captureEvent is called with a ScopeCallback then the modified scope is sent to the client`() { - val (sut, mockClient) = getEnabledHub() - - sut.captureEvent(SentryEvent(), null) { - it.setTag("test", "testValue") - } - - verify(mockClient).captureEvent( - any(), - check { - assertEquals("testValue", it.tags["test"]) - }, - anyOrNull() - ) - } - - @Test - fun `when captureEvent is called with a ScopeCallback then subsequent calls to captureEvent send the unmodified Scope to the client`() { - val (sut, mockClient) = getEnabledHub() - val argumentCaptor = argumentCaptor() - - sut.captureEvent(SentryEvent(), null) { - it.setTag("test", "testValue") - } - - sut.captureEvent(SentryEvent()) - - verify(mockClient, times(2)).captureEvent( - any(), - argumentCaptor.capture(), - anyOrNull() - ) - - assertEquals("testValue", argumentCaptor.allValues[0].tags["test"]) - assertNull(argumentCaptor.allValues[1].tags["test"]) - } - - @Test - fun `when captureEvent is called with a ScopeCallback that crashes then the event should still be captured`() { - val (sut, mockClient, logger) = getEnabledHub() - - val exception = Exception("scope callback exception") - sut.captureEvent(SentryEvent(), null) { - throw exception - } - - verify(mockClient).captureEvent( - any(), - anyOrNull(), - anyOrNull() - ) - - verify(logger).log(eq(SentryLevel.ERROR), any(), eq(exception)) - } - //endregion - - //region captureMessage tests - @Test - fun `when captureMessage is called and event is null, lastEventId is empty`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - sut.callMethod("captureMessage", String::class.java, null) - assertEquals(SentryId.EMPTY_ID, sut.lastEventId) - } - - @Test - fun `when captureMessage is called on disabled client, do nothing`() { - val (sut, mockClient) = getEnabledHub() - sut.close() - - sut.captureMessage("test") - verify(mockClient, never()).captureMessage(any(), any()) - } - - @Test - fun `when captureMessage is called with a valid message, captureMessage on the client should be called`() { - val (sut, mockClient) = getEnabledHub() - - sut.captureMessage("test") - verify(mockClient).captureMessage(any(), any(), any()) - } - - @Test - fun `when captureMessage is called, level is INFO by default`() { - val (sut, mockClient) = getEnabledHub() - sut.captureMessage("test") - verify(mockClient).captureMessage(eq("test"), eq(SentryLevel.INFO), any()) - } - - @Test - fun `when captureMessage is called with a ScopeCallback then the modified scope is sent to the client`() { - val (sut, mockClient) = getEnabledHub() - - sut.captureMessage("test") { - it.setTag("test", "testValue") - } - - verify(mockClient).captureMessage( - any(), - any(), - check { - assertEquals("testValue", it.tags["test"]) - } - ) - } - - @Test - fun `when captureMessage is called with a ScopeCallback then subsequent calls to captureMessage send the unmodified Scope to the client`() { - val (sut, mockClient) = getEnabledHub() - val argumentCaptor = argumentCaptor() - - sut.captureMessage("testMessage") { - it.setTag("test", "testValue") - } - - sut.captureMessage("test", SentryLevel.INFO) - - verify(mockClient, times(2)).captureMessage( - any(), - any(), - argumentCaptor.capture() - ) - - assertEquals("testValue", argumentCaptor.allValues[0].tags["test"]) - assertNull(argumentCaptor.allValues[1].tags["test"]) - } - - @Test - fun `when captureMessage is called with a ScopeCallback that crashes then the message should still be captured`() { - val (sut, mockClient, logger) = getEnabledHub() - - val exception = Exception("scope callback exception") - sut.captureMessage("Hello World") { - throw exception - } - - verify(mockClient).captureMessage( - any(), - anyOrNull(), - anyOrNull() - ) - - verify(logger).log(eq(SentryLevel.ERROR), any(), eq(exception)) - } - - //endregion - - //region captureException tests - @Test - fun `when captureException is called and exception is null, lastEventId is empty`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - sut.callMethod("captureException", Throwable::class.java, null) - assertEquals(SentryId.EMPTY_ID, sut.lastEventId) - } - - @Test - fun `when captureException is called on disabled client, do nothing`() { - val (sut, mockClient) = getEnabledHub() - sut.close() - - sut.captureException(Throwable()) - verify(mockClient, never()).captureEvent(any(), any(), any()) - } - - @Test - fun `when captureException is called with a valid argument and hint, captureEvent on the client should be called`() { - val (sut, mockClient) = getEnabledHub() - - val hints = HintUtils.createWithTypeCheckHint({}) - sut.captureException(Throwable(), hints) - verify(mockClient).captureEvent(any(), any(), any()) - } - - @Test - fun `when captureException is called with a valid argument but no hint, captureEvent on the client should be called`() { - val (sut, mockClient) = getEnabledHub() - - sut.captureException(Throwable()) - verify(mockClient).captureEvent(any(), any(), any()) - } - - @Test - fun `when captureException is called with an exception which has been previously attached with span context, span context should be set on the event before capturing`() { - val (sut, mockClient) = getEnabledHub() - val throwable = Throwable() - val span = mock() - whenever(span.spanContext).thenReturn(SpanContext("op")) - sut.setSpanContext(throwable, span, "tx-name") - - sut.captureException(throwable) - verify(mockClient).captureEvent( - check { - assertEquals(span.spanContext, it.contexts.trace) - assertEquals("tx-name", it.transaction) - }, - any(), - anyOrNull() - ) - } - - @Test - fun `when captureException is called with an exception which has not been previously attached with span context, span context should not be set on the event before capturing`() { - val (sut, mockClient) = getEnabledHub() - val span = mock() - whenever(span.spanContext).thenReturn(SpanContext("op")) - sut.setSpanContext(Throwable(), span, "tx-name") - - sut.captureException(Throwable()) - verify(mockClient).captureEvent( - check { - assertNull(it.contexts.trace) - }, - any(), - anyOrNull() - ) - } - - @Test - fun `when captureException is called with a ScopeCallback then the modified scope is sent to the client`() { - val (sut, mockClient) = getEnabledHub() - - sut.captureException(Throwable(), null) { - it.setTag("test", "testValue") - } - - verify(mockClient).captureEvent( - any(), - check { - assertEquals("testValue", it.tags["test"]) - }, - anyOrNull() - ) - } - - @Test - fun `when captureException is called with a ScopeCallback then subsequent calls to captureException send the unmodified Scope to the client`() { - val (sut, mockClient) = getEnabledHub() - val argumentCaptor = argumentCaptor() - - sut.captureException(Throwable(), null) { - it.setTag("test", "testValue") - } - - sut.captureException(Throwable()) - - verify(mockClient, times(2)).captureEvent( - any(), - argumentCaptor.capture(), - anyOrNull() - ) - - assertEquals("testValue", argumentCaptor.allValues[0].tags["test"]) - assertNull(argumentCaptor.allValues[1].tags["test"]) - } - - @Test - fun `when captureException is called with a ScopeCallback that crashes then the exception should still be captured`() { - val (sut, mockClient, logger) = getEnabledHub() - - val exception = Exception("scope callback exception") - sut.captureException(Throwable()) { - throw exception - } - - verify(mockClient).captureEvent( - any(), - anyOrNull(), - anyOrNull() - ) - - verify(logger).log(eq(SentryLevel.ERROR), any(), eq(exception)) - } - - //endregion - - //region captureUserFeedback tests - - @Test - fun `when captureUserFeedback is called it is forwarded to the client`() { - val (sut, mockClient) = getEnabledHub() - sut.captureUserFeedback(userFeedback) - - verify(mockClient).captureUserFeedback( - check { - assertEquals(userFeedback.eventId, it.eventId) - assertEquals(userFeedback.email, it.email) - assertEquals(userFeedback.name, it.name) - assertEquals(userFeedback.comments, it.comments) - } - ) - } - - @Test - fun `when captureUserFeedback is called on disabled client, do nothing`() { - val (sut, mockClient) = getEnabledHub() - sut.close() - - sut.captureUserFeedback(userFeedback) - verify(mockClient, never()).captureUserFeedback(any()) - } - - @Test - fun `when captureUserFeedback is called and client throws, don't crash`() { - val (sut, mockClient) = getEnabledHub() - - whenever(mockClient.captureUserFeedback(any())).doThrow(IllegalArgumentException("")) - - sut.captureUserFeedback(userFeedback) - } - - private val userFeedback: UserFeedback get() { - val eventId = SentryId("c2fb8fee2e2b49758bcb67cda0f713c7") - return UserFeedback(eventId).apply { - name = "John" - email = "john@me.com" - comments = "comment" - } - } - - //region captureCheckIn tests - - @Test - fun `when captureCheckIn is called it is forwarded to the client`() { - val (sut, mockClient) = getEnabledHub() - sut.captureCheckIn(checkIn) - - verify(mockClient).captureCheckIn( - check { - assertEquals(checkIn.checkInId, it.checkInId) - assertEquals(checkIn.monitorSlug, it.monitorSlug) - assertEquals(checkIn.status, it.status) - }, - any(), - anyOrNull() - ) - } - - @Test - fun `when captureCheckIn is called on disabled client, do nothing`() { - val (sut, mockClient) = getEnabledHub() - sut.close() - - sut.captureCheckIn(checkIn) - verify(mockClient, never()).captureCheckIn(any(), any(), anyOrNull()) - } - - @Test - fun `when captureCheckIn is called and client throws, don't crash`() { - val (sut, mockClient) = getEnabledHub() - - whenever(mockClient.captureCheckIn(any(), any(), anyOrNull())).doThrow(IllegalArgumentException("")) - - sut.captureCheckIn(checkIn) - } - - private val checkIn: CheckIn = CheckIn("some_slug", CheckInStatus.OK) - - //endregion - - //region close tests - @Test - fun `when close is called on disabled client, do nothing`() { - val (sut, mockClient) = getEnabledHub() - sut.close() - - sut.close() - verify(mockClient).close(eq(false)) // 1 to close, but next one wont be recorded - } - - @Test - fun `when close is called and client is alive, close on the client should be called`() { - val (sut, mockClient) = getEnabledHub() - - sut.close() - verify(mockClient).close(eq(false)) - } - - @Test - fun `when close is called with isRestarting false and client is alive, close on the client should be called with isRestarting false`() { - val (sut, mockClient) = getEnabledHub() - - sut.close(false) - verify(mockClient).close(eq(false)) - } - - @Test - fun `when close is called with isRestarting true and client is alive, close on the client should be called with isRestarting true`() { - val (sut, mockClient) = getEnabledHub() - - sut.close(true) - verify(mockClient).close(eq(true)) - } - //endregion - - //region withScope tests - @Test - fun `when withScope is called on disabled client, execute on NoOpScope`() { - val (sut) = getEnabledHub() - - val scopeCallback = mock() - sut.close() - - sut.withScope(scopeCallback) - verify(scopeCallback).run(NoOpScope.getInstance()) - } - - @Test - fun `when withScope is called with alive client, run should be called`() { - val (sut) = getEnabledHub() - - val scopeCallback = mock() - - sut.withScope(scopeCallback) - verify(scopeCallback).run(any()) - } - - @Test - fun `when withScope throws an exception then it should be caught`() { - val (scopes, _, logger) = getEnabledHub() - - val exception = Exception("scope callback exception") - val scopeCallback = ScopeCallback { - throw exception - } - - scopes.withScope(scopeCallback) - - verify(logger).log(eq(SentryLevel.ERROR), any(), eq(exception)) - } - //endregion - - //region configureScope tests - @Test - fun `when configureScope is called on disabled client, do nothing`() { - val (sut) = getEnabledHub() - - val scopeCallback = mock() - sut.close() - - sut.configureScope(scopeCallback) - verify(scopeCallback, never()).run(any()) - } - - @Test - fun `when configureScope is called with alive client, run should be called`() { - val (sut) = getEnabledHub() - - val scopeCallback = mock() - - sut.configureScope(scopeCallback) - verify(scopeCallback).run(any()) - } - - @Test - fun `when configureScope throws an exception then it should be caught`() { - val (scopes, _, logger) = getEnabledHub() - - val exception = Exception("scope callback exception") - val scopeCallback = ScopeCallback { - throw exception - } - - scopes.configureScope(scopeCallback) - - verify(logger).log(eq(SentryLevel.ERROR), any(), eq(exception)) - } - //endregion - - @Test - fun `when integration is registered, scopes is enabled`() { - val mock = mock() - - var options: SentryOptions? = null - // init main scopes and make it enabled - Sentry.init { - it.addIntegration(mock) - it.dsn = "https://key@sentry.io/proj" - it.cacheDirPath = file.absolutePath - it.setSerializer(mock()) - options = it - } - - doAnswer { - val scopes = it.arguments[0] as IScopes - assertTrue(scopes.isEnabled) - }.whenever(mock).register(any(), eq(options!!)) - - verify(mock).register(any(), eq(options!!)) - } - - //region setLevel tests - @Test - fun `when setLevel is called on disabled client, do nothing`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - scopes.close() - - scopes.setLevel(SentryLevel.INFO) - assertNull(scope?.level) - } - - @Test - fun `when setLevel is called, level is set`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - - scopes.setLevel(SentryLevel.INFO) - assertEquals(SentryLevel.INFO, scope?.level) - } - //endregion - - //region setTransaction tests - @Test - fun `when setTransaction is called on disabled client, do nothing`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - scopes.close() - - scopes.setTransaction("test") - assertNull(scope?.transactionName) - } - - @Test - fun `when setTransaction is called, and transaction is not set, transaction name is changed`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - - scopes.setTransaction("test") - assertEquals("test", scope?.transactionName) - } - - @Test - fun `when setTransaction is called, and transaction is set, transaction name is changed`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - - val tx = scopes.startTransaction("test", "op") - scopes.configureScope { it.setTransaction(tx) } - - assertEquals("test", scope?.transactionName) - } - - @Test - fun `when startTransaction is called with different instrumenter, no-op is returned`() { - val scopes = generateHub() - - val transactionContext = TransactionContext("test", "op").also { it.instrumenter = Instrumenter.OTEL } - val transactionOptions = TransactionOptions() - val tx = scopes.startTransaction(transactionContext, transactionOptions) - - assertTrue(tx is NoOpTransaction) - } - - @Test - fun `when startTransaction is called with different instrumenter, no-op is returned 2`() { - val scopes = generateHub() { - it.instrumenter = Instrumenter.OTEL - } - - val tx = scopes.startTransaction("test", "op") - - assertTrue(tx is NoOpTransaction) - } - - @Test - fun `when startTransaction is called with configured instrumenter, it works`() { - val scopes = generateHub() { - it.instrumenter = Instrumenter.OTEL - } - - val transactionContext = TransactionContext("test", "op").also { it.instrumenter = Instrumenter.OTEL } - val transactionOptions = TransactionOptions() - val tx = scopes.startTransaction(transactionContext, transactionOptions) - - assertFalse(tx is NoOpTransaction) - } - //endregion - - //region setUser tests - @Test - fun `when setUser is called on disabled client, do nothing`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - scopes.close() - - scopes.setUser(User()) - assertNull(scope?.user) - } - - @Test - fun `when setUser is called, user is set`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - - val user = User() - scopes.setUser(user) - assertEquals(user, scope?.user) - } - //endregion - - //region setFingerprint tests - @Test - fun `when setFingerprint is called on disabled client, do nothing`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - scopes.close() - - val fingerprint = listOf("abc") - scopes.setFingerprint(fingerprint) - assertEquals(0, scope?.fingerprint?.count()) - } - - @Test - fun `when setFingerprint is called with null parameter, do nothing`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - - scopes.callMethod("setFingerprint", List::class.java, null) - assertEquals(0, scope?.fingerprint?.count()) - } - - @Test - fun `when setFingerprint is called, fingerprint is set`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - - val fingerprint = listOf("abc") - scopes.setFingerprint(fingerprint) - assertEquals(1, scope?.fingerprint?.count()) - } - //endregion - - //region clearBreadcrumbs tests - @Test - fun `when clearBreadcrumbs is called on disabled client, do nothing`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - scopes.addBreadcrumb(Breadcrumb()) - assertEquals(1, scope?.breadcrumbs?.count()) - - scopes.close() - - assertEquals(0, scope?.breadcrumbs?.count()) - } - - @Test - fun `when clearBreadcrumbs is called, clear breadcrumbs`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - - scopes.addBreadcrumb(Breadcrumb()) - assertEquals(1, scope?.breadcrumbs?.count()) - scopes.clearBreadcrumbs() - assertEquals(0, scope?.breadcrumbs?.count()) - } - //endregion - - //region setTag tests - @Test - fun `when setTag is called on disabled client, do nothing`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - scopes.close() - - scopes.setTag("test", "test") - assertEquals(0, scope?.tags?.count()) - } - - @Test - fun `when setTag is called with null parameters, do nothing`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - - scopes.callMethod("setTag", parameterTypes = arrayOf(String::class.java, String::class.java), null, null) - assertEquals(0, scope?.tags?.count()) - } - - @Test - fun `when setTag is called, tag is set`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - - scopes.setTag("test", "test") - assertEquals(1, scope?.tags?.count()) - } - //endregion - - //region setExtra tests - @Test - fun `when setExtra is called on disabled client, do nothing`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - scopes.close() - - scopes.setExtra("test", "test") - assertEquals(0, scope?.extras?.count()) - } - - @Test - fun `when setExtra is called with null parameters, do nothing`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - - scopes.callMethod("setExtra", parameterTypes = arrayOf(String::class.java, String::class.java), null, null) - assertEquals(0, scope?.extras?.count()) - } - - @Test - fun `when setExtra is called, extra is set`() { - val scopes = generateHub() - var scope: IScope? = null - scopes.configureScope { - scope = it - } - - scopes.setExtra("test", "test") - assertEquals(1, scope?.extras?.count()) - } - //endregion - - //region captureEnvelope tests - @Test - fun `when captureEnvelope is called and envelope is null, throws IllegalArgumentException`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - try { - sut.callMethod("captureEnvelope", SentryEnvelope::class.java, null) - fail() - } catch (e: Exception) { - assertTrue(e.cause is java.lang.IllegalArgumentException) - } - } - - @Test - fun `when captureEnvelope is called on disabled client, do nothing`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - sut.close() - - sut.captureEnvelope(SentryEnvelope(SentryId(UUID.randomUUID()), null, setOf())) - verify(mockClient, never()).captureEnvelope(any(), any()) - } - - @Test - fun `when captureEnvelope is called with a valid envelope, captureEnvelope on the client should be called`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - - val envelope = SentryEnvelope(SentryId(UUID.randomUUID()), null, setOf()) - sut.captureEnvelope(envelope) - verify(mockClient).captureEnvelope(any(), anyOrNull()) - } - - @Test - fun `when captureEnvelope is called, lastEventId is not set`() { - val options = SentryOptions().apply { - dsn = "https://key@sentry.io/proj" - setSerializer(mock()) - } - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - whenever(mockClient.captureEnvelope(any(), anyOrNull())).thenReturn(SentryId()) - val envelope = SentryEnvelope(SentryId(UUID.randomUUID()), null, setOf()) - sut.captureEnvelope(envelope) - assertEquals(SentryId.EMPTY_ID, sut.lastEventId) - } - //endregion - - //region startSession tests - @Test - fun `when startSession is called on disabled client, do nothing`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.release = "0.0.1" - options.setSerializer(mock()) - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - sut.close() - - sut.startSession() - verify(mockClient, never()).captureSession(any(), any()) - } - - @Test - fun `when startSession is called, starts a session`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.release = "0.0.1" - options.setSerializer(mock()) - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - - sut.startSession() - verify(mockClient).captureSession(any(), argWhere { HintUtils.hasType(it, SessionStartHint::class.java) }) - } - - @Test - fun `when startSession is called and there's a session, stops it and starts a new one`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.release = "0.0.1" - options.setSerializer(mock()) - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - - sut.startSession() - sut.startSession() - verify(mockClient).captureSession(any(), argWhere { HintUtils.hasType(it, SessionEndHint::class.java) }) - verify(mockClient, times(2)).captureSession(any(), argWhere { HintUtils.hasType(it, SessionStartHint::class.java) }) - } - //endregion - - //region endSession tests - @Test - fun `when endSession is called on disabled client, do nothing`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.release = "0.0.1" - options.setSerializer(mock()) - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - sut.close() - - sut.endSession() - verify(mockClient, never()).captureSession(any(), any()) - } - - @Test - fun `when endSession is called and session tracking is disabled, do nothing`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.release = "0.0.1" - options.setSerializer(mock()) - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - - sut.endSession() - verify(mockClient, never()).captureSession(any(), any()) - } - - @Test - fun `when endSession is called, end a session`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.release = "0.0.1" - options.setSerializer(mock()) - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - - sut.startSession() - sut.endSession() - verify(mockClient).captureSession(any(), argWhere { HintUtils.hasType(it, SessionStartHint::class.java) }) - verify(mockClient).captureSession(any(), argWhere { HintUtils.hasType(it, SessionEndHint::class.java) }) - } - - @Test - fun `when endSession is called and there's no session, do nothing`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.release = "0.0.1" - options.setSerializer(mock()) - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - - sut.endSession() - verify(mockClient, never()).captureSession(any(), any()) - } - //endregion - - //region captureTransaction tests - @Test - fun `when captureTransaction is called on disabled client, do nothing`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - sut.close() - - val sentryTracer = SentryTracer(TransactionContext("name", "op"), sut) - sentryTracer.finish() - sut.captureTransaction(SentryTransaction(sentryTracer), null as TraceContext?) - verify(mockClient, never()).captureTransaction(any(), any(), any()) - verify(mockClient, never()).captureTransaction(any(), any(), any(), anyOrNull(), anyOrNull()) - } - - @Test - fun `when captureTransaction and transaction is sampled, captureTransaction on the client should be called`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - - val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), sut) - sentryTracer.finish() - val traceContext = sentryTracer.traceContext() - verify(mockClient).captureTransaction(any(), equalTraceContext(traceContext), any(), eq(null), eq(null)) - } - - @Test - fun `when captureTransaction is called, lastEventId is not set`() { - val options = SentryOptions().apply { - dsn = "https://key@sentry.io/proj" - setSerializer(mock()) - } - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - whenever(mockClient.captureTransaction(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())).thenReturn(SentryId()) - - val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), sut) - sentryTracer.finish() - assertEquals(SentryId.EMPTY_ID, sut.lastEventId) - } - - @Test - fun `when captureTransaction and transaction is not finished, captureTransaction on the client should not be called`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - - val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), sut) - sut.captureTransaction(SentryTransaction(sentryTracer), null as TraceContext?) - verify(mockClient, never()).captureTransaction(any(), any(), any(), eq(null), anyOrNull()) - } - - @Test - fun `when captureTransaction and transaction is not sampled, captureTransaction on the client should not be called`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - - val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(false)), sut) - sentryTracer.finish() - val traceContext = sentryTracer.traceContext() - verify(mockClient, never()).captureTransaction(any(), equalTraceContext(traceContext), any(), eq(null), anyOrNull()) - } - - @Test - fun `transactions lost due to sampling are recorded as lost`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - - val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(false)), sut) - sentryTracer.finish() - - assertClientReport( - options.clientReportRecorder, - listOf(DiscardedEvent(DiscardReason.SAMPLE_RATE.reason, DataCategory.Transaction.category, 1)) - ) - } - - @Test - fun `transactions lost due to sampling caused by backpressure are recorded as lost`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - val mockBackpressureMonitor = mock() - options.backpressureMonitor = mockBackpressureMonitor - whenever(mockBackpressureMonitor.downsampleFactor).thenReturn(1) - - val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(false)), sut) - sentryTracer.finish() - - assertClientReport( - options.clientReportRecorder, - listOf(DiscardedEvent(DiscardReason.BACKPRESSURE.reason, DataCategory.Transaction.category, 1)) - ) - } - //endregion - - //region profiling tests - - @Test - fun `when startTransaction and profiling is enabled, transaction is profiled only if sampled`() { - val mockTransactionProfiler = mock() - val mockClient = mock() - whenever(mockTransactionProfiler.onTransactionFinish(any(), anyOrNull(), anyOrNull())).thenAnswer { mockClient.captureEnvelope(mock()) } - val scopes = generateHub { - it.setTransactionProfiler(mockTransactionProfiler) - } - scopes.bindClient(mockClient) - // Transaction is not sampled, so it should not be profiled - val contexts = TransactionContext("name", "op", TracesSamplingDecision(false, null, true, null)) - val transaction = scopes.startTransaction(contexts) - transaction.finish() - verify(mockClient, never()).captureEnvelope(any()) - - // Transaction is sampled, so it should be profiled - val sampledContexts = TransactionContext("name", "op", TracesSamplingDecision(true, null, true, null)) - val sampledTransaction = scopes.startTransaction(sampledContexts) - sampledTransaction.finish() - verify(mockClient).captureEnvelope(any()) - } - - @Test - fun `when startTransaction and is sampled but profiling is disabled, transaction is not profiled`() { - val mockTransactionProfiler = mock() - val mockClient = mock() - whenever(mockTransactionProfiler.onTransactionFinish(any(), anyOrNull(), anyOrNull())).thenAnswer { mockClient.captureEnvelope(mock()) } - val scopes = generateHub { - it.profilesSampleRate = 0.0 - it.setTransactionProfiler(mockTransactionProfiler) - } - scopes.bindClient(mockClient) - val contexts = TransactionContext("name", "op") - val transaction = scopes.startTransaction(contexts) - transaction.finish() - verify(mockClient, never()).captureEnvelope(any()) - } - - @Test - fun `when profiler is running and isAppStartTransaction is false, startTransaction does not interact with profiler`() { - val mockTransactionProfiler = mock() - whenever(mockTransactionProfiler.isRunning).thenReturn(true) - val scopes = generateHub { - it.profilesSampleRate = 1.0 - it.setTransactionProfiler(mockTransactionProfiler) - } - val context = TransactionContext("name", "op") - scopes.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = false }) - verify(mockTransactionProfiler, never()).start() - verify(mockTransactionProfiler, never()).bindTransaction(any()) - } - - @Test - fun `when profiler is running and isAppStartTransaction is true, startTransaction binds current profile`() { - val mockTransactionProfiler = mock() - whenever(mockTransactionProfiler.isRunning).thenReturn(true) - val scopes = generateHub { - it.profilesSampleRate = 1.0 - it.setTransactionProfiler(mockTransactionProfiler) - } - val context = TransactionContext("name", "op") - val transaction = scopes.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = true }) - verify(mockTransactionProfiler, never()).start() - verify(mockTransactionProfiler).bindTransaction(eq(transaction)) - } - - @Test - fun `when profiler is not running, startTransaction starts and binds current profile`() { - val mockTransactionProfiler = mock() - whenever(mockTransactionProfiler.isRunning).thenReturn(false) - val scopes = generateHub { - it.profilesSampleRate = 1.0 - it.setTransactionProfiler(mockTransactionProfiler) - } - val context = TransactionContext("name", "op") - val transaction = scopes.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = false }) - verify(mockTransactionProfiler).start() - verify(mockTransactionProfiler).bindTransaction(eq(transaction)) - } - //endregion - - //region startTransaction tests - @Test - fun `when startTransaction, creates transaction`() { - val scopes = generateHub() - val contexts = TransactionContext("name", "op") - - val transaction = scopes.startTransaction(contexts) - assertTrue(transaction is SentryTracer) - assertEquals(contexts, transaction.root.spanContext) - } - - @Test - fun `when startTransaction with bindToScope set to false, transaction is not attached to the scope`() { - val scopes = generateHub() - - scopes.startTransaction("name", "op", TransactionOptions()) - - scopes.configureScope { - assertNull(it.span) - } - } - - @Test - fun `when startTransaction without bindToScope set, transaction is not attached to the scope`() { - val scopes = generateHub() - - scopes.startTransaction("name", "op") - - scopes.configureScope { - assertNull(it.span) - } - } - - @Test - fun `when startTransaction with bindToScope set to true, transaction is attached to the scope`() { - val scopes = generateHub() - - val transaction = scopes.startTransaction("name", "op", TransactionOptions().also { it.isBindToScope = true }) - - scopes.configureScope { - assertEquals(transaction, it.span) - } - } - - @Test - fun `when startTransaction and no tracing sampling is configured, event is not sampled`() { - val scopes = generateHub { - it.tracesSampleRate = 0.0 - } - - val transaction = scopes.startTransaction("name", "op") - assertFalse(transaction.isSampled!!) - } - - @Test - fun `when startTransaction and no profile sampling is configured, profile is not sampled`() { - val scopes = generateHub { - it.tracesSampleRate = 1.0 - it.profilesSampleRate = 0.0 - } - - val transaction = scopes.startTransaction("name", "op") - assertTrue(transaction.isSampled!!) - assertFalse(transaction.isProfileSampled!!) - } - - @Test - fun `when startTransaction with parent sampled and no traces sampler provided, transaction inherits sampling decision`() { - val scopes = generateHub() - val transactionContext = TransactionContext("name", "op") - transactionContext.parentSampled = true - val transaction = scopes.startTransaction(transactionContext) - assertNotNull(transaction) - assertNotNull(transaction.isSampled) - assertTrue(transaction.isSampled!!) - } - - @Test - fun `when startTransaction with parent profile sampled and no profile sampler provided, transaction inherits profile sampling decision`() { - val scopes = generateHub() - val transactionContext = TransactionContext("name", "op") - transactionContext.setParentSampled(true, true) - val transaction = scopes.startTransaction(transactionContext) - assertTrue(transaction.isProfileSampled!!) - } - - @Test - fun `Hub should close the sentry executor processor, profiler and performance collector on close call`() { - val executor = mock() - val profiler = mock() - val performanceCollector = mock() - val options = SentryOptions().apply { - dsn = "https://key@sentry.io/proj" - cacheDirPath = file.absolutePath - executorService = executor - setTransactionProfiler(profiler) - transactionPerformanceCollector = performanceCollector - } - val sut = Hub(options) - sut.close() - verify(executor).close(any()) - verify(profiler).close() - verify(performanceCollector).close() - } - - @Test - fun `Hub with isRestarting true should close the sentry executor in the background`() { - val executor = spy(DeferredExecutorService()) - val options = SentryOptions().apply { - dsn = "https://key@sentry.io/proj" - executorService = executor - } - val sut = Hub(options) - sut.close(true) - verify(executor, never()).close(any()) - executor.runAll() - verify(executor).close(any()) - } - - @Test - fun `Hub with isRestarting false should close the sentry executor in the background`() { - val executor = mock() - val options = SentryOptions().apply { - dsn = "https://key@sentry.io/proj" - executorService = executor - } - val sut = Hub(options) - sut.close(false) - verify(executor).close(any()) - } - - @Test - fun `Hub close should clear the scope`() { - val options = SentryOptions().apply { - dsn = "https://key@sentry.io/proj" - } - - val sut = Hub(options) - sut.addBreadcrumb("Test") - sut.startTransaction("test", "test.op", TransactionOptions().also { it.isBindToScope = true }) - sut.close() - - // we have to clone the scope, so its isEnabled returns true, but it's still built up from - // the old scope preserving its data - val clone = sut.clone() - var oldScope: IScope? = null - clone.configureScope { scope -> oldScope = scope } - assertNull(oldScope!!.transaction) - assertTrue(oldScope!!.breadcrumbs.isEmpty()) - } - - @Test - fun `when tracesSampleRate and tracesSampler are not set on SentryOptions, startTransaction returns NoOp`() { - val scopes = generateHub { - it.tracesSampleRate = null - it.tracesSampler = null - } - val transaction = scopes.startTransaction(TransactionContext("name", "op", TracesSamplingDecision(true))) - assertTrue(transaction is NoOpTransaction) - } - //endregion - - //region startTransaction tests - @Test - fun `when traceHeaders and no transaction is active, traceHeaders are generated from scope`() { - val scopes = generateHub() - - var spanId: SpanId? = null - scopes.configureScope { spanId = it.propagationContext.spanId } - - val traceHeader = scopes.traceHeaders() - assertNotNull(traceHeader) - assertEquals(spanId, traceHeader.spanId) - } - - @Test - fun `when traceHeaders and there is an active transaction, traceHeaders are not null`() { - val scopes = generateHub() - val tx = scopes.startTransaction("aTransaction", "op") - scopes.configureScope { it.setTransaction(tx) } - - assertNotNull(scopes.traceHeaders()) - } - //endregion - - //region getSpan tests - @Test - fun `when there is no active transaction, getSpan returns null`() { - val scopes = generateHub() - assertNull(scopes.span) - } - - @Test - fun `when there is no active transaction, getTransaction returns null`() { - val scopes = generateHub() - assertNull(scopes.transaction) - } - - @Test - fun `when there is active transaction bound to the scope, getTransaction and getSpan return active transaction`() { - val scopes = generateHub() - val tx = scopes.startTransaction("aTransaction", "op") - scopes.configureScope { it.transaction = tx } - - assertEquals(tx, scopes.transaction) - assertEquals(tx, scopes.span) - } - - @Test - fun `when there is a transaction but the scopes is closed, getTransaction returns null`() { - val scopes = generateHub() - scopes.startTransaction("name", "op") - scopes.close() - - assertNull(scopes.transaction) - } - - @Test - fun `when there is active span within a transaction bound to the scope, getSpan returns active span`() { - val scopes = generateHub() - val tx = scopes.startTransaction("aTransaction", "op") - scopes.configureScope { it.setTransaction(tx) } - scopes.configureScope { it.setTransaction(tx) } - val span = tx.startChild("op") - - assertEquals(tx, scopes.transaction) - assertEquals(span, scopes.span) - } - // endregion - - //region setSpanContext - @Test - fun `associates span context with throwable`() { - val (scopes, mockClient) = getEnabledHub() - val transaction = scopes.startTransaction("aTransaction", "op") - val span = transaction.startChild("op") - val exception = RuntimeException() - - scopes.setSpanContext(exception, span, "tx-name") - scopes.captureEvent(SentryEvent(exception)) - - verify(mockClient).captureEvent( - check { - assertEquals(span.spanContext, it.contexts.trace) - }, - anyOrNull(), - anyOrNull() - ) - } - - @Test - fun `returns null when no span context associated with throwable`() { - val scopes = generateHub() as Hub - assertNull(scopes.getSpanContext(RuntimeException())) - } - // endregion - - @Test - fun `isCrashedLastRun does not delete native marker if auto session is enabled`() { - val nativeMarker = File(hashedFolder(), EnvelopeCache.NATIVE_CRASH_MARKER_FILE) - nativeMarker.mkdirs() - nativeMarker.createNewFile() - val scopes = generateHub() as Hub - - assertTrue(scopes.isCrashedLastRun!!) - assertTrue(nativeMarker.exists()) - } - - @Test - fun `isCrashedLastRun deletes the native marker if auto session is disabled`() { - val nativeMarker = File(hashedFolder(), EnvelopeCache.NATIVE_CRASH_MARKER_FILE) - nativeMarker.mkdirs() - nativeMarker.createNewFile() - val scopes = generateHub { - it.isEnableAutoSessionTracking = false - } - - assertTrue(scopes.isCrashedLastRun!!) - assertFalse(nativeMarker.exists()) - } - - @Test - fun `reportFullyDisplayed is ignored if TimeToFullDisplayTracing is disabled`() { - var called = false - val scopes = generateHub { - it.fullyDisplayedReporter.registerFullyDrawnListener { - called = !called - } - } - scopes.reportFullyDisplayed() - assertFalse(called) - } - - @Test - fun `reportFullyDisplayed calls FullyDisplayedReporter if TimeToFullDisplayTracing is enabled`() { - var called = false - val scopes = generateHub { - it.isEnableTimeToFullDisplayTracing = true - it.fullyDisplayedReporter.registerFullyDrawnListener { - called = !called - } - } - scopes.reportFullyDisplayed() - assertTrue(called) - } - - @Test - fun `reportFullyDisplayed calls FullyDisplayedReporter only once`() { - var called = false - val scopes = generateHub { - it.isEnableTimeToFullDisplayTracing = true - it.fullyDisplayedReporter.registerFullyDrawnListener { - called = !called - } - } - scopes.reportFullyDisplayed() - assertTrue(called) - scopes.reportFullyDisplayed() - assertTrue(called) - } - - @Test - fun `reportFullDisplayed calls reportFullyDisplayed`() { - val scopes = spy(generateHub()) - scopes.reportFullDisplayed() - verify(scopes).reportFullyDisplayed() - } - - @Test - fun `continueTrace creates propagation context from headers and returns transaction context if performance enabled`() { - val scopes = generateHub() - val traceId = SentryId() - val parentSpanId = SpanId() - val transactionContext = scopes.continueTrace("$traceId-$parentSpanId-1", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) - - scopes.configureScope { scope -> - assertEquals(traceId, scope.propagationContext.traceId) - assertEquals(parentSpanId, scope.propagationContext.parentSpanId) - } - - assertEquals(traceId, transactionContext!!.traceId) - assertEquals(parentSpanId, transactionContext!!.parentSpanId) - } - - @Test - fun `continueTrace creates new propagation context if header invalid and returns transaction context if performance enabled`() { - val scopes = generateHub() - val traceId = SentryId() - var propagationContextHolder = AtomicReference() - - scopes.configureScope { propagationContextHolder.set(it.propagationContext) } - val propagationContextAtStart = propagationContextHolder.get()!! - - val transactionContext = scopes.continueTrace("invalid", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) - - scopes.configureScope { scope -> - assertNotEquals(propagationContextAtStart.traceId, scope.propagationContext.traceId) - assertNotEquals(propagationContextAtStart.parentSpanId, scope.propagationContext.parentSpanId) - assertNotEquals(propagationContextAtStart.spanId, scope.propagationContext.spanId) - - assertEquals(scope.propagationContext.traceId, transactionContext!!.traceId) - assertEquals(scope.propagationContext.parentSpanId, transactionContext!!.parentSpanId) - assertEquals(scope.propagationContext.spanId, transactionContext!!.spanId) - } - } - - @Test - fun `continueTrace creates propagation context from headers and returns null if performance disabled`() { - val scopes = generateHub { it.enableTracing = false } - val traceId = SentryId() - val parentSpanId = SpanId() - val transactionContext = scopes.continueTrace("$traceId-$parentSpanId-1", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) - - scopes.configureScope { scope -> - assertEquals(traceId, scope.propagationContext.traceId) - assertEquals(parentSpanId, scope.propagationContext.parentSpanId) - } - - assertNull(transactionContext) - } - - @Test - fun `continueTrace creates new propagation context if header invalid and returns null if performance disabled`() { - val scopes = generateHub { it.enableTracing = false } - val traceId = SentryId() - var propagationContextHolder = AtomicReference() - - scopes.configureScope { propagationContextHolder.set(it.propagationContext) } - val propagationContextAtStart = propagationContextHolder.get()!! - - val transactionContext = scopes.continueTrace("invalid", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) - - scopes.configureScope { scope -> - assertNotEquals(propagationContextAtStart.traceId, scope.propagationContext.traceId) - assertNotEquals(propagationContextAtStart.parentSpanId, scope.propagationContext.parentSpanId) - assertNotEquals(propagationContextAtStart.spanId, scope.propagationContext.spanId) - } - - assertNull(transactionContext) - } - - @Test - fun `scopes provides no tags for metrics, if metric option is disabled`() { - val scopes = generateHub { - it.isEnableMetrics = false - it.isEnableDefaultTagsForMetrics = true - } as Hub - - assertTrue( - scopes.defaultTagsForMetrics.isEmpty() - ) - } - - @Test - fun `scopes provides no tags for metrics, if default tags option is disabled`() { - val scopes = generateHub { - it.isEnableMetrics = true - it.isEnableDefaultTagsForMetrics = false - } as Hub - - assertTrue( - scopes.defaultTagsForMetrics.isEmpty() - ) - } - - @Test - fun `scopes provides minimum default tags for metrics, if nothing is set up`() { - val scopes = generateHub { - it.isEnableMetrics = true - it.isEnableDefaultTagsForMetrics = true - } as Hub - - assertEquals( - mapOf( - "environment" to "production" - ), - scopes.defaultTagsForMetrics - ) - } - - @Test - fun `scopes provides default tags for metrics, based on options and running transaction`() { - val scopes = generateHub { - it.isEnableMetrics = true - it.isEnableDefaultTagsForMetrics = true - it.environment = "test" - it.release = "1.0" - } as Hub - scopes.startTransaction( - "name", - "op", - TransactionOptions().apply { isBindToScope = true } - ) - - assertEquals( - mapOf( - "environment" to "test", - "release" to "1.0", - "transaction" to "name" - ), - scopes.defaultTagsForMetrics - ) - } - - @Test - fun `scopes provides no local metric aggregator if metrics feature is disabled`() { - val scopes = generateHub { - it.isEnableMetrics = false - it.isEnableSpanLocalMetricAggregation = true - } as Hub - - scopes.startTransaction( - "name", - "op", - TransactionOptions().apply { isBindToScope = true } - ) - - assertNull(scopes.localMetricsAggregator) - } - - @Test - fun `scopes provides no local metric aggregator if local aggregation feature is disabled`() { - val scopes = generateHub { - it.isEnableMetrics = true - it.isEnableSpanLocalMetricAggregation = false - } as Hub - - scopes.startTransaction( - "name", - "op", - TransactionOptions().apply { isBindToScope = true } - ) - - assertNull(scopes.localMetricsAggregator) - } - - @Test - fun `scopes provides local metric aggregator if feature is enabled`() { - val scopes = generateHub { - it.isEnableMetrics = true - it.isEnableSpanLocalMetricAggregation = true - } as Hub - - scopes.startTransaction( - "name", - "op", - TransactionOptions().apply { isBindToScope = true } - ) - assertNotNull(scopes.localMetricsAggregator) - } - - @Test - fun `scopes startSpanForMetric starts a child span`() { - val scopes = generateHub { - it.isEnableMetrics = true - it.isEnableSpanLocalMetricAggregation = true - it.sampleRate = 1.0 - } as Hub - - val txn = scopes.startTransaction( - "name.txn", - "op.txn", - TransactionOptions().apply { isBindToScope = true } - ) - - val span = scopes.startSpanForMetric("op", "key")!! - - assertEquals("op", span.spanContext.op) - assertEquals("key", span.spanContext.description) - assertEquals(span.spanContext.parentSpanId, txn.spanContext.spanId) - } - - private val dsnTest = "https://key@sentry.io/proj" - - private fun generateHub(optionsConfiguration: Sentry.OptionsConfiguration? = null): IScopes { - val options = SentryOptions().apply { - dsn = dsnTest - cacheDirPath = file.absolutePath - setSerializer(mock()) - tracesSampleRate = 1.0 - } - optionsConfiguration?.configure(options) - return Hub(options) - } - - private fun getEnabledHub(): Triple { - val logger = mock() - - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - options.tracesSampleRate = 1.0 - options.isDebug = true - options.setLogger(logger) - - val sut = Hub(options) - val mockClient = mock() - sut.bindClient(mockClient) - return Triple(sut, mockClient, logger) - } - - private fun hashedFolder(): String { - val hash = StringUtils.calculateStringHash(dsnTest, mock()) - val fileHashFolder = File(file.absolutePath, hash!!) - return fileHashFolder.absolutePath - } - - private fun equalTraceContext(expectedContext: TraceContext?): TraceContext? { - expectedContext ?: return eq(null) - - return argWhere { actual -> - expectedContext.traceId == actual.traceId && - expectedContext.transaction == actual.transaction && - expectedContext.environment == actual.environment && - expectedContext.release == actual.release && - expectedContext.publicKey == actual.publicKey && - expectedContext.sampleRate == actual.sampleRate && - expectedContext.userId == actual.userId && - expectedContext.userSegment == actual.userSegment - } - } -} From 5e2029b792bf9c25a82b687b24cb91fb732dbffd Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Fri, 3 May 2024 07:10:37 +0200 Subject: [PATCH 43/91] Hubs/Scopes Merge 42b - Merge fingerprints from all scopes (#3395) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Merge fingerprints from scopes --- .../java/io/sentry/CombinedScopeView.java | 15 +++++-------- .../java/io/sentry/CombinedScopeViewTest.kt | 21 ++----------------- 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/sentry/src/main/java/io/sentry/CombinedScopeView.java b/sentry/src/main/java/io/sentry/CombinedScopeView.java index 86b90379ad..ef6031cc0a 100644 --- a/sentry/src/main/java/io/sentry/CombinedScopeView.java +++ b/sentry/src/main/java/io/sentry/CombinedScopeView.java @@ -145,16 +145,11 @@ public void setRequest(@Nullable Request request) { @Override public @NotNull List getFingerprint() { - // TODO [HSM] should these be merged? - final @Nullable List current = scope.getFingerprint(); - if (!current.isEmpty()) { - return current; - } - final @Nullable List isolation = isolationScope.getFingerprint(); - if (!isolation.isEmpty()) { - return isolation; - } - return globalScope.getFingerprint(); + final @NotNull List allFingerprints = new CopyOnWriteArrayList<>(); + allFingerprints.addAll(globalScope.getFingerprint()); + allFingerprints.addAll(isolationScope.getFingerprint()); + allFingerprints.addAll(scope.getFingerprint()); + return allFingerprints; } @Override diff --git a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt index b73a7adcc8..abdb549207 100644 --- a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt +++ b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt @@ -1071,30 +1071,13 @@ class CombinedScopeViewTest { } @Test - fun `prefers fingerprint from current scope`() { + fun `combines fingerprints from current all scopes`() { val combined = fixture.getSut() fixture.scope.fingerprint = listOf("scopeFingerprint") fixture.isolationScope.fingerprint = listOf("isolationFingerprint") fixture.globalScope.fingerprint = listOf("globalFingerprint") - assertEquals(listOf("scopeFingerprint"), combined.fingerprint) - } - - @Test - fun `uses isolation scope fingerprint if current scope does not have one`() { - val combined = fixture.getSut() - fixture.isolationScope.fingerprint = listOf("isolationFingerprint") - fixture.globalScope.fingerprint = listOf("globalFingerprint") - - assertEquals(listOf("isolationFingerprint"), combined.fingerprint) - } - - @Test - fun `uses global scope fingerprint if current and isolation scope do not have one`() { - val combined = fixture.getSut() - fixture.globalScope.fingerprint = listOf("globalFingerprint") - - assertEquals(listOf("globalFingerprint"), combined.fingerprint) + assertEquals(listOf("globalFingerprint", "isolationFingerprint", "scopeFingerprint"), combined.fingerprint) } // TODO [HSM] test clone From e7007dd97a095ebfcb4c7fd8d49ba8a92efdbda6 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 7 May 2024 06:37:02 +0200 Subject: [PATCH 44/91] Hubs/Scopes Merge 42d - Close previous scopes before binding a new global client (#3404) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Close previous scopes before binding a new global client --- sentry/src/main/java/io/sentry/Sentry.java | 4 +-- sentry/src/test/java/io/sentry/SentryTest.kt | 32 +++++++++++++++++++ .../sentry/metrics/MetricsIntegrationTest.kt | 6 ++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 05cba51dc0..ead61260bc 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -55,7 +55,7 @@ private Sentry() {} */ // TODO [HSM] use SentryOptions.empty and address // https://github.com/getsentry/sentry-java/issues/2541 - private static volatile @NotNull IScope globalScope = new Scope(SentryOptions.empty()); + private static final @NotNull IScope globalScope = new Scope(SentryOptions.empty()); /** Default value for globalHubMode is false */ private static final boolean GLOBAL_HUB_DEFAULT_MODE = false; @@ -277,12 +277,12 @@ private static synchronized void init( final IScopes scopes = getCurrentScopes(); final IScope rootScope = new Scope(options); final IScope rootIsolationScope = new Scope(options); - globalScope.bindClient(new SentryClient(options)); rootScopes = new Scopes(rootScope, rootIsolationScope, globalScope, "Sentry.init"); getScopesStorage().set(rootScopes); scopes.close(true); + globalScope.bindClient(new SentryClient(options)); // If the executorService passed in the init is the same that was previously closed, we have to // set a new one diff --git a/sentry/src/test/java/io/sentry/SentryTest.kt b/sentry/src/test/java/io/sentry/SentryTest.kt index 28305b8eec..ebd5e92c2b 100644 --- a/sentry/src/test/java/io/sentry/SentryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTest.kt @@ -76,6 +76,38 @@ class SentryTest { verify(scopes).close(eq(true)) } + @Test + fun `global client is enabled after restart`() { + val scopes = mock() + whenever(scopes.close()).then { Sentry.getGlobalScope().client.close() } + whenever(scopes.close(anyOrNull())).then { Sentry.getGlobalScope().client.close() } + + Sentry.init { + it.dsn = dsn + } + Sentry.setCurrentScopes(scopes) + Sentry.init { + it.dsn = dsn + } + verify(scopes).close(eq(true)) + assertTrue(Sentry.getGlobalScope().client.isEnabled) + } + + @Test + fun `global client is disabled after close`() { + val scopes = mock() + whenever(scopes.close()).then { Sentry.getGlobalScope().client.close() } + whenever(scopes.close(anyOrNull())).then { Sentry.getGlobalScope().client.close() } + + Sentry.init { + it.dsn = dsn + } + Sentry.setCurrentScopes(scopes) + Sentry.close() + verify(scopes).close(eq(false)) + assertFalse(Sentry.getGlobalScope().client.isEnabled) + } + @Test fun `close calls scopes close with isRestarting false`() { val scopes = mock() diff --git a/sentry/src/test/java/io/sentry/metrics/MetricsIntegrationTest.kt b/sentry/src/test/java/io/sentry/metrics/MetricsIntegrationTest.kt index bf7a1f0f43..a5850ab5a0 100644 --- a/sentry/src/test/java/io/sentry/metrics/MetricsIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/metrics/MetricsIntegrationTest.kt @@ -11,10 +11,16 @@ import org.mockito.kotlin.check import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever +import kotlin.test.BeforeTest import kotlin.test.Test class MetricsIntegrationTest { + @BeforeTest + fun setup() { + Sentry.close() + } + @Test fun `metrics are collected`() { val options = SentryOptions().apply { From dc56a6ae2c69b141e5821983033380e30c954f75 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 13 May 2024 14:24:48 +0200 Subject: [PATCH 45/91] Report suppressed exceptions as exception group (#3396) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Report suppressed exceptions as exception group * api dump * add tests for suppressed exceptions * Format code * add additinoal test * Format code * apply suggestion * add changelog * fix changelog --------- Co-authored-by: Lukas Bloder Co-authored-by: Sentry Github Bot --- CHANGELOG.md | 1 + sentry/api/sentry.api | 9 + .../io/sentry/SentryExceptionFactory.java | 40 +++- .../java/io/sentry/protocol/Mechanism.java | 57 ++++++ .../io/sentry/SentryExceptionFactoryTest.kt | 187 ++++++++++++++++++ 5 files changed, 288 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b578ed17f..1c90c3589e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - Add support for Spring Rest Client ([#3199](https://github.com/getsentry/sentry-java/pull/3199)) +- Report exceptions returned by Throwable.getSuppressed() to Sentry as exception groups ([#3396] https://github.com/getsentry/sentry-java/pull/3396) ## 7.6.0 diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index b201a47d0c..5a68a92811 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -4472,18 +4472,24 @@ public final class io/sentry/protocol/Mechanism : io/sentry/JsonSerializable, io public fun (Ljava/lang/Thread;)V public fun getData ()Ljava/util/Map; public fun getDescription ()Ljava/lang/String; + public fun getExceptionId ()Ljava/lang/Integer; public fun getHelpLink ()Ljava/lang/String; public fun getMeta ()Ljava/util/Map; + public fun getParentId ()Ljava/lang/Integer; public fun getSynthetic ()Ljava/lang/Boolean; public fun getType ()Ljava/lang/String; public fun getUnknown ()Ljava/util/Map; + public fun isExceptionGroup ()Ljava/lang/Boolean; public fun isHandled ()Ljava/lang/Boolean; public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V public fun setData (Ljava/util/Map;)V public fun setDescription (Ljava/lang/String;)V + public fun setExceptionGroup (Ljava/lang/Boolean;)V + public fun setExceptionId (Ljava/lang/Integer;)V public fun setHandled (Ljava/lang/Boolean;)V public fun setHelpLink (Ljava/lang/String;)V public fun setMeta (Ljava/util/Map;)V + public fun setParentId (Ljava/lang/Integer;)V public fun setSynthetic (Ljava/lang/Boolean;)V public fun setType (Ljava/lang/String;)V public fun setUnknown (Ljava/util/Map;)V @@ -4498,9 +4504,12 @@ public final class io/sentry/protocol/Mechanism$Deserializer : io/sentry/JsonDes public final class io/sentry/protocol/Mechanism$JsonKeys { public static final field DATA Ljava/lang/String; public static final field DESCRIPTION Ljava/lang/String; + public static final field EXCEPTION_ID Ljava/lang/String; public static final field HANDLED Ljava/lang/String; public static final field HELP_LINK Ljava/lang/String; + public static final field IS_EXCEPTION_GROUP Ljava/lang/String; public static final field META Ljava/lang/String; + public static final field PARENT_ID Ljava/lang/String; public static final field SYNTHETIC Ljava/lang/String; public static final field TYPE Ljava/lang/String; public fun ()V diff --git a/sentry/src/main/java/io/sentry/SentryExceptionFactory.java b/sentry/src/main/java/io/sentry/SentryExceptionFactory.java index 6652ebb504..2808df11c0 100644 --- a/sentry/src/main/java/io/sentry/SentryExceptionFactory.java +++ b/sentry/src/main/java/io/sentry/SentryExceptionFactory.java @@ -12,7 +12,7 @@ import java.util.Deque; import java.util.HashSet; import java.util.List; -import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -136,12 +136,20 @@ public List getSentryExceptions(final @NotNull Throwable throwa @TestOnly @NotNull Deque extractExceptionQueue(final @NotNull Throwable throwable) { - final Deque exceptions = new ArrayDeque<>(); - final Set circularityDetector = new HashSet<>(); + return extractExceptionQueueInternal( + throwable, new AtomicInteger(-1), new HashSet<>(), new ArrayDeque<>()); + } + + Deque extractExceptionQueueInternal( + final @NotNull Throwable throwable, + final @NotNull AtomicInteger exceptionId, + final @NotNull HashSet circularityDetector, + final @NotNull Deque exceptions) { Mechanism exceptionMechanism; Thread thread; Throwable currentThrowable = throwable; + int parentId = exceptionId.get(); // Stack the exceptions to send them in the reverse order while (currentThrowable != null && circularityDetector.add(currentThrowable)) { @@ -155,12 +163,11 @@ Deque extractExceptionQueue(final @NotNull Throwable throwable) thread = exceptionMechanismThrowable.getThread(); snapshot = exceptionMechanismThrowable.isSnapshot(); } else { - exceptionMechanism = null; + exceptionMechanism = new Mechanism(); thread = Thread.currentThread(); } - final boolean includeSentryFrames = - exceptionMechanism != null && Boolean.FALSE.equals(exceptionMechanism.isHandled()); + final boolean includeSentryFrames = Boolean.FALSE.equals(exceptionMechanism.isHandled()); final List frames = sentryStackTraceFactory.getStackFrames( currentThrowable.getStackTrace(), includeSentryFrames); @@ -168,7 +175,28 @@ Deque extractExceptionQueue(final @NotNull Throwable throwable) getSentryException( currentThrowable, exceptionMechanism, thread.getId(), frames, snapshot); exceptions.addFirst(exception); + + if (exceptionMechanism.getType() == null) { + exceptionMechanism.setType("chained"); + } + + if (exceptionId.get() >= 0) { + exceptionMechanism.setParentId(parentId); + } + + final int currentExceptionId = exceptionId.incrementAndGet(); + exceptionMechanism.setExceptionId(currentExceptionId); + + Throwable[] suppressed = currentThrowable.getSuppressed(); + if (suppressed != null && suppressed.length > 0) { + exceptionMechanism.setExceptionGroup(true); + for (Throwable suppressedThrowable : suppressed) { + extractExceptionQueueInternal( + suppressedThrowable, exceptionId, circularityDetector, exceptions); + } + } currentThrowable = currentThrowable.getCause(); + parentId = currentExceptionId; } return exceptions; diff --git a/sentry/src/main/java/io/sentry/protocol/Mechanism.java b/sentry/src/main/java/io/sentry/protocol/Mechanism.java index 648aed39c2..8fc9aedf77 100644 --- a/sentry/src/main/java/io/sentry/protocol/Mechanism.java +++ b/sentry/src/main/java/io/sentry/protocol/Mechanism.java @@ -67,6 +67,18 @@ public final class Mechanism implements JsonUnknown, JsonSerializable { * for grouping or display purposes. */ private @Nullable Boolean synthetic; + /** + * Exception ID. Used. e.g. for exception groups to build a hierarchy. This is referenced as + * parent by child exceptions which for Java SDK means Throwable.getSuppressed(). + */ + private @Nullable Integer exceptionId; + /** Parent exception ID. Used e.g. for exception groups to build a hierarchy. */ + private @Nullable Integer parentId; + /** + * Whether this is a group of exceptions. For Java SDK this means there were suppressed + * exceptions. + */ + private @Nullable Boolean exceptionGroup; @SuppressWarnings("unused") private @Nullable Map unknown; @@ -140,6 +152,30 @@ public void setSynthetic(final @Nullable Boolean synthetic) { this.synthetic = synthetic; } + public @Nullable Integer getExceptionId() { + return exceptionId; + } + + public void setExceptionId(final @Nullable Integer exceptionId) { + this.exceptionId = exceptionId; + } + + public @Nullable Integer getParentId() { + return parentId; + } + + public void setParentId(final @Nullable Integer parentId) { + this.parentId = parentId; + } + + public @Nullable Boolean isExceptionGroup() { + return exceptionGroup; + } + + public void setExceptionGroup(final @Nullable Boolean exceptionGroup) { + this.exceptionGroup = exceptionGroup; + } + // JsonKeys public static final class JsonKeys { @@ -150,6 +186,9 @@ public static final class JsonKeys { public static final String META = "meta"; public static final String DATA = "data"; public static final String SYNTHETIC = "synthetic"; + public static final String EXCEPTION_ID = "exception_id"; + public static final String PARENT_ID = "parent_id"; + public static final String IS_EXCEPTION_GROUP = "is_exception_group"; } // JsonUnknown @@ -191,6 +230,15 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger if (synthetic != null) { writer.name(JsonKeys.SYNTHETIC).value(synthetic); } + if (exceptionId != null) { + writer.name(JsonKeys.EXCEPTION_ID).value(logger, exceptionId); + } + if (parentId != null) { + writer.name(JsonKeys.PARENT_ID).value(logger, parentId); + } + if (exceptionGroup != null) { + writer.name(JsonKeys.IS_EXCEPTION_GROUP).value(exceptionGroup); + } if (unknown != null) { for (String key : unknown.keySet()) { Object value = unknown.get(key); @@ -238,6 +286,15 @@ public static final class Deserializer implements JsonDeserializer { case JsonKeys.SYNTHETIC: mechanism.synthetic = reader.nextBooleanOrNull(); break; + case JsonKeys.EXCEPTION_ID: + mechanism.exceptionId = reader.nextIntegerOrNull(); + break; + case JsonKeys.PARENT_ID: + mechanism.parentId = reader.nextIntegerOrNull(); + break; + case JsonKeys.IS_EXCEPTION_GROUP: + mechanism.exceptionGroup = reader.nextBooleanOrNull(); + break; default: if (unknown == null) { unknown = new HashMap<>(); diff --git a/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt b/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt index 888f17e0a3..8eb7f0e42d 100644 --- a/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt @@ -209,6 +209,193 @@ class SentryExceptionFactoryTest { assertEquals(777, frame.lineno) } + @Test + fun `when exception with mechanism suppressed exceptions, add them and show as group`() { + val exception = Exception("message") + val suppressedException = Exception("suppressed") + exception.addSuppressed(suppressedException) + + val mechanism = Mechanism() + mechanism.type = "ANR" + val thread = Thread() + val throwable = ExceptionMechanismException(mechanism, exception, thread) + + val queue = fixture.getSut().extractExceptionQueue(throwable) + + val suppressedInQueue = queue.pop() + val mainInQueue = queue.pop() + + assertEquals("suppressed", suppressedInQueue.value) + assertEquals(1, suppressedInQueue.mechanism?.exceptionId) + assertEquals(0, suppressedInQueue.mechanism?.parentId) + + assertEquals("message", mainInQueue.value) + assertEquals(0, mainInQueue.mechanism?.exceptionId) + assertEquals(true, mainInQueue.mechanism?.isExceptionGroup) + } + + @Test + fun `nested exception that contains suppressed exceptions is marked as group`() { + val exception = Exception("inner") + val suppressedException = Exception("suppressed") + exception.addSuppressed(suppressedException) + + val outerException = Exception("outer", exception) + + val queue = fixture.getSut().extractExceptionQueue(outerException) + + val suppressedInQueue = queue.pop() + val mainInQueue = queue.pop() + val outerInQueue = queue.pop() + + assertEquals("suppressed", suppressedInQueue.value) + assertEquals(2, suppressedInQueue.mechanism?.exceptionId) + assertEquals(1, suppressedInQueue.mechanism?.parentId) + + assertEquals("inner", mainInQueue.value) + assertEquals(1, mainInQueue.mechanism?.exceptionId) + assertEquals(0, mainInQueue.mechanism?.parentId) + assertEquals(true, mainInQueue.mechanism?.isExceptionGroup) + + assertEquals("outer", outerInQueue.value) + assertEquals(0, outerInQueue.mechanism?.exceptionId) + assertNull(outerInQueue.mechanism?.parentId) + assertNull(outerInQueue.mechanism?.isExceptionGroup) + } + + @Test + fun `nested exception within Mechanism that contains suppressed exceptions is marked as group`() { + val exception = Exception("inner") + val suppressedException = Exception("suppressed") + exception.addSuppressed(suppressedException) + + val mechanism = Mechanism() + mechanism.type = "ANR" + val thread = Thread() + + val outerException = ExceptionMechanismException(mechanism, Exception("outer", exception), thread) + + val queue = fixture.getSut().extractExceptionQueue(outerException) + + val suppressedInQueue = queue.pop() + val mainInQueue = queue.pop() + val outerInQueue = queue.pop() + + assertEquals("suppressed", suppressedInQueue.value) + assertEquals(2, suppressedInQueue.mechanism?.exceptionId) + assertEquals(1, suppressedInQueue.mechanism?.parentId) + + assertEquals("inner", mainInQueue.value) + assertEquals(1, mainInQueue.mechanism?.exceptionId) + assertEquals(0, mainInQueue.mechanism?.parentId) + assertEquals(true, mainInQueue.mechanism?.isExceptionGroup) + + assertEquals("outer", outerInQueue.value) + assertEquals(0, outerInQueue.mechanism?.exceptionId) + assertNull(outerInQueue.mechanism?.parentId) + assertNull(outerInQueue.mechanism?.isExceptionGroup) + } + + @Test + fun `nested exception with nested exception that contain suppressed exceptions are marked as group`() { + val innerMostException = Exception("innermost") + val innerMostSuppressed = Exception("innermostSuppressed") + innerMostException.addSuppressed(innerMostSuppressed) + + val innerException = Exception("inner", innerMostException) + val innerSuppressed = Exception("suppressed") + innerException.addSuppressed(innerSuppressed) + + val outerException = Exception("outer", innerException) + + val queue = fixture.getSut().extractExceptionQueue(outerException) + + val innerMostSuppressedInQueue = queue.pop() + val innerMostExceptionInQueue = queue.pop() + val innerSuppressedInQueue = queue.pop() + val innerExceptionInQueue = queue.pop() + val outerInQueue = queue.pop() + + assertEquals("innermostSuppressed", innerMostSuppressedInQueue.value) + assertEquals(4, innerMostSuppressedInQueue.mechanism?.exceptionId) + assertEquals(3, innerMostSuppressedInQueue.mechanism?.parentId) + assertNull(innerMostSuppressedInQueue.mechanism?.isExceptionGroup) + + assertEquals("innermost", innerMostExceptionInQueue.value) + assertEquals(3, innerMostExceptionInQueue.mechanism?.exceptionId) + assertEquals(1, innerMostExceptionInQueue.mechanism?.parentId) + assertEquals(true, innerMostExceptionInQueue.mechanism?.isExceptionGroup) + + assertEquals("suppressed", innerSuppressedInQueue.value) + assertEquals(2, innerSuppressedInQueue.mechanism?.exceptionId) + assertEquals(1, innerSuppressedInQueue.mechanism?.parentId) + assertNull(innerSuppressedInQueue.mechanism?.isExceptionGroup) + + assertEquals("inner", innerExceptionInQueue.value) + assertEquals(1, innerExceptionInQueue.mechanism?.exceptionId) + assertEquals(0, innerExceptionInQueue.mechanism?.parentId) + assertEquals(true, innerExceptionInQueue.mechanism?.isExceptionGroup) + + assertEquals("outer", outerInQueue.value) + assertEquals(0, outerInQueue.mechanism?.exceptionId) + assertNull(outerInQueue.mechanism?.parentId) + assertNull(outerInQueue.mechanism?.isExceptionGroup) + } + + @Test + fun `nested exception with nested exception that contain suppressed exceptions with a nested exception are marked as group`() { + val innerMostException = Exception("innermost") + + val innerMostSuppressedNestedException = Exception("innermostSuppressedNested") + val innerMostSuppressed = Exception("innermostSuppressed", innerMostSuppressedNestedException) + innerMostException.addSuppressed(innerMostSuppressed) + + val innerException = Exception("inner", innerMostException) + val innerSuppressed = Exception("suppressed") + innerException.addSuppressed(innerSuppressed) + + val outerException = Exception("outer", innerException) + + val queue = fixture.getSut().extractExceptionQueue(outerException) + + val innerMostSuppressedNestedExceptionInQueue = queue.pop() + val innerMostSuppressedInQueue = queue.pop() + val innerMostExceptionInQueue = queue.pop() + val innerSuppressedInQueue = queue.pop() + val innerExceptionInQueue = queue.pop() + val outerInQueue = queue.pop() + + assertEquals("innermostSuppressedNested", innerMostSuppressedNestedExceptionInQueue.value) + assertEquals(5, innerMostSuppressedNestedExceptionInQueue.mechanism?.exceptionId) + assertEquals(4, innerMostSuppressedNestedExceptionInQueue.mechanism?.parentId) + assertNull(innerMostSuppressedNestedExceptionInQueue.mechanism?.isExceptionGroup) + + assertEquals("innermostSuppressed", innerMostSuppressedInQueue.value) + assertEquals(4, innerMostSuppressedInQueue.mechanism?.exceptionId) + assertEquals(3, innerMostSuppressedInQueue.mechanism?.parentId) + assertNull(innerMostSuppressedInQueue.mechanism?.isExceptionGroup) + + assertEquals("innermost", innerMostExceptionInQueue.value) + assertEquals(3, innerMostExceptionInQueue.mechanism?.exceptionId) + assertEquals(1, innerMostExceptionInQueue.mechanism?.parentId) + assertEquals(true, innerMostExceptionInQueue.mechanism?.isExceptionGroup) + + assertEquals("suppressed", innerSuppressedInQueue.value) + assertEquals(2, innerSuppressedInQueue.mechanism?.exceptionId) + assertEquals(1, innerSuppressedInQueue.mechanism?.parentId) + assertNull(innerSuppressedInQueue.mechanism?.isExceptionGroup) + + assertEquals("inner", innerExceptionInQueue.value) + assertEquals(1, innerExceptionInQueue.mechanism?.exceptionId) + assertEquals(0, innerExceptionInQueue.mechanism?.parentId) + assertEquals(true, innerExceptionInQueue.mechanism?.isExceptionGroup) + + assertEquals("outer", outerInQueue.value) + assertEquals(0, outerInQueue.mechanism?.exceptionId) + assertNull(outerInQueue.mechanism?.parentId) + assertNull(outerInQueue.mechanism?.isExceptionGroup) + } + internal class InnerClassThrowable constructor(cause: Throwable? = null) : Throwable(cause) private val anonymousException = object : Exception() { From 9114f949e68a02d9b5c248367be72f2313ed9f55 Mon Sep 17 00:00:00 2001 From: Lukas Bloder Date: Tue, 14 May 2024 07:11:23 +0200 Subject: [PATCH 46/91] HSM 43a Fix Android Tests Alternative (#3418) * add DisabledSentryClient to distinguish between scopes with noopclient and a disabled one * fix sdkInitTests, fix order of scope creation and closing --- .../io/sentry/uitest/android/SdkInitTests.kt | 22 +++++++++++++++++-- sentry/src/main/java/io/sentry/Sentry.java | 7 +++--- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt index c942783548..b615406a3d 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt +++ b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt @@ -56,6 +56,7 @@ class SdkInitTests : BaseUiTest() { it.isDebug = true } relayIdlingResource.increment() + relayIdlingResource.increment() transaction.finish() sampleScenario.moveToState(Lifecycle.State.DESTROYED) val transaction2 = Sentry.startTransaction("e2etests2", "testInit") @@ -63,7 +64,23 @@ class SdkInitTests : BaseUiTest() { relay.assert { findEnvelope { - assertEnvelopeTransaction(it.items.toList(), AndroidLogger()).transaction == "e2etests2" + assertEnvelopeTransaction( + it.items.toList(), + AndroidLogger() + ).transaction == "e2etests" + }.assert { + val transactionItem: SentryTransaction = it.assertTransaction() + it.assertNoOtherItems() + assertEquals("e2etests", transactionItem.transaction) + } + } + + relay.assert { + findEnvelope { + assertEnvelopeTransaction( + it.items.toList(), + AndroidLogger() + ).transaction == "e2etests2" }.assert { val transactionItem: SentryTransaction = it.assertTransaction() // Profiling uses executorService, so if the executorService is shutdown it would fail @@ -105,7 +122,8 @@ class SdkInitTests : BaseUiTest() { Sentry.startTransaction("afterRestart", "emptyTransaction").finish() // We assert for less than 1 second just to account for slow devices in saucelabs or headless emulator - assertTrue(restartMs < 1000, "Expected less than 1000 ms for SDK restart. Got $restartMs ms") + // TODO: Revert back to 1000ms after making scope.close() faster again + assertTrue(restartMs < 2500, "Expected less than 2500 ms for SDK restart. Got $restartMs ms") relay.assert { findEnvelope { diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index ead61260bc..bc85ae6339 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -272,16 +272,15 @@ private static synchronized void init( options.getLogger().log(SentryLevel.INFO, "GlobalHubMode: '%s'", String.valueOf(globalHubMode)); Sentry.globalHubMode = globalHubMode; - globalScope.replaceOptions(options); final IScopes scopes = getCurrentScopes(); final IScope rootScope = new Scope(options); final IScope rootIsolationScope = new Scope(options); - rootScopes = new Scopes(rootScope, rootIsolationScope, globalScope, "Sentry.init"); - - getScopesStorage().set(rootScopes); scopes.close(true); + globalScope.replaceOptions(options); + rootScopes = new Scopes(rootScope, rootIsolationScope, globalScope, "Sentry.init"); + getScopesStorage().set(rootScopes); globalScope.bindClient(new SentryClient(options)); // If the executorService passed in the init is the same that was previously closed, we have to From 4efa6f77e7a9beee4ab9d4ed98ade362d358473b Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 14 May 2024 09:20:15 +0200 Subject: [PATCH 47/91] fix after merge --- .../core/PerformanceAndroidEventProcessorTest.kt | 10 +++++----- .../spring/boot/jakarta/SentryAutoConfiguration.java | 2 +- .../io/sentry/spring/boot/SentryAutoConfiguration.java | 2 +- .../test/java/io/sentry/transport/RateLimiterTest.kt | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt index 9fff9e2f49..7577f1cd1e 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt @@ -473,7 +473,7 @@ class PerformanceAndroidEventProcessorTest { val sut = fixture.getSut(enablePerformanceV2 = true) val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.hub) + val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) // when it contains no app start span and is processed @@ -490,7 +490,7 @@ class PerformanceAndroidEventProcessorTest { val sut = fixture.getSut(enablePerformanceV2 = true) val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.hub) + val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) val appStartSpan = SentrySpan( @@ -525,7 +525,7 @@ class PerformanceAndroidEventProcessorTest { val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.hub) + val tracer = SentryTracer(context, fixture.scopes) val tr = SentryTransaction(tracer) // given a ttid from 0.0 -> 1.0 @@ -649,7 +649,7 @@ class PerformanceAndroidEventProcessorTest { val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.hub) + val tracer = SentryTracer(context, fixture.scopes) val tr = SentryTransaction(tracer) val span = SentrySpan( @@ -683,7 +683,7 @@ class PerformanceAndroidEventProcessorTest { val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.hub) + val tracer = SentryTracer(context, fixture.scopes) val tr = SentryTransaction(tracer) // given a ttid from 0.0 -> 1.0 diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java index 7991a5ce01..d7f5099aa2 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java @@ -301,7 +301,7 @@ static class SentryMvcModeConfig { @Bean @ConditionalOnMissingBean public @NotNull SentryExceptionResolver sentryExceptionResolver( - final @NotNull IScopes scopes + final @NotNull IScopes scopes, final @NotNull TransactionNameProvider transactionNameProvider, final @NotNull SentryProperties options) { return new SentryExceptionResolver( diff --git a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java index 93668971eb..df3ca097bc 100644 --- a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java +++ b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java @@ -290,7 +290,7 @@ public FilterRegistrationBean sentryTracingFilter( filter.setOrder(SENTRY_SPRING_FILTER_PRECEDENCE + 1); // must run after SentrySpringFilter return filter; } - + @Configuration(proxyBeanMethods = false) @ConditionalOnClass(HandlerExceptionResolver.class) @Open diff --git a/sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt b/sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt index 140037b0c6..557085031c 100644 --- a/sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt +++ b/sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt @@ -279,12 +279,12 @@ class RateLimiterTest { @Test fun `drop metrics items as lost`() { val rateLimiter = fixture.getSUT() - val hub = mock() - whenever(hub.options).thenReturn(SentryOptions()) + val scopes = mock() + whenever(scopes.options).thenReturn(SentryOptions()) val eventItem = SentryEnvelopeItem.fromEvent(fixture.serializer, SentryEvent()) val f = File.createTempFile("test", "trace") - val transaction = SentryTracer(TransactionContext("name", "op"), hub) + val transaction = SentryTracer(TransactionContext("name", "op"), scopes) val profileItem = SentryEnvelopeItem.fromProfilingTrace(ProfilingTraceData(f, transaction), 1000, fixture.serializer) val statsdItem = SentryEnvelopeItem.fromMetrics(EncodedMetrics(emptyMap())) val envelope = SentryEnvelope(SentryEnvelopeHeader(), arrayListOf(eventItem, profileItem, statsdItem)) From 6c31cc0e09e6e077fc62f81c30a2f854c87f97d2 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 14 May 2024 16:14:10 +0200 Subject: [PATCH 48/91] 8.x Cleanup (#3419) * fix webflux tests that are now reporting a suppressed exception * wrapCallable and wrapSupplier now isolate by default, non isolation variant has been removed for now * cleanup TODOs * Ignore current thread with mechanism for abnormal crash detection (#3420) --- .../webflux/SentryWebfluxIntegrationTest.kt | 2 +- .../webflux/SentryWebfluxIntegrationTest.kt | 2 +- sentry/api/sentry.api | 2 - .../java/io/sentry/DefaultScopesStorage.java | 2 - sentry/src/main/java/io/sentry/Scope.java | 2 - sentry/src/main/java/io/sentry/Scopes.java | 2 - sentry/src/main/java/io/sentry/Sentry.java | 3 +- .../java/io/sentry/SentryThreadFactory.java | 4 +- .../main/java/io/sentry/SentryWrapper.java | 46 +---- .../test/java/io/sentry/SentryWrapperTest.kt | 185 +----------------- 10 files changed, 16 insertions(+), 234 deletions(-) diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebfluxIntegrationTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebfluxIntegrationTest.kt index 1751b83d63..9033028dfc 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebfluxIntegrationTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebfluxIntegrationTest.kt @@ -95,7 +95,7 @@ class SentryWebfluxIntegrationTest { checkEvent { event -> assertEquals("GET /throws", event.transaction) assertNotNull(event.exceptions) { - val ex = it.first() + val ex = it.last() assertEquals("something went wrong", ex.value) assertNotNull(ex.mechanism) { assertThat(it.isHandled).isFalse() diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebfluxIntegrationTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebfluxIntegrationTest.kt index 8c5aeb1c0a..316aaf8738 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebfluxIntegrationTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebfluxIntegrationTest.kt @@ -95,7 +95,7 @@ class SentryWebfluxIntegrationTest { checkEvent { event -> assertEquals("GET /throws", event.transaction) assertNotNull(event.exceptions) { - val ex = it.first() + val ex = it.last() assertEquals("something went wrong", ex.value) assertNotNull(ex.mechanism) { assertThat(it.isHandled).isFalse() diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 11cdefddcd..dc0c3ebd5e 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -2961,9 +2961,7 @@ public final class io/sentry/SentryTracer : io/sentry/ITransaction { public final class io/sentry/SentryWrapper { public fun ()V public static fun wrapCallable (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Callable; - public static fun wrapCallableIsolated (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Callable; public static fun wrapSupplier (Ljava/util/function/Supplier;)Ljava/util/function/Supplier; - public static fun wrapSupplierIsolated (Ljava/util/function/Supplier;)Ljava/util/function/Supplier; } public final class io/sentry/Session : io/sentry/JsonSerializable, io/sentry/JsonUnknown { diff --git a/sentry/src/main/java/io/sentry/DefaultScopesStorage.java b/sentry/src/main/java/io/sentry/DefaultScopesStorage.java index 4a054ee7cc..1ed80ceea8 100644 --- a/sentry/src/main/java/io/sentry/DefaultScopesStorage.java +++ b/sentry/src/main/java/io/sentry/DefaultScopesStorage.java @@ -21,8 +21,6 @@ public ISentryLifecycleToken set(@Nullable IScopes scopes) { @Override public void close() { - // TODO [HSM] prevent further storing? would this cause problems if singleton, closed and - // re-initialized? currentScopes.remove(); } diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index 0d42326e13..0f2441a467 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -92,8 +92,6 @@ public final class Scope implements IScope { private @NotNull ISentryClient client = NoOpSentryClient.getInstance(); - // TODO [HSM] intended only for global scope - // TODO [HSM] test for memory leak private final @NotNull Map, String>> throwableToSpan = Collections.synchronizedMap(new WeakHashMap<>()); diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 59540d105b..08144aa702 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -87,7 +87,6 @@ private Scopes( return globalScope; } - // TODO [HSM] add to IScopes interface? public boolean isAncestorOf(final @Nullable Scopes otherScopes) { if (otherScopes == null) { return false; @@ -623,7 +622,6 @@ public void popScope() { } } - // TODO [HSM] lots of testing required to see how ThreadLocal is affected @Override public void withScope(final @NotNull ScopeCallback callback) { if (!isEnabled()) { diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 07e289519d..4add79ad10 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -53,8 +53,7 @@ private Sentry() {} * *

    For Android options will also be (temporarily) replaced by SentryAndroid static block. */ - // TODO [HSM] use SentryOptions.empty and address - // https://github.com/getsentry/sentry-java/issues/2541 + // TODO https://github.com/getsentry/sentry-java/issues/2541 private static final @NotNull IScope globalScope = new Scope(SentryOptions.empty()); /** Default value for globalHubMode is false */ diff --git a/sentry/src/main/java/io/sentry/SentryThreadFactory.java b/sentry/src/main/java/io/sentry/SentryThreadFactory.java index 832ec8ea72..8af5f0aa0c 100644 --- a/sentry/src/main/java/io/sentry/SentryThreadFactory.java +++ b/sentry/src/main/java/io/sentry/SentryThreadFactory.java @@ -105,7 +105,9 @@ List getCurrentThreads( final Thread thread = item.getKey(); final boolean crashed = (thread == currentThread && !ignoreCurrentThread) - || (mechanismThreadIds != null && mechanismThreadIds.contains(thread.getId())); + || (mechanismThreadIds != null + && mechanismThreadIds.contains(thread.getId()) + && !ignoreCurrentThread); result.add(getSentryThread(crashed, item.getValue(), item.getKey())); } diff --git a/sentry/src/main/java/io/sentry/SentryWrapper.java b/sentry/src/main/java/io/sentry/SentryWrapper.java index 78505cf297..e4ccefed48 100644 --- a/sentry/src/main/java/io/sentry/SentryWrapper.java +++ b/sentry/src/main/java/io/sentry/SentryWrapper.java @@ -16,30 +16,8 @@ * scope(s) are forked, depends on the method used here. This prevents reused threads (e.g. from * thread-pools) from getting an incorrect state. */ -// TODO [HSM] only deliver isolated variant as default for now public final class SentryWrapper { - /** - * Helper method to wrap {@link Callable} - * - *

    Forks current scope before execution and restores previous state afterwards. This prevents - * reused threads (e.g. from thread-pools) from getting an incorrect state. - * - * @param callable - the {@link Callable} to be wrapped - * @return the wrapped {@link Callable} - * @param - the result type of the {@link Callable} - */ - public static Callable wrapCallable(final @NotNull Callable callable) { - final IScopes newScopes = - Sentry.getCurrentScopes().forkedCurrentScope("SentryWrapper.wrapCallable"); - - return () -> { - try (ISentryLifecycleToken ignored = newScopes.makeCurrent()) { - return callable.call(); - } - }; - } - /** * Helper method to wrap {@link Callable} * @@ -50,7 +28,7 @@ public static Callable wrapCallable(final @NotNull Callable callable) * @return the wrapped {@link Callable} * @param - the result type of the {@link Callable} */ - public static Callable wrapCallableIsolated(final @NotNull Callable callable) { + public static Callable wrapCallable(final @NotNull Callable callable) { final IScopes newScopes = Sentry.getCurrentScopes().forkedScopes("SentryWrapper.wrapCallable"); return () -> { @@ -60,26 +38,6 @@ public static Callable wrapCallableIsolated(final @NotNull Callable ca }; } - /** - * Helper method to wrap {@link Supplier} - * - *

    Forks current scope before execution and restores previous state afterwards. This prevents - * reused threads (e.g. from thread-pools) from getting an incorrect state. - * - * @param supplier - the {@link Supplier} to be wrapped - * @return the wrapped {@link Supplier} - * @param - the result type of the {@link Supplier} - */ - public static Supplier wrapSupplier(final @NotNull Supplier supplier) { - final IScopes newScopes = Sentry.forkedCurrentScope("SentryWrapper.wrapSupplier"); - - return () -> { - try (ISentryLifecycleToken ignore = newScopes.makeCurrent()) { - return supplier.get(); - } - }; - } - /** * Helper method to wrap {@link Supplier} * @@ -90,7 +48,7 @@ public static Supplier wrapSupplier(final @NotNull Supplier supplier) * @return the wrapped {@link Supplier} * @param - the result type of the {@link Supplier} */ - public static Supplier wrapSupplierIsolated(final @NotNull Supplier supplier) { + public static Supplier wrapSupplier(final @NotNull Supplier supplier) { final IScopes newScopes = Sentry.forkedScopes("SentryWrapper.wrapSupplier"); return () -> { diff --git a/sentry/src/test/java/io/sentry/SentryWrapperTest.kt b/sentry/src/test/java/io/sentry/SentryWrapperTest.kt index a830446428..6fd32ac57b 100644 --- a/sentry/src/test/java/io/sentry/SentryWrapperTest.kt +++ b/sentry/src/test/java/io/sentry/SentryWrapperTest.kt @@ -27,176 +27,7 @@ class SentryWrapperTest { } @Test - fun `scopes are reset to its state within the thread after supply is done`() { - Sentry.init { - it.dsn = dsn - it.beforeSend = SentryOptions.BeforeSendCallback { event, hint -> - event - } - } - - val mainScopes = Sentry.getCurrentScopes() - val threadedScopes = mainScopes.forkedCurrentScope("test") - - executor.submit { - Sentry.setCurrentScopes(threadedScopes) - }.get() - - assertEquals(mainScopes, Sentry.getCurrentScopes()) - - val callableFuture = - CompletableFuture.supplyAsync( - SentryWrapper.wrapSupplierIsolated { - assertNotEquals(mainScopes, Sentry.getCurrentScopes()) - assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) - "Result 1" - }, - executor - ) - - callableFuture.join() - - executor.submit { - assertNotEquals(mainScopes, Sentry.getCurrentScopes()) - assertEquals(threadedScopes, Sentry.getCurrentScopes()) - }.get() - } - - @Test - fun `wrapped supply async does not isolate Scopes`() { - val capturedEvents = mutableListOf() - - Sentry.init { - it.dsn = dsn - it.beforeSend = SentryOptions.BeforeSendCallback { event, hint -> - capturedEvents.add(event) - event - } - } - - Sentry.addBreadcrumb("MyOriginalBreadcrumbBefore") - Sentry.captureMessage("OriginalMessageBefore") - - val callableFuture = - CompletableFuture.supplyAsync( - SentryWrapper.wrapSupplier { - Thread.sleep(20) - Sentry.addBreadcrumb("MyClonedBreadcrumb") - Sentry.captureMessage("ClonedMessage") - "Result 1" - }, - executor - ) - - val callableFuture2 = - CompletableFuture.supplyAsync( - SentryWrapper.wrapSupplier { - Thread.sleep(10) - Sentry.addBreadcrumb("MyClonedBreadcrumb2") - Sentry.captureMessage("ClonedMessage2") - "Result 2" - }, - executor - ) - - Sentry.addBreadcrumb("MyOriginalBreadcrumb") - Sentry.captureMessage("OriginalMessage") - - callableFuture.join() - callableFuture2.join() - - val mainEvent = capturedEvents.firstOrNull { it.message?.formatted == "OriginalMessage" } - val clonedEvent = capturedEvents.firstOrNull { it.message?.formatted == "ClonedMessage" } - val clonedEvent2 = capturedEvents.firstOrNull { it.message?.formatted == "ClonedMessage2" } - - assertEquals(2, mainEvent?.breadcrumbs?.size) - assertEquals(3, clonedEvent?.breadcrumbs?.size) - assertEquals(4, clonedEvent2?.breadcrumbs?.size) - } - - @Test - fun `wrapped callable does not isolate Scopes`() { - val capturedEvents = mutableListOf() - - Sentry.init { - it.dsn = dsn - it.beforeSend = SentryOptions.BeforeSendCallback { event, hint -> - capturedEvents.add(event) - event - } - } - - Sentry.addBreadcrumb("MyOriginalBreadcrumbBefore") - Sentry.captureMessage("OriginalMessageBefore") - println(Thread.currentThread().name) - - val future1 = executor.submit( - SentryWrapper.wrapCallable { - Thread.sleep(20) - Sentry.addBreadcrumb("MyClonedBreadcrumb") - Sentry.captureMessage("ClonedMessage") - "Result 1" - } - ) - - val future2 = executor.submit( - SentryWrapper.wrapCallable { - Thread.sleep(10) - Sentry.addBreadcrumb("MyClonedBreadcrumb2") - Sentry.captureMessage("ClonedMessage2") - "Result 2" - } - ) - - Sentry.addBreadcrumb("MyOriginalBreadcrumb") - Sentry.captureMessage("OriginalMessage") - - future1.get() - future2.get() - - val mainEvent = capturedEvents.firstOrNull { it.message?.formatted == "OriginalMessage" } - val clonedEvent = capturedEvents.firstOrNull { it.message?.formatted == "ClonedMessage" } - val clonedEvent2 = capturedEvents.firstOrNull { it.message?.formatted == "ClonedMessage2" } - - assertEquals(2, mainEvent?.breadcrumbs?.size) - assertEquals(3, clonedEvent?.breadcrumbs?.size) - assertEquals(4, clonedEvent2?.breadcrumbs?.size) - } - - @Test - fun `scopes are reset to its state within the thread after callable is done`() { - Sentry.init { - it.dsn = dsn - } - - val mainScopes = Sentry.getCurrentScopes() - val threadedScopes = Sentry.getCurrentScopes().forkedCurrentScope("test") - - executor.submit { - Sentry.setCurrentScopes(threadedScopes) - }.get() - - assertEquals(mainScopes, Sentry.getCurrentScopes()) - - val callableFuture = - executor.submit( - SentryWrapper.wrapCallable { - assertNotEquals(mainScopes, Sentry.getCurrentScopes()) - assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) - "Result 1" - } - ) - - callableFuture.get() - - executor.submit { - assertNotEquals(mainScopes, Sentry.getCurrentScopes()) - assertEquals(threadedScopes, Sentry.getCurrentScopes()) - }.get() - } - - @Test - fun `scopes is reset to its state within the thread after isolated supply is done`() { + fun `scopes is reset to state within the thread after isolated supply is done`() { Sentry.init { it.dsn = dsn it.beforeSend = SentryOptions.BeforeSendCallback { event, hint -> @@ -215,7 +46,7 @@ class SentryWrapperTest { val callableFuture = CompletableFuture.supplyAsync( - SentryWrapper.wrapSupplierIsolated { + SentryWrapper.wrapSupplier { assertNotEquals(mainScopes, Sentry.getCurrentScopes()) assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) "Result 1" @@ -248,7 +79,7 @@ class SentryWrapperTest { val callableFuture = CompletableFuture.supplyAsync( - SentryWrapper.wrapSupplierIsolated { + SentryWrapper.wrapSupplier { Thread.sleep(20) Sentry.addBreadcrumb("MyClonedBreadcrumb") Sentry.captureMessage("ClonedMessage") @@ -259,7 +90,7 @@ class SentryWrapperTest { val callableFuture2 = CompletableFuture.supplyAsync( - SentryWrapper.wrapSupplierIsolated { + SentryWrapper.wrapSupplier { Thread.sleep(10) Sentry.addBreadcrumb("MyClonedBreadcrumb2") Sentry.captureMessage("ClonedMessage2") @@ -300,7 +131,7 @@ class SentryWrapperTest { println(Thread.currentThread().name) val future1 = executor.submit( - SentryWrapper.wrapCallableIsolated { + SentryWrapper.wrapCallable { Thread.sleep(20) Sentry.addBreadcrumb("MyClonedBreadcrumb") Sentry.captureMessage("ClonedMessage") @@ -309,7 +140,7 @@ class SentryWrapperTest { ) val future2 = executor.submit( - SentryWrapper.wrapCallableIsolated { + SentryWrapper.wrapCallable { Thread.sleep(10) Sentry.addBreadcrumb("MyClonedBreadcrumb2") Sentry.captureMessage("ClonedMessage2") @@ -333,7 +164,7 @@ class SentryWrapperTest { } @Test - fun `scopes is reset to its state within the thread after isolated callable is done`() { + fun `scopes is reset to state within the thread after isolated callable is done`() { Sentry.init { it.dsn = dsn } @@ -349,7 +180,7 @@ class SentryWrapperTest { val callableFuture = executor.submit( - SentryWrapper.wrapCallableIsolated { + SentryWrapper.wrapCallable { assertNotEquals(mainScopes, Sentry.getCurrentScopes()) assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) "Result 1" From ea117ff0f76e18ee77c4113ccc9c362581e12139 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 14 May 2024 16:14:55 +0200 Subject: [PATCH 49/91] Add changelog for `8.x` (alpha) release (#3421) * changelog and more deprecation javadoc * Update CHANGELOG.md Co-authored-by: Markus Hintersteiner --------- Co-authored-by: Markus Hintersteiner --- CHANGELOG.md | 49 +++++++++++++++++++ .../io/sentry/graphql/ExceptionReporter.java | 3 ++ .../sentry/graphql/SentryInstrumentation.java | 3 ++ .../webflux/AbstractSentryWebFilter.java | 5 ++ .../spring/webflux/SentryWebFilter.java | 4 ++ .../src/main/java/io/sentry/HubAdapter.java | 9 ++++ .../main/java/io/sentry/HubScopesWrapper.java | 9 ++++ sentry/src/main/java/io/sentry/NoOpHub.java | 9 ++++ .../src/main/java/io/sentry/NoOpScopes.java | 8 +++ sentry/src/main/java/io/sentry/Scopes.java | 9 ++++ .../main/java/io/sentry/ScopesAdapter.java | 9 ++++ sentry/src/main/java/io/sentry/Sentry.java | 4 ++ 12 files changed, 121 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0b2f2e11c..99dfe88cfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,55 @@ ## Unreleased +Version 8 of the Sentry Android/Java SDK brings a variety of features and fixes. The most notable changes are: + +- New `Scope` types have been introduced, see "Behavioural Changes" for more details. +- Lifecycle tokens have been introduced to manage `Scope` lifecycle, see "Behavioural Changes" for more details. +- `Hub` has been replaced by `Scopes` + +### Behavioural Changes + +- We're introducing some new `Scope` types in the SDK, allowing for better control over what data is attached where. Previously there was a stack of scopes that was pushed and popped. Instead we now fork scopes for a given lifecycle and then restore the previous scopes. Since `Hub` is gone, it is also never cloned anymore. Separation of data now happens through the different scope types while making it easier to manipulate exactly what you need without having to attach data at the right time to have it apply where wanted. + - Global scope is attached to all events created by the SDK. It can also be modified before `Sentry.init` has been called. It can be manipulated using `Sentry.configureScope(ScopeType.GLOBAL, (scope) -> { ... })`. + - Isolation scope can be used e.g. to attach data to all events that come up while handling an incoming request. It can also be used for other isolation purposes. It can be manipulated using `Sentry.configureScope(ScopeType.ISOLATION, (scope) -> { ... })`. The SDK automatically forks isolation scope in certain cases like incoming requests, CRON jobs, Spring `@Async` and more. + - Current scope is forked often and data added to it is only added to events that are created while this scope is active. Data is also passed on to newly forked child scopes but not to parents. +- `Sentry.popScope` has been deprecated, please call `.close()` on the token returned by `Sentry.pushScope` instead or use it in a way described in more detail in "Migration Guide". +- We have chosen a default scope that is used for `Sentry.configureScope()` as well as API like `Sentry.setTag()` + - For Android the type defaults to `CURRENT` scope + - For Backend and other JVM applicatons it defaults to `ISOLATION` scope +- Event processors on `Scope` can now be ordered by overriding the `getOrder` method on implementations of `EventProcessor`. NOTE: This order only applies to event processors on `Scope` but not `SentryOptions` at the moment. Feel free to request this if you need it. +- `Hub` is deprecated in favor of `Scopes`, alongside some `Hub` relevant APIs. More details can be found in the "Migration Guide" section. + +### Breaking Changes + +- `Contexts` no longer extends `ConcurrentHashMap`, instead we offer a selected set of methods. + +### Migration Guide / Deprecations + +- `Hub` has been deprecated, we're replacing the following: + - `IHub` has been replaced by `IScopes`, however you should be able to simply pass `IHub` instances to code expecting `IScopes`, allowing for an easier migration. + - `HubAdapter.getInstance()` has been replaced by `ScopesAdapter.getInstance()` + - The `.clone()` method on `IHub`/`IScopes` has been deprecated, please use `.pushScope()` or `.pushIsolationScope()` instead + - Some internal methods like `.getCurrentHub()` and `.setCurrentHub()` have also been replaced. +- `Sentry.popScope` has been replaced by calling `.close()` on the token returned by `Sentry.pushScope()` and `Sentry.pushIsolationScope()`. The token can also be used in a `try` block like this: + +``` +try (final @NotNull ISentryLifecycleToken ignored = Sentry.pushScope()) { + // this block has its separate current scope +} +``` + +as well as: + + +``` +try (final @NotNull ISentryLifecycleToken ignored = Sentry.pushIsolationScope()) { + // this block has its separate isolation scope +} +``` + +You may also use `LifecycleHelper.close(token)`, e.g. in case you need to pass the token around for closing later. + ### Features - Report exceptions returned by Throwable.getSuppressed() to Sentry as exception groups ([#3396] https://github.com/getsentry/sentry-java/pull/3396) diff --git a/sentry-graphql/src/main/java/io/sentry/graphql/ExceptionReporter.java b/sentry-graphql/src/main/java/io/sentry/graphql/ExceptionReporter.java index 843ca77494..9bca0955e4 100644 --- a/sentry-graphql/src/main/java/io/sentry/graphql/ExceptionReporter.java +++ b/sentry-graphql/src/main/java/io/sentry/graphql/ExceptionReporter.java @@ -149,6 +149,9 @@ public boolean isSubscription() { return isSubscription; } + /** + * @deprecated please use {@link ExceptionDetails#getScopes()} instead. + */ @Deprecated public @NotNull IScopes getHub() { return scopes; diff --git a/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java b/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java index e4f85d12a2..c122ff5b9a 100644 --- a/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java +++ b/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java @@ -48,6 +48,9 @@ public final class SentryInstrumentation ); public static final @NotNull String SENTRY_SCOPES_CONTEXT_KEY = "sentry.scopes"; + /** + * @deprecated please use {@link SentryInstrumentation#SENTRY_SCOPES_CONTEXT_KEY} instead. + */ @Deprecated public static final @NotNull String SENTRY_HUB_CONTEXT_KEY = SENTRY_SCOPES_CONTEXT_KEY; diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java index 86e17f27c3..e626e69d3b 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java @@ -35,7 +35,12 @@ public abstract class AbstractSentryWebFilter implements WebFilter { private final @NotNull SentryRequestResolver sentryRequestResolver; public static final String SENTRY_SCOPES_KEY = "sentry-scopes"; + + /** + * @deprecated please use {@link AbstractSentryWebFilter#SENTRY_SCOPES_KEY} instead. + */ @Deprecated public static final String SENTRY_HUB_KEY = SENTRY_SCOPES_KEY; + private static final String TRANSACTION_OP = "http.server"; public AbstractSentryWebFilter(final @NotNull IScopes scopes) { diff --git a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java index 0601dcaeb3..3549b95c81 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java @@ -35,7 +35,11 @@ @ApiStatus.Experimental public final class SentryWebFilter implements WebFilter { public static final String SENTRY_SCOPES_KEY = "sentry-scopes"; + /** + * @deprecated please use {@link SentryWebFilter#SENTRY_SCOPES_KEY} instead. + */ @Deprecated public static final String SENTRY_HUB_KEY = SENTRY_SCOPES_KEY; + private static final String TRANSACTION_OP = "http.server"; private static final String TRACE_ORIGIN = "auto.spring.webflux"; diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index df0669504a..28c974bd59 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -163,6 +163,10 @@ public void removeExtra(@NotNull String key) { return Sentry.pushIsolationScope(); } + /** + * @deprecated please call {@link ISentryLifecycleToken#close()} on the token returned by {@link + * ScopesAdapter#pushScope()} or {@link ScopesAdapter#pushIsolationScope()} instead. + */ @Override @Deprecated public void popScope() { @@ -199,6 +203,11 @@ public void flush(long timeoutMillis) { Sentry.flush(timeoutMillis); } + /** + * @deprecated please use {@link IScopes#forkedScopes(String)} or {@link + * IScopes#forkedCurrentScope(String)} instead. + */ + @Deprecated @Override public @NotNull IHub clone() { return Sentry.getCurrentScopes().clone(); diff --git a/sentry/src/main/java/io/sentry/HubScopesWrapper.java b/sentry/src/main/java/io/sentry/HubScopesWrapper.java index 4909f6b193..195371ee52 100644 --- a/sentry/src/main/java/io/sentry/HubScopesWrapper.java +++ b/sentry/src/main/java/io/sentry/HubScopesWrapper.java @@ -158,6 +158,10 @@ public void removeExtra(@NotNull String key) { return scopes.pushIsolationScope(); } + /** + * @deprecated please call {@link ISentryLifecycleToken#close()} on the token returned by {@link + * IScopes#pushScope()} or {@link IScopes#pushIsolationScope()} instead. + */ @Override @Deprecated public void popScope() { @@ -194,7 +198,12 @@ public void flush(long timeoutMillis) { scopes.flush(timeoutMillis); } + /** + * @deprecated please use {@link IScopes#forkedScopes(String)} or {@link + * IScopes#forkedCurrentScope(String)} instead. + */ @Override + @Deprecated public @NotNull IHub clone() { return scopes.clone(); } diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index 3625eb7c06..e2d2b411ec 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -136,6 +136,10 @@ public void removeExtra(@NotNull String key) {} return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); } + /** + * @deprecated please call {@link ISentryLifecycleToken#close()} on the token returned by {@link + * IScopes#pushScope()} or {@link IScopes#pushIsolationScope()} instead. + */ @Override @Deprecated public void popScope() {} @@ -164,6 +168,11 @@ public boolean isHealthy() { @Override public void flush(long timeoutMillis) {} + /** + * @deprecated please use {@link IScopes#forkedScopes(String)} or {@link + * IScopes#forkedCurrentScope(String)} instead. + */ + @Deprecated @Override public @NotNull IHub clone() { return instance; diff --git a/sentry/src/main/java/io/sentry/NoOpScopes.java b/sentry/src/main/java/io/sentry/NoOpScopes.java index 945203066b..7058cf9c28 100644 --- a/sentry/src/main/java/io/sentry/NoOpScopes.java +++ b/sentry/src/main/java/io/sentry/NoOpScopes.java @@ -131,6 +131,10 @@ public void removeExtra(@NotNull String key) {} return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); } + /** + * @deprecated please call {@link ISentryLifecycleToken#close()} on the token returned by {@link + * IScopes#pushScope()} or {@link IScopes#pushIsolationScope()} instead. + */ @Override @Deprecated public void popScope() {} @@ -159,6 +163,10 @@ public boolean isHealthy() { @Override public void flush(long timeoutMillis) {} + /** + * @deprecated please use {@link IScopes#forkedScopes(String)} or {@link + * IScopes#forkedCurrentScope(String)} instead. + */ @Deprecated @Override public @NotNull IHub clone() { diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 08144aa702..63b1b37508 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -607,6 +607,10 @@ public ISentryLifecycleToken pushIsolationScope() { return Sentry.setCurrentScopes(this); } + /** + * @deprecated please call {@link ISentryLifecycleToken#close()} on the token returned by {@link + * IScopes#pushScope()} or {@link IScopes#pushIsolationScope()} instead. + */ @Override @Deprecated public void popScope() { @@ -715,7 +719,12 @@ public void flush(long timeoutMillis) { } } + /** + * @deprecated please use {@link IScopes#forkedScopes(String)} or {@link + * IScopes#forkedCurrentScope(String)} instead. + */ @Override + @Deprecated @SuppressWarnings("deprecation") public @NotNull IHub clone() { if (!isEnabled()) { diff --git a/sentry/src/main/java/io/sentry/ScopesAdapter.java b/sentry/src/main/java/io/sentry/ScopesAdapter.java index 005480ccf5..63e6d1ee3c 100644 --- a/sentry/src/main/java/io/sentry/ScopesAdapter.java +++ b/sentry/src/main/java/io/sentry/ScopesAdapter.java @@ -159,6 +159,10 @@ public void removeExtra(@NotNull String key) { return Sentry.pushIsolationScope(); } + /** + * @deprecated please call {@link ISentryLifecycleToken#close()} on the token returned by {@link + * IScopes#pushScope()} or {@link IScopes#pushIsolationScope()} instead. + */ @Override @Deprecated public void popScope() { @@ -195,6 +199,11 @@ public void flush(long timeoutMillis) { Sentry.flush(timeoutMillis); } + /** + * @deprecated please use {@link IScopes#forkedScopes(String)} or {@link + * IScopes#forkedCurrentScope(String)} instead. + */ + @Deprecated @Override @SuppressWarnings("deprecation") public @NotNull IHub clone() { diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 4add79ad10..240af80ea3 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -75,6 +75,7 @@ private Sentry() {} /** * Returns the current (threads) hub, if none, clones the rootScopes and returns it. * + * @deprecated please use {@link Sentry#getCurrentScopes()} instead * @return the hub */ @ApiStatus.Internal // exposed for the coroutines integration in SentryContext @@ -123,6 +124,9 @@ private Sentry() {} return getCurrentScopes().forkedCurrentScope(creator); } + /** + * @deprecated please use {@link Sentry#setCurrentScopes} instead. + */ @ApiStatus.Internal // exposed for the coroutines integration in SentryContext @Deprecated @SuppressWarnings({"deprecation", "InlineMeSuggester"}) From 218eb603c37257026ab50a3d01c98752bbac4c67 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Tue, 14 May 2024 14:25:55 +0000 Subject: [PATCH 50/91] release: 8.0.0-alpha.1 --- CHANGELOG.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99dfe88cfd..1900c51541 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 8.0.0-alpha.1 Version 8 of the Sentry Android/Java SDK brings a variety of features and fixes. The most notable changes are: diff --git a/gradle.properties b/gradle.properties index 00358cbb2f..a4760b08dd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ android.useAndroidX=true android.defaults.buildfeatures.buildconfig=true # Release information -versionName=7.9.0 +versionName=8.0.0-alpha.1 # Override the SDK name on native crashes on Android sentryAndroidSdkName=sentry.native.android From c2f2cc79b72787940a2f2382a0a1113b5bb04fa5 Mon Sep 17 00:00:00 2001 From: Omar Aloraini Date: Tue, 28 May 2024 16:02:41 +0300 Subject: [PATCH 51/91] Add data fetching environment hint to breadcrumb (#3413) (#3431) * Add data fetching environment hint to breadcrumb (#3413) * add environment Hint to test * add changelog * format, dumpApi * fix merge issues --------- Co-authored-by: Lukas Bloder --- CHANGELOG.md | 1 + .../main/java/io/sentry/graphql/SentryInstrumentation.java | 7 ++++++- .../io/sentry/graphql/SentryInstrumentationAnotherTest.kt | 6 ++++++ sentry/api/sentry.api | 1 + sentry/src/main/java/io/sentry/TypeCheckHint.java | 3 +++ 5 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7854b9a98a..d7dd1e9d5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - Publish Gradle module metadata ([#3422](https://github.com/getsentry/sentry-java/pull/3422)) +- Add data fetching environment hint to breadcrumb for GraphQL (#3413) ([#3431](https://github.com/getsentry/sentry-java/pull/3431)) ### Fixes diff --git a/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java b/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java index c122ff5b9a..2de9b82f75 100644 --- a/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java +++ b/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java @@ -18,12 +18,14 @@ import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLOutputType; import io.sentry.Breadcrumb; +import io.sentry.Hint; import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.NoOpScopes; import io.sentry.Sentry; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SpanStatus; +import io.sentry.TypeCheckHint; import io.sentry.util.StringUtils; import java.util.ArrayList; import java.util.Arrays; @@ -300,13 +302,16 @@ private boolean isIgnored(final @Nullable String errorType) { return environment -> { final @Nullable ExecutionStepInfo executionStepInfo = environment.getExecutionStepInfo(); if (executionStepInfo != null) { + Hint hint = new Hint(); + hint.set(TypeCheckHint.GRAPHQL_DATA_FETCHING_ENVIRONMENT, environment); scopesFromContext(parameters.getExecutionContext().getGraphQLContext()) .addBreadcrumb( Breadcrumb.graphqlDataFetcher( StringUtils.toString(executionStepInfo.getPath()), GraphqlStringUtils.fieldToString(executionStepInfo.getField()), GraphqlStringUtils.typeToString(executionStepInfo.getType()), - GraphqlStringUtils.objectTypeToString(executionStepInfo.getObjectType()))); + GraphqlStringUtils.objectTypeToString(executionStepInfo.getObjectType())), + hint); } final TracingState tracingState = parameters.getInstrumentationState(); final ISpan transaction = tracingState.getTransaction(); diff --git a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationAnotherTest.kt b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationAnotherTest.kt index 6309155929..7b673a66a8 100644 --- a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationAnotherTest.kt +++ b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationAnotherTest.kt @@ -28,11 +28,13 @@ import graphql.schema.GraphQLObjectType import graphql.schema.GraphQLScalarType import graphql.schema.GraphQLSchema import io.sentry.Breadcrumb +import io.sentry.Hint import io.sentry.IScopes import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.SentryTracer import io.sentry.TransactionContext +import io.sentry.TypeCheckHint import io.sentry.graphql.ExceptionReporter.ExceptionDetails import io.sentry.graphql.SentryInstrumentation.SENTRY_EXCEPTIONS_CONTEXT_KEY import io.sentry.graphql.SentryInstrumentation.TracingState @@ -245,6 +247,10 @@ class SentryInstrumentationAnotherTest { assertEquals("myFieldName", breadcrumb.data["field"]) assertEquals("MyResponseType", breadcrumb.data["type"]) assertEquals("QUERY", breadcrumb.data["object_type"]) + }, + org.mockito.kotlin.check { hint -> + val environment = hint.getAs(TypeCheckHint.GRAPHQL_DATA_FETCHING_ENVIRONMENT, DataFetchingEnvironment::class.java) + assertNotNull(environment) } ) } diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index dc0c3ebd5e..ef46c29c62 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -3343,6 +3343,7 @@ public final class io/sentry/TypeCheckHint { public static final field ANDROID_VIEW Ljava/lang/String; public static final field APOLLO_REQUEST Ljava/lang/String; public static final field APOLLO_RESPONSE Ljava/lang/String; + public static final field GRAPHQL_DATA_FETCHING_ENVIRONMENT Ljava/lang/String; public static final field GRAPHQL_HANDLER_PARAMETERS Ljava/lang/String; public static final field JUL_LOG_RECORD Ljava/lang/String; public static final field LOG4J_LOG_EVENT Ljava/lang/String; diff --git a/sentry/src/main/java/io/sentry/TypeCheckHint.java b/sentry/src/main/java/io/sentry/TypeCheckHint.java index e9d219c385..cbe784db5b 100644 --- a/sentry/src/main/java/io/sentry/TypeCheckHint.java +++ b/sentry/src/main/java/io/sentry/TypeCheckHint.java @@ -55,6 +55,9 @@ public final class TypeCheckHint { /** Used for GraphQl handler exceptions. */ public static final String GRAPHQL_HANDLER_PARAMETERS = "graphql:handlerParameters"; + /** Used for GraphQl data fetcher breadcrumbs. */ + public static final String GRAPHQL_DATA_FETCHING_ENVIRONMENT = "graphql:dataFetchingEnvironment"; + /** Used for JUL breadcrumbs. */ public static final String JUL_LOG_RECORD = "jul:logRecord"; From a3c251e7427812b0e800104537f713ba7ef6b663 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Tue, 28 May 2024 15:19:23 +0200 Subject: [PATCH 52/91] Move NDK from sentry-java to sentry-native (#3189) * Move NDK JNI code to sentry-native --- .github/workflows/update-deps.yml | 2 +- .gitmodules | 3 - CHANGELOG.md | 12 + build.gradle.kts | 5 +- buildSrc/src/main/java/Config.kt | 5 - scripts/update-sentry-native-ndk.sh | 35 ++ sentry-android-ndk/CMakeLists.txt | 17 - sentry-android-ndk/api/sentry-android-ndk.api | 2 +- sentry-android-ndk/build.gradle.kts | 32 +- sentry-android-ndk/sentry-native | 1 - .../sentry/android/ndk/DebugImagesLoader.java | 18 +- .../io/sentry/android/ndk/INativeScope.java | 18 - .../android/ndk/NativeModuleListLoader.java | 19 - .../io/sentry/android/ndk/NativeScope.java | 55 -- .../sentry/android/ndk/NdkScopeObserver.java | 2 + .../java/io/sentry/android/ndk/SentryNdk.java | 32 +- sentry-android-ndk/src/main/jni/sentry.c | 494 ------------------ .../android/ndk/DebugImagesLoaderTest.kt | 5 +- .../android/ndk/NdkScopeObserverTest.kt | 1 + .../sentry-samples-android/CMakeLists.txt | 9 +- .../sentry-samples-android/build.gradle.kts | 13 +- settings.gradle.kts | 9 - 22 files changed, 94 insertions(+), 695 deletions(-) create mode 100755 scripts/update-sentry-native-ndk.sh delete mode 100644 sentry-android-ndk/CMakeLists.txt delete mode 160000 sentry-android-ndk/sentry-native delete mode 100644 sentry-android-ndk/src/main/java/io/sentry/android/ndk/INativeScope.java delete mode 100644 sentry-android-ndk/src/main/java/io/sentry/android/ndk/NativeModuleListLoader.java delete mode 100644 sentry-android-ndk/src/main/java/io/sentry/android/ndk/NativeScope.java delete mode 100644 sentry-android-ndk/src/main/jni/sentry.c diff --git a/.github/workflows/update-deps.yml b/.github/workflows/update-deps.yml index 24fce64050..83d90bb919 100644 --- a/.github/workflows/update-deps.yml +++ b/.github/workflows/update-deps.yml @@ -13,7 +13,7 @@ jobs: native: uses: getsentry/github-workflows/.github/workflows/updater.yml@v2 with: - path: sentry-android-ndk/sentry-native + path: scripts/update-sentry-native-ndk.sh name: Native SDK secrets: # If a custom token is used instead, a CI would be triggered on a created PR. diff --git a/.gitmodules b/.gitmodules index fe6c3b7cc0..e69de29bb2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "sentry-android-ndk/sentry-native"] - path = sentry-android-ndk/sentry-native - url = https://github.com/getsentry/sentry-native diff --git a/CHANGELOG.md b/CHANGELOG.md index d7dd1e9d5a..57f12ff897 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +### Behavioural Changes + +- (Android) The JNI layer for sentry-native has now been moved from sentry-java to sentry-native ([#3189](https://github.com/getsentry/sentry-java/pull/3189)) + - This now includes prefab support for sentry-native, allowing you to link and access the sentry-native API within your native app code + - Checkout the `sentry-samples/sentry-samples-android` example on how to configure CMake and consume `sentry.h` + ### Features - Publish Gradle module metadata ([#3422](https://github.com/getsentry/sentry-java/pull/3422)) @@ -11,6 +17,12 @@ - Fix faulty `span.frame_delay` calculation for early app start spans ([#3427](https://github.com/getsentry/sentry-java/pull/3427)) +### Dependencies + +- Bump Native SDK from v0.7.0 to v0.7.5 ([#3441](https://github.com/getsentry/sentry-java/pull/3189)) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#075) + - [diff](https://github.com/getsentry/sentry-native/compare/0.7.0...0.7.5) + ## 8.0.0-alpha.1 Version 8 of the Sentry Android/Java SDK brings a variety of features and fixes. The most notable changes are: diff --git a/build.gradle.kts b/build.gradle.kts index a0235890b4..03b5723da0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,10 +32,6 @@ buildscript { classpath(Config.QualityPlugins.errorpronePlugin) classpath(Config.QualityPlugins.gradleVersionsPlugin) - // add classpath of androidNativeBundle - // com.ydq.android.gradle.build.tool:nativeBundle:{version}} - classpath(Config.NativePlugins.nativeBundlePlugin) - // add classpath of sentry android gradle plugin // classpath("io.sentry:sentry-android-gradle-plugin:{version}") @@ -78,6 +74,7 @@ allprojects { repositories { google() mavenCentral() + mavenLocal() } group = Config.Sentry.group version = properties[Config.Sentry.versionNameProp].toString() diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index 7a0081d5f4..31c99bad3f 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -254,9 +254,4 @@ object Config { val errorprone = "com.google.errorprone:error_prone_core:2.11.0" val errorProneNullAway = "com.uber.nullaway:nullaway:0.9.5" } - - object NativePlugins { - val nativeBundlePlugin = "io.github.howardpang:androidNativeBundle:1.1.1" - val nativeBundleExport = "com.ydq.android.gradle.native-aar.export" - } } diff --git a/scripts/update-sentry-native-ndk.sh b/scripts/update-sentry-native-ndk.sh new file mode 100755 index 0000000000..544dc403ac --- /dev/null +++ b/scripts/update-sentry-native-ndk.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd $(dirname "$0")/../ +GRADLE_NDK_FILEPATH=sentry-android-ndk/build.gradle.kts +GRADLE_SAMPLE_FILEPATH=sentry-samples/sentry-samples-android/build.gradle.kts + +case $1 in +get-version) + version=$(perl -ne 'print "$1\n" if ( m/io\.sentry:sentry-native-ndk:([0-9.]+)+/ )' $GRADLE_NDK_FILEPATH) + + echo "v$version" + ;; +get-repo) + echo "https://github.com/getsentry/sentry-native.git" + ;; +set-version) + version=$2 + + # Remove leading "v" + if [[ "$version" == v* ]]; then + version="${version:1}" + fi + + echo "Setting sentry-native-ndk version to '$version'" + + PATTERN="io\.sentry:sentry-native-ndk:([0-9.]+)+" + perl -pi -e "s/$PATTERN/io.sentry:sentry-native-ndk:$version/g" $GRADLE_NDK_FILEPATH + perl -pi -e "s/$PATTERN/io.sentry:sentry-native-ndk:$version/g" $GRADLE_SAMPLE_FILEPATH + ;; +*) + echo "Unknown argument $1" + exit 1 + ;; +esac diff --git a/sentry-android-ndk/CMakeLists.txt b/sentry-android-ndk/CMakeLists.txt deleted file mode 100644 index c9a0181935..0000000000 --- a/sentry-android-ndk/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -cmake_minimum_required(VERSION 3.10) -project(Sentry-Android LANGUAGES C CXX) - -# Add sentry-android shared library -add_library(sentry-android SHARED src/main/jni/sentry.c) - -# make sure that we build it as a shared lib instead of a static lib -set(BUILD_SHARED_LIBS ON) -set(SENTRY_BUILD_SHARED_LIBS ON) - -# Adding sentry-native submodule subdirectory -add_subdirectory(${SENTRY_NATIVE_SRC} sentry_build) - -# Link to sentry-native -target_link_libraries(sentry-android PRIVATE - $ -) diff --git a/sentry-android-ndk/api/sentry-android-ndk.api b/sentry-android-ndk/api/sentry-android-ndk.api index e8f838ce8b..155a368b11 100644 --- a/sentry-android-ndk/api/sentry-android-ndk.api +++ b/sentry-android-ndk/api/sentry-android-ndk.api @@ -7,7 +7,7 @@ public final class io/sentry/android/ndk/BuildConfig { } public final class io/sentry/android/ndk/DebugImagesLoader : io/sentry/android/core/IDebugImagesLoader { - public fun (Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/android/ndk/NativeModuleListLoader;)V + public fun (Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/ndk/NativeModuleListLoader;)V public fun clearDebugImages ()V public fun loadDebugImages ()Ljava/util/List; } diff --git a/sentry-android-ndk/build.gradle.kts b/sentry-android-ndk/build.gradle.kts index f6564cd97f..fe67063139 100644 --- a/sentry-android-ndk/build.gradle.kts +++ b/sentry-android-ndk/build.gradle.kts @@ -5,38 +5,21 @@ plugins { kotlin("android") jacoco id(Config.QualityPlugins.jacocoAndroid) - id(Config.NativePlugins.nativeBundleExport) id(Config.QualityPlugins.gradleVersions) } -var sentryNativeSrc: String = "sentry-native" val sentryAndroidSdkName: String by project android { compileSdk = Config.Android.compileSdkVersion namespace = "io.sentry.android.ndk" - sentryNativeSrc = if (File("${project.projectDir}/sentry-native-local").exists()) { - "sentry-native-local" - } else { - "sentry-native" - } - println("sentry-android-ndk: $sentryNativeSrc") - defaultConfig { targetSdk = Config.Android.targetSdkVersion minSdk = Config.Android.minSdkVersionNdk // NDK requires a higher API level than core. testInstrumentationRunner = Config.TestLibs.androidJUnitRunner - externalNativeBuild { - cmake { - arguments.add(0, "-DANDROID_STL=c++_static") - arguments.add(0, "-DSENTRY_NATIVE_SRC=$sentryNativeSrc") - arguments.add(0, "-DSENTRY_SDK_NAME=$sentryAndroidSdkName") - } - } - ndk { abiFilters.addAll(Config.Android.abiFilters) } @@ -45,15 +28,6 @@ android { buildConfigField("String", "VERSION_NAME", "\"${project.version}\"") } - // we use the default NDK and CMake versions based on the AGP's version - // https://developer.android.com/studio/projects/install-ndk#apply-specific-version - - externalNativeBuild { - cmake { - path("CMakeLists.txt") - } - } - buildTypes { getByName("debug") getByName("release") { @@ -81,10 +55,6 @@ android { checkReleaseBuilds = false } - nativeBundleExport { - headerDir = "${project.projectDir}/$sentryNativeSrc/include" - } - // needed because of Kotlin 1.4.x configurations.all { resolutionStrategy.force(Config.CompileOnly.jetbrainsAnnotations) @@ -101,6 +71,8 @@ dependencies { api(projects.sentry) api(projects.sentryAndroidCore) + implementation("io.sentry:sentry-native-ndk:0.7.5") + compileOnly(Config.CompileOnly.jetbrainsAnnotations) testImplementation(kotlin(Config.kotlinStdLib, KotlinCompilerVersion.VERSION)) diff --git a/sentry-android-ndk/sentry-native b/sentry-android-ndk/sentry-native deleted file mode 160000 index 4ec95c0725..0000000000 --- a/sentry-android-ndk/sentry-native +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4ec95c0725df5f34440db8fa8d37b4c519fce74e diff --git a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/DebugImagesLoader.java b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/DebugImagesLoader.java index cb38db498a..2e069dcc74 100644 --- a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/DebugImagesLoader.java +++ b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/DebugImagesLoader.java @@ -4,9 +4,10 @@ import io.sentry.SentryOptions; import io.sentry.android.core.IDebugImagesLoader; import io.sentry.android.core.SentryAndroidOptions; +import io.sentry.ndk.NativeModuleListLoader; import io.sentry.protocol.DebugImage; import io.sentry.util.Objects; -import java.util.Arrays; +import java.util.ArrayList; import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -45,9 +46,20 @@ public DebugImagesLoader( synchronized (debugImagesLock) { if (debugImages == null) { try { - final DebugImage[] debugImagesArr = moduleListLoader.loadModuleList(); + final io.sentry.ndk.DebugImage[] debugImagesArr = moduleListLoader.loadModuleList(); if (debugImagesArr != null) { - debugImages = Arrays.asList(debugImagesArr); + debugImages = new ArrayList<>(debugImagesArr.length); + for (io.sentry.ndk.DebugImage d : debugImagesArr) { + final DebugImage debugImage = new DebugImage(); + debugImage.setUuid(d.getUuid()); + debugImage.setType(d.getType()); + debugImage.setDebugId(d.getDebugId()); + debugImage.setCodeId(d.getCodeId()); + debugImage.setImageAddr(d.getImageAddr()); + debugImage.setImageSize(d.getImageSize()); + debugImage.setArch(d.getArch()); + debugImages.add(debugImage); + } options .getLogger() .log(SentryLevel.DEBUG, "Debug images loaded: %d", debugImages.size()); diff --git a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/INativeScope.java b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/INativeScope.java deleted file mode 100644 index a8d50e40fe..0000000000 --- a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/INativeScope.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.sentry.android.ndk; - -interface INativeScope { - void setTag(String key, String value); - - void removeTag(String key); - - void setExtra(String key, String value); - - void removeExtra(String key); - - void setUser(String id, String email, String ipAddress, String username); - - void removeUser(); - - void addBreadcrumb( - String level, String message, String category, String type, String timestamp, String data); -} diff --git a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NativeModuleListLoader.java b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NativeModuleListLoader.java deleted file mode 100644 index 464fcd3992..0000000000 --- a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NativeModuleListLoader.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.sentry.android.ndk; - -import io.sentry.protocol.DebugImage; -import org.jetbrains.annotations.Nullable; - -final class NativeModuleListLoader { - - public @Nullable DebugImage[] loadModuleList() { - return nativeLoadModuleList(); - } - - public void clearModuleList() { - nativeClearModuleList(); - } - - public static native DebugImage[] nativeLoadModuleList(); - - public static native void nativeClearModuleList(); -} diff --git a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NativeScope.java b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NativeScope.java deleted file mode 100644 index 9d82f9d5c8..0000000000 --- a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NativeScope.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.sentry.android.ndk; - -final class NativeScope implements INativeScope { - @Override - public void setTag(String key, String value) { - nativeSetTag(key, value); - } - - @Override - public void removeTag(String key) { - nativeRemoveTag(key); - } - - @Override - public void setExtra(String key, String value) { - nativeSetExtra(key, value); - } - - @Override - public void removeExtra(String key) { - nativeRemoveExtra(key); - } - - @Override - public void setUser(String id, String email, String ipAddress, String username) { - nativeSetUser(id, email, ipAddress, username); - } - - @Override - public void removeUser() { - nativeRemoveUser(); - } - - @Override - public void addBreadcrumb( - String level, String message, String category, String type, String timestamp, String data) { - nativeAddBreadcrumb(level, message, category, type, timestamp, data); - } - - public static native void nativeSetTag(String key, String value); - - public static native void nativeRemoveTag(String key); - - public static native void nativeSetExtra(String key, String value); - - public static native void nativeRemoveExtra(String key); - - public static native void nativeSetUser( - String id, String email, String ipAddress, String username); - - public static native void nativeRemoveUser(); - - public static native void nativeAddBreadcrumb( - String level, String message, String category, String type, String timestamp, String data); -} diff --git a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NdkScopeObserver.java b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NdkScopeObserver.java index 009bba9b81..4a4237ba08 100644 --- a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NdkScopeObserver.java +++ b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NdkScopeObserver.java @@ -5,6 +5,8 @@ import io.sentry.ScopeObserverAdapter; import io.sentry.SentryLevel; import io.sentry.SentryOptions; +import io.sentry.ndk.INativeScope; +import io.sentry.ndk.NativeScope; import io.sentry.protocol.User; import io.sentry.util.Objects; import java.util.Locale; diff --git a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/SentryNdk.java b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/SentryNdk.java index 1ddc04c524..ebce1a12fd 100644 --- a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/SentryNdk.java +++ b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/SentryNdk.java @@ -1,6 +1,8 @@ package io.sentry.android.ndk; import io.sentry.android.core.SentryAndroidOptions; +import io.sentry.ndk.NativeModuleListLoader; +import io.sentry.ndk.NdkOptions; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -9,21 +11,6 @@ public final class SentryNdk { private SentryNdk() {} - static { - // On older Android versions, it was necessary to manually call "`System.loadLibrary` on all - // transitive dependencies before loading [the] main library." - // The dependencies of `libsentry.so` are currently `lib{c,m,dl,log}.so`. - // See - // https://android.googlesource.com/platform/bionic/+/master/android-changes-for-ndk-developers.md#changes-to-library-dependency-resolution - System.loadLibrary("log"); - System.loadLibrary("sentry"); - System.loadLibrary("sentry-android"); - } - - private static native void initSentryNative(@NotNull final SentryAndroidOptions options); - - private static native void shutdown(); - /** * Init the NDK integration * @@ -31,7 +18,18 @@ private SentryNdk() {} */ public static void init(@NotNull final SentryAndroidOptions options) { SentryNdkUtil.addPackage(options.getSdkVersion()); - initSentryNative(options); + + final @NotNull NdkOptions ndkOptions = + new NdkOptions( + options.getDsn(), + options.isDebug(), + options.getOutboxPath(), + options.getRelease(), + options.getEnvironment(), + options.getDist(), + options.getMaxBreadcrumbs(), + options.getNativeSdkName()); + io.sentry.ndk.SentryNdk.init(ndkOptions); // only add scope sync observer if the scope sync is enabled. if (options.isEnableScopeSync()) { @@ -43,6 +41,6 @@ public static void init(@NotNull final SentryAndroidOptions options) { /** Closes the NDK integration */ public static void close() { - shutdown(); + io.sentry.ndk.SentryNdk.close(); } } diff --git a/sentry-android-ndk/src/main/jni/sentry.c b/sentry-android-ndk/src/main/jni/sentry.c deleted file mode 100644 index d62ef56123..0000000000 --- a/sentry-android-ndk/src/main/jni/sentry.c +++ /dev/null @@ -1,494 +0,0 @@ -#include -#include -#include -#include -#include - -#define ENSURE(Expr) \ - if (!(Expr)) \ - return - -#define ENSURE_OR_FAIL(Expr) \ - if (!(Expr)) \ - goto fail - -static bool get_string_into(JNIEnv *env, jstring jstr, char* buf, size_t buf_len) -{ - jsize utf_len = (*env)->GetStringUTFLength(env, jstr); - if ((size_t)utf_len >= buf_len) { - return false; - } - - jsize j_len = (*env)->GetStringLength(env, jstr); - - (*env)->GetStringUTFRegion(env, jstr, 0, j_len, buf); - if ((*env)->ExceptionCheck(env) == JNI_TRUE) { - return false; - } - - buf[utf_len] = '\0'; - return true; -} - -static char* get_string(JNIEnv *env, jstring jstr) { - char *buf = NULL; - - jsize utf_len = (*env)->GetStringUTFLength(env, jstr); - size_t buf_len = (size_t)utf_len + 1; - buf = sentry_malloc(buf_len); - ENSURE_OR_FAIL(buf); - - ENSURE_OR_FAIL(get_string_into(env, jstr, buf, buf_len)); - - return buf; - -fail: - sentry_free(buf); - - return NULL; -} - -static char *call_get_string(JNIEnv *env, jobject obj, jmethodID mid) -{ - jstring j_str = (jstring)(*env)->CallObjectMethod(env, obj, mid); - ENSURE_OR_FAIL(j_str); - char* str = get_string(env, j_str); - (*env)->DeleteLocalRef(env, j_str); - - return str; - -fail: - return NULL; -} - -JNIEXPORT void JNICALL -Java_io_sentry_android_ndk_NativeScope_nativeSetTag( - JNIEnv *env, - jclass cls, - jstring key, - jstring value) { - const char *charKey = (*env)->GetStringUTFChars(env, key, 0); - const char *charValue = (*env)->GetStringUTFChars(env, value, 0); - - sentry_set_tag(charKey, charValue); - - (*env)->ReleaseStringUTFChars(env, key, charKey); - (*env)->ReleaseStringUTFChars(env, value, charValue); -} - -JNIEXPORT void JNICALL -Java_io_sentry_android_ndk_NativeScope_nativeRemoveTag(JNIEnv *env, jclass cls, jstring key) { - const char *charKey = (*env)->GetStringUTFChars(env, key, 0); - - sentry_remove_tag(charKey); - - (*env)->ReleaseStringUTFChars(env, key, charKey); -} - -JNIEXPORT void JNICALL -Java_io_sentry_android_ndk_NativeScope_nativeSetExtra( - JNIEnv *env, - jclass cls, - jstring key, - jstring value) { - const char *charKey = (*env)->GetStringUTFChars(env, key, 0); - const char *charValue = (*env)->GetStringUTFChars(env, value, 0); - - sentry_value_t sentryValue = sentry_value_new_string(charValue); - sentry_set_extra(charKey, sentryValue); - - (*env)->ReleaseStringUTFChars(env, key, charKey); - (*env)->ReleaseStringUTFChars(env, value, charValue); -} - -JNIEXPORT void JNICALL -Java_io_sentry_android_ndk_NativeScope_nativeRemoveExtra(JNIEnv *env, jclass cls, jstring key) { - const char *charKey = (*env)->GetStringUTFChars(env, key, 0); - - sentry_remove_extra(charKey); - - (*env)->ReleaseStringUTFChars(env, key, charKey); -} - -JNIEXPORT void JNICALL -Java_io_sentry_android_ndk_NativeScope_nativeSetUser( - JNIEnv *env, - jclass cls, - jstring id, - jstring email, - jstring ipAddress, - jstring username) { - sentry_value_t user = sentry_value_new_object(); - if (id) { - const char *charId = (*env)->GetStringUTFChars(env, id, 0); - sentry_value_set_by_key(user, "id", sentry_value_new_string(charId)); - (*env)->ReleaseStringUTFChars(env, id, charId); - } - if (email) { - const char *charEmail = (*env)->GetStringUTFChars(env, email, 0); - sentry_value_set_by_key( - user, "email", sentry_value_new_string(charEmail)); - (*env)->ReleaseStringUTFChars(env, email, charEmail); - } - if (ipAddress) { - const char *charIpAddress = (*env)->GetStringUTFChars(env, ipAddress, 0); - sentry_value_set_by_key( - user, "ip_address", sentry_value_new_string(charIpAddress)); - (*env)->ReleaseStringUTFChars(env, ipAddress, charIpAddress); - } - if (username) { - const char *charUsername = (*env)->GetStringUTFChars(env, username, 0); - sentry_value_set_by_key( - user, "username", sentry_value_new_string(charUsername)); - (*env)->ReleaseStringUTFChars(env, username, charUsername); - } - sentry_set_user(user); -} - -JNIEXPORT void JNICALL -Java_io_sentry_android_ndk_NativeScope_nativeRemoveUser(JNIEnv *env, jclass cls) { - sentry_remove_user(); -} - -JNIEXPORT void JNICALL -Java_io_sentry_android_ndk_NativeScope_nativeAddBreadcrumb( - JNIEnv *env, - jclass cls, - jstring level, - jstring message, - jstring category, - jstring type, - jstring timestamp, - jstring data) { - if (!level && !message && !category && !type) { - return; - } - const char *charMessage = NULL; - if (message) { - charMessage = (*env)->GetStringUTFChars(env, message, 0); - } - const char *charType = NULL; - if (type) { - charType = (*env)->GetStringUTFChars(env, type, 0); - } - sentry_value_t crumb = sentry_value_new_breadcrumb(charType, charMessage); - - if (charMessage) { - (*env)->ReleaseStringUTFChars(env, message, charMessage); - } - if (charType) { - (*env)->ReleaseStringUTFChars(env, type, charType); - } - - if (category) { - const char *charCategory = (*env)->GetStringUTFChars(env, category, 0); - sentry_value_set_by_key( - crumb, "category", sentry_value_new_string(charCategory)); - (*env)->ReleaseStringUTFChars(env, category, charCategory); - } - if (level) { - const char *charLevel = (*env)->GetStringUTFChars(env, level, 0); - sentry_value_set_by_key( - crumb, "level", sentry_value_new_string(charLevel)); - (*env)->ReleaseStringUTFChars(env, level, charLevel); - } - - if (timestamp) { - // overwrite timestamp that is already created on sentry_value_new_breadcrumb - const char *charTimestamp = (*env)->GetStringUTFChars(env, timestamp, 0); - sentry_value_set_by_key( - crumb, "timestamp", sentry_value_new_string(charTimestamp)); - (*env)->ReleaseStringUTFChars(env, timestamp, charTimestamp); - } - - if (data) { - const char *charData = (*env)->GetStringUTFChars(env, data, 0); - - // we create an object because the Java layer parses it as a Map - sentry_value_t dataObject = sentry_value_new_object(); - sentry_value_set_by_key(dataObject, "data", sentry_value_new_string(charData)); - - sentry_value_set_by_key(crumb, "data", dataObject); - - (*env)->ReleaseStringUTFChars(env, data, charData); - } - - sentry_add_breadcrumb(crumb); -} - -static void send_envelope(sentry_envelope_t *envelope, void *data) { - const char *outbox_path = (const char *) data; - char envelope_id_str[40]; - - sentry_uuid_t envelope_id = sentry_uuid_new_v4(); - sentry_uuid_as_string(&envelope_id, envelope_id_str); - - size_t outbox_len = strlen(outbox_path); - size_t final_len = outbox_len + 42; // "/" + envelope_id_str + "\0" = 42 - char* envelope_path = sentry_malloc(final_len); - ENSURE(envelope_path); - int written = snprintf(envelope_path, final_len, "%s/%s", outbox_path, envelope_id_str); - if (written > outbox_len && written < final_len) { - sentry_envelope_write_to_file(envelope, envelope_path); - } - - sentry_free(envelope_path); - sentry_envelope_free(envelope); -} - -JNIEXPORT void JNICALL -Java_io_sentry_android_ndk_SentryNdk_initSentryNative( - JNIEnv *env, - jclass cls, - jobject sentry_sdk_options) { - jclass options_cls = (*env)->GetObjectClass(env, sentry_sdk_options); - jmethodID outbox_path_mid = (*env)->GetMethodID(env, options_cls, "getOutboxPath", - "()Ljava/lang/String;"); - jmethodID dsn_mid = (*env)->GetMethodID(env, options_cls, "getDsn", "()Ljava/lang/String;"); - jmethodID is_debug_mid = (*env)->GetMethodID(env, options_cls, "isDebug", "()Z"); - jmethodID release_mid = (*env)->GetMethodID(env, options_cls, "getRelease", - "()Ljava/lang/String;"); - jmethodID environment_mid = (*env)->GetMethodID(env, options_cls, "getEnvironment", - "()Ljava/lang/String;"); - jmethodID dist_mid = (*env)->GetMethodID(env, options_cls, "getDist", "()Ljava/lang/String;"); - jmethodID max_crumbs_mid = (*env)->GetMethodID(env, options_cls, "getMaxBreadcrumbs", "()I"); - jmethodID native_sdk_name_mid = (*env)->GetMethodID(env, options_cls, "getNativeSdkName", - "()Ljava/lang/String;"); - - (*env)->DeleteLocalRef(env, options_cls); - - char *outbox_path = NULL; - sentry_transport_t *transport = NULL; - bool transport_owns_path = false; - sentry_options_t *options = NULL; - bool options_owns_transport = false; - char *dsn_str = NULL; - char *release_str = NULL; - char *environment_str = NULL; - char *dist_str = NULL; - char *native_sdk_name_str = NULL; - - options = sentry_options_new(); - ENSURE_OR_FAIL(options); - - // session tracking is enabled by default, but the Android SDK already handles it - sentry_options_set_auto_session_tracking(options, 0); - - jboolean debug = (jboolean)(*env)->CallBooleanMethod(env, sentry_sdk_options, is_debug_mid); - sentry_options_set_debug(options, debug); - - jint max_crumbs = (jint) (*env)->CallIntMethod(env, sentry_sdk_options, max_crumbs_mid); - sentry_options_set_max_breadcrumbs(options, max_crumbs); - - outbox_path = call_get_string(env, sentry_sdk_options, outbox_path_mid); - ENSURE_OR_FAIL(outbox_path); - - transport = sentry_transport_new(send_envelope); - ENSURE_OR_FAIL(transport); - sentry_transport_set_state(transport, outbox_path); - sentry_transport_set_free_func(transport, sentry_free); - transport_owns_path = true; - - sentry_options_set_transport(options, transport); - options_owns_transport = true; - - // give sentry-native its own database path it can work with, next to the outbox - size_t outbox_len = strlen(outbox_path); - size_t final_len = outbox_len + 15; // len(".sentry-native\0") = 15 - char* database_path = sentry_malloc(final_len); - ENSURE_OR_FAIL(database_path); - strncpy(database_path, outbox_path, final_len); - char *dir = strrchr(database_path, '/'); - if (dir) - { - strncpy(dir + 1, ".sentry-native", final_len - (dir + 1 - database_path)); - } - sentry_options_set_database_path(options, database_path); - sentry_free(database_path); - - dsn_str = call_get_string(env, sentry_sdk_options, dsn_mid); - ENSURE_OR_FAIL(dsn_str); - sentry_options_set_dsn(options, dsn_str); - sentry_free(dsn_str); - - release_str = call_get_string(env, sentry_sdk_options, release_mid); - if (release_str) { - sentry_options_set_release(options, release_str); - sentry_free(release_str); - } - - environment_str = call_get_string(env, sentry_sdk_options, environment_mid); - if (environment_str) - { - sentry_options_set_environment(options, environment_str); - sentry_free(environment_str); - } - - dist_str = call_get_string(env, sentry_sdk_options, dist_mid); - if (dist_str) - { - sentry_options_set_dist(options, dist_str); - sentry_free(dist_str); - } - - native_sdk_name_str = call_get_string(env, sentry_sdk_options, native_sdk_name_mid); - if (native_sdk_name_str) { - sentry_options_set_sdk_name(options, native_sdk_name_str); - sentry_free(native_sdk_name_str); - } - - sentry_init(options); - return; - -fail: - if (!transport_owns_path) { - sentry_free(outbox_path); - } - if (!options_owns_transport) { - sentry_transport_free(transport); - } - sentry_options_free(options); -} - -JNIEXPORT void JNICALL -Java_io_sentry_android_ndk_NativeModuleListLoader_nativeClearModuleList(JNIEnv *env, jclass cls) { - sentry_clear_modulecache(); -} - -JNIEXPORT jobjectArray JNICALL -Java_io_sentry_android_ndk_NativeModuleListLoader_nativeLoadModuleList(JNIEnv *env, jclass cls) { - sentry_value_t image_list_t = sentry_get_modules_list(); - jobjectArray image_list = NULL; - - if (sentry_value_get_type(image_list_t) == SENTRY_VALUE_TYPE_LIST) { - size_t len_t = sentry_value_get_length(image_list_t); - - jclass image_class = (*env)->FindClass(env, "io/sentry/protocol/DebugImage"); - image_list = (*env)->NewObjectArray(env, len_t, image_class, NULL); - - jmethodID image_addr_method = (*env)->GetMethodID(env, image_class, "setImageAddr", - "(Ljava/lang/String;)V"); - - jmethodID image_size_method = (*env)->GetMethodID(env, image_class, "setImageSize", - "(J)V"); - - jmethodID code_file_method = (*env)->GetMethodID(env, image_class, "setCodeFile", - "(Ljava/lang/String;)V"); - - jmethodID image_addr_ctor = (*env)->GetMethodID(env, image_class, "", - "()V"); - - jmethodID type_method = (*env)->GetMethodID(env, image_class, "setType", - "(Ljava/lang/String;)V"); - - jmethodID debug_id_method = (*env)->GetMethodID(env, image_class, "setDebugId", - "(Ljava/lang/String;)V"); - - jmethodID code_id_method = (*env)->GetMethodID(env, image_class, "setCodeId", - "(Ljava/lang/String;)V"); - - jmethodID debug_file_method = (*env)->GetMethodID(env, image_class, "setDebugFile", - "(Ljava/lang/String;)V"); - - for (size_t i = 0; i < len_t; i++) { - sentry_value_t image_t = sentry_value_get_by_index(image_list_t, i); - - if (!sentry_value_is_null(image_t)) { - jobject image = (*env)->NewObject(env, image_class, image_addr_ctor); - - sentry_value_t image_addr_t = sentry_value_get_by_key(image_t, "image_addr"); - if (!sentry_value_is_null(image_addr_t)) { - - const char *value_v = sentry_value_as_string(image_addr_t); - jstring value = (*env)->NewStringUTF(env, value_v); - - (*env)->CallVoidMethod(env, image, image_addr_method, value); - - // Local refs (eg NewStringUTF) are freed automatically when the native method - // returns, but if you're iterating a large array, it's recommended to release - // manually due to allocation limits (512) on Android < 8 or OOM. - // https://developer.android.com/training/articles/perf-jni.html#local-and-global-references - (*env)->DeleteLocalRef(env, value); - } - - sentry_value_t image_size_t = sentry_value_get_by_key(image_t, "image_size"); - if (!sentry_value_is_null(image_size_t)) { - - int32_t value_v = sentry_value_as_int32(image_size_t); - jlong value = (jlong) value_v; - - (*env)->CallVoidMethod(env, image, image_size_method, value); - } - - sentry_value_t code_file_t = sentry_value_get_by_key(image_t, "code_file"); - if (!sentry_value_is_null(code_file_t)) { - - const char *value_v = sentry_value_as_string(code_file_t); - jstring value = (*env)->NewStringUTF(env, value_v); - - (*env)->CallVoidMethod(env, image, code_file_method, value); - - (*env)->DeleteLocalRef(env, value); - } - - sentry_value_t code_type_t = sentry_value_get_by_key(image_t, "type"); - if (!sentry_value_is_null(code_type_t)) { - - const char *value_v = sentry_value_as_string(code_type_t); - jstring value = (*env)->NewStringUTF(env, value_v); - - (*env)->CallVoidMethod(env, image, type_method, value); - - (*env)->DeleteLocalRef(env, value); - } - - sentry_value_t debug_id_t = sentry_value_get_by_key(image_t, "debug_id"); - if (!sentry_value_is_null(code_type_t)) { - - const char *value_v = sentry_value_as_string(debug_id_t); - jstring value = (*env)->NewStringUTF(env, value_v); - - (*env)->CallVoidMethod(env, image, debug_id_method, value); - - (*env)->DeleteLocalRef(env, value); - } - - sentry_value_t code_id_t = sentry_value_get_by_key(image_t, "code_id"); - if (!sentry_value_is_null(code_id_t)) { - - const char *value_v = sentry_value_as_string(code_id_t); - jstring value = (*env)->NewStringUTF(env, value_v); - - (*env)->CallVoidMethod(env, image, code_id_method, value); - - (*env)->DeleteLocalRef(env, value); - } - - // not needed on Android, but keeping for forward compatibility - sentry_value_t debug_file_t = sentry_value_get_by_key(image_t, "debug_file"); - if (!sentry_value_is_null(debug_file_t)) { - - const char *value_v = sentry_value_as_string(debug_file_t); - jstring value = (*env)->NewStringUTF(env, value_v); - - (*env)->CallVoidMethod(env, image, debug_file_method, value); - - (*env)->DeleteLocalRef(env, value); - } - - (*env)->SetObjectArrayElement(env, image_list, i, image); - - (*env)->DeleteLocalRef(env, image); - } - } - - sentry_value_decref(image_list_t); - } - - return image_list; -} - -JNIEXPORT void JNICALL -Java_io_sentry_android_ndk_SentryNdk_shutdown(JNIEnv *env, jclass cls) { - sentry_close(); -} diff --git a/sentry-android-ndk/src/test/java/io/sentry/android/ndk/DebugImagesLoaderTest.kt b/sentry-android-ndk/src/test/java/io/sentry/android/ndk/DebugImagesLoaderTest.kt index db584c814f..927ce98c3b 100644 --- a/sentry-android-ndk/src/test/java/io/sentry/android/ndk/DebugImagesLoaderTest.kt +++ b/sentry-android-ndk/src/test/java/io/sentry/android/ndk/DebugImagesLoaderTest.kt @@ -1,11 +1,10 @@ package io.sentry.android.ndk import io.sentry.android.core.SentryAndroidOptions -import io.sentry.protocol.DebugImage +import io.sentry.ndk.NativeModuleListLoader import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever -import java.lang.RuntimeException import kotlin.test.Test import kotlin.test.assertNotNull import kotlin.test.assertNull @@ -38,7 +37,7 @@ class DebugImagesLoaderTest { whenever(fixture.nativeLoader.loadModuleList()).thenReturn(arrayOf()) assertNotNull(sut.loadDebugImages()) - whenever(fixture.nativeLoader.loadModuleList()).thenReturn(arrayOf(DebugImage())) + whenever(fixture.nativeLoader.loadModuleList()).thenReturn(arrayOf(io.sentry.ndk.DebugImage())) assertTrue(sut.loadDebugImages()!!.isEmpty()) } diff --git a/sentry-android-ndk/src/test/java/io/sentry/android/ndk/NdkScopeObserverTest.kt b/sentry-android-ndk/src/test/java/io/sentry/android/ndk/NdkScopeObserverTest.kt index 335a7679e1..ad523a883e 100644 --- a/sentry-android-ndk/src/test/java/io/sentry/android/ndk/NdkScopeObserverTest.kt +++ b/sentry-android-ndk/src/test/java/io/sentry/android/ndk/NdkScopeObserverTest.kt @@ -5,6 +5,7 @@ import io.sentry.DateUtils import io.sentry.JsonSerializer import io.sentry.SentryLevel import io.sentry.SentryOptions +import io.sentry.ndk.INativeScope import io.sentry.protocol.User import org.mockito.kotlin.eq import org.mockito.kotlin.mock diff --git a/sentry-samples/sentry-samples-android/CMakeLists.txt b/sentry-samples/sentry-samples-android/CMakeLists.txt index ad170fe404..19dca2b80d 100644 --- a/sentry-samples/sentry-samples-android/CMakeLists.txt +++ b/sentry-samples/sentry-samples-android/CMakeLists.txt @@ -3,15 +3,12 @@ project(Sentry-Sample LANGUAGES C CXX) add_library(native-sample SHARED src/main/cpp/native-sample.cpp) -# make sure that we build it as a shared lib instead of a static lib -set(BUILD_SHARED_LIBS ON) -set(SENTRY_BUILD_SHARED_LIBS ON) - -add_subdirectory(../../sentry-android-ndk/${SENTRY_NATIVE_SRC} sentry_build) +find_package(sentry-native-ndk REQUIRED CONFIG) find_library(LOG_LIB log) target_link_libraries(native-sample PRIVATE ${LOG_LIB} - $ + sentry-native-ndk::sentry-android + sentry-native-ndk::sentry ) diff --git a/sentry-samples/sentry-samples-android/build.gradle.kts b/sentry-samples/sentry-samples-android/build.gradle.kts index a8d8897519..871f46ce09 100644 --- a/sentry-samples/sentry-samples-android/build.gradle.kts +++ b/sentry-samples/sentry-samples-android/build.gradle.kts @@ -15,16 +15,8 @@ android { versionName = project.version.toString() externalNativeBuild { - val sentryNativeSrc = if (File("${project.projectDir}/../../sentry-android-ndk/sentry-native-local").exists()) { - "sentry-native-local" - } else { - "sentry-native" - } - println("sentry-samples-android: $sentryNativeSrc") - cmake { - arguments.add(0, "-DANDROID_STL=c++_static") - arguments.add(0, "-DSENTRY_NATIVE_SRC=$sentryNativeSrc") + arguments.add(0, "-DANDROID_STL=c++_shared") } } @@ -38,6 +30,7 @@ android { // Note that the viewBinding.enabled property is now deprecated. viewBinding = true compose = true + prefab = true } composeOptions { @@ -134,4 +127,6 @@ dependencies { implementation(Config.Libs.composeMaterial) debugImplementation(Config.Libs.leakCanary) + + implementation("io.sentry:sentry-native-ndk:0.7.5") } diff --git a/settings.gradle.kts b/settings.gradle.kts index 028037372d..b6de6741ed 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -66,12 +66,3 @@ include( "sentry-android-integration-tests:test-app-sentry", "sentry-samples:sentry-samples-openfeign" ) - -gradle.beforeProject { - if (project.name == "sentry-android-ndk" || project.name == "sentry-samples-android") { - exec { - logger.log(LogLevel.LIFECYCLE, "Initializing git submodules") - commandLine("git", "submodule", "update", "--init", "--recursive") - } - } -} From 1161c1a4c125451fdc41e370c2d04bce8ec05efe Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 18 Jun 2024 07:18:19 +0200 Subject: [PATCH 53/91] POTEL 1 - Use OpenTelemetry for Performance and `Scopes` propagation (#3399) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation --- buildSrc/src/main/java/Config.kt | 8 +- .../build.gradle.kts | 1 + .../build.gradle.kts | 1 + ...ryAutoConfigurationCustomizerProvider.java | 10 +- .../SentryPropagatorProvider.java | 3 +- .../api/sentry-opentelemetry-bootstrap.api | 44 ++ .../build.gradle.kts | 77 ++++ .../InternalSemanticAttributes.java | 26 ++ .../OtelContextScopesStorage.java | 45 ++ .../opentelemetry/SentryContextStorage.java | 44 ++ .../opentelemetry/SentryContextWrapper.java | 86 ++++ .../sentry/opentelemetry/SentryOtelKeys.java | 3 + .../opentelemetry/SentryWeakSpanStorage.java | 49 +++ .../api/sentry-opentelemetry-core.api | 35 +- .../build.gradle.kts | 3 + .../opentelemetry/PotelSentryPropagator.java | 165 ++++++++ .../PotelSentrySpanProcessor.java | 94 +++++ .../opentelemetry/SentrySpanExporter.java | 387 ++++++++++++++++++ .../SpanDescriptionExtractor.java | 1 + .../io/sentry/opentelemetry/SpanNode.java | 56 +++ sentry/api/sentry.api | 27 +- .../java/io/sentry/CombinedScopeView.java | 15 +- .../src/main/java/io/sentry/HubAdapter.java | 10 + .../main/java/io/sentry/HubScopesWrapper.java | 10 + sentry/src/main/java/io/sentry/IScopes.java | 19 + sentry/src/main/java/io/sentry/NoOpHub.java | 10 + .../src/main/java/io/sentry/NoOpScopes.java | 10 + sentry/src/main/java/io/sentry/Scopes.java | 14 +- .../main/java/io/sentry/ScopesAdapter.java | 10 + .../java/io/sentry/ScopesStorageFactory.java | 38 ++ sentry/src/main/java/io/sentry/Sentry.java | 5 +- .../main/java/io/sentry/util/LoadClass.java | 47 +++ .../java/io/sentry/CombinedScopeViewTest.kt | 21 +- settings.gradle.kts | 1 + 34 files changed, 1355 insertions(+), 20 deletions(-) create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/build.gradle.kts create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextStorage.java create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextWrapper.java rename sentry-opentelemetry/{sentry-opentelemetry-core => sentry-opentelemetry-bootstrap}/src/main/java/io/sentry/opentelemetry/SentryOtelKeys.java (79%) create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryWeakSpanStorage.java create mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentryPropagator.java create mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java create mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java create mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanNode.java create mode 100644 sentry/src/main/java/io/sentry/ScopesStorageFactory.java create mode 100644 sentry/src/main/java/io/sentry/util/LoadClass.java diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index 31c99bad3f..a3ccdc3af5 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -151,9 +151,9 @@ object Config { val apolloKotlin = "com.apollographql.apollo3:apollo-runtime:3.8.2" object OpenTelemetry { - val otelVersion = "1.33.0" + val otelVersion = "1.37.0" val otelAlphaVersion = "$otelVersion-alpha" - val otelJavaagentVersion = "1.32.0" + val otelJavaagentVersion = "2.3.0" val otelJavaagentAlphaVersion = "$otelJavaagentVersion-alpha" val otelSemanticConvetionsVersion = "1.23.1-alpha" @@ -199,7 +199,9 @@ object Config { object QualityPlugins { object Jacoco { val version = "0.8.7" - val minimumCoverage = BigDecimal.valueOf(0.6) + + // TODO [POTEL] add tests and restore + val minimumCoverage = BigDecimal.valueOf(0.1) } val spotless = "com.diffplug.spotless" val spotlessVersion = "6.11.0" diff --git a/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts index 80b68430db..4c00cb8d81 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts @@ -53,6 +53,7 @@ val upstreamAgent = configurations.create("upstreamAgent") { dependencies { bootstrapLibs(projects.sentry) + bootstrapLibs(projects.sentryOpentelemetry.sentryOpentelemetryBootstrap) javaagentLibs(projects.sentryOpentelemetry.sentryOpentelemetryAgentcustomization) upstreamAgent(Config.Libs.OpenTelemetry.otelJavaAgent) } diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts index 79e3599cc8..475796d246 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts @@ -24,6 +24,7 @@ dependencies { exclude(group = "io.opentelemetry") exclude(group = "io.opentelemetry.javaagent") } + implementation(projects.sentryOpentelemetry.sentryOpentelemetryBootstrap) compileOnly(Config.Libs.OpenTelemetry.otelSdk) compileOnly(Config.Libs.OpenTelemetry.otelExtensionAutoconfigureSpi) diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java index e808db8fcf..94def04774 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java @@ -1,9 +1,11 @@ package io.sentry.opentelemetry; +import io.opentelemetry.context.ContextStorage; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; +import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; import io.sentry.Instrumenter; import io.sentry.Sentry; import io.sentry.SentryIntegrationPackageStorage; @@ -50,6 +52,8 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) { } } + ContextStorage.addWrapper((storage) -> new SentryContextStorage(storage)); + autoConfiguration .addTracerProviderCustomizer(this::configureSdkTracerProvider) .addPropertiesSupplier(this::getDefaultProperties); @@ -140,7 +144,11 @@ private static class VersionInfoHolder { private SdkTracerProviderBuilder configureSdkTracerProvider( SdkTracerProviderBuilder tracerProvider, ConfigProperties config) { - return tracerProvider.addSpanProcessor(new SentrySpanProcessor()); + // TODO [POTEL] configurable or separate packages for old vs new way + // return tracerProvider.addSpanProcessor(new SentrySpanProcessor()); + return tracerProvider + .addSpanProcessor(new PotelSentrySpanProcessor()) + .addSpanProcessor(BatchSpanProcessor.builder(new SentrySpanExporter()).build()); } private Map getDefaultProperties() { diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryPropagatorProvider.java b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryPropagatorProvider.java index 49acd725fb..ac507badb4 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryPropagatorProvider.java +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryPropagatorProvider.java @@ -7,7 +7,8 @@ public final class SentryPropagatorProvider implements ConfigurablePropagatorProvider { @Override public TextMapPropagator getPropagator(ConfigProperties config) { - return new SentryPropagator(); + // return new SentryPropagator(); + return new PotelSentryPropagator(); } @Override diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api new file mode 100644 index 0000000000..e86ec628c2 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api @@ -0,0 +1,44 @@ +public final class io/sentry/opentelemetry/InternalSemanticAttributes { + public static final field BREADCRUMB_TYPE Lio/opentelemetry/api/common/AttributeKey; + public static final field IS_REMOTE_PARENT Lio/opentelemetry/api/common/AttributeKey; + public static final field OP Lio/opentelemetry/api/common/AttributeKey; + public static final field ORIGIN Lio/opentelemetry/api/common/AttributeKey; + public static final field PARENT_SAMPLED Lio/opentelemetry/api/common/AttributeKey; + public static final field SAMPLE_RATE Lio/opentelemetry/api/common/AttributeKey; + public static final field SOURCE Lio/opentelemetry/api/common/AttributeKey; + public fun ()V +} + +public final class io/sentry/opentelemetry/OtelContextScopesStorage : io/sentry/IScopesStorage { + public fun ()V + public fun close ()V + public fun get ()Lio/sentry/IScopes; + public fun set (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; +} + +public final class io/sentry/opentelemetry/SentryContextStorage : io/opentelemetry/context/ContextStorage { + public fun (Lio/opentelemetry/context/ContextStorage;)V + public fun attach (Lio/opentelemetry/context/Context;)Lio/opentelemetry/context/Scope; + public fun current ()Lio/opentelemetry/context/Context; +} + +public final class io/sentry/opentelemetry/SentryContextWrapper : io/opentelemetry/context/Context { + public fun get (Lio/opentelemetry/context/ContextKey;)Ljava/lang/Object; + public fun toString ()Ljava/lang/String; + public fun with (Lio/opentelemetry/context/ContextKey;Ljava/lang/Object;)Lio/opentelemetry/context/Context; + public static fun wrap (Lio/opentelemetry/context/Context;)Lio/sentry/opentelemetry/SentryContextWrapper; +} + +public final class io/sentry/opentelemetry/SentryOtelKeys { + public static final field SENTRY_BAGGAGE_KEY Lio/opentelemetry/context/ContextKey; + public static final field SENTRY_SCOPES_KEY Lio/opentelemetry/context/ContextKey; + public static final field SENTRY_TRACE_KEY Lio/opentelemetry/context/ContextKey; + public fun ()V +} + +public final class io/sentry/opentelemetry/SentryWeakSpanStorage { + public static fun getInstance ()Lio/sentry/opentelemetry/SentryWeakSpanStorage; + public fun getScopes (Lio/opentelemetry/api/trace/SpanContext;)Lio/sentry/IScopes; + public fun storeScopes (Lio/opentelemetry/api/trace/SpanContext;Lio/sentry/IScopes;)V +} + diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/build.gradle.kts new file mode 100644 index 0000000000..f5aeed0b44 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/build.gradle.kts @@ -0,0 +1,77 @@ +import net.ltgt.gradle.errorprone.errorprone +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + `java-library` + kotlin("jvm") + jacoco + id(Config.QualityPlugins.errorProne) + id(Config.QualityPlugins.gradleVersions) +} + +configure { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +tasks.withType().configureEach { + kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8.toString() +} + +dependencies { + compileOnly(projects.sentry) + + compileOnly(Config.Libs.OpenTelemetry.otelSdk) + + compileOnly(Config.CompileOnly.nopen) + errorprone(Config.CompileOnly.nopenChecker) + errorprone(Config.CompileOnly.errorprone) + compileOnly(Config.CompileOnly.jetbrainsAnnotations) + errorprone(Config.CompileOnly.errorProneNullAway) + + // tests + testImplementation(projects.sentryTestSupport) + testImplementation(kotlin(Config.kotlinStdLib)) + testImplementation(Config.TestLibs.kotlinTestJunit) + testImplementation(Config.TestLibs.mockitoKotlin) + testImplementation(Config.TestLibs.awaitility) + + testImplementation(Config.Libs.OpenTelemetry.otelSdk) + testImplementation(Config.Libs.OpenTelemetry.otelSemconv) +} + +configure { + test { + java.srcDir("src/test/java") + } +} + +jacoco { + toolVersion = Config.QualityPlugins.Jacoco.version +} + +tasks.jacocoTestReport { + reports { + xml.required.set(true) + html.required.set(false) + } +} + +tasks { + jacocoTestCoverageVerification { + violationRules { + rule { limit { minimum = Config.QualityPlugins.Jacoco.minimumCoverage } } + } + } + check { + dependsOn(jacocoTestCoverageVerification) + dependsOn(jacocoTestReport) + } +} + +tasks.withType().configureEach { + options.errorprone { + check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR) + option("NullAway:AnnotatedPackages", "io.sentry") + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java new file mode 100644 index 0000000000..e8d9d34c49 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java @@ -0,0 +1,26 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.api.common.AttributeKey; + +// TODO [POTEL] context key vs attribute key +public final class InternalSemanticAttributes { + public static final AttributeKey ORIGIN = AttributeKey.stringKey("sentry.origin"); + public static final AttributeKey OP = AttributeKey.stringKey("sentry.op"); + public static final AttributeKey SOURCE = AttributeKey.stringKey("sentry.source"); + public static final AttributeKey SAMPLE_RATE = + AttributeKey.doubleKey("sentry.sample_rate"); + public static final AttributeKey PARENT_SAMPLED = + AttributeKey.booleanKey("sentry.parentSampled"); + public static final AttributeKey IS_REMOTE_PARENT = + AttributeKey.booleanKey("sentry.isParentRemote"); + public static final AttributeKey BREADCRUMB_TYPE = + AttributeKey.stringKey("sentry.breadcrumb.type"); + // public static final AttributeKey BREADCRUMB_TYPE = + // InternalAttributeKeyImpl.create("sentry.breadcrumb.type", SentryLevel.class); + // BREADCRUMB_TYPE("sentry.breadcrumb.type"), + // BREADCRUMB_LEVEL("sentry.breadcrumb.level"), + // BREADCRUMB_EVENT_ID("sentry.breadcrumb.event_id"), + // BREADCRUMB_CATEGORY("sentry.breadcrumb.category"), + // BREADCRUMB_DATA("sentry.breadcrumb.data"); + +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java new file mode 100644 index 0000000000..8f6842f71e --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java @@ -0,0 +1,45 @@ +package io.sentry.opentelemetry; + +import static io.sentry.opentelemetry.SentryOtelKeys.SENTRY_SCOPES_KEY; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.sentry.IScopes; +import io.sentry.IScopesStorage; +import io.sentry.ISentryLifecycleToken; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@SuppressWarnings("MustBeClosedChecker") +public final class OtelContextScopesStorage implements IScopesStorage { + + @Override + public ISentryLifecycleToken set(@Nullable IScopes scopes) { + Scope otelScope = Context.current().with(SENTRY_SCOPES_KEY, scopes).makeCurrent(); + return new OtelContextScopesStorageToken(otelScope); + } + + @Override + public @Nullable IScopes get() { + return Context.current().get(SENTRY_SCOPES_KEY); + } + + @Override + public void close() { + // TODO [POTEL] can we do something here? + } + + static final class OtelContextScopesStorageToken implements ISentryLifecycleToken { + + private final @NotNull Scope otelScope; + + OtelContextScopesStorageToken(final @NotNull Scope otelScope) { + this.otelScope = otelScope; + } + + @Override + public void close() { + otelScope.close(); + } + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextStorage.java new file mode 100644 index 0000000000..34b71b5426 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextStorage.java @@ -0,0 +1,44 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextStorage; +import io.opentelemetry.context.Scope; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.jetbrains.annotations.NotNull; + +public final class SentryContextStorage implements ContextStorage { + private final @NotNull Logger logger = Logger.getLogger(SentryContextStorage.class.getName()); + + private final @NotNull ContextStorage contextStorage; + + public SentryContextStorage(final @NotNull ContextStorage contextStorage) { + this.contextStorage = contextStorage; + logger.log(Level.SEVERE, "SentryContextStorage ctor called"); + } + + @Override + public Scope attach(Context toAttach) { + // TODO [POTEL] do we need to fork here as well? + // scenario: Context is propagated from thread A to thread B without changes + // OTEL likely also dosn't fork in that case so we probably also don't have to + // or maybe shouldn't even to better align with OTEL + // but since OTEL Context is immutable it doesn't have the same consequence for OTEL as for us + + // TODO [POTEL] sometimes context has already gone through forking but is still an + // ArrayBaseContext + // most likely due to OTEL bridging between agent and app + + // incoming non sentry wrapped context that already has scopes in it + if (toAttach instanceof SentryContextWrapper) { + return contextStorage.attach(toAttach); + } else { + return contextStorage.attach(SentryContextWrapper.wrap(toAttach)); + } + } + + @Override + public Context current() { + return contextStorage.current(); + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextWrapper.java new file mode 100644 index 0000000000..1efc6621a4 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextWrapper.java @@ -0,0 +1,86 @@ +package io.sentry.opentelemetry; + +import static io.sentry.opentelemetry.SentryOtelKeys.SENTRY_SCOPES_KEY; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextKey; +import io.sentry.IScopes; +import io.sentry.Sentry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class SentryContextWrapper implements Context { + + private final @NotNull Context delegate; + + private SentryContextWrapper(final @NotNull Context delegate) { + this.delegate = delegate; + } + + @Override + public V get(final @NotNull ContextKey contextKey) { + return delegate.get(contextKey); + } + + @Override + public Context with(final @NotNull ContextKey contextKey, V v) { + final @NotNull Context modifiedContext = delegate.with(contextKey, v); + + if (isOpentelemetrySpan(contextKey)) { + return forkCurrentScope(modifiedContext); + } else { + return modifiedContext; + } + } + + private boolean isOpentelemetrySpan(final @NotNull ContextKey contextKey) { + return "opentelemetry-trace-span-key".equals(contextKey.toString()); + } + + private static @NotNull Context forkCurrentScope(final @NotNull Context context) { + final @Nullable IScopes scopesInContext = context.get(SENTRY_SCOPES_KEY); + final @Nullable IScopes spanScopes = getCurrentSpanScopesFromGlobalStorage(context); + + if (scopesInContext != null && spanScopes != null) { + if (scopesInContext.isAncestorOf(spanScopes)) { + return context.with( + SENTRY_SCOPES_KEY, spanScopes.forkedCurrentScope("contextwrapper.spanancestor")); + } + } + + if (scopesInContext != null) { + return context.with( + SENTRY_SCOPES_KEY, scopesInContext.forkedCurrentScope("contextwrapper.scopeincontext")); + } + + if (spanScopes != null) { + return context.with( + SENTRY_SCOPES_KEY, spanScopes.forkedCurrentScope("contextwrapper.spanscope")); + } + + return context.with(SENTRY_SCOPES_KEY, Sentry.forkedRootScopes("contextwrapper.fallback")); + } + + private static @Nullable IScopes getCurrentSpanScopesFromGlobalStorage( + final @NotNull Context context) { + @Nullable final Span span = Span.fromContext(context); + + if (span != null) { + return SentryWeakSpanStorage.getInstance().getScopes(span.getSpanContext()); + } + + return null; + } + + public static @NotNull SentryContextWrapper wrap(final @NotNull Context context) { + // we have to fork here because the first time we get to wrap a context it may already have a + // span and a scope + return new SentryContextWrapper(forkCurrentScope(context)); + } + + @Override + public String toString() { + return delegate.toString(); + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryOtelKeys.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelKeys.java similarity index 79% rename from sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryOtelKeys.java rename to sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelKeys.java index 51ead00c6f..54889d1e73 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryOtelKeys.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelKeys.java @@ -2,6 +2,7 @@ import io.opentelemetry.context.ContextKey; import io.sentry.Baggage; +import io.sentry.IScopes; import io.sentry.SentryTraceHeader; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -13,4 +14,6 @@ public final class SentryOtelKeys { ContextKey.named("sentry.trace"); public static final @NotNull ContextKey SENTRY_BAGGAGE_KEY = ContextKey.named("sentry.baggage"); + public static final @NotNull ContextKey SENTRY_SCOPES_KEY = + ContextKey.named("sentry.scopes"); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryWeakSpanStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryWeakSpanStorage.java new file mode 100644 index 0000000000..713b2f3d8e --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryWeakSpanStorage.java @@ -0,0 +1,49 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.context.internal.shaded.WeakConcurrentMap; +import io.sentry.IScopes; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * This class may have to be moved to a new gradle module to include it in the bootstrap + * classloader. + * + *

    This uses multiple maps instead of a single one with a wrapper object as porting this to + * Android would mean there's no access to methods like compute etc. before API level 24. There's + * also no easy way to pre-initialize the map for all keys as spans are used as keys. For span IDs + * it would also not work as they are random. For client report storage we know beforehand what keys + * can exist. + */ +@ApiStatus.Internal +public final class SentryWeakSpanStorage { + private static volatile @Nullable SentryWeakSpanStorage INSTANCE; + + public static @NotNull SentryWeakSpanStorage getInstance() { + if (INSTANCE == null) { + synchronized (SentryWeakSpanStorage.class) { + if (INSTANCE == null) { + INSTANCE = new SentryWeakSpanStorage(); + } + } + } + + return INSTANCE; + } + + // weak keys, spawns a thread to clean up values that have been garbage collected + private final @NotNull WeakConcurrentMap scopes = + new WeakConcurrentMap<>(true); + + private SentryWeakSpanStorage() {} + + public @Nullable IScopes getScopes(final @NotNull SpanContext spanContext) { + return scopes.get(spanContext); + } + + public void storeScopes(final @NotNull SpanContext otelSpan, final @NotNull IScopes scopes) { + this.scopes.put(otelSpan, scopes); + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api index 78eee943ed..ee32c2f135 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api +++ b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api @@ -11,10 +11,19 @@ public final class io/sentry/opentelemetry/OtelSpanInfo { public fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; } -public final class io/sentry/opentelemetry/SentryOtelKeys { - public static final field SENTRY_BAGGAGE_KEY Lio/opentelemetry/context/ContextKey; - public static final field SENTRY_TRACE_KEY Lio/opentelemetry/context/ContextKey; +public final class io/sentry/opentelemetry/PotelSentryPropagator : io/opentelemetry/context/propagation/TextMapPropagator { public fun ()V + public fun extract (Lio/opentelemetry/context/Context;Ljava/lang/Object;Lio/opentelemetry/context/propagation/TextMapGetter;)Lio/opentelemetry/context/Context; + public fun fields ()Ljava/util/Collection; + public fun inject (Lio/opentelemetry/context/Context;Ljava/lang/Object;Lio/opentelemetry/context/propagation/TextMapSetter;)V +} + +public final class io/sentry/opentelemetry/PotelSentrySpanProcessor : io/opentelemetry/sdk/trace/SpanProcessor { + public fun ()V + public fun isEndRequired ()Z + public fun isStartRequired ()Z + public fun onEnd (Lio/opentelemetry/sdk/trace/ReadableSpan;)V + public fun onStart (Lio/opentelemetry/context/Context;Lio/opentelemetry/sdk/trace/ReadWriteSpan;)V } public final class io/sentry/opentelemetry/SentryPropagator : io/opentelemetry/context/propagation/TextMapPropagator { @@ -24,6 +33,14 @@ public final class io/sentry/opentelemetry/SentryPropagator : io/opentelemetry/c public fun inject (Lio/opentelemetry/context/Context;Ljava/lang/Object;Lio/opentelemetry/context/propagation/TextMapSetter;)V } +public final class io/sentry/opentelemetry/SentrySpanExporter : io/opentelemetry/sdk/trace/export/SpanExporter { + public fun ()V + public fun (Lio/sentry/IScopes;)V + public fun export (Ljava/util/Collection;)Lio/opentelemetry/sdk/common/CompletableResultCode; + public fun flush ()Lio/opentelemetry/sdk/common/CompletableResultCode; + public fun shutdown ()Lio/opentelemetry/sdk/common/CompletableResultCode; +} + public final class io/sentry/opentelemetry/SentrySpanProcessor : io/opentelemetry/sdk/trace/SpanProcessor { public fun ()V public fun isEndRequired ()Z @@ -37,6 +54,18 @@ public final class io/sentry/opentelemetry/SpanDescriptionExtractor { public fun extractSpanDescription (Lio/opentelemetry/sdk/trace/ReadableSpan;)Lio/sentry/opentelemetry/OtelSpanInfo; } +public final class io/sentry/opentelemetry/SpanNode { + public fun (Ljava/lang/String;)V + public fun addChild (Lio/sentry/opentelemetry/SpanNode;)V + public fun addChildren (Ljava/util/List;)V + public fun getChildren ()Ljava/util/List; + public fun getId ()Ljava/lang/String; + public fun getParentNode ()Lio/sentry/opentelemetry/SpanNode; + public fun getSpan ()Lio/opentelemetry/sdk/trace/data/SpanData; + public fun setParentNode (Lio/sentry/opentelemetry/SpanNode;)V + public fun setSpan (Lio/opentelemetry/sdk/trace/data/SpanData;)V +} + public final class io/sentry/opentelemetry/TraceData { public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryTraceHeader;Lio/sentry/Baggage;)V public fun getBaggage ()Lio/sentry/Baggage; diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-core/build.gradle.kts index 1dad433555..542bc4332b 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-core/build.gradle.kts @@ -20,6 +20,8 @@ tasks.withType().configureEach { dependencies { compileOnly(projects.sentry) + // TODO implementation? + compileOnly(projects.sentryOpentelemetry.sentryOpentelemetryBootstrap) implementation(Config.Libs.OpenTelemetry.otelSdk) compileOnly(Config.Libs.OpenTelemetry.otelSemconv) @@ -31,6 +33,7 @@ dependencies { errorprone(Config.CompileOnly.errorProneNullAway) // tests + testImplementation(projects.sentryOpentelemetry.sentryOpentelemetryBootstrap) testImplementation(projects.sentryTestSupport) testImplementation(kotlin(Config.kotlinStdLib)) testImplementation(Config.TestLibs.kotlinTestJunit) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentryPropagator.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentryPropagator.java new file mode 100644 index 0000000000..2164950ef8 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentryPropagator.java @@ -0,0 +1,165 @@ +package io.sentry.opentelemetry; + +import static io.sentry.opentelemetry.SentryOtelKeys.SENTRY_SCOPES_KEY; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.TraceFlags; +import io.opentelemetry.api.trace.TraceState; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.propagation.TextMapGetter; +import io.opentelemetry.context.propagation.TextMapPropagator; +import io.opentelemetry.context.propagation.TextMapSetter; +import io.sentry.BaggageHeader; +import io.sentry.IScopes; +import io.sentry.PropagationContext; +import io.sentry.ScopesAdapter; +import io.sentry.Sentry; +import io.sentry.SentryLevel; +import io.sentry.SentryTraceHeader; +import io.sentry.exception.InvalidSentryTraceHeaderException; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class PotelSentryPropagator implements TextMapPropagator { + + private static final @NotNull List FIELDS = + Arrays.asList(SentryTraceHeader.SENTRY_TRACE_HEADER, BaggageHeader.BAGGAGE_HEADER); + // private final @NotNull SentryWeakSpanStorage spanStorage = + // SentryWeakSpanStorage.getInstance(); + private final @NotNull IScopes scopes; + + public PotelSentryPropagator() { + this(ScopesAdapter.getInstance()); + } + + PotelSentryPropagator(final @NotNull IScopes scopes) { + this.scopes = scopes; + } + + @Override + public Collection fields() { + return FIELDS; + } + + @Override + public void inject(final Context context, final C carrier, final TextMapSetter setter) { + final @NotNull Span otelSpan = Span.fromContext(context); + final @NotNull SpanContext otelSpanContext = otelSpan.getSpanContext(); + if (!otelSpanContext.isValid()) { + scopes + .getOptions() + .getLogger() + .log( + SentryLevel.DEBUG, + "Not injecting Sentry tracing information for invalid OpenTelemetry span."); + return; + } + + /** + * TODO + * + *

    maybe it could work like this: + * + *

    getIsolationScope() check if there's a PropagationContext on there and use that for + * generating headers and freezing + * + *

    if that's not there check Context for data and attach headers + */ + + // TODO: inject from OTEL SpanContext and TraceState + System.out.println("TODO"); + // TODO how to inject? + // final @Nullable ISpan sentrySpan = spanStorage.get(otelSpanContext.getSpanId()); + // if (sentrySpan == null || sentrySpan.isNoOp()) { + // hub.getOptions() + // .getLogger() + // .log( + // SentryLevel.DEBUG, + // "Not injecting Sentry tracing information for span %s as no Sentry span has been + // found or it is a NoOp (trace %s). This might simply mean this is a request to Sentry.", + // otelSpanContext.getSpanId(), + // otelSpanContext.getTraceId()); + // return; + // } + // + // final @NotNull SentryTraceHeader sentryTraceHeader = sentrySpan.toSentryTrace(); + // setter.set(carrier, sentryTraceHeader.getName(), sentryTraceHeader.getValue()); + // final @Nullable BaggageHeader baggageHeader = + // sentrySpan.toBaggageHeader(Collections.emptyList()); + // if (baggageHeader != null) { + // setter.set(carrier, baggageHeader.getName(), baggageHeader.getValue()); + // } + } + + @Override + public Context extract( + final Context context, final C carrier, final TextMapGetter getter) { + final @Nullable IScopes scopesFromParentContext = context.get(SENTRY_SCOPES_KEY); + final @NotNull IScopes scopesToUse = + scopesFromParentContext != null + ? scopesFromParentContext.forkedScopes("propagator") + : Sentry.forkedRootScopes("propagator"); + + final @Nullable String sentryTraceString = + getter.get(carrier, SentryTraceHeader.SENTRY_TRACE_HEADER); + if (sentryTraceString == null) { + + final @NotNull Context modifiedContext = context.with(SENTRY_SCOPES_KEY, scopesToUse); + // return context.with(SENTRY_SCOPES_KEY, scopesToUse); + return modifiedContext; + } + // else { + // // TODO clean up code here + // // TODO should we rely on OTEL trace/span ids here? + // scopesToUse.getIsolationScope().setPropagationContext(new PropagationContext()); + // } + + try { + SentryTraceHeader sentryTraceHeader = new SentryTraceHeader(sentryTraceString); + + final @Nullable String baggageString = getter.get(carrier, BaggageHeader.BAGGAGE_HEADER); + // Baggage baggage = Baggage.fromHeader(baggageString); + + // final @NotNull TraceState traceState = TraceState.builder().put("todo.dsc", + // baggage.).build(); + final @NotNull TraceState traceState = TraceState.getDefault(); + + SpanContext otelSpanContext = + SpanContext.createFromRemoteParent( + sentryTraceHeader.getTraceId().toString(), + sentryTraceHeader.getSpanId().toString(), + TraceFlags.getSampled(), + traceState); + + Span wrappedSpan = Span.wrap(otelSpanContext); + + final @NotNull Context modifiedContext = + context.with(wrappedSpan).with(SENTRY_SCOPES_KEY, scopesToUse); + + scopes + .getOptions() + .getLogger() + .log(SentryLevel.DEBUG, "Continuing Sentry trace %s", sentryTraceHeader.getTraceId()); + + final @NotNull PropagationContext propagationContext = + PropagationContext.fromHeaders( + scopes.getOptions().getLogger(), sentryTraceString, baggageString); + scopesToUse.getIsolationScope().setPropagationContext(propagationContext); + + return modifiedContext; + } catch (InvalidSentryTraceHeaderException e) { + scopes + .getOptions() + .getLogger() + .log( + SentryLevel.ERROR, + "Unable to extract Sentry tracing information from invalid header.", + e); + return context; + } + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java new file mode 100644 index 0000000000..25f9556c71 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java @@ -0,0 +1,94 @@ +package io.sentry.opentelemetry; + +import static io.sentry.opentelemetry.InternalSemanticAttributes.IS_REMOTE_PARENT; +import static io.sentry.opentelemetry.SentryOtelKeys.SENTRY_SCOPES_KEY; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.trace.ReadWriteSpan; +import io.opentelemetry.sdk.trace.ReadableSpan; +import io.opentelemetry.sdk.trace.SpanProcessor; +import io.sentry.IScopes; +import io.sentry.ScopesAdapter; +import io.sentry.Sentry; +import io.sentry.SentryLevel; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class PotelSentrySpanProcessor implements SpanProcessor { + private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); + private final @NotNull IScopes scopes; + + public PotelSentrySpanProcessor() { + this(ScopesAdapter.getInstance()); + } + + PotelSentrySpanProcessor(final @NotNull IScopes scopes) { + this.scopes = scopes; + } + + @Override + public void onStart(final @NotNull Context parentContext, final @NotNull ReadWriteSpan otelSpan) { + if (!ensurePrerequisites(otelSpan)) { + return; + } + + final @Nullable Span parentSpan = Span.fromContextOrNull(parentContext); + if (parentSpan != null) { + otelSpan.setAttribute(IS_REMOTE_PARENT, parentSpan.getSpanContext().isRemote()); + } + + final @Nullable IScopes scopesFromContext = parentContext.get(SENTRY_SCOPES_KEY); + final @NotNull IScopes scopes = + scopesFromContext != null + ? scopesFromContext.forkedCurrentScope("spanprocessor") + : Sentry.forkedRootScopes("spanprocessor"); + final @NotNull SpanContext spanContext = otelSpan.getSpanContext(); + spanStorage.storeScopes(spanContext, scopes); + } + + @Override + public boolean isStartRequired() { + return true; + } + + @Override + public void onEnd(final @NotNull ReadableSpan spanBeingEnded) { + System.out.println("span ended: " + spanBeingEnded.getSpanContext().getSpanId()); + } + + @Override + public boolean isEndRequired() { + return true; + } + + private boolean ensurePrerequisites(final @NotNull ReadableSpan otelSpan) { + if (!hasSentryBeenInitialized()) { + scopes + .getOptions() + .getLogger() + .log( + SentryLevel.DEBUG, + "Not forwarding OpenTelemetry span to Sentry as Sentry has not yet been initialized."); + return false; + } + + final @NotNull SpanContext otelSpanContext = otelSpan.getSpanContext(); + if (!otelSpanContext.isValid()) { + scopes + .getOptions() + .getLogger() + .log( + SentryLevel.DEBUG, + "Not forwarding OpenTelemetry span to Sentry as the span is invalid."); + return false; + } + + return true; + } + + private boolean hasSentryBeenInitialized() { + return scopes.isEnabled(); + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java new file mode 100644 index 0000000000..63358382cc --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -0,0 +1,387 @@ +package io.sentry.opentelemetry; + +import static io.sentry.opentelemetry.InternalSemanticAttributes.IS_REMOTE_PARENT; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import io.opentelemetry.semconv.SemanticAttributes; +import io.sentry.DateUtils; +import io.sentry.DsnUtil; +import io.sentry.IScopes; +import io.sentry.ISpan; +import io.sentry.ITransaction; +import io.sentry.Instrumenter; +import io.sentry.ScopesAdapter; +import io.sentry.SentryDate; +import io.sentry.SentryInstantDate; +import io.sentry.SentryLevel; +import io.sentry.SentryLongDate; +import io.sentry.SpanId; +import io.sentry.SpanStatus; +import io.sentry.TransactionContext; +import io.sentry.TransactionOptions; +import io.sentry.protocol.SentryId; +import io.sentry.protocol.TransactionNameSource; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class SentrySpanExporter implements SpanExporter { + private volatile boolean stopped = false; + // TODO is a strong ref problematic here? + private final List finishedSpans = new CopyOnWriteArrayList<>(); + private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); + private final @NotNull IScopes scopes; + + private final @NotNull List spanKindsConsideredForSentryRequests = + Arrays.asList(SpanKind.CLIENT, SpanKind.INTERNAL); + private static final @NotNull Long SPAN_TIMEOUT = DateUtils.secondsToNanos(5 * 60); + + private static final String TRACE_ORIGN = "auto.potel"; + + public SentrySpanExporter() { + this(ScopesAdapter.getInstance()); + } + + public SentrySpanExporter(final @NotNull IScopes scopes) { + this.scopes = scopes; + } + + @Override + public CompletableResultCode export(Collection spans) { + if (stopped) { + // TODO unsure if there's a way to attach a message + return CompletableResultCode.ofFailure(); + } + + final int openSpanCount = finishedSpans.size(); + final int newSpanCount = spans.size(); + + final @NotNull List nonSentryRequestSpans = filterOutSentrySpans(spans); + + finishedSpans.addAll(nonSentryRequestSpans); + final @NotNull List remaining = maybeSend(finishedSpans); + final int remainingSpanCount = remaining.size(); + final int sentSpanCount = openSpanCount + newSpanCount - remainingSpanCount; + + scopes + .getOptions() + .getLogger() + .log( + SentryLevel.DEBUG, + "SpanExporter exported %s spans, %s unset spans remaining.", + sentSpanCount, + remainingSpanCount); + + this.finishedSpans.clear(); + + final @NotNull SentryInstantDate now = new SentryInstantDate(); + + final @NotNull List nonExpired = + remaining.stream().filter((span) -> isSpanTooOld(span, now)).collect(Collectors.toList()); + this.finishedSpans.addAll(nonExpired); + + // TODO + + return CompletableResultCode.ofSuccess(); + } + + private boolean isSpanTooOld(final @NotNull SpanData span, final @NotNull SentryInstantDate now) { + final @NotNull SentryDate startDate = new SentryLongDate(span.getStartEpochNanos()); + boolean isTimedOut = now.diff(startDate) > SPAN_TIMEOUT; + if (isTimedOut) { + scopes + .getOptions() + .getLogger() + .log( + SentryLevel.DEBUG, + "Dropping span %s as it was pending for too long.", + span.getSpanId()); + } + return isTimedOut; + } + + private @NotNull List filterOutSentrySpans(final @NotNull Collection spans) { + return spans.stream().filter((span) -> !isSentryRequest(span)).collect(Collectors.toList()); + } + + @SuppressWarnings("deprecation") + private boolean isSentryRequest(final @NotNull SpanData spanData) { + final @NotNull SpanKind kind = spanData.getKind(); + if (!spanKindsConsideredForSentryRequests.contains(kind)) { + return false; + } + + final @Nullable String httpUrl = spanData.getAttributes().get(SemanticAttributes.HTTP_URL); + if (DsnUtil.urlContainsDsnHost(scopes.getOptions(), httpUrl)) { + return true; + } + + final @Nullable String fullUrl = spanData.getAttributes().get(SemanticAttributes.URL_FULL); + return DsnUtil.urlContainsDsnHost(scopes.getOptions(), fullUrl); + } + + private List maybeSend(final @NotNull List spans) { + final @NotNull List grouped = groupSpansWithParents(spans); + final @NotNull List remaining = new CopyOnWriteArrayList<>(grouped); + final @NotNull List rootNodes = findCompletedRootNodes(grouped); + + for (final @NotNull SpanNode rootNode : rootNodes) { + remaining.remove(rootNode); + final @Nullable SpanData span = rootNode.getSpan(); + if (span == null) { + // TODO log + continue; + } + final @Nullable ITransaction transaction = createTransactionForOtelSpan(span); + if (transaction == null) { + // TODO log + continue; + } + + for (final @NotNull SpanNode childNode : rootNode.getChildren()) { + createAndFinishSpanForOtelSpan(childNode, transaction, remaining); + } + + // spanStorage.getScope() + // transaction.finishWithScope + // TODO status + transaction.finish(SpanStatus.OK, new SentryLongDate(span.getEndEpochNanos())); + } + + return remaining.stream() + .map((node) -> node.getSpan()) + .filter((it) -> it != null) + .collect(Collectors.toList()); + } + + private void createAndFinishSpanForOtelSpan( + final @NotNull SpanNode spanNode, + final @NotNull ISpan sentrySpan, + final @NotNull List remaining) { + remaining.remove(spanNode); + final @Nullable SpanData spanData = spanNode.getSpan(); + + // If this span should be dropped, we still want to create spans for the children of this + if (spanData == null) { + for (SpanNode childNode : spanNode.getChildren()) { + createAndFinishSpanForOtelSpan(childNode, sentrySpan, remaining); + } + return; + } + + final @NotNull String spanId = spanData.getSpanId(); + // TODO attributes + // TODO cleanup sentry attributes + + scopes + .getOptions() + .getLogger() + .log( + SentryLevel.DEBUG, + "Creating Sentry child span for OpenTelemetry span %s (trace %s). Parent span is %s.", + spanId, + spanData.getTraceId(), + spanData.getParentSpanId()); + final @NotNull SentryDate startDate = new SentryLongDate(spanData.getStartEpochNanos()); + final @NotNull ISpan sentryChildSpan = + sentrySpan.startChild(spanData.getName(), spanData.getName(), startDate, Instrumenter.OTEL); + sentryChildSpan.getSpanContext().setOrigin(TRACE_ORIGN); + + for (SpanNode childNode : spanNode.getChildren()) { + createAndFinishSpanForOtelSpan(childNode, sentryChildSpan, remaining); + } + + sentryChildSpan.finish( + mapOtelStatus(spanData), new SentryLongDate(spanData.getEndEpochNanos())); + } + + private @Nullable ITransaction createTransactionForOtelSpan(final @NotNull SpanData span) { + final @NotNull String spanId = span.getSpanId(); + final @NotNull String traceId = span.getTraceId(); + // final @Nullable IScope scope = spanStorage.getScope(spanId); + final @Nullable IScopes scopesMaybe = spanStorage.getScopes(span.getSpanContext()); + final @NotNull IScopes scopesToUse = + scopesMaybe == null ? ScopesAdapter.getInstance() : scopesMaybe; + + // final @Nullable Boolean parentSampled = + // span.getAttributes().get(InternalSemanticAttributes.PARENT_SAMPLED); + // TODO DSC + // TODO op, desc, tags, data, origin, source + // TODO metadata + + // TODO we'll have to copy some of otel span attributes over to our transaction/span, e.g. + // thread info is wrong because it's created here in the exporter + + scopesToUse + .getOptions() + .getLogger() + .log( + SentryLevel.DEBUG, + "Creating Sentry transaction for OpenTelemetry span %s (trace %s).", + spanId, + traceId); + final @NotNull String transactionName = span.getName(); + final @NotNull TransactionNameSource transactionNameSource = TransactionNameSource.CUSTOM; + final @Nullable String op = span.getName(); + final SpanId sentrySpanId = new SpanId(spanId); + + final @NotNull TransactionContext transactionContext = + new TransactionContext(new SentryId(traceId), sentrySpanId, null, null, null); + // traceData.getSentryTraceHeader() == null + // ? new TransactionContext( + // new SentryId(traceData.getTraceId()), spanId, null, null, null) + // : TransactionContext.fromPropagationContext( + // PropagationContext.fromHeaders( + // traceData.getSentryTraceHeader(), traceData.getBaggage(), spanId)); + + transactionContext.setName(transactionName); + transactionContext.setTransactionNameSource(transactionNameSource); + transactionContext.setOperation(op); + transactionContext.setInstrumenter(Instrumenter.OTEL); + + TransactionOptions transactionOptions = new TransactionOptions(); + transactionOptions.setStartTimestamp(new SentryLongDate(span.getStartEpochNanos())); + + ITransaction sentryTransaction = + scopesToUse.startTransaction(transactionContext, transactionOptions); + sentryTransaction.getSpanContext().setOrigin(TRACE_ORIGN); + + return sentryTransaction; + } + + private List findCompletedRootNodes(final @NotNull List grouped) { + final @NotNull Predicate isRootPredicate = + (node) -> { + return node.getParentNode() == null && node.getSpan() != null; + }; + return grouped.stream().filter(isRootPredicate).collect(Collectors.toList()); + } + + private List groupSpansWithParents(final @NotNull List spans) { + final @NotNull Map nodeMap = new HashMap<>(); + + for (final @NotNull SpanData spanData : spans) { + createOrUpdateSpanNodeAndRefs(nodeMap, spanData); + } + + return nodeMap.values().stream().collect(Collectors.toList()); + } + + private void createOrUpdateSpanNodeAndRefs( + final @NotNull Map nodeMap, final @NotNull SpanData spanData) { + final @NotNull String spanId = spanData.getSpanId(); + final String parentId = getParentId(spanData); + if (parentId == null) { + createOrUpdateNode(nodeMap, spanId, spanData, null, null); + return; + } + + final @NotNull SpanNode parentNode = createOrGetParentNode(nodeMap, parentId); + final @NotNull SpanNode spanNode = + createOrUpdateNode(nodeMap, spanId, spanData, null, parentNode); + parentNode.addChild(spanNode); + } + + private @Nullable String getParentId(final @NotNull SpanData spanData) { + final @NotNull String parentSpanId = spanData.getParentSpanId(); + final @Nullable Boolean isRemoteParent = spanData.getAttributes().get(IS_REMOTE_PARENT); + if (isRemoteParent != null && isRemoteParent) { + return null; + } + if (io.opentelemetry.api.trace.SpanId.isValid(parentSpanId)) { + return parentSpanId; + } + return null; + } + + private @NotNull SpanNode createOrGetParentNode( + final @NotNull Map nodeMap, final @NotNull String spanId) { + final @Nullable SpanNode existingNode = nodeMap.get(spanId); + + if (existingNode == null) { + return createOrUpdateNode(nodeMap, spanId, null, null, null); + } + + return existingNode; + } + + // TODO do we ever pass children? + private @NotNull SpanNode createOrUpdateNode( + final @NotNull Map nodeMap, + final @NotNull String spanId, + final @Nullable SpanData spanData, + final @Nullable List children, + final @Nullable SpanNode parentNode) { + final @Nullable SpanNode existingNode = nodeMap.get(spanId); + + if (existingNode != null) { + final @Nullable SpanData existingNodeSpan = existingNode.getSpan(); + + if (existingNodeSpan != null) { + // If span is already set, nothing to do here + return existingNode; + } + + // If span is not set yet, we update it + existingNode.setSpan(spanData); + existingNode.setParentNode(parentNode); + + return existingNode; + } + + final @NotNull SpanNode spanNode = new SpanNode(spanId); + spanNode.setSpan(spanData); + spanNode.setParentNode(parentNode); + spanNode.addChildren(children); + + nodeMap.put(spanId, spanNode); + + return spanNode; + } + + @SuppressWarnings("deprecation") + private SpanStatus mapOtelStatus(final @NotNull SpanData otelSpanData) { + final @NotNull StatusData otelStatus = otelSpanData.getStatus(); + final @NotNull StatusCode otelStatusCode = otelStatus.getStatusCode(); + + if (StatusCode.OK.equals(otelStatusCode) || StatusCode.UNSET.equals(otelStatusCode)) { + return SpanStatus.OK; + } + + final @Nullable Long httpStatus = + otelSpanData.getAttributes().get(SemanticAttributes.HTTP_STATUS_CODE); + if (httpStatus != null) { + final @Nullable SpanStatus spanStatus = SpanStatus.fromHttpStatusCode(httpStatus.intValue()); + if (spanStatus != null) { + return spanStatus; + } + } + + return SpanStatus.UNKNOWN_ERROR; + } + + @Override + public CompletableResultCode flush() { + scopes.flush(10000); + return CompletableResultCode.ofSuccess(); + } + + @Override + public CompletableResultCode shutdown() { + stopped = true; + scopes.close(); + return CompletableResultCode.ofSuccess(); + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java index 57db007b0a..06efb84fa9 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java @@ -56,6 +56,7 @@ private OtelSpanInfo descriptionForHttpMethod( return new OtelSpanInfo(op, description, transactionNameSource); } + @SuppressWarnings("deprecation") private OtelSpanInfo descriptionForDbSystem(final @NotNull ReadableSpan otelSpan) { @Nullable String dbStatement = otelSpan.getAttribute(SemanticAttributes.DB_STATEMENT); @NotNull String description = dbStatement != null ? dbStatement : otelSpan.getName(); diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanNode.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanNode.java new file mode 100644 index 0000000000..7342ec3f2b --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanNode.java @@ -0,0 +1,56 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.sdk.trace.data.SpanData; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class SpanNode { + private final @NotNull String id; + + // TODO [POTEL] should this be ReadableSpan? if so weak or strong ref? + private @Nullable SpanData span; + private @Nullable SpanNode parentNode; + private @NotNull List children = new CopyOnWriteArrayList<>(); + + public SpanNode(final @NotNull String spanId) { + this.id = spanId; + } + + public @NotNull String getId() { + return id; + } + + public @Nullable SpanData getSpan() { + return span; + } + + public void setSpan(final @Nullable SpanData span) { + this.span = span; + } + + public @Nullable SpanNode getParentNode() { + return parentNode; + } + + public void setParentNode(final @Nullable SpanNode parentNode) { + this.parentNode = parentNode; + } + + public @NotNull List getChildren() { + return children; + } + + public void addChildren(final @Nullable List children) { + if (children != null) { + this.children.addAll(children); + } + } + + public void addChild(final @Nullable SpanNode child) { + if (child != null) { + this.children.add(child); + } + } +} diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index ef46c29c62..7ef410d7d4 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -550,11 +550,13 @@ public final class io/sentry/HubAdapter : io/sentry/IHub { public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getOptions ()Lio/sentry/SentryOptions; + public fun getParentScopes ()Lio/sentry/IScopes; public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; public fun getScope ()Lio/sentry/IScope; public fun getSpan ()Lio/sentry/ISpan; public fun getTraceparent ()Lio/sentry/SentryTraceHeader; public fun getTransaction ()Lio/sentry/ITransaction; + public fun isAncestorOf (Lio/sentry/IScopes;)Z public fun isCrashedLastRun ()Ljava/lang/Boolean; public fun isEnabled ()Z public fun isHealthy ()Z @@ -612,11 +614,13 @@ public final class io/sentry/HubScopesWrapper : io/sentry/IHub { public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getOptions ()Lio/sentry/SentryOptions; + public fun getParentScopes ()Lio/sentry/IScopes; public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; public fun getScope ()Lio/sentry/IScope; public fun getSpan ()Lio/sentry/ISpan; public fun getTraceparent ()Lio/sentry/SentryTraceHeader; public fun getTransaction ()Lio/sentry/ITransaction; + public fun isAncestorOf (Lio/sentry/IScopes;)Z public fun isCrashedLastRun ()Ljava/lang/Boolean; public fun isEnabled ()Z public fun isHealthy ()Z @@ -839,11 +843,13 @@ public abstract interface class io/sentry/IScopes { public abstract fun getIsolationScope ()Lio/sentry/IScope; public abstract fun getLastEventId ()Lio/sentry/protocol/SentryId; public abstract fun getOptions ()Lio/sentry/SentryOptions; + public abstract fun getParentScopes ()Lio/sentry/IScopes; public abstract fun getRateLimiter ()Lio/sentry/transport/RateLimiter; public abstract fun getScope ()Lio/sentry/IScope; public abstract fun getSpan ()Lio/sentry/ISpan; public abstract fun getTraceparent ()Lio/sentry/SentryTraceHeader; public abstract fun getTransaction ()Lio/sentry/ITransaction; + public abstract fun isAncestorOf (Lio/sentry/IScopes;)Z public abstract fun isCrashedLastRun ()Ljava/lang/Boolean; public abstract fun isEnabled ()Z public abstract fun isHealthy ()Z @@ -1338,11 +1344,13 @@ public final class io/sentry/NoOpHub : io/sentry/IHub { public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getOptions ()Lio/sentry/SentryOptions; + public fun getParentScopes ()Lio/sentry/IScopes; public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; public fun getScope ()Lio/sentry/IScope; public fun getSpan ()Lio/sentry/ISpan; public fun getTraceparent ()Lio/sentry/SentryTraceHeader; public fun getTransaction ()Lio/sentry/ITransaction; + public fun isAncestorOf (Lio/sentry/IScopes;)Z public fun isCrashedLastRun ()Ljava/lang/Boolean; public fun isEnabled ()Z public fun isHealthy ()Z @@ -1473,11 +1481,13 @@ public final class io/sentry/NoOpScopes : io/sentry/IScopes { public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getOptions ()Lio/sentry/SentryOptions; + public fun getParentScopes ()Lio/sentry/IScopes; public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; public fun getScope ()Lio/sentry/IScope; public fun getSpan ()Lio/sentry/ISpan; public fun getTraceparent ()Lio/sentry/SentryTraceHeader; public fun getTransaction ()Lio/sentry/ITransaction; + public fun isAncestorOf (Lio/sentry/IScopes;)Z public fun isCrashedLastRun ()Ljava/lang/Boolean; public fun isEnabled ()Z public fun isHealthy ()Z @@ -1962,12 +1972,13 @@ public final class io/sentry/Scopes : io/sentry/IScopes, io/sentry/metrics/Metri public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; public fun getMetricsAggregator ()Lio/sentry/IMetricsAggregator; public fun getOptions ()Lio/sentry/SentryOptions; + public fun getParentScopes ()Lio/sentry/IScopes; public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; public fun getScope ()Lio/sentry/IScope; public fun getSpan ()Lio/sentry/ISpan; public fun getTraceparent ()Lio/sentry/SentryTraceHeader; public fun getTransaction ()Lio/sentry/ITransaction; - public fun isAncestorOf (Lio/sentry/Scopes;)Z + public fun isAncestorOf (Lio/sentry/IScopes;)Z public fun isCrashedLastRun ()Ljava/lang/Boolean; public fun isEnabled ()Z public fun isHealthy ()Z @@ -2026,11 +2037,13 @@ public final class io/sentry/ScopesAdapter : io/sentry/IScopes { public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getOptions ()Lio/sentry/SentryOptions; + public fun getParentScopes ()Lio/sentry/IScopes; public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; public fun getScope ()Lio/sentry/IScope; public fun getSpan ()Lio/sentry/ISpan; public fun getTraceparent ()Lio/sentry/SentryTraceHeader; public fun getTransaction ()Lio/sentry/ITransaction; + public fun isAncestorOf (Lio/sentry/IScopes;)Z public fun isCrashedLastRun ()Ljava/lang/Boolean; public fun isEnabled ()Z public fun isHealthy ()Z @@ -2056,6 +2069,11 @@ public final class io/sentry/ScopesAdapter : io/sentry/IScopes { public fun withScope (Lio/sentry/ScopeCallback;)V } +public final class io/sentry/ScopesStorageFactory { + public fun ()V + public static fun create (Lio/sentry/util/LoadClass;Lio/sentry/ILogger;)Lio/sentry/IScopesStorage; +} + public final class io/sentry/SendCachedEnvelopeFireAndForgetIntegration : io/sentry/IConnectionStatusProvider$IConnectionStatusObserver, io/sentry/Integration, java/io/Closeable { public fun (Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForgetFactory;)V public fun close ()V @@ -5466,6 +5484,13 @@ public final class io/sentry/util/LifecycleHelper { public static fun close (Ljava/lang/Object;)V } +public final class io/sentry/util/LoadClass { + public fun ()V + public fun isClassAvailable (Ljava/lang/String;Lio/sentry/ILogger;)Z + public fun isClassAvailable (Ljava/lang/String;Lio/sentry/SentryOptions;)Z + public fun loadClass (Ljava/lang/String;Lio/sentry/ILogger;)Ljava/lang/Class; +} + public final class io/sentry/util/LogUtils { public fun ()V public static fun logNotInstanceOf (Ljava/lang/Class;Ljava/lang/Object;Lio/sentry/ILogger;)V diff --git a/sentry/src/main/java/io/sentry/CombinedScopeView.java b/sentry/src/main/java/io/sentry/CombinedScopeView.java index ef6031cc0a..86b90379ad 100644 --- a/sentry/src/main/java/io/sentry/CombinedScopeView.java +++ b/sentry/src/main/java/io/sentry/CombinedScopeView.java @@ -145,11 +145,16 @@ public void setRequest(@Nullable Request request) { @Override public @NotNull List getFingerprint() { - final @NotNull List allFingerprints = new CopyOnWriteArrayList<>(); - allFingerprints.addAll(globalScope.getFingerprint()); - allFingerprints.addAll(isolationScope.getFingerprint()); - allFingerprints.addAll(scope.getFingerprint()); - return allFingerprints; + // TODO [HSM] should these be merged? + final @Nullable List current = scope.getFingerprint(); + if (!current.isEmpty()) { + return current; + } + final @Nullable List isolation = isolationScope.getFingerprint(); + if (!isolation.isEmpty()) { + return isolation; + } + return globalScope.getFingerprint(); } @Override diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index 28c974bd59..9c09be075c 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -251,6 +251,16 @@ public void flush(long timeoutMillis) { return Sentry.getGlobalScope(); } + @Override + public @Nullable IScopes getParentScopes() { + return Sentry.getCurrentScopes().getParentScopes(); + } + + @Override + public boolean isAncestorOf(final @Nullable IScopes otherScopes) { + return Sentry.getCurrentScopes().isAncestorOf(otherScopes); + } + @Override public @NotNull SentryId captureTransaction( @NotNull SentryTransaction transaction, diff --git a/sentry/src/main/java/io/sentry/HubScopesWrapper.java b/sentry/src/main/java/io/sentry/HubScopesWrapper.java index 195371ee52..371321eaf2 100644 --- a/sentry/src/main/java/io/sentry/HubScopesWrapper.java +++ b/sentry/src/main/java/io/sentry/HubScopesWrapper.java @@ -246,6 +246,16 @@ public void flush(long timeoutMillis) { return Sentry.getGlobalScope(); } + @Override + public @Nullable IScopes getParentScopes() { + return scopes.getParentScopes(); + } + + @Override + public boolean isAncestorOf(final @Nullable IScopes otherScopes) { + return scopes.isAncestorOf(otherScopes); + } + @ApiStatus.Internal @Override public @NotNull SentryId captureTransaction( diff --git a/sentry/src/main/java/io/sentry/IScopes.java b/sentry/src/main/java/io/sentry/IScopes.java index d6b95574d7..400f08e457 100644 --- a/sentry/src/main/java/io/sentry/IScopes.java +++ b/sentry/src/main/java/io/sentry/IScopes.java @@ -456,6 +456,25 @@ default void configureScope(@NotNull ScopeCallback callback) { @NotNull IScope getGlobalScope(); + /** + * Returns the parent of this Scopes instance or null, if it does not have a parent. The parent is + * the Scopes instance this instance was forked from. + * + * @return parent Scopes or null + */ + @ApiStatus.Internal + @Nullable + IScopes getParentScopes(); + + /** + * Checks whether this Scopes instance is direct or indirect parent of the other Scopes instance. + * + * @param otherScopes Scopes instance that could be a direct or indirect child. + * @return true if this Scopes instance is a direct or indirect parent of the other Scopes. + */ + @ApiStatus.Internal + boolean isAncestorOf(final @Nullable IScopes otherScopes); + /** * Captures the transaction and enqueues it for sending to Sentry server. * diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index e2d2b411ec..580cd74284 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -211,6 +211,16 @@ public void flush(long timeoutMillis) {} return NoOpScope.getInstance(); } + @Override + public @Nullable IScopes getParentScopes() { + return null; + } + + @Override + public boolean isAncestorOf(@Nullable IScopes otherScopes) { + return false; + } + @Override public @NotNull IScopes forkedRootScopes(final @NotNull String creator) { return NoOpScopes.getInstance(); diff --git a/sentry/src/main/java/io/sentry/NoOpScopes.java b/sentry/src/main/java/io/sentry/NoOpScopes.java index 7058cf9c28..4528a285ca 100644 --- a/sentry/src/main/java/io/sentry/NoOpScopes.java +++ b/sentry/src/main/java/io/sentry/NoOpScopes.java @@ -211,6 +211,16 @@ public void flush(long timeoutMillis) {} return NoOpScope.getInstance(); } + @Override + public @Nullable IScopes getParentScopes() { + return null; + } + + @Override + public boolean isAncestorOf(@Nullable IScopes otherScopes) { + return false; + } + @Override public @NotNull SentryId captureTransaction( final @NotNull SentryTransaction transaction, diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 63b1b37508..bd601ec208 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -87,7 +87,15 @@ private Scopes( return globalScope; } - public boolean isAncestorOf(final @Nullable Scopes otherScopes) { + @Override + @ApiStatus.Internal + public @Nullable IScopes getParentScopes() { + return parentScopes; + } + + @Override + @ApiStatus.Internal + public boolean isAncestorOf(final @Nullable IScopes otherScopes) { if (otherScopes == null) { return false; } @@ -96,8 +104,8 @@ public boolean isAncestorOf(final @Nullable Scopes otherScopes) { return true; } - if (otherScopes.parentScopes != null) { - return isAncestorOf(otherScopes.parentScopes); + if (otherScopes.getParentScopes() != null) { + return isAncestorOf(otherScopes.getParentScopes()); } return false; diff --git a/sentry/src/main/java/io/sentry/ScopesAdapter.java b/sentry/src/main/java/io/sentry/ScopesAdapter.java index 63e6d1ee3c..3a0669eb35 100644 --- a/sentry/src/main/java/io/sentry/ScopesAdapter.java +++ b/sentry/src/main/java/io/sentry/ScopesAdapter.java @@ -248,6 +248,16 @@ public void flush(long timeoutMillis) { return Sentry.getGlobalScope(); } + @Override + public @Nullable IScopes getParentScopes() { + return Sentry.getCurrentScopes().getParentScopes(); + } + + @Override + public boolean isAncestorOf(final @Nullable IScopes otherScopes) { + return Sentry.getCurrentScopes().isAncestorOf(otherScopes); + } + @ApiStatus.Internal @Override public @NotNull SentryId captureTransaction( diff --git a/sentry/src/main/java/io/sentry/ScopesStorageFactory.java b/sentry/src/main/java/io/sentry/ScopesStorageFactory.java new file mode 100644 index 0000000000..abaf557f87 --- /dev/null +++ b/sentry/src/main/java/io/sentry/ScopesStorageFactory.java @@ -0,0 +1,38 @@ +package io.sentry; + +import io.sentry.util.LoadClass; +import java.lang.reflect.InvocationTargetException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class ScopesStorageFactory { + + private static final String OTEL_SCOPES_STORAGE = + "io.sentry.opentelemetry.OtelContextScopesStorage"; + + public static @NotNull IScopesStorage create( + final @NotNull LoadClass loadClass, final @NotNull ILogger logger) { + if (loadClass.isClassAvailable(OTEL_SCOPES_STORAGE, logger)) { + Class otelScopesStorageClazz = loadClass.loadClass(OTEL_SCOPES_STORAGE, logger); + if (otelScopesStorageClazz != null) { + try { + final @Nullable Object otelScopesStorage = + otelScopesStorageClazz.getDeclaredConstructor().newInstance(); + if (otelScopesStorage != null && otelScopesStorage instanceof IScopesStorage) { + return (IScopesStorage) otelScopesStorage; + } + } catch (InstantiationException e) { + // TODO log + } catch (IllegalAccessException e) { + // TODO log + } catch (InvocationTargetException e) { + // TODO log + } catch (NoSuchMethodException e) { + // TODO log + } + } + } + + return new DefaultScopesStorage(); + } +} diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 240af80ea3..2842845ca6 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -17,6 +17,7 @@ import io.sentry.transport.NoOpEnvelopeCache; import io.sentry.util.DebugMetaPropertiesApplier; import io.sentry.util.FileUtils; +import io.sentry.util.LoadClass; import io.sentry.util.Platform; import io.sentry.util.thread.IMainThreadChecker; import io.sentry.util.thread.MainThreadChecker; @@ -43,7 +44,9 @@ public final class Sentry { private Sentry() {} - private static volatile @NotNull IScopesStorage scopesStorage = new DefaultScopesStorage(); + // TODO logger? + private static volatile @NotNull IScopesStorage scopesStorage = + ScopesStorageFactory.create(new LoadClass(), NoOpLogger.getInstance()); /** The root Scopes or NoOp if Sentry is disabled. */ private static volatile @NotNull IScopes rootScopes = NoOpScopes.getInstance(); diff --git a/sentry/src/main/java/io/sentry/util/LoadClass.java b/sentry/src/main/java/io/sentry/util/LoadClass.java new file mode 100644 index 0000000000..b41f64fe14 --- /dev/null +++ b/sentry/src/main/java/io/sentry/util/LoadClass.java @@ -0,0 +1,47 @@ +package io.sentry.util; + +import io.sentry.ILogger; +import io.sentry.SentryLevel; +import io.sentry.SentryOptions; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** An Adapter for making Class.forName testable */ +// TODO [POTEL] deduplicate +public final class LoadClass { + + /** + * Try to load a class via reflection + * + * @param clazz the full class name + * @param logger an instance of ILogger + * @return a Class<?> if it's available, or null + */ + public @Nullable Class loadClass(final @NotNull String clazz, final @Nullable ILogger logger) { + try { + return Class.forName(clazz); + } catch (ClassNotFoundException e) { + if (logger != null) { + logger.log(SentryLevel.DEBUG, "Class not available:" + clazz, e); + } + } catch (UnsatisfiedLinkError e) { + if (logger != null) { + logger.log(SentryLevel.ERROR, "Failed to load (UnsatisfiedLinkError) " + clazz, e); + } + } catch (Throwable e) { + if (logger != null) { + logger.log(SentryLevel.ERROR, "Failed to initialize " + clazz, e); + } + } + return null; + } + + public boolean isClassAvailable(final @NotNull String clazz, final @Nullable ILogger logger) { + return loadClass(clazz, logger) != null; + } + + public boolean isClassAvailable( + final @NotNull String clazz, final @Nullable SentryOptions options) { + return isClassAvailable(clazz, options != null ? options.getLogger() : null); + } +} diff --git a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt index abdb549207..b73a7adcc8 100644 --- a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt +++ b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt @@ -1071,13 +1071,30 @@ class CombinedScopeViewTest { } @Test - fun `combines fingerprints from current all scopes`() { + fun `prefers fingerprint from current scope`() { val combined = fixture.getSut() fixture.scope.fingerprint = listOf("scopeFingerprint") fixture.isolationScope.fingerprint = listOf("isolationFingerprint") fixture.globalScope.fingerprint = listOf("globalFingerprint") - assertEquals(listOf("globalFingerprint", "isolationFingerprint", "scopeFingerprint"), combined.fingerprint) + assertEquals(listOf("scopeFingerprint"), combined.fingerprint) + } + + @Test + fun `uses isolation scope fingerprint if current scope does not have one`() { + val combined = fixture.getSut() + fixture.isolationScope.fingerprint = listOf("isolationFingerprint") + fixture.globalScope.fingerprint = listOf("globalFingerprint") + + assertEquals(listOf("isolationFingerprint"), combined.fingerprint) + } + + @Test + fun `uses global scope fingerprint if current and isolation scope do not have one`() { + val combined = fixture.getSut() + fixture.globalScope.fingerprint = listOf("globalFingerprint") + + assertEquals(listOf("globalFingerprint"), combined.fingerprint) } // TODO [HSM] test clone diff --git a/settings.gradle.kts b/settings.gradle.kts index b6de6741ed..1f7d5c2226 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -42,6 +42,7 @@ include( "sentry-openfeign", "sentry-graphql", "sentry-jdbc", + "sentry-opentelemetry:sentry-opentelemetry-bootstrap", "sentry-opentelemetry:sentry-opentelemetry-core", "sentry-opentelemetry:sentry-opentelemetry-agentcustomization", "sentry-opentelemetry:sentry-opentelemetry-agent", From f85f1e21dd69fb47b3b86f269f4f2981cf3a3505 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 18 Jun 2024 07:20:00 +0200 Subject: [PATCH 54/91] POTEL 2 - Promote OpenTelemetry Span attributes (#3402) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes --- .../api/sentry-opentelemetry-core.api | 16 +++ .../io/sentry/opentelemetry/OtelSpanInfo.java | 24 ++++ .../opentelemetry/SentrySpanExporter.java | 58 +++++++-- .../SpanDescriptionExtractor.java | 117 ++++++++++++++++++ 4 files changed, 205 insertions(+), 10 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api index ee32c2f135..7322d23435 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api +++ b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api @@ -6,6 +6,9 @@ public final class io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor public final class io/sentry/opentelemetry/OtelSpanInfo { public fun (Ljava/lang/String;Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V + public fun (Ljava/lang/String;Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;Ljava/util/Map;)V + public fun addDataField (Ljava/lang/String;Ljava/lang/Object;)V + public fun getDataFields ()Ljava/util/Map; public fun getDescription ()Ljava/lang/String; public fun getOp ()Ljava/lang/String; public fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; @@ -52,6 +55,19 @@ public final class io/sentry/opentelemetry/SentrySpanProcessor : io/opentelemetr public final class io/sentry/opentelemetry/SpanDescriptionExtractor { public fun ()V public fun extractSpanDescription (Lio/opentelemetry/sdk/trace/ReadableSpan;)Lio/sentry/opentelemetry/OtelSpanInfo; + public fun extractSpanInfo (Lio/opentelemetry/sdk/trace/data/SpanData;)Lio/sentry/opentelemetry/OtelSpanInfo; +} + +public final class io/sentry/opentelemetry/SpanNode { + public fun (Ljava/lang/String;)V + public fun addChild (Lio/sentry/opentelemetry/SpanNode;)V + public fun addChildren (Ljava/util/List;)V + public fun getChildren ()Ljava/util/List; + public fun getId ()Ljava/lang/String; + public fun getParentNode ()Lio/sentry/opentelemetry/SpanNode; + public fun getSpan ()Lio/opentelemetry/sdk/trace/data/SpanData; + public fun setParentNode (Lio/sentry/opentelemetry/SpanNode;)V + public fun setSpan (Lio/opentelemetry/sdk/trace/data/SpanData;)V } public final class io/sentry/opentelemetry/SpanNode { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanInfo.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanInfo.java index 6ad39d3793..0cc0cd0236 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanInfo.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanInfo.java @@ -1,6 +1,8 @@ package io.sentry.opentelemetry; import io.sentry.protocol.TransactionNameSource; +import java.util.HashMap; +import java.util.Map; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -11,6 +13,19 @@ public final class OtelSpanInfo { private final @NotNull String description; private final @NotNull TransactionNameSource transactionNameSource; + private final @NotNull Map dataFields; + + public OtelSpanInfo( + final @NotNull String op, + final @NotNull String description, + final @NotNull TransactionNameSource transactionNameSource, + final @NotNull Map dataFields) { + this.op = op; + this.description = description; + this.transactionNameSource = transactionNameSource; + this.dataFields = dataFields; + } + public OtelSpanInfo( final @NotNull String op, final @NotNull String description, @@ -18,6 +33,7 @@ public OtelSpanInfo( this.op = op; this.description = description; this.transactionNameSource = transactionNameSource; + this.dataFields = new HashMap<>(); } public @NotNull String getOp() { @@ -31,4 +47,12 @@ public OtelSpanInfo( public @NotNull TransactionNameSource getTransactionNameSource() { return transactionNameSource; } + + public @NotNull Map getDataFields() { + return dataFields; + } + + public void addDataField(final @NotNull String key, final @NotNull Object value) { + dataFields.put(key, value); + } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index 63358382cc..a9f57c2680 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -2,6 +2,7 @@ import static io.sentry.opentelemetry.InternalSemanticAttributes.IS_REMOTE_PARENT; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.sdk.common.CompletableResultCode; @@ -25,7 +26,6 @@ import io.sentry.TransactionContext; import io.sentry.TransactionOptions; import io.sentry.protocol.SentryId; -import io.sentry.protocol.TransactionNameSource; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; @@ -42,6 +42,8 @@ public final class SentrySpanExporter implements SpanExporter { // TODO is a strong ref problematic here? private final List finishedSpans = new CopyOnWriteArrayList<>(); private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); + private final @NotNull SpanDescriptionExtractor spanDescriptionExtractor = + new SpanDescriptionExtractor(); private final @NotNull IScopes scopes; private final @NotNull List spanKindsConsideredForSentryRequests = @@ -156,8 +158,7 @@ private List maybeSend(final @NotNull List spans) { // spanStorage.getScope() // transaction.finishWithScope - // TODO status - transaction.finish(SpanStatus.OK, new SentryLongDate(span.getEndEpochNanos())); + transaction.finish(mapOtelStatus(span), new SentryLongDate(span.getEndEpochNanos())); } return remaining.stream() @@ -182,6 +183,7 @@ private void createAndFinishSpanForOtelSpan( } final @NotNull String spanId = spanData.getSpanId(); + final @NotNull OtelSpanInfo spanInfo = spanDescriptionExtractor.extractSpanInfo(spanData); // TODO attributes // TODO cleanup sentry attributes @@ -196,8 +198,13 @@ private void createAndFinishSpanForOtelSpan( spanData.getParentSpanId()); final @NotNull SentryDate startDate = new SentryLongDate(spanData.getStartEpochNanos()); final @NotNull ISpan sentryChildSpan = - sentrySpan.startChild(spanData.getName(), spanData.getName(), startDate, Instrumenter.OTEL); + sentrySpan.startChild( + spanInfo.getOp(), spanInfo.getDescription(), startDate, Instrumenter.OTEL); + sentryChildSpan.getSpanContext().setOrigin(TRACE_ORIGN); + for (Map.Entry dataField : spanInfo.getDataFields().entrySet()) { + sentryChildSpan.setData(dataField.getKey(), dataField.getValue()); + } for (SpanNode childNode : spanNode.getChildren()) { createAndFinishSpanForOtelSpan(childNode, sentryChildSpan, remaining); @@ -214,6 +221,7 @@ private void createAndFinishSpanForOtelSpan( final @Nullable IScopes scopesMaybe = spanStorage.getScopes(span.getSpanContext()); final @NotNull IScopes scopesToUse = scopesMaybe == null ? ScopesAdapter.getInstance() : scopesMaybe; + final @NotNull OtelSpanInfo spanInfo = spanDescriptionExtractor.extractSpanInfo(span); // final @Nullable Boolean parentSampled = // span.getAttributes().get(InternalSemanticAttributes.PARENT_SAMPLED); @@ -232,11 +240,10 @@ private void createAndFinishSpanForOtelSpan( "Creating Sentry transaction for OpenTelemetry span %s (trace %s).", spanId, traceId); - final @NotNull String transactionName = span.getName(); - final @NotNull TransactionNameSource transactionNameSource = TransactionNameSource.CUSTOM; - final @Nullable String op = span.getName(); final SpanId sentrySpanId = new SpanId(spanId); + // TODO parentSpanId, parentSamplingDecision, baggage + final @NotNull TransactionContext transactionContext = new TransactionContext(new SentryId(traceId), sentrySpanId, null, null, null); // traceData.getSentryTraceHeader() == null @@ -246,9 +253,9 @@ private void createAndFinishSpanForOtelSpan( // PropagationContext.fromHeaders( // traceData.getSentryTraceHeader(), traceData.getBaggage(), spanId)); - transactionContext.setName(transactionName); - transactionContext.setTransactionNameSource(transactionNameSource); - transactionContext.setOperation(op); + transactionContext.setName(spanInfo.getDescription()); + transactionContext.setTransactionNameSource(spanInfo.getTransactionNameSource()); + transactionContext.setOperation(spanInfo.getOp()); transactionContext.setInstrumenter(Instrumenter.OTEL); TransactionOptions transactionOptions = new TransactionOptions(); @@ -258,6 +265,13 @@ private void createAndFinishSpanForOtelSpan( scopesToUse.startTransaction(transactionContext, transactionOptions); sentryTransaction.getSpanContext().setOrigin(TRACE_ORIGN); + final @NotNull Map otelContext = toOtelContext(span); + sentryTransaction.setContext("otel", otelContext); + + for (Map.Entry dataField : spanInfo.getDataFields().entrySet()) { + sentryTransaction.setData(dataField.getKey(), dataField.getValue()); + } + return sentryTransaction; } @@ -372,6 +386,30 @@ private SpanStatus mapOtelStatus(final @NotNull SpanData otelSpanData) { return SpanStatus.UNKNOWN_ERROR; } + private @NotNull Map toOtelContext(final @NotNull SpanData spanData) { + final @NotNull Map context = new HashMap<>(); + + context.put("attributes", toMapWithStringKeys(spanData.getAttributes())); + context.put("resource", toMapWithStringKeys(spanData.getResource().getAttributes())); + + return context; + } + + private @NotNull Map toMapWithStringKeys(final @Nullable Attributes attributes) { + final @NotNull Map mapWithStringKeys = new HashMap<>(); + + if (attributes != null) { + attributes.forEach( + (key, value) -> { + if (key != null) { + mapWithStringKeys.put(key.getKey(), value); + } + }); + } + + return mapWithStringKeys; + } + @Override public CompletableResultCode flush() { scopes.flush(10000); diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java index 06efb84fa9..891faae93a 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java @@ -1,9 +1,13 @@ package io.sentry.opentelemetry; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.sdk.trace.ReadableSpan; +import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.semconv.SemanticAttributes; import io.sentry.protocol.TransactionNameSource; +import java.util.HashMap; +import java.util.Map; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -62,4 +66,117 @@ private OtelSpanInfo descriptionForDbSystem(final @NotNull ReadableSpan otelSpan @NotNull String description = dbStatement != null ? dbStatement : otelSpan.getName(); return new OtelSpanInfo("db", description, TransactionNameSource.TASK); } + + @SuppressWarnings("deprecation") + public @NotNull OtelSpanInfo extractSpanInfo(final @NotNull SpanData otelSpan) { + OtelSpanInfo spanInfo = extractSpanDescription(otelSpan); + + final @Nullable Long threadId = otelSpan.getAttributes().get(SemanticAttributes.THREAD_ID); + if (threadId != null) { + spanInfo.addDataField("thread.id", threadId); + } + + final @Nullable String threadName = + otelSpan.getAttributes().get(SemanticAttributes.THREAD_NAME); + if (threadName != null) { + spanInfo.addDataField("thread.name", threadName); + } + + final @Nullable String dbSystem = otelSpan.getAttributes().get(SemanticAttributes.DB_SYSTEM); + if (dbSystem != null) { + spanInfo.addDataField("db.system", dbSystem); + } + + final @Nullable String dbName = otelSpan.getAttributes().get(SemanticAttributes.DB_NAME); + if (dbName != null) { + spanInfo.addDataField("db.name", dbName); + } + + return spanInfo; + } + + @SuppressWarnings("deprecation") + private OtelSpanInfo extractSpanDescription(SpanData otelSpan) { + final @NotNull String name = otelSpan.getName(); + final @NotNull Attributes attributes = otelSpan.getAttributes(); + + final @Nullable String httpMethod = attributes.get(SemanticAttributes.HTTP_METHOD); + if (httpMethod != null) { + return descriptionForHttpMethod(otelSpan, httpMethod); + } + + final @Nullable String httpRequestMethod = + attributes.get(SemanticAttributes.HTTP_REQUEST_METHOD); + if (httpRequestMethod != null) { + return descriptionForHttpMethod(otelSpan, httpRequestMethod); + } + + final @Nullable String dbSystem = attributes.get(SemanticAttributes.DB_SYSTEM); + if (dbSystem != null) { + return descriptionForDbSystem(otelSpan); + } + + return new OtelSpanInfo(name, name, TransactionNameSource.CUSTOM); + } + + @SuppressWarnings("deprecation") + private OtelSpanInfo descriptionForHttpMethod( + final @NotNull SpanData otelSpan, final @NotNull String httpMethod) { + final @NotNull String name = otelSpan.getName(); + final @NotNull SpanKind kind = otelSpan.getKind(); + final @NotNull StringBuilder opBuilder = new StringBuilder("http"); + final @NotNull Attributes attributes = otelSpan.getAttributes(); + final @NotNull Map dataFields = new HashMap<>(); + dataFields.put("http.request.method", httpMethod); + + if (SpanKind.CLIENT.equals(kind)) { + opBuilder.append(".client"); + } else if (SpanKind.SERVER.equals(kind)) { + opBuilder.append(".server"); + } + final @Nullable String httpTarget = attributes.get(SemanticAttributes.HTTP_TARGET); + final @Nullable String httpRoute = attributes.get(SemanticAttributes.HTTP_ROUTE); + @Nullable String httpPath = httpRoute; + if (httpPath == null) { + httpPath = httpTarget; + } + final @NotNull String op = opBuilder.toString(); + + final @Nullable Long httpStatusCode = + attributes.get(SemanticAttributes.HTTP_RESPONSE_STATUS_CODE); + if (httpStatusCode != null) { + dataFields.put("http.response.status_code", httpStatusCode); + } + + final @Nullable String serverAddress = attributes.get(SemanticAttributes.SERVER_ADDRESS); + if (serverAddress != null) { + dataFields.put("server.address", serverAddress); + } + + final @Nullable String urlFull = attributes.get(SemanticAttributes.URL_FULL); + if (urlFull != null) { + dataFields.put("url.full", urlFull); + if (httpPath == null) { + httpPath = urlFull; + } + } + + if (httpPath == null) { + return new OtelSpanInfo(op, name, TransactionNameSource.CUSTOM, dataFields); + } + + final @NotNull String description = httpMethod + " " + httpPath; + final @NotNull TransactionNameSource transactionNameSource = + httpRoute != null ? TransactionNameSource.ROUTE : TransactionNameSource.URL; + + return new OtelSpanInfo(op, description, transactionNameSource, dataFields); + } + + @SuppressWarnings("deprecation") + private OtelSpanInfo descriptionForDbSystem(final @NotNull SpanData otelSpan) { + final @NotNull Attributes attributes = otelSpan.getAttributes(); + @Nullable String dbStatement = attributes.get(SemanticAttributes.DB_STATEMENT); + @NotNull String description = dbStatement != null ? dbStatement : otelSpan.getName(); + return new OtelSpanInfo("db", description, TransactionNameSource.TASK); + } } From a43af1c86deead70cb2e657aa34a61c6c16682ee Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 18 Jun 2024 07:23:05 +0200 Subject: [PATCH 55/91] POTEL 3 - Use OpenTelemetry in Sentry Performance API (#3416) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API --- ...ryAutoConfigurationCustomizerProvider.java | 4 + .../api/sentry-opentelemetry-bootstrap.api | 116 ++++- .../OtelContextScopesStorage.java | 3 +- .../sentry/opentelemetry/OtelSpanFactory.java | 65 +++ .../sentry/opentelemetry/OtelSpanWrapper.java | 460 ++++++++++++++++++ .../OtelTransactionSpanForwarder.java | 320 ++++++++++++ .../opentelemetry/SentryContextWrapper.java | 8 +- .../opentelemetry/SentryWeakSpanStorage.java | 12 +- .../api/sentry-opentelemetry-core.api | 12 - .../PotelSentrySpanProcessor.java | 2 +- .../opentelemetry/SentrySpanExporter.java | 54 +- .../SpanDescriptionExtractor.java | 1 + sentry/api/sentry.api | 68 ++- .../java/io/sentry/DefaultSpanFactory.java | 32 ++ sentry/src/main/java/io/sentry/ISpan.java | 31 ++ .../src/main/java/io/sentry/ISpanFactory.java | 25 + .../src/main/java/io/sentry/ITransaction.java | 48 +- .../java/io/sentry/NoOpScopesStorage.java | 3 +- sentry/src/main/java/io/sentry/NoOpSpan.java | 46 ++ .../main/java/io/sentry/NoOpTransaction.java | 12 +- sentry/src/main/java/io/sentry/Scope.java | 2 +- sentry/src/main/java/io/sentry/Scopes.java | 32 +- sentry/src/main/java/io/sentry/Sentry.java | 5 +- .../main/java/io/sentry/SentryOptions.java | 11 + .../src/main/java/io/sentry/SentryTracer.java | 18 +- sentry/src/main/java/io/sentry/Span.java | 49 ++ .../java/io/sentry/TransactionOptions.java | 14 + 27 files changed, 1353 insertions(+), 100 deletions(-) create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java create mode 100644 sentry/src/main/java/io/sentry/DefaultSpanFactory.java create mode 100644 sentry/src/main/java/io/sentry/ISpanFactory.java diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java index 94def04774..c914ee9f0b 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java @@ -30,12 +30,16 @@ public final class SentryAutoConfigurationCustomizerProvider @Override public void customize(AutoConfigurationCustomizer autoConfiguration) { final @Nullable VersionInfoHolder versionInfoHolder = createVersionInfo(); + + ContextStorage.addWrapper((storage) -> new SentryContextStorage(storage)); + if (isSentryAutoInitEnabled()) { Sentry.init( options -> { options.setEnableExternalConfiguration(true); options.setInstrumenter(Instrumenter.OTEL); options.addEventProcessor(new OpenTelemetryLinkErrorEventProcessor()); + options.setSpanFactory(new OtelSpanFactory()); final @Nullable SdkVersion sdkVersion = createSdkVersion(options, versionInfoHolder); if (sdkVersion != null) { options.setSdkVersion(sdkVersion); diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api index e86ec628c2..ac0a83fa46 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api @@ -16,6 +16,118 @@ public final class io/sentry/opentelemetry/OtelContextScopesStorage : io/sentry/ public fun set (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; } +public final class io/sentry/opentelemetry/OtelSpanFactory : io/sentry/ISpanFactory { + public fun ()V + public fun createSpan (Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/ISpan;)Lio/sentry/ISpan; + public fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; + public fun retrieveCurrentSpan (Lio/sentry/IScopes;)Lio/sentry/ISpan; +} + +public final class io/sentry/opentelemetry/OtelSpanWrapper : io/sentry/ISpan { + public fun (Lio/opentelemetry/api/trace/Span;Lio/sentry/IScopes;)V + public fun finish ()V + public fun finish (Lio/sentry/SpanStatus;)V + public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V + public fun getContexts ()Lio/sentry/protocol/Contexts; + public fun getData ()Ljava/util/Map; + public fun getData (Ljava/lang/String;)Ljava/lang/Object; + public fun getDescription ()Ljava/lang/String; + public fun getEventId ()Lio/sentry/protocol/SentryId; + public fun getFinishDate ()Lio/sentry/SentryDate; + public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; + public fun getMeasurements ()Ljava/util/Map; + public fun getName ()Ljava/lang/String; + public fun getNameSource ()Lio/sentry/protocol/TransactionNameSource; + public fun getOperation ()Ljava/lang/String; + public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision; + public fun getScopes ()Lio/sentry/IScopes; + public fun getSpanContext ()Lio/sentry/SpanContext; + public fun getStartDate ()Lio/sentry/SentryDate; + public fun getStatus ()Lio/sentry/SpanStatus; + public fun getTag (Ljava/lang/String;)Ljava/lang/String; + public fun getThrowable ()Ljava/lang/Throwable; + public fun getTraceId ()Lio/sentry/protocol/SentryId; + public fun isFinished ()Z + public fun isNoOp ()Z + public fun isProfileSampled ()Ljava/lang/Boolean; + public fun isSampled ()Ljava/lang/Boolean; + public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; + public fun setContext (Ljava/lang/String;Ljava/lang/Object;)V + public fun setData (Ljava/lang/String;Ljava/lang/Object;)V + public fun setDescription (Ljava/lang/String;)V + public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;)V + public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;Lio/sentry/MeasurementUnit;)V + public fun setName (Ljava/lang/String;)V + public fun setName (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V + public fun setOperation (Ljava/lang/String;)V + public fun setStatus (Lio/sentry/SpanStatus;)V + public fun setTag (Ljava/lang/String;Ljava/lang/String;)V + 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/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; + public fun toBaggageHeader (Ljava/util/List;)Lio/sentry/BaggageHeader; + public fun toSentryTrace ()Lio/sentry/SentryTraceHeader; + public fun traceContext ()Lio/sentry/TraceContext; + public fun updateEndDate (Lio/sentry/SentryDate;)Z +} + +public final class io/sentry/opentelemetry/OtelTransactionSpanForwarder : io/sentry/ITransaction { + public fun (Lio/sentry/ISpan;)V + public fun finish ()V + public fun finish (Lio/sentry/SpanStatus;)V + public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V + public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;ZLio/sentry/Hint;)V + public fun forceFinish (Lio/sentry/SpanStatus;ZLio/sentry/Hint;)V + public fun getContexts ()Lio/sentry/protocol/Contexts; + public fun getData (Ljava/lang/String;)Ljava/lang/Object; + public fun getDescription ()Ljava/lang/String; + public fun getEventId ()Lio/sentry/protocol/SentryId; + public fun getFinishDate ()Lio/sentry/SentryDate; + public fun getLatestActiveSpan ()Lio/sentry/ISpan; + public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; + public fun getName ()Ljava/lang/String; + public fun getNameSource ()Lio/sentry/protocol/TransactionNameSource; + public fun getOperation ()Ljava/lang/String; + public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision; + public fun getSpanContext ()Lio/sentry/SpanContext; + public fun getSpans ()Ljava/util/List; + public fun getStartDate ()Lio/sentry/SentryDate; + public fun getStatus ()Lio/sentry/SpanStatus; + public fun getTag (Ljava/lang/String;)Ljava/lang/String; + public fun getThrowable ()Ljava/lang/Throwable; + public fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; + public fun isFinished ()Z + public fun isNoOp ()Z + public fun isProfileSampled ()Ljava/lang/Boolean; + public fun isSampled ()Ljava/lang/Boolean; + public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; + public fun scheduleFinish ()V + public fun setContext (Ljava/lang/String;Ljava/lang/Object;)V + public fun setData (Ljava/lang/String;Ljava/lang/Object;)V + public fun setDescription (Ljava/lang/String;)V + public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;)V + public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;Lio/sentry/MeasurementUnit;)V + public fun setName (Ljava/lang/String;)V + public fun setName (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V + public fun setOperation (Ljava/lang/String;)V + public fun setStatus (Lio/sentry/SpanStatus;)V + public fun setTag (Ljava/lang/String;Ljava/lang/String;)V + 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; + public fun toBaggageHeader (Ljava/util/List;)Lio/sentry/BaggageHeader; + public fun toSentryTrace ()Lio/sentry/SentryTraceHeader; + public fun traceContext ()Lio/sentry/TraceContext; + public fun updateEndDate (Lio/sentry/SentryDate;)Z +} + public final class io/sentry/opentelemetry/SentryContextStorage : io/opentelemetry/context/ContextStorage { public fun (Lio/opentelemetry/context/ContextStorage;)V public fun attach (Lio/opentelemetry/context/Context;)Lio/opentelemetry/context/Scope; @@ -38,7 +150,7 @@ public final class io/sentry/opentelemetry/SentryOtelKeys { public final class io/sentry/opentelemetry/SentryWeakSpanStorage { public static fun getInstance ()Lio/sentry/opentelemetry/SentryWeakSpanStorage; - public fun getScopes (Lio/opentelemetry/api/trace/SpanContext;)Lio/sentry/IScopes; - public fun storeScopes (Lio/opentelemetry/api/trace/SpanContext;Lio/sentry/IScopes;)V + public fun getSentrySpan (Lio/opentelemetry/api/trace/SpanContext;)Lio/sentry/opentelemetry/OtelSpanWrapper; + public fun storeSentrySpan (Lio/opentelemetry/api/trace/SpanContext;Lio/sentry/opentelemetry/OtelSpanWrapper;)V } diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java index 8f6842f71e..09014a77bb 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java @@ -15,7 +15,8 @@ public final class OtelContextScopesStorage implements IScopesStorage { @Override public ISentryLifecycleToken set(@Nullable IScopes scopes) { - Scope otelScope = Context.current().with(SENTRY_SCOPES_KEY, scopes).makeCurrent(); + final @NotNull Scope otelScope = + Context.current().with(SENTRY_SCOPES_KEY, scopes).makeCurrent(); return new OtelContextScopesStorageToken(otelScope); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java new file mode 100644 index 0000000000..3db940c317 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java @@ -0,0 +1,65 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.api.trace.Tracer; +import io.sentry.IScopes; +import io.sentry.ISpan; +import io.sentry.ISpanFactory; +import io.sentry.ITransaction; +import io.sentry.SpanOptions; +import io.sentry.TransactionContext; +import io.sentry.TransactionOptions; +import io.sentry.TransactionPerformanceCollector; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@ApiStatus.Internal +public final class OtelSpanFactory implements ISpanFactory { + + private final @NotNull SentryWeakSpanStorage storage = SentryWeakSpanStorage.getInstance(); + + @Override + public @NotNull ITransaction createTransaction( + @NotNull TransactionContext context, + @NotNull IScopes scopes, + @NotNull TransactionOptions transactionOptions, + @Nullable TransactionPerformanceCollector transactionPerformanceCollector) { + final @NotNull ISpan span = createSpan(context.getName(), scopes, transactionOptions, null); + return new OtelTransactionSpanForwarder(span); + } + + @Override + public @NotNull ISpan createSpan( + final @NotNull String name, + final @NotNull IScopes scopes, + final @NotNull SpanOptions spanOptions, + final @Nullable ISpan parentSpan) { + final @NotNull SpanBuilder spanBuilder = getTracer().spanBuilder(name); + if (parentSpan == null) { + spanBuilder.setNoParent(); + } else { + if (parentSpan instanceof OtelSpanWrapper) { + // TODO [POTEL] retrieve context from span + // spanBuilder.setParent() + } + } + // TODO [POTEL] start timestamp + final @NotNull Span span = spanBuilder.startSpan(); + return new OtelSpanWrapper(span, scopes); + } + + @Override + public @Nullable ISpan retrieveCurrentSpan(IScopes scopes) { + // TODO [POTEL] should we use Context.fromContextOrNull and read span from there? + final @NotNull Span span = Span.current(); + return storage.getSentrySpan(span.getSpanContext()); + } + + private @NotNull Tracer getTracer() { + return GlobalOpenTelemetry.getTracer( + "sentry-instrumentation-scope-name", "sentry-instrumentation-scope-version"); + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java new file mode 100644 index 0000000000..89ba53ff42 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java @@ -0,0 +1,460 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Scope; +import io.sentry.BaggageHeader; +import io.sentry.IScopes; +import io.sentry.ISentryLifecycleToken; +import io.sentry.ISpan; +import io.sentry.Instrumenter; +import io.sentry.MeasurementUnit; +import io.sentry.NoOpScopesStorage; +import io.sentry.SentryDate; +import io.sentry.SentryTraceHeader; +import io.sentry.SpanContext; +import io.sentry.SpanId; +import io.sentry.SpanOptions; +import io.sentry.SpanStatus; +import io.sentry.TraceContext; +import io.sentry.TracesSamplingDecision; +import io.sentry.metrics.LocalMetricsAggregator; +import io.sentry.protocol.Contexts; +import io.sentry.protocol.MeasurementValue; +import io.sentry.protocol.SentryId; +import io.sentry.protocol.TransactionNameSource; +import io.sentry.util.Objects; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@ApiStatus.Internal +public final class OtelSpanWrapper implements ISpan { + + private final @NotNull IScopes scopes; + + /** The moment in time when span was started. */ + private @NotNull SentryDate startTimestamp; + // TODO [POTEL] Set end timestamp in SpanProcessor, read it in exporter + // private @Nullable SentryDate endTimestamp = null; + + /** + * OpenTelemetry span which this wrapper wraps. Needs to be referenced weakly as otherwise we'd + * create a circular reference from {@link io.opentelemetry.sdk.trace.data.SpanData} to {@link + * OtelSpanWrapper} and indirectly back to {@link io.opentelemetry.sdk.trace.data.SpanData} via + * {@link Span}. Also see {@link SentryWeakSpanStorage}. + */ + private final @NotNull WeakReference span; + // private final @NotNull SpanContext context; + // private final @NotNull SpanOptions options; + private final @NotNull Contexts contexts = new Contexts(); + // TODO [POTEL] should be on SpanContext and retrieved from there in ctor here + private @NotNull TransactionNameSource nameSource = TransactionNameSource.CUSTOM; + private @NotNull String name = ""; + + // public OtelSpanWrapper( + // final @NotNull SpanBuilder spanBuilder, + // final @NotNull TransactionContext context, + // final @NotNull IScopes scopes, + // final @Nullable SentryDate startTimestamp, + // final @NotNull SpanOptions options) { + //// this.context = Objects.requireNonNull(context, "context is required"); + //// this.transaction = Objects.requireNonNull(transaction, "transaction is required"); + // this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + // // this.spanFinishedCallback = null; + // if (startTimestamp != null) { + // this.startTimestamp = startTimestamp; + // } else { + // this.startTimestamp = scopes.getOptions().getDateProvider().now(); + // } + // spanBuilder.setStartTimestamp(this.startTimestamp.nanoTimestamp(), TimeUnit.NANOSECONDS); + // spanBuilder.setNoParent(); + // // this.options = options; + // this.span = new WeakReference<>(spanBuilder.startSpan()); + // } + + public OtelSpanWrapper(final @NotNull Span span, final @NotNull IScopes scopes) { + this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + this.span = new WeakReference<>(span); + // TODO [POTEL] how could we make this work? + this.startTimestamp = scopes.getOptions().getDateProvider().now(); + } + + // OtelSpanWrapper( + // final @NotNull SpanBuilder spanBuilder, + // final @NotNull SentryId traceId, + // final @Nullable SpanId parentSpanId, + // final @NotNull String operation, + // final @NotNull IScopes scopes, + // final @Nullable SentryDate startTimestamp, + // final @NotNull SpanOptions options + // /*final @Nullable SpanFinishedCallback spanFinishedCallback*/ ) { + // this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + //// this.context = + //// new SpanContext( + //// traceId, new SpanId(), operation, parentSpanId, + // transaction.getSamplingDecision()); + //// this.transaction = Objects.requireNonNull(transaction, "transaction is required"); + // Objects.requireNonNull(scopes, "Scopes are required"); + // // this.options = options; + // // this.spanFinishedCallback = spanFinishedCallback; + // if (startTimestamp != null) { + // this.startTimestamp = startTimestamp; + // } else { + // this.startTimestamp = scopes.getOptions().getDateProvider().now(); + // } + // spanBuilder.setStartTimestamp(this.startTimestamp.nanoTimestamp(), TimeUnit.NANOSECONDS); + // this.span = new WeakReference<>(spanBuilder.startSpan()); + // } + + @Override + public @NotNull ISpan startChild(@NotNull String operation) { + return startChild(operation, (String) null); + } + + @Override + public @NotNull ISpan startChild( + @NotNull String operation, @Nullable String description, @NotNull SpanOptions spanOptions) { + // TODO [POTEL] check finished + // return transaction.startChild(context.getSpanId(), operation, description, spanOptions); + // TODO [POTEL] use description + return scopes.getOptions().getSpanFactory().createSpan(operation, scopes, spanOptions, this); + } + + @Override + public @NotNull ISpan startChild( + @NotNull String operation, + @Nullable String description, + @Nullable SentryDate timestamp, + @NotNull Instrumenter instrumenter) { + return startChild(operation, description, timestamp, instrumenter, new SpanOptions()); + } + + @Override + public @NotNull ISpan startChild( + @NotNull String operation, + @Nullable String description, + @Nullable SentryDate timestamp, + @NotNull Instrumenter instrumenter, + @NotNull SpanOptions spanOptions) { + // TODO [POTEL] check finished + // return transaction.startChild( + // context.getSpanId(), operation, description, timestamp, instrumenter, spanOptions); + // TODO [POTEL] use description, timestamp, instrumenter + return scopes.getOptions().getSpanFactory().createSpan(operation, scopes, spanOptions, this); + } + + @Override + public @NotNull ISpan startChild(@NotNull String operation, @Nullable String description) { + // TODO [POTEL] check finished + // return transaction.startChild(context.getSpanId(), operation, description); + return scopes + .getOptions() + .getSpanFactory() + .createSpan(operation, scopes, new SpanOptions(), this); + } + + @Override + public @NotNull SentryTraceHeader toSentryTrace() { + return new SentryTraceHeader(getTraceId(), getOtelSpanId(), isSampled()); + } + + private @NotNull SpanId getOtelSpanId() { + final @Nullable Span otelSpan = getSpan(); + if (otelSpan != null) { + return new SpanId(otelSpan.getSpanContext().getSpanId()); + } else { + return SpanId.EMPTY_ID; + } + } + + private @Nullable Span getSpan() { + return span.get(); + } + + @Override + public @Nullable TraceContext traceContext() { + // return transaction.traceContext(); + // TODO [POTEL] + return null; + } + + @Override + public @Nullable BaggageHeader toBaggageHeader(@Nullable List thirdPartyBaggageHeaders) { + // return transaction.toBaggageHeader(thirdPartyBaggageHeaders); + // TODO [POTEL] + return null; + } + + @Override + public void finish() { + // finish(this.context.getStatus()); + // TODO [POTEL] + finish(SpanStatus.OK); + } + + @Override + public void finish(@Nullable SpanStatus status) { + setStatus(status); + final @Nullable Span otelSpan = getSpan(); + if (otelSpan != null) { + otelSpan.end(); + } + } + + @Override + public void finish(@Nullable SpanStatus status, @Nullable SentryDate timestamp) { + setStatus(status); + final @Nullable Span otelSpan = getSpan(); + if (otelSpan != null) { + if (timestamp != null) { + otelSpan.end(timestamp.nanoTimestamp(), TimeUnit.NANOSECONDS); + } else { + otelSpan.end(); + } + } + } + + @Override + public void setOperation(@NotNull String operation) {} + + @Override + public @NotNull String getOperation() { + // TODO [POTEL] + return ""; + } + + @Override + public void setDescription(@Nullable String description) { + // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter + // ^ could go in span attributes + } + + @Override + public @Nullable String getDescription() { + // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter + return null; + } + + @Override + public void setStatus(@Nullable SpanStatus status) { + // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter + // ^ could go in span attributes + // this.context.setStatus(status); + } + + @Override + public @Nullable SpanStatus getStatus() { + // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter + // return context.getStatus(); + return null; + } + + @Override + public void setThrowable(@Nullable Throwable throwable) { + // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter + } + + @Override + public @Nullable Throwable getThrowable() { + // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter + return null; + } + + @Override + public @NotNull SpanContext getSpanContext() { + // TODO [POTEL] usage outside: setSampled, setOrigin, getTraceId, contexts.setTrace(), status, + // getOrigin + // TODO [POTEL] op, util for spanid, parentSpanId + return new SpanContext(getTraceId(), getOtelSpanId(), "TODO op", null, getSamplingDecision()); + } + + @Override + public void setTag(@NotNull String key, @NotNull String value) { + // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter + // context.setTag(key, value); + } + + @Override + public @Nullable String getTag(@NotNull String key) { + // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter + // return context.getTags().get(key); + return null; + } + + @Override + public boolean isFinished() { + // TODO [POTEL] find a way to check + return false; + } + + @Override + public void setData(@NotNull String key, @NotNull Object value) { + // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter + } + + @Override + public @Nullable Object getData(@NotNull String key) { + // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter + return null; + } + + @Override + public void setMeasurement(@NotNull String name, @NotNull Number value) { + // TODO [POTEL] + } + + @Override + public void setMeasurement( + @NotNull String name, @NotNull Number value, @NotNull MeasurementUnit unit) { + // TODO [POTEL] + } + + @Override + public boolean updateEndDate(@NotNull SentryDate date) { + return false; + } + + @Override + public @NotNull SentryDate getStartDate() { + return startTimestamp; + } + + @Override + public @Nullable SentryDate getFinishDate() { + // TODO [POTEL] cannot access spandata.getEndEpochNanos + return null; + } + + @Override + public boolean isNoOp() { + return false; + } + + @Override + public @Nullable LocalMetricsAggregator getLocalMetricsAggregator() { + return null; + } + + @Override + public void setContext(@NotNull String key, @NotNull Object context) { + contexts.put(key, context); + } + + @Override + public @NotNull Contexts getContexts() { + // TODO [POTEL] only works for root span atm + return contexts; + } + + @Override + public void setName(@NotNull String name) { + setName(name, TransactionNameSource.CUSTOM); + } + + @Override + public void setName(@NotNull String name, @NotNull TransactionNameSource nameSource) { + this.name = name; + this.nameSource = nameSource; + } + + @Override + public @NotNull TransactionNameSource getNameSource() { + return nameSource; + } + + @Override + public @NotNull String getName() { + return this.name; + } + + @NotNull + public SentryId getTraceId() { + final @Nullable Span otelSpan = getSpan(); + if (otelSpan != null) { + return new SentryId(otelSpan.getSpanContext().getTraceId()); + } else { + return SentryId.EMPTY_ID; + } + } + + public @NotNull Map getData() { + // return data; + // TODO [POTEL] + return new HashMap<>(); + } + + @NotNull + public Map getMeasurements() { + // return measurements; + // TODO [POTEL] + return new HashMap<>(); + } + + @Override + public @Nullable Boolean isSampled() { + final @Nullable Span otelSpan = getSpan(); + if (otelSpan != null) { + return otelSpan.getSpanContext().isSampled(); + } + return null; + } + + public @Nullable Boolean isProfileSampled() { + // we do not support profiling for OpenTelemetry yet + return false; + } + + @Override + public @Nullable TracesSamplingDecision getSamplingDecision() { + // TODO [POTEL] + + final @Nullable Span otelSpan = getSpan(); + if (otelSpan != null) { + return new TracesSamplingDecision(otelSpan.getSpanContext().isSampled()); + } + + return null; + } + + @Override + public @NotNull SentryId getEventId() { + // TODO [POTEL] + return new SentryId(getOtelSpanId().toString()); + } + + @ApiStatus.Internal + public @NotNull IScopes getScopes() { + return scopes; + } + + @SuppressWarnings("MustBeClosedChecker") + @Override + public @NotNull ISentryLifecycleToken makeCurrent() { + final @Nullable Span otelSpan = getSpan(); + if (otelSpan != null) { + final @NotNull Scope otelScope = otelSpan.makeCurrent(); + return new OtelContextSpanStorageToken(otelScope); + } + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + } + + // TODO [POTEL] extract generic + static final class OtelContextSpanStorageToken implements ISentryLifecycleToken { + + private final @NotNull Scope otelScope; + + OtelContextSpanStorageToken(final @NotNull Scope otelScope) { + this.otelScope = otelScope; + } + + @Override + public void close() { + otelScope.close(); + } + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java new file mode 100644 index 0000000000..25129db7e3 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java @@ -0,0 +1,320 @@ +package io.sentry.opentelemetry; + +import io.sentry.BaggageHeader; +import io.sentry.Hint; +import io.sentry.ISentryLifecycleToken; +import io.sentry.ISpan; +import io.sentry.ITransaction; +import io.sentry.Instrumenter; +import io.sentry.MeasurementUnit; +import io.sentry.SentryDate; +import io.sentry.SentryTraceHeader; +import io.sentry.SpanContext; +import io.sentry.SpanOptions; +import io.sentry.SpanStatus; +import io.sentry.TraceContext; +import io.sentry.TracesSamplingDecision; +import io.sentry.metrics.LocalMetricsAggregator; +import io.sentry.protocol.Contexts; +import io.sentry.protocol.SentryId; +import io.sentry.protocol.TransactionNameSource; +import io.sentry.util.Objects; +import java.util.ArrayList; +import java.util.List; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@ApiStatus.Internal +public final class OtelTransactionSpanForwarder implements ITransaction { + + private final @NotNull ISpan rootSpan; + + public OtelTransactionSpanForwarder(final @NotNull ISpan rootSpan) { + this.rootSpan = Objects.requireNonNull(rootSpan, "root span is required"); + } + + @Override + public @NotNull ISpan startChild(@NotNull String operation) { + return rootSpan.startChild(operation); + } + + @Override + public @NotNull ISpan startChild( + @NotNull String operation, @Nullable String description, @NotNull SpanOptions spanOptions) { + return rootSpan.startChild(operation, description, spanOptions); + } + + @Override + public @NotNull ISpan startChild( + @NotNull String operation, + @Nullable String description, + @Nullable SentryDate timestamp, + @NotNull Instrumenter instrumenter) { + return rootSpan.startChild(operation, description, timestamp, instrumenter); + } + + @Override + public @NotNull ISpan startChild( + @NotNull String operation, + @Nullable String description, + @Nullable SentryDate timestamp, + @NotNull Instrumenter instrumenter, + @NotNull SpanOptions spanOptions) { + // TODO [POTEL] + // return rootSpan.startChild(operation, description, timestamp, spanOptions); + return rootSpan.startChild(operation, description, timestamp, Instrumenter.SENTRY); + } + + @Override + public @NotNull ISpan startChild(@NotNull String operation, @Nullable String description) { + return rootSpan.startChild(operation, description); + } + + @Override + public @NotNull SentryTraceHeader toSentryTrace() { + // TODO [POTEL] root span? + return rootSpan.toSentryTrace(); + } + + @Override + public @Nullable TraceContext traceContext() { + // TODO [POTEL] root span? + return rootSpan.traceContext(); + } + + @Override + public @Nullable BaggageHeader toBaggageHeader(@Nullable List thirdPartyBaggageHeaders) { + // TODO [POTEL] root span? + return rootSpan.toBaggageHeader(thirdPartyBaggageHeaders); + } + + @Override + public void finish() { + // TODO [POTEL] should this finish all spans? + rootSpan.finish(); + } + + @Override + public void finish(@Nullable SpanStatus status) { + rootSpan.finish(status); + } + + @Override + public void finish(@Nullable SpanStatus status, @Nullable SentryDate timestamp) { + rootSpan.finish(status, timestamp); + } + + @Override + public void setOperation(@NotNull String operation) { + rootSpan.startChild(operation); + } + + @Override + public @NotNull String getOperation() { + return rootSpan.getOperation(); + } + + @Override + public void setDescription(@Nullable String description) { + rootSpan.setDescription(description); + } + + @Override + public @Nullable String getDescription() { + return rootSpan.getDescription(); + } + + @Override + public void setStatus(@Nullable SpanStatus status) { + rootSpan.setStatus(status); + } + + @Override + public @Nullable SpanStatus getStatus() { + return rootSpan.getStatus(); + } + + @Override + public void setThrowable(@Nullable Throwable throwable) { + rootSpan.setThrowable(throwable); + } + + @Override + public @Nullable Throwable getThrowable() { + return rootSpan.getThrowable(); + } + + @Override + public @NotNull SpanContext getSpanContext() { + return rootSpan.getSpanContext(); + } + + @Override + public void setTag(@NotNull String key, @NotNull String value) { + rootSpan.setTag(key, value); + } + + @Override + public @Nullable String getTag(@NotNull String key) { + return rootSpan.getTag(key); + } + + @Override + public boolean isFinished() { + return rootSpan.isFinished(); + } + + @Override + public void setData(@NotNull String key, @NotNull Object value) { + rootSpan.setData(key, value); + } + + @Override + public @Nullable Object getData(@NotNull String key) { + return rootSpan.getData(key); + } + + @Override + public void setMeasurement(@NotNull String name, @NotNull Number value) { + rootSpan.setMeasurement(name, value); + } + + @Override + public void setMeasurement( + @NotNull String name, @NotNull Number value, @NotNull MeasurementUnit unit) { + rootSpan.setMeasurement(name, value, unit); + } + + @Override + public boolean updateEndDate(@NotNull SentryDate date) { + return rootSpan.updateEndDate(date); + } + + @Override + public @NotNull SentryDate getStartDate() { + return rootSpan.getStartDate(); + } + + @Override + public @Nullable SentryDate getFinishDate() { + return rootSpan.getFinishDate(); + } + + @Override + public boolean isNoOp() { + return rootSpan.isNoOp(); + } + + @Override + public @Nullable LocalMetricsAggregator getLocalMetricsAggregator() { + return rootSpan.getLocalMetricsAggregator(); + } + + @Override + public @NotNull TransactionNameSource getTransactionNameSource() { + return rootSpan.getNameSource(); + } + + @Override + public @NotNull List getSpans() { + // TODO [POTEL] + return new ArrayList<>(); + } + + @Override + public @NotNull ISpan startChild( + @NotNull String operation, @Nullable String description, @Nullable SentryDate timestamp) { + // TODO [POTEL] + return rootSpan.startChild(operation, description, timestamp, Instrumenter.SENTRY); + } + + @Override + public @Nullable Boolean isSampled() { + return rootSpan.isSampled(); + } + + @Override + public @Nullable Boolean isProfileSampled() { + // TODO [POTEL] + return null; + } + + @Override + public @Nullable TracesSamplingDecision getSamplingDecision() { + return rootSpan.getSamplingDecision(); + } + + @Override + public @Nullable ISpan getLatestActiveSpan() { + return rootSpan; + } + + @Override + public @NotNull SentryId getEventId() { + return rootSpan.getEventId(); + } + + @Override + public @NotNull ISentryLifecycleToken makeCurrent() { + return rootSpan.makeCurrent(); + } + + @Override + public void scheduleFinish() { + // TODO [POTEL] + } + + @Override + public void forceFinish( + @NotNull SpanStatus status, boolean dropIfNoChildren, @Nullable Hint hint) { + // TODO [POTEL] + rootSpan.finish(status); + } + + @Override + public void finish( + @Nullable SpanStatus status, + @Nullable SentryDate timestamp, + boolean dropIfNoChildren, + @Nullable Hint hint) { + // TODO [POTEL] + rootSpan.finish(status, timestamp); + } + + @Override + public void setContext(@NotNull String key, @NotNull Object context) { + // TODO [POTEL] either set on root span or store in global storage or store on scopes + // thoughts: + // - span would have to save it on global storage too since we can't add complex data to otel + // span + // - with span ingestion there isn't a transaction anymore, so if we still need Contexts it + // should go on the (root) span + rootSpan.setContext(key, context); + } + + @Override + public @NotNull Contexts getContexts() { + return rootSpan.getContexts(); + } + + @Override + public void setName(@NotNull String name) { + rootSpan.setName(name); + } + + @Override + public void setName(@NotNull String name, @NotNull TransactionNameSource nameSource) { + rootSpan.setName(name, nameSource); + } + + @Override + public @NotNull TransactionNameSource getNameSource() { + return rootSpan.getNameSource(); + } + + @Override + public @NotNull String getName() { + return rootSpan.getName(); + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextWrapper.java index 1efc6621a4..cf5a6495f0 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextWrapper.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextWrapper.java @@ -64,10 +64,14 @@ private boolean isOpentelemetrySpan(final @NotNull ContextKey contextKey) private static @Nullable IScopes getCurrentSpanScopesFromGlobalStorage( final @NotNull Context context) { - @Nullable final Span span = Span.fromContext(context); + @Nullable final Span span = Span.fromContextOrNull(context); if (span != null) { - return SentryWeakSpanStorage.getInstance().getScopes(span.getSpanContext()); + final @Nullable OtelSpanWrapper sentrySpan = + SentryWeakSpanStorage.getInstance().getSentrySpan(span.getSpanContext()); + if (sentrySpan != null) { + return sentrySpan.getScopes(); + } } return null; diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryWeakSpanStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryWeakSpanStorage.java index 713b2f3d8e..ebcf89ce89 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryWeakSpanStorage.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryWeakSpanStorage.java @@ -2,7 +2,6 @@ import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.context.internal.shaded.WeakConcurrentMap; -import io.sentry.IScopes; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -34,16 +33,17 @@ public final class SentryWeakSpanStorage { } // weak keys, spawns a thread to clean up values that have been garbage collected - private final @NotNull WeakConcurrentMap scopes = + private final @NotNull WeakConcurrentMap sentrySpans = new WeakConcurrentMap<>(true); private SentryWeakSpanStorage() {} - public @Nullable IScopes getScopes(final @NotNull SpanContext spanContext) { - return scopes.get(spanContext); + public @Nullable OtelSpanWrapper getSentrySpan(final @NotNull SpanContext spanContext) { + return sentrySpans.get(spanContext); } - public void storeScopes(final @NotNull SpanContext otelSpan, final @NotNull IScopes scopes) { - this.scopes.put(otelSpan, scopes); + public void storeSentrySpan( + final @NotNull SpanContext otelSpan, final @NotNull OtelSpanWrapper sentrySpan) { + this.sentrySpans.put(otelSpan, sentrySpan); } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api index 7322d23435..834c593723 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api +++ b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api @@ -70,18 +70,6 @@ public final class io/sentry/opentelemetry/SpanNode { public fun setSpan (Lio/opentelemetry/sdk/trace/data/SpanData;)V } -public final class io/sentry/opentelemetry/SpanNode { - public fun (Ljava/lang/String;)V - public fun addChild (Lio/sentry/opentelemetry/SpanNode;)V - public fun addChildren (Ljava/util/List;)V - public fun getChildren ()Ljava/util/List; - public fun getId ()Ljava/lang/String; - public fun getParentNode ()Lio/sentry/opentelemetry/SpanNode; - public fun getSpan ()Lio/opentelemetry/sdk/trace/data/SpanData; - public fun setParentNode (Lio/sentry/opentelemetry/SpanNode;)V - public fun setSpan (Lio/opentelemetry/sdk/trace/data/SpanData;)V -} - public final class io/sentry/opentelemetry/TraceData { public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryTraceHeader;Lio/sentry/Baggage;)V public fun getBaggage ()Lio/sentry/Baggage; diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java index 25f9556c71..9932065769 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java @@ -45,7 +45,7 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri ? scopesFromContext.forkedCurrentScope("spanprocessor") : Sentry.forkedRootScopes("spanprocessor"); final @NotNull SpanContext spanContext = otelSpan.getSpanContext(); - spanStorage.storeScopes(spanContext, scopes); + spanStorage.storeSentrySpan(spanContext, new OtelSpanWrapper(otelSpan, scopes)); } @Override diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index a9f57c2680..c0fdc2dcdb 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -11,6 +11,7 @@ import io.opentelemetry.sdk.trace.export.SpanExporter; import io.opentelemetry.semconv.SemanticAttributes; import io.sentry.DateUtils; +import io.sentry.DefaultSpanFactory; import io.sentry.DsnUtil; import io.sentry.IScopes; import io.sentry.ISpan; @@ -25,11 +26,13 @@ import io.sentry.SpanStatus; import io.sentry.TransactionContext; import io.sentry.TransactionOptions; +import io.sentry.protocol.Contexts; import io.sentry.protocol.SentryId; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Predicate; @@ -40,6 +43,8 @@ public final class SentrySpanExporter implements SpanExporter { private volatile boolean stopped = false; // TODO is a strong ref problematic here? + // TODO [POTEL] a weak ref could mean spans are gone before we had a chance to attach them + // somewhere private final List finishedSpans = new CopyOnWriteArrayList<>(); private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); private final @NotNull SpanDescriptionExtractor spanDescriptionExtractor = @@ -131,7 +136,41 @@ private boolean isSentryRequest(final @NotNull SpanData spanData) { } final @Nullable String fullUrl = spanData.getAttributes().get(SemanticAttributes.URL_FULL); - return DsnUtil.urlContainsDsnHost(scopes.getOptions(), fullUrl); + if (DsnUtil.urlContainsDsnHost(scopes.getOptions(), fullUrl)) { + return true; + } + + // TODO [POTEL] should check if enabled but multi init with different options makes testing hard + // atm + // if (scopes.getOptions().isEnableSpotlight()) { + final @Nullable String spotlightUrl = scopes.getOptions().getSpotlightConnectionUrl(); + if (spotlightUrl != null) { + if (containsSpotlightUrl(fullUrl, spotlightUrl)) { + return true; + } + if (containsSpotlightUrl(httpUrl, spotlightUrl)) { + return true; + } + } else { + if (containsSpotlightUrl(fullUrl, "http://localhost:8969/stream")) { + return true; + } + if (containsSpotlightUrl(httpUrl, "http://localhost:8969/stream")) { + return true; + } + } + // } + + return false; + } + + private boolean containsSpotlightUrl( + final @Nullable String requestUrl, final @NotNull String spotlightUrl) { + if (requestUrl == null) { + return false; + } + + return requestUrl.toLowerCase(Locale.ROOT).contains(spotlightUrl.toLowerCase(Locale.ROOT)); } private List maybeSend(final @NotNull List spans) { @@ -218,7 +257,11 @@ private void createAndFinishSpanForOtelSpan( final @NotNull String spanId = span.getSpanId(); final @NotNull String traceId = span.getTraceId(); // final @Nullable IScope scope = spanStorage.getScope(spanId); - final @Nullable IScopes scopesMaybe = spanStorage.getScopes(span.getSpanContext()); + final @Nullable OtelSpanWrapper sentrySpanMaybe = + spanStorage.getSentrySpan(span.getSpanContext()); + + final @Nullable IScopes scopesMaybe = + sentrySpanMaybe != null ? sentrySpanMaybe.getScopes() : null; final @NotNull IScopes scopesToUse = scopesMaybe == null ? ScopesAdapter.getInstance() : scopesMaybe; final @NotNull OtelSpanInfo spanInfo = spanDescriptionExtractor.extractSpanInfo(span); @@ -260,6 +303,7 @@ private void createAndFinishSpanForOtelSpan( TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setStartTimestamp(new SentryLongDate(span.getStartEpochNanos())); + transactionOptions.setSpanFactory(new DefaultSpanFactory()); ITransaction sentryTransaction = scopesToUse.startTransaction(transactionContext, transactionOptions); @@ -272,6 +316,12 @@ private void createAndFinishSpanForOtelSpan( sentryTransaction.setData(dataField.getKey(), dataField.getValue()); } + if (sentrySpanMaybe != null) { + final @NotNull ISpan sentrySpan = sentrySpanMaybe; + final @NotNull Contexts contexts = sentrySpan.getContexts(); + sentryTransaction.getContexts().putAll(contexts); + } + return sentryTransaction; } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java index 891faae93a..bdfc9c81ce 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java @@ -15,6 +15,7 @@ @ApiStatus.Internal public final class SpanDescriptionExtractor { + // TODO [POTEL] remove these method overloads and pass in SpanData instead (span.toSpanData()) @SuppressWarnings("deprecation") public @NotNull OtelSpanInfo extractSpanDescription(final @NotNull ReadableSpan otelSpan) { final @NotNull String name = otelSpan.getName(); diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 7ef410d7d4..1d803703ee 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -365,6 +365,13 @@ public final class io/sentry/DefaultScopesStorage : io/sentry/IScopesStorage { public fun set (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; } +public final class io/sentry/DefaultSpanFactory : io/sentry/ISpanFactory { + public fun ()V + public fun createSpan (Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/ISpan;)Lio/sentry/ISpan; + public fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; + public fun retrieveCurrentSpan (Lio/sentry/IScopes;)Lio/sentry/ISpan; +} + public final class io/sentry/DefaultTransactionPerformanceCollector : io/sentry/TransactionPerformanceCollector { public fun (Lio/sentry/SentryOptions;)V public fun close ()V @@ -942,11 +949,16 @@ public abstract interface class io/sentry/ISpan { public abstract fun finish ()V public abstract fun finish (Lio/sentry/SpanStatus;)V public abstract fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V + public abstract fun getContexts ()Lio/sentry/protocol/Contexts; public abstract fun getData (Ljava/lang/String;)Ljava/lang/Object; public abstract fun getDescription ()Ljava/lang/String; + public abstract fun getEventId ()Lio/sentry/protocol/SentryId; public abstract fun getFinishDate ()Lio/sentry/SentryDate; public abstract fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; + public abstract fun getName ()Ljava/lang/String; + public abstract fun getNameSource ()Lio/sentry/protocol/TransactionNameSource; public abstract fun getOperation ()Ljava/lang/String; + public abstract fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision; public abstract fun getSpanContext ()Lio/sentry/SpanContext; public abstract fun getStartDate ()Lio/sentry/SentryDate; public abstract fun getStatus ()Lio/sentry/SpanStatus; @@ -954,10 +966,15 @@ public abstract interface class io/sentry/ISpan { public abstract fun getThrowable ()Ljava/lang/Throwable; public abstract fun isFinished ()Z public abstract fun isNoOp ()Z + public abstract fun isSampled ()Ljava/lang/Boolean; + public abstract fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; + public abstract fun setContext (Ljava/lang/String;Ljava/lang/Object;)V public abstract fun setData (Ljava/lang/String;Ljava/lang/Object;)V public abstract fun setDescription (Ljava/lang/String;)V public abstract fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;)V public abstract fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;Lio/sentry/MeasurementUnit;)V + public abstract fun setName (Ljava/lang/String;)V + public abstract fun setName (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V public abstract fun setOperation (Ljava/lang/String;)V public abstract fun setStatus (Lio/sentry/SpanStatus;)V public abstract fun setTag (Ljava/lang/String;Ljava/lang/String;)V @@ -973,22 +990,20 @@ public abstract interface class io/sentry/ISpan { public abstract fun updateEndDate (Lio/sentry/SentryDate;)Z } +public abstract interface class io/sentry/ISpanFactory { + public abstract fun createSpan (Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/ISpan;)Lio/sentry/ISpan; + public abstract fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; + public abstract fun retrieveCurrentSpan (Lio/sentry/IScopes;)Lio/sentry/ISpan; +} + public abstract interface class io/sentry/ITransaction : io/sentry/ISpan { public abstract fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;ZLio/sentry/Hint;)V public abstract fun forceFinish (Lio/sentry/SpanStatus;ZLio/sentry/Hint;)V - public abstract fun getContexts ()Lio/sentry/protocol/Contexts; - public abstract fun getEventId ()Lio/sentry/protocol/SentryId; - public abstract fun getLatestActiveSpan ()Lio/sentry/Span; - public abstract fun getName ()Ljava/lang/String; - public abstract fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision; + public abstract fun getLatestActiveSpan ()Lio/sentry/ISpan; public abstract fun getSpans ()Ljava/util/List; public abstract fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; public abstract fun isProfileSampled ()Ljava/lang/Boolean; - public abstract fun isSampled ()Ljava/lang/Boolean; public abstract fun scheduleFinish ()V - 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; } @@ -1521,16 +1536,26 @@ public final class io/sentry/NoOpScopesStorage : io/sentry/IScopesStorage { public fun set (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; } +public final class io/sentry/NoOpScopesStorage$NoOpScopesLifecycleToken : io/sentry/ISentryLifecycleToken { + public fun close ()V + public static fun getInstance ()Lio/sentry/NoOpScopesStorage$NoOpScopesLifecycleToken; +} + public final class io/sentry/NoOpSpan : io/sentry/ISpan { public fun finish ()V public fun finish (Lio/sentry/SpanStatus;)V public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V + public fun getContexts ()Lio/sentry/protocol/Contexts; public fun getData (Ljava/lang/String;)Ljava/lang/Object; public fun getDescription ()Ljava/lang/String; + public fun getEventId ()Lio/sentry/protocol/SentryId; public fun getFinishDate ()Lio/sentry/SentryDate; public static fun getInstance ()Lio/sentry/NoOpSpan; public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; + public fun getName ()Ljava/lang/String; + public fun getNameSource ()Lio/sentry/protocol/TransactionNameSource; public fun getOperation ()Ljava/lang/String; + public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision; public fun getSpanContext ()Lio/sentry/SpanContext; public fun getStartDate ()Lio/sentry/SentryDate; public fun getStatus ()Lio/sentry/SpanStatus; @@ -1538,10 +1563,15 @@ public final class io/sentry/NoOpSpan : io/sentry/ISpan { public fun getThrowable ()Ljava/lang/Throwable; public fun isFinished ()Z public fun isNoOp ()Z + public fun isSampled ()Ljava/lang/Boolean; + public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; + public fun setContext (Ljava/lang/String;Ljava/lang/Object;)V public fun setData (Ljava/lang/String;Ljava/lang/Object;)V public fun setDescription (Ljava/lang/String;)V public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;)V public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;Lio/sentry/MeasurementUnit;)V + public fun setName (Ljava/lang/String;)V + public fun setName (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V public fun setOperation (Ljava/lang/String;)V public fun setStatus (Lio/sentry/SpanStatus;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V @@ -1569,9 +1599,10 @@ public final class io/sentry/NoOpTransaction : io/sentry/ITransaction { public fun getEventId ()Lio/sentry/protocol/SentryId; public fun getFinishDate ()Lio/sentry/SentryDate; public static fun getInstance ()Lio/sentry/NoOpTransaction; - public fun getLatestActiveSpan ()Lio/sentry/Span; + public fun getLatestActiveSpan ()Lio/sentry/ISpan; public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; public fun getName ()Ljava/lang/String; + public fun getNameSource ()Lio/sentry/protocol/TransactionNameSource; public fun getOperation ()Ljava/lang/String; public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision; public fun getSpanContext ()Lio/sentry/SpanContext; @@ -1585,6 +1616,7 @@ public final class io/sentry/NoOpTransaction : io/sentry/ITransaction { public fun isNoOp ()Z public fun isProfileSampled ()Ljava/lang/Boolean; public fun isSampled ()Ljava/lang/Boolean; + public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; public fun scheduleFinish ()V public fun setContext (Ljava/lang/String;Ljava/lang/Object;)V public fun setData (Ljava/lang/String;Ljava/lang/Object;)V @@ -2683,6 +2715,7 @@ public class io/sentry/SentryOptions { public fun getSessionTrackingIntervalMillis ()J public fun getShutdownTimeout ()J public fun getShutdownTimeoutMillis ()J + public fun getSpanFactory ()Lio/sentry/ISpanFactory; public fun getSpotlightConnectionUrl ()Ljava/lang/String; public fun getSslSocketFactory ()Ljavax/net/ssl/SSLSocketFactory; public fun getTags ()Ljava/util/Map; @@ -2805,6 +2838,7 @@ public class io/sentry/SentryOptions { public fun setSessionTrackingIntervalMillis (J)V public fun setShutdownTimeout (J)V public fun setShutdownTimeoutMillis (J)V + public fun setSpanFactory (Lio/sentry/ISpanFactory;)V public fun setSpotlightConnectionUrl (Ljava/lang/String;)V public fun setSslSocketFactory (Ljavax/net/ssl/SSLSocketFactory;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V @@ -2934,9 +2968,10 @@ public final class io/sentry/SentryTracer : io/sentry/ITransaction { public fun getDescription ()Ljava/lang/String; public fun getEventId ()Lio/sentry/protocol/SentryId; public fun getFinishDate ()Lio/sentry/SentryDate; - public fun getLatestActiveSpan ()Lio/sentry/Span; + public fun getLatestActiveSpan ()Lio/sentry/ISpan; public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; public fun getName ()Ljava/lang/String; + public fun getNameSource ()Lio/sentry/protocol/TransactionNameSource; public fun getOperation ()Ljava/lang/String; public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision; public fun getSpanContext ()Lio/sentry/SpanContext; @@ -2950,6 +2985,7 @@ public final class io/sentry/SentryTracer : io/sentry/ITransaction { public fun isNoOp ()Z public fun isProfileSampled ()Ljava/lang/Boolean; public fun isSampled ()Ljava/lang/Boolean; + public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; public fun scheduleFinish ()V public fun setContext (Ljava/lang/String;Ljava/lang/Object;)V public fun setData (Ljava/lang/String;Ljava/lang/Object;)V @@ -3058,12 +3094,16 @@ public final class io/sentry/Span : io/sentry/ISpan { public fun finish ()V public fun finish (Lio/sentry/SpanStatus;)V public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V + public fun getContexts ()Lio/sentry/protocol/Contexts; public fun getData ()Ljava/util/Map; public fun getData (Ljava/lang/String;)Ljava/lang/Object; public fun getDescription ()Ljava/lang/String; + public fun getEventId ()Lio/sentry/protocol/SentryId; public fun getFinishDate ()Lio/sentry/SentryDate; public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; public fun getMeasurements ()Ljava/util/Map; + public fun getName ()Ljava/lang/String; + public fun getNameSource ()Lio/sentry/protocol/TransactionNameSource; public fun getOperation ()Ljava/lang/String; public fun getParentSpanId ()Lio/sentry/SpanId; public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision; @@ -3079,10 +3119,14 @@ public final class io/sentry/Span : io/sentry/ISpan { public fun isNoOp ()Z public fun isProfileSampled ()Ljava/lang/Boolean; public fun isSampled ()Ljava/lang/Boolean; + public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; + public fun setContext (Ljava/lang/String;Ljava/lang/Object;)V public fun setData (Ljava/lang/String;Ljava/lang/Object;)V public fun setDescription (Ljava/lang/String;)V public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;)V public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;Lio/sentry/MeasurementUnit;)V + public fun setName (Ljava/lang/String;)V + public fun setName (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V public fun setOperation (Ljava/lang/String;)V public fun setStatus (Lio/sentry/SpanStatus;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V @@ -3326,6 +3370,7 @@ public final class io/sentry/TransactionOptions : io/sentry/SpanOptions { public fun getCustomSamplingContext ()Lio/sentry/CustomSamplingContext; public fun getDeadlineTimeout ()Ljava/lang/Long; public fun getIdleTimeout ()Ljava/lang/Long; + public fun getSpanFactory ()Lio/sentry/ISpanFactory; public fun getStartTimestamp ()Lio/sentry/SentryDate; public fun getTransactionFinishedCallback ()Lio/sentry/TransactionFinishedCallback; public fun isAppStartTransaction ()Z @@ -3336,6 +3381,7 @@ public final class io/sentry/TransactionOptions : io/sentry/SpanOptions { public fun setCustomSamplingContext (Lio/sentry/CustomSamplingContext;)V public fun setDeadlineTimeout (Ljava/lang/Long;)V public fun setIdleTimeout (Ljava/lang/Long;)V + public fun setSpanFactory (Lio/sentry/ISpanFactory;)V public fun setStartTimestamp (Lio/sentry/SentryDate;)V public fun setTransactionFinishedCallback (Lio/sentry/TransactionFinishedCallback;)V public fun setWaitForChildren (Z)V diff --git a/sentry/src/main/java/io/sentry/DefaultSpanFactory.java b/sentry/src/main/java/io/sentry/DefaultSpanFactory.java new file mode 100644 index 0000000000..e2d54ff5f7 --- /dev/null +++ b/sentry/src/main/java/io/sentry/DefaultSpanFactory.java @@ -0,0 +1,32 @@ +package io.sentry; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@ApiStatus.Internal +public final class DefaultSpanFactory implements ISpanFactory { + @Override + public @NotNull ITransaction createTransaction( + @NotNull TransactionContext context, + @NotNull IScopes scopes, + @NotNull TransactionOptions transactionOptions, + @Nullable TransactionPerformanceCollector transactionPerformanceCollector) { + return new SentryTracer(context, scopes, transactionOptions, transactionPerformanceCollector); + } + + @Override + public @NotNull ISpan createSpan( + @NotNull String name, + @NotNull IScopes scopes, + @NotNull SpanOptions spanOptions, + @Nullable ISpan parentSpan) { + // TODO [POTEL] forward to SentryTracer.createChild? + return NoOpSpan.getInstance(); + } + + @Override + public @Nullable ISpan retrieveCurrentSpan(IScopes scopes) { + return scopes.getSpan(); + } +} diff --git a/sentry/src/main/java/io/sentry/ISpan.java b/sentry/src/main/java/io/sentry/ISpan.java index 5b251930ae..e54754390f 100644 --- a/sentry/src/main/java/io/sentry/ISpan.java +++ b/sentry/src/main/java/io/sentry/ISpan.java @@ -1,6 +1,9 @@ package io.sentry; import io.sentry.metrics.LocalMetricsAggregator; +import io.sentry.protocol.Contexts; +import io.sentry.protocol.SentryId; +import io.sentry.protocol.TransactionNameSource; import java.util.List; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -263,4 +266,32 @@ ISpan startChild( */ @Nullable LocalMetricsAggregator getLocalMetricsAggregator(); + + void setContext(@NotNull String key, @NotNull Object context); + + @NotNull + Contexts getContexts(); + + void setName(@NotNull String name); + + void setName(@NotNull String name, @NotNull TransactionNameSource nameSource); + + @NotNull + TransactionNameSource getNameSource(); + + // TODO [POTEL] nullable? + @NotNull + String getName(); + + @Nullable + Boolean isSampled(); + + @Nullable + TracesSamplingDecision getSamplingDecision(); + + @NotNull + SentryId getEventId(); + + @NotNull + ISentryLifecycleToken makeCurrent(); } diff --git a/sentry/src/main/java/io/sentry/ISpanFactory.java b/sentry/src/main/java/io/sentry/ISpanFactory.java new file mode 100644 index 0000000000..b89ae5dddf --- /dev/null +++ b/sentry/src/main/java/io/sentry/ISpanFactory.java @@ -0,0 +1,25 @@ +package io.sentry; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@ApiStatus.Internal +public interface ISpanFactory { + @NotNull + ITransaction createTransaction( + @NotNull TransactionContext context, + @NotNull IScopes scopes, + @NotNull TransactionOptions transactionOptions, + @Nullable TransactionPerformanceCollector transactionPerformanceCollector); + + @NotNull + ISpan createSpan( + @NotNull String name, + @NotNull IScopes scopes, + @NotNull SpanOptions spanOptions, + @Nullable ISpan parentSpan); + + @Nullable + ISpan retrieveCurrentSpan(IScopes scopes); +} diff --git a/sentry/src/main/java/io/sentry/ITransaction.java b/sentry/src/main/java/io/sentry/ITransaction.java index a34e491802..645b6e03d7 100644 --- a/sentry/src/main/java/io/sentry/ITransaction.java +++ b/sentry/src/main/java/io/sentry/ITransaction.java @@ -1,7 +1,5 @@ package io.sentry; -import io.sentry.protocol.Contexts; -import io.sentry.protocol.SentryId; import io.sentry.protocol.TransactionNameSource; import java.util.List; import org.jetbrains.annotations.ApiStatus; @@ -11,24 +9,6 @@ public interface ITransaction extends ISpan { - /** - * Sets transaction name. - * - * @param name - transaction name - */ - void setName(@NotNull String name); - - @ApiStatus.Internal - void setName(@NotNull String name, @NotNull TransactionNameSource transactionNameSource); - - /** - * Returns transaction name. - * - * @return transaction name - */ - @NotNull - String getName(); - /** * Returns the source of the transaction name. * @@ -53,14 +33,6 @@ public interface ITransaction extends ISpan { ISpan startChild( @NotNull String operation, @Nullable String description, @Nullable SentryDate timestamp); - /** - * Returns if transaction is sampled. - * - * @return is sampled - */ - @Nullable - Boolean isSampled(); - /** * Returns if the profile of a transaction is sampled. * @@ -69,24 +41,13 @@ ISpan startChild( @Nullable Boolean isProfileSampled(); - @Nullable - TracesSamplingDecision getSamplingDecision(); - /** * Returns the latest span that is not finished. * * @return span or null if not found. */ @Nullable - Span getLatestActiveSpan(); - - /** - * Returns transaction's event id. - * - * @return the event id - */ - @NotNull - SentryId getEventId(); + ISpan getLatestActiveSpan(); /** Schedules when transaction should be automatically finished. */ void scheduleFinish(); @@ -110,11 +71,4 @@ void finish( @Nullable SentryDate timestamp, boolean dropIfNoChildren, @Nullable Hint hint); - - @ApiStatus.Internal - void setContext(@NotNull String key, @NotNull Object context); - - @ApiStatus.Internal - @NotNull - Contexts getContexts(); } diff --git a/sentry/src/main/java/io/sentry/NoOpScopesStorage.java b/sentry/src/main/java/io/sentry/NoOpScopesStorage.java index fa507987ae..dcf4d7c816 100644 --- a/sentry/src/main/java/io/sentry/NoOpScopesStorage.java +++ b/sentry/src/main/java/io/sentry/NoOpScopesStorage.java @@ -24,7 +24,8 @@ public ISentryLifecycleToken set(@Nullable IScopes scopes) { @Override public void close() {} - static final class NoOpScopesLifecycleToken implements ISentryLifecycleToken { + // TODO [POTEL] extract into its own class + public static final class NoOpScopesLifecycleToken implements ISentryLifecycleToken { private static final NoOpScopesLifecycleToken instance = new NoOpScopesLifecycleToken(); diff --git a/sentry/src/main/java/io/sentry/NoOpSpan.java b/sentry/src/main/java/io/sentry/NoOpSpan.java index 2d11f016fb..d616a2d9d3 100644 --- a/sentry/src/main/java/io/sentry/NoOpSpan.java +++ b/sentry/src/main/java/io/sentry/NoOpSpan.java @@ -1,7 +1,9 @@ package io.sentry; import io.sentry.metrics.LocalMetricsAggregator; +import io.sentry.protocol.Contexts; import io.sentry.protocol.SentryId; +import io.sentry.protocol.TransactionNameSource; import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -165,4 +167,48 @@ public boolean isNoOp() { public @Nullable LocalMetricsAggregator getLocalMetricsAggregator() { return null; } + + @Override + public void setContext(@NotNull String key, @NotNull Object context) {} + + @Override + public @NotNull Contexts getContexts() { + return new Contexts(); + } + + @Override + public void setName(@NotNull String name) {} + + @Override + public void setName(@NotNull String name, @NotNull TransactionNameSource nameSource) {} + + @Override + public @NotNull TransactionNameSource getNameSource() { + return TransactionNameSource.CUSTOM; + } + + @Override + public @NotNull String getName() { + return ""; + } + + @Override + public @Nullable Boolean isSampled() { + return null; + } + + @Override + public @Nullable TracesSamplingDecision getSamplingDecision() { + return null; + } + + @Override + public @NotNull SentryId getEventId() { + return SentryId.EMPTY_ID; + } + + @Override + public @NotNull ISentryLifecycleToken makeCurrent() { + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + } } diff --git a/sentry/src/main/java/io/sentry/NoOpTransaction.java b/sentry/src/main/java/io/sentry/NoOpTransaction.java index fedbbb0667..2984af7316 100644 --- a/sentry/src/main/java/io/sentry/NoOpTransaction.java +++ b/sentry/src/main/java/io/sentry/NoOpTransaction.java @@ -27,6 +27,11 @@ public void setName(@NotNull String name) {} @Override public void setName(@NotNull String name, @NotNull TransactionNameSource transactionNameSource) {} + @Override + public @NotNull TransactionNameSource getNameSource() { + return TransactionNameSource.CUSTOM; + } + @Override public @NotNull String getName() { return ""; @@ -90,7 +95,7 @@ public void setName(@NotNull String name, @NotNull TransactionNameSource transac } @Override - public @Nullable Span getLatestActiveSpan() { + public @Nullable ISpan getLatestActiveSpan() { return null; } @@ -99,6 +104,11 @@ public void setName(@NotNull String name, @NotNull TransactionNameSource transac return SentryId.EMPTY_ID; } + @Override + public @NotNull ISentryLifecycleToken makeCurrent() { + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + } + @Override public void scheduleFinish() {} diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index 0f2441a467..cf0503d31c 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -234,7 +234,7 @@ public void setTransaction(final @NotNull String transaction) { public ISpan getSpan() { final ITransaction tx = transaction; if (tx != null) { - final Span span = tx.getLatestActiveSpan(); + final ISpan span = tx.getLatestActiveSpan(); if (span != null) { return span; diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index bd601ec208..a038be000d 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -826,15 +826,17 @@ public void flush(long timeoutMillis) { SentryLevel.WARNING, "Instance is disabled and this 'startTransaction' returns a no-op."); transaction = NoOpTransaction.getInstance(); - } else if (!getOptions().getInstrumenter().equals(transactionContext.getInstrumenter())) { - getOptions() - .getLogger() - .log( - SentryLevel.DEBUG, - "Returning no-op for instrumenter %s as the SDK has been configured to use instrumenter %s", - transactionContext.getInstrumenter(), - getOptions().getInstrumenter()); - transaction = NoOpTransaction.getInstance(); + // } else if (!getOptions().getInstrumenter().equals(transactionContext.getInstrumenter())) + // { + // getOptions() + // .getLogger() + // .log( + // SentryLevel.DEBUG, + // "Returning no-op for instrumenter %s as the SDK has been configured to use + // instrumenter %s", + // transactionContext.getInstrumenter(), + // getOptions().getInstrumenter()); + // transaction = NoOpTransaction.getInstance(); } else if (!getOptions().isTracingEnabled()) { getOptions() .getLogger() @@ -847,9 +849,16 @@ public void flush(long timeoutMillis) { @NotNull TracesSamplingDecision samplingDecision = tracesSampler.sample(samplingContext); transactionContext.setSamplingDecision(samplingDecision); + final @Nullable ISpanFactory maybeSpanFactory = transactionOptions.getSpanFactory(); + final @NotNull ISpanFactory spanFactory = + maybeSpanFactory == null ? getOptions().getSpanFactory() : maybeSpanFactory; + transaction = - new SentryTracer( + spanFactory.createTransaction( transactionContext, this, transactionOptions, transactionPerformanceCollector); + // new SentryTracer( + // transactionContext, this, transactionOptions, + // transactionPerformanceCollector); // The listener is called only if the transaction exists, as the transaction is needed to // stop it @@ -866,7 +875,8 @@ public void flush(long timeoutMillis) { } } if (transactionOptions.isBindToScope()) { - configureScope(scope -> scope.setTransaction(transaction)); + transaction.makeCurrent(); + // configureScope(scope -> scope.setTransaction(transaction)); } return transaction; } diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 2842845ca6..34eb7f87fa 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -1021,7 +1021,10 @@ public static void endSession() { if (globalHubMode && Platform.isAndroid()) { return getCurrentScopes().getTransaction(); } else { - return getCurrentScopes().getSpan(); + return getCurrentScopes() + .getOptions() + .getSpanFactory() + .retrieveCurrentSpan(getCurrentScopes()); } } diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index c3aca9742d..386bf6fee1 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -470,6 +470,8 @@ public class SentryOptions { private @Nullable BeforeEmitMetricCallback beforeEmitMetricCallback = null; + private @NotNull ISpanFactory spanFactory = new DefaultSpanFactory(); + /** * Profiling traces rate. 101 hz means 101 traces in 1 second. Defaults to 101 to avoid possible * lockstep sampling. More on @@ -2681,6 +2683,15 @@ private void addPackageInfo() { .addPackage("maven:io.sentry:sentry", BuildConfig.VERSION_NAME); } + public @NotNull ISpanFactory getSpanFactory() { + // TODO [POTEL] use a util for checking if OTel is active or similar + return spanFactory; + } + + public void setSpanFactory(final @NotNull ISpanFactory spanFactory) { + this.spanFactory = spanFactory; + } + public static final class Proxy { private @Nullable String host; private @Nullable String port; diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index 2a45ba968f..32a2a94df4 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -821,6 +821,11 @@ public void setName(@NotNull String name, @NotNull TransactionNameSource transac this.transactionNameSource = transactionNameSource; } + @Override + public @NotNull TransactionNameSource getNameSource() { + return getTransactionNameSource(); + } + @Override public @NotNull String getName() { return this.name; @@ -837,7 +842,7 @@ public void setName(@NotNull String name, @NotNull TransactionNameSource transac } @Override - public @Nullable Span getLatestActiveSpan() { + public @Nullable ISpan getLatestActiveSpan() { final List spans = new ArrayList<>(this.children); if (!spans.isEmpty()) { for (int i = spans.size() - 1; i >= 0; i--) { @@ -854,6 +859,17 @@ public void setName(@NotNull String name, @NotNull TransactionNameSource transac return eventId; } + @Override + public @NotNull ISentryLifecycleToken makeCurrent() { + scopes.configureScope( + (scope) -> { + scope.setTransaction(this); + }); + + // TODO [POTEL] can we return an actual token here + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + } + @NotNull Span getRoot() { return root; diff --git a/sentry/src/main/java/io/sentry/Span.java b/sentry/src/main/java/io/sentry/Span.java index 1c9d180b29..79f4c28c41 100644 --- a/sentry/src/main/java/io/sentry/Span.java +++ b/sentry/src/main/java/io/sentry/Span.java @@ -1,8 +1,10 @@ package io.sentry; import io.sentry.metrics.LocalMetricsAggregator; +import io.sentry.protocol.Contexts; import io.sentry.protocol.MeasurementValue; import io.sentry.protocol.SentryId; +import io.sentry.protocol.TransactionNameSource; import io.sentry.util.LazyEvaluator; import io.sentry.util.Objects; import java.util.ArrayList; @@ -46,6 +48,8 @@ public final class Span implements ISpan { private final @NotNull Map data = new ConcurrentHashMap<>(); private final @NotNull Map measurements = new ConcurrentHashMap<>(); + private final @NotNull Contexts contexts = new Contexts(); + @SuppressWarnings("Convert2MethodRef") // older AGP versions do not support method references private final @NotNull LazyEvaluator metricsAggregator = new LazyEvaluator<>(() -> new LocalMetricsAggregator()); @@ -291,6 +295,7 @@ public boolean isFinished() { return data; } + @Override public @Nullable Boolean isSampled() { return context.getSampled(); } @@ -299,10 +304,17 @@ public boolean isFinished() { return context.getProfileSampled(); } + @Override public @Nullable TracesSamplingDecision getSamplingDecision() { return context.getSamplingDecision(); } + @Override + public @NotNull SentryId getEventId() { + // TODO [POTEL] + return new SentryId(); + } + @Override public void setThrowable(final @Nullable Throwable throwable) { this.throwable = throwable; @@ -407,6 +419,38 @@ public boolean isNoOp() { return metricsAggregator.getValue(); } + @Override + public void setContext(@NotNull String key, @NotNull Object context) { + this.contexts.put(key, context); + } + + @Override + public @NotNull Contexts getContexts() { + return contexts; + } + + @Override + public void setName(@NotNull String name) { + // TODO [POTEL] + } + + @Override + public void setName(@NotNull String name, @NotNull TransactionNameSource nameSource) { + // TODO [POTEL] + } + + @Override + public @NotNull TransactionNameSource getNameSource() { + // TODO [POTEL] + return TransactionNameSource.CUSTOM; + } + + @Override + public @NotNull String getName() { + // TODO [POTEL] + return getOperation(); + } + void setSpanFinishedCallback(final @Nullable SpanFinishedCallback callback) { this.spanFinishedCallback = callback; } @@ -433,4 +477,9 @@ private List getDirectChildren() { } return children; } + + @Override + public @NotNull ISentryLifecycleToken makeCurrent() { + return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + } } diff --git a/sentry/src/main/java/io/sentry/TransactionOptions.java b/sentry/src/main/java/io/sentry/TransactionOptions.java index 6d0eac8b7b..f3301c53e7 100644 --- a/sentry/src/main/java/io/sentry/TransactionOptions.java +++ b/sentry/src/main/java/io/sentry/TransactionOptions.java @@ -1,6 +1,7 @@ package io.sentry; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** Sentry Transaction options */ @@ -56,6 +57,9 @@ public final class TransactionOptions extends SpanOptions { */ private @Nullable TransactionFinishedCallback transactionFinishedCallback = null; + /** Span factory to use. Uses factory configured in {@link SentryOptions} if `null`. */ + @ApiStatus.Internal private @Nullable ISpanFactory spanFactory = null; + /** * Gets the customSamplingContext * @@ -196,4 +200,14 @@ public void setAppStartTransaction(final boolean appStartTransaction) { public boolean isAppStartTransaction() { return isAppStartTransaction; } + + @ApiStatus.Internal + public @Nullable ISpanFactory getSpanFactory() { + return this.spanFactory; + } + + @ApiStatus.Internal + public void setSpanFactory(final @NotNull ISpanFactory spanFactory) { + this.spanFactory = spanFactory; + } } From f6bd820207b4b383276237f72a9d1d016dfcb0d2 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 18 Jun 2024 07:24:31 +0200 Subject: [PATCH 56/91] POTEL 4 - Deduplicate `SpanInfo` extraction (#3423) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction --- .../api/sentry-opentelemetry-core.api | 1 - .../opentelemetry/SentrySpanProcessor.java | 4 +- .../SpanDescriptionExtractor.java | 84 ++++++++++++++++--- 3 files changed, 74 insertions(+), 15 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api index 834c593723..88a7c23530 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api +++ b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api @@ -54,7 +54,6 @@ public final class io/sentry/opentelemetry/SentrySpanProcessor : io/opentelemetr public final class io/sentry/opentelemetry/SpanDescriptionExtractor { public fun ()V - public fun extractSpanDescription (Lio/opentelemetry/sdk/trace/ReadableSpan;)Lio/sentry/opentelemetry/OtelSpanInfo; public fun extractSpanInfo (Lio/opentelemetry/sdk/trace/data/SpanData;)Lio/sentry/opentelemetry/OtelSpanInfo; } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java index 6b7797153b..9e78927980 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java @@ -282,7 +282,7 @@ private boolean isSentryRequest(final @NotNull ReadableSpan otelSpan) { private void updateTransactionWithOtelData( final @NotNull ITransaction sentryTransaction, final @NotNull ReadableSpan otelSpan) { final @NotNull OtelSpanInfo otelSpanInfo = - spanDescriptionExtractor.extractSpanDescription(otelSpan); + spanDescriptionExtractor.extractSpanInfo(otelSpan.toSpanData()); sentryTransaction.setOperation(otelSpanInfo.getOp()); sentryTransaction.setName( otelSpanInfo.getDescription(), otelSpanInfo.getTransactionNameSource()); @@ -317,7 +317,7 @@ private void updateSpanWithOtelData( }); final @NotNull OtelSpanInfo otelSpanInfo = - spanDescriptionExtractor.extractSpanDescription(otelSpan); + spanDescriptionExtractor.extractSpanInfo(otelSpan.toSpanData()); sentrySpan.setOperation(otelSpanInfo.getOp()); sentrySpan.setDescription(otelSpanInfo.getDescription()); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java index bdfc9c81ce..d1c48cc8cd 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java @@ -2,7 +2,6 @@ import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.sdk.trace.ReadableSpan; import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.semconv.SemanticAttributes; import io.sentry.protocol.TransactionNameSource; @@ -17,15 +16,50 @@ public final class SpanDescriptionExtractor { // TODO [POTEL] remove these method overloads and pass in SpanData instead (span.toSpanData()) @SuppressWarnings("deprecation") - public @NotNull OtelSpanInfo extractSpanDescription(final @NotNull ReadableSpan otelSpan) { + public @NotNull OtelSpanInfo extractSpanInfo(final @NotNull SpanData otelSpan) { + OtelSpanInfo spanInfo = extractSpanDescription(otelSpan); + + final @Nullable Long threadId = otelSpan.getAttributes().get(SemanticAttributes.THREAD_ID); + if (threadId != null) { + spanInfo.addDataField("thread.id", threadId); + } + + final @Nullable String threadName = + otelSpan.getAttributes().get(SemanticAttributes.THREAD_NAME); + if (threadName != null) { + spanInfo.addDataField("thread.name", threadName); + } + + final @Nullable String dbSystem = otelSpan.getAttributes().get(SemanticAttributes.DB_SYSTEM); + if (dbSystem != null) { + spanInfo.addDataField("db.system", dbSystem); + } + + final @Nullable String dbName = otelSpan.getAttributes().get(SemanticAttributes.DB_NAME); + if (dbName != null) { + spanInfo.addDataField("db.name", dbName); + } + + return spanInfo; + } + + @SuppressWarnings("deprecation") + private OtelSpanInfo extractSpanDescription(SpanData otelSpan) { final @NotNull String name = otelSpan.getName(); + final @NotNull Attributes attributes = otelSpan.getAttributes(); - final @Nullable String httpMethod = otelSpan.getAttribute(SemanticAttributes.HTTP_METHOD); + final @Nullable String httpMethod = attributes.get(SemanticAttributes.HTTP_METHOD); if (httpMethod != null) { return descriptionForHttpMethod(otelSpan, httpMethod); } - final @Nullable String dbSystem = otelSpan.getAttribute(SemanticAttributes.DB_SYSTEM); + final @Nullable String httpRequestMethod = + attributes.get(SemanticAttributes.HTTP_REQUEST_METHOD); + if (httpRequestMethod != null) { + return descriptionForHttpMethod(otelSpan, httpRequestMethod); + } + + final @Nullable String dbSystem = attributes.get(SemanticAttributes.DB_SYSTEM); if (dbSystem != null) { return descriptionForDbSystem(otelSpan); } @@ -35,35 +69,61 @@ public final class SpanDescriptionExtractor { @SuppressWarnings("deprecation") private OtelSpanInfo descriptionForHttpMethod( - final @NotNull ReadableSpan otelSpan, final @NotNull String httpMethod) { + final @NotNull SpanData otelSpan, final @NotNull String httpMethod) { final @NotNull String name = otelSpan.getName(); final @NotNull SpanKind kind = otelSpan.getKind(); final @NotNull StringBuilder opBuilder = new StringBuilder("http"); + final @NotNull Attributes attributes = otelSpan.getAttributes(); + final @NotNull Map dataFields = new HashMap<>(); + dataFields.put("http.request.method", httpMethod); if (SpanKind.CLIENT.equals(kind)) { opBuilder.append(".client"); } else if (SpanKind.SERVER.equals(kind)) { opBuilder.append(".server"); } - final @Nullable String httpTarget = otelSpan.getAttribute(SemanticAttributes.HTTP_TARGET); - final @Nullable String httpRoute = otelSpan.getAttribute(SemanticAttributes.HTTP_ROUTE); - final @Nullable String httpPath = httpRoute != null ? httpRoute : httpTarget; + final @Nullable String httpTarget = attributes.get(SemanticAttributes.HTTP_TARGET); + final @Nullable String httpRoute = attributes.get(SemanticAttributes.HTTP_ROUTE); + @Nullable String httpPath = httpRoute; + if (httpPath == null) { + httpPath = httpTarget; + } final @NotNull String op = opBuilder.toString(); + final @Nullable Long httpStatusCode = + attributes.get(SemanticAttributes.HTTP_RESPONSE_STATUS_CODE); + if (httpStatusCode != null) { + dataFields.put("http.response.status_code", httpStatusCode); + } + + final @Nullable String serverAddress = attributes.get(SemanticAttributes.SERVER_ADDRESS); + if (serverAddress != null) { + dataFields.put("server.address", serverAddress); + } + + final @Nullable String urlFull = attributes.get(SemanticAttributes.URL_FULL); + if (urlFull != null) { + dataFields.put("url.full", urlFull); + if (httpPath == null) { + httpPath = urlFull; + } + } + if (httpPath == null) { - return new OtelSpanInfo(op, name, TransactionNameSource.CUSTOM); + return new OtelSpanInfo(op, name, TransactionNameSource.CUSTOM, dataFields); } final @NotNull String description = httpMethod + " " + httpPath; final @NotNull TransactionNameSource transactionNameSource = httpRoute != null ? TransactionNameSource.ROUTE : TransactionNameSource.URL; - return new OtelSpanInfo(op, description, transactionNameSource); + return new OtelSpanInfo(op, description, transactionNameSource, dataFields); } @SuppressWarnings("deprecation") - private OtelSpanInfo descriptionForDbSystem(final @NotNull ReadableSpan otelSpan) { - @Nullable String dbStatement = otelSpan.getAttribute(SemanticAttributes.DB_STATEMENT); + private OtelSpanInfo descriptionForDbSystem(final @NotNull SpanData otelSpan) { + final @NotNull Attributes attributes = otelSpan.getAttributes(); + @Nullable String dbStatement = attributes.get(SemanticAttributes.DB_STATEMENT); @NotNull String description = dbStatement != null ? dbStatement : otelSpan.getName(); return new OtelSpanInfo("db", description, TransactionNameSource.TASK); } From 3975e93791f26b8b26da202e93c697d9e952548d Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 18 Jun 2024 07:32:25 +0200 Subject: [PATCH 57/91] POTEL 5 - Start and end time, data, tags etc. now work with Sentry API (#3437) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel --- .../api/sentry-opentelemetry-bootstrap.api | 16 +- .../sentry/opentelemetry/OtelSpanFactory.java | 50 ++- .../sentry/opentelemetry/OtelSpanWrapper.java | 312 +++++++++--------- .../OtelTransactionSpanForwarder.java | 34 +- .../PotelSentrySpanProcessor.java | 14 +- .../opentelemetry/SentrySpanExporter.java | 67 +++- .../SpanDescriptionExtractor.java | 114 +------ .../io/sentry/opentelemetry/SpanNode.java | 2 - sentry/api/sentry.api | 15 +- .../java/io/sentry/DefaultSpanFactory.java | 1 + sentry/src/main/java/io/sentry/ISpan.java | 12 - .../src/main/java/io/sentry/ISpanFactory.java | 1 + sentry/src/main/java/io/sentry/NoOpSpan.java | 16 - .../java/io/sentry/SentryNanotimeDate.java | 2 +- sentry/src/main/java/io/sentry/Span.java | 22 -- .../java/io/sentry/SpanFinishedCallback.java | 4 +- .../src/main/java/io/sentry/SpanOptions.java | 23 ++ .../java/io/sentry/TransactionOptions.java | 21 -- sentry/src/test/java/io/sentry/ScopesTest.kt | 5 + sentry/src/test/java/io/sentry/SentryTest.kt | 8 +- 20 files changed, 346 insertions(+), 393 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api index ac0a83fa46..cff2574792 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api @@ -18,13 +18,13 @@ public final class io/sentry/opentelemetry/OtelContextScopesStorage : io/sentry/ public final class io/sentry/opentelemetry/OtelSpanFactory : io/sentry/ISpanFactory { public fun ()V - public fun createSpan (Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/ISpan;)Lio/sentry/ISpan; + public fun createSpan (Ljava/lang/String;Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/ISpan;)Lio/sentry/ISpan; public fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; public fun retrieveCurrentSpan (Lio/sentry/IScopes;)Lio/sentry/ISpan; } public final class io/sentry/opentelemetry/OtelSpanWrapper : io/sentry/ISpan { - public fun (Lio/opentelemetry/api/trace/Span;Lio/sentry/IScopes;)V + public fun (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/IScopes;Lio/sentry/SentryDate;Lio/opentelemetry/api/trace/Span;)V public fun finish ()V public fun finish (Lio/sentry/SpanStatus;)V public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V @@ -36,8 +36,6 @@ public final class io/sentry/opentelemetry/OtelSpanWrapper : io/sentry/ISpan { public fun getFinishDate ()Lio/sentry/SentryDate; public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; public fun getMeasurements ()Ljava/util/Map; - public fun getName ()Ljava/lang/String; - public fun getNameSource ()Lio/sentry/protocol/TransactionNameSource; public fun getOperation ()Ljava/lang/String; public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision; public fun getScopes ()Lio/sentry/IScopes; @@ -45,8 +43,11 @@ public final class io/sentry/opentelemetry/OtelSpanWrapper : io/sentry/ISpan { public fun getStartDate ()Lio/sentry/SentryDate; public fun getStatus ()Lio/sentry/SpanStatus; public fun getTag (Ljava/lang/String;)Ljava/lang/String; + public fun getTags ()Ljava/util/Map; public fun getThrowable ()Ljava/lang/Throwable; public fun getTraceId ()Lio/sentry/protocol/SentryId; + public fun getTransactionName ()Ljava/lang/String; + public fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; public fun isFinished ()Z public fun isNoOp ()Z public fun isProfileSampled ()Ljava/lang/Boolean; @@ -57,12 +58,12 @@ public final class io/sentry/opentelemetry/OtelSpanWrapper : io/sentry/ISpan { public fun setDescription (Ljava/lang/String;)V public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;)V public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;Lio/sentry/MeasurementUnit;)V - public fun setName (Ljava/lang/String;)V - public fun setName (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V public fun setOperation (Ljava/lang/String;)V public fun setStatus (Lio/sentry/SpanStatus;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V public fun setThrowable (Ljava/lang/Throwable;)V + public fun setTransactionName (Ljava/lang/String;)V + public fun setTransactionName (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)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/Instrumenter;)Lio/sentry/ISpan; @@ -75,7 +76,7 @@ public final class io/sentry/opentelemetry/OtelSpanWrapper : io/sentry/ISpan { } public final class io/sentry/opentelemetry/OtelTransactionSpanForwarder : io/sentry/ITransaction { - public fun (Lio/sentry/ISpan;)V + public fun (Lio/sentry/opentelemetry/OtelSpanWrapper;)V public fun finish ()V public fun finish (Lio/sentry/SpanStatus;)V public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V @@ -89,7 +90,6 @@ public final class io/sentry/opentelemetry/OtelTransactionSpanForwarder : io/sen public fun getLatestActiveSpan ()Lio/sentry/ISpan; public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; public fun getName ()Ljava/lang/String; - public fun getNameSource ()Lio/sentry/protocol/TransactionNameSource; public fun getOperation ()Ljava/lang/String; public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision; public fun getSpanContext ()Lio/sentry/SpanContext; diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java index 3db940c317..0b859c00cc 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java @@ -4,14 +4,19 @@ import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanBuilder; import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.ISpanFactory; import io.sentry.ITransaction; +import io.sentry.NoOpSpan; +import io.sentry.NoOpTransaction; +import io.sentry.SentryDate; import io.sentry.SpanOptions; import io.sentry.TransactionContext; import io.sentry.TransactionOptions; import io.sentry.TransactionPerformanceCollector; +import java.util.concurrent.TimeUnit; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,13 +32,33 @@ public final class OtelSpanFactory implements ISpanFactory { @NotNull IScopes scopes, @NotNull TransactionOptions transactionOptions, @Nullable TransactionPerformanceCollector transactionPerformanceCollector) { - final @NotNull ISpan span = createSpan(context.getName(), scopes, transactionOptions, null); + final @Nullable OtelSpanWrapper span = + createSpanInternal( + context.getName(), context.getDescription(), scopes, transactionOptions, null); + if (span == null) { + return NoOpTransaction.getInstance(); + } return new OtelTransactionSpanForwarder(span); } @Override public @NotNull ISpan createSpan( final @NotNull String name, + final @Nullable String description, + final @NotNull IScopes scopes, + final @NotNull SpanOptions spanOptions, + final @Nullable ISpan parentSpan) { + final @Nullable OtelSpanWrapper span = + createSpanInternal(name, description, scopes, spanOptions, parentSpan); + if (span == null) { + return NoOpSpan.getInstance(); + } + return span; + } + + private @Nullable OtelSpanWrapper createSpanInternal( + final @NotNull String name, + final @Nullable String description, final @NotNull IScopes scopes, final @NotNull SpanOptions spanOptions, final @Nullable ISpan parentSpan) { @@ -46,15 +71,28 @@ public final class OtelSpanFactory implements ISpanFactory { // spanBuilder.setParent() } } - // TODO [POTEL] start timestamp - final @NotNull Span span = spanBuilder.startSpan(); - return new OtelSpanWrapper(span, scopes); + + final @Nullable SentryDate startTimestampFromOptions = spanOptions.getStartTimestamp(); + final @NotNull SentryDate startTimestamp = + startTimestampFromOptions == null + ? scopes.getOptions().getDateProvider().now() + : startTimestampFromOptions; + spanBuilder.setStartTimestamp(startTimestamp.nanoTimestamp(), TimeUnit.NANOSECONDS); + + final @NotNull Span otelSpan = spanBuilder.startSpan(); + final @Nullable OtelSpanWrapper sentrySpan = storage.getSentrySpan(otelSpan.getSpanContext()); + if (sentrySpan != null && description != null) { + sentrySpan.setDescription(description); + } + return sentrySpan; } @Override public @Nullable ISpan retrieveCurrentSpan(IScopes scopes) { - // TODO [POTEL] should we use Context.fromContextOrNull and read span from there? - final @NotNull Span span = Span.current(); + final @Nullable Span span = Span.fromContextOrNull(Context.current()); + if (span == null) { + return null; + } return storage.getSentrySpan(span.getSpanContext()); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java index 89ba53ff42..2edaa88382 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java @@ -2,6 +2,7 @@ import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Scope; +import io.opentelemetry.sdk.trace.ReadWriteSpan; import io.sentry.BaggageHeader; import io.sentry.IScopes; import io.sentry.ISentryLifecycleToken; @@ -9,7 +10,9 @@ import io.sentry.Instrumenter; import io.sentry.MeasurementUnit; import io.sentry.NoOpScopesStorage; +import io.sentry.NoOpSpan; import io.sentry.SentryDate; +import io.sentry.SentryLevel; import io.sentry.SentryTraceHeader; import io.sentry.SpanContext; import io.sentry.SpanId; @@ -22,16 +25,18 @@ import io.sentry.protocol.MeasurementValue; import io.sentry.protocol.SentryId; import io.sentry.protocol.TransactionNameSource; +import io.sentry.util.LazyEvaluator; import io.sentry.util.Objects; import java.lang.ref.WeakReference; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +/** NOTE: This wrapper is not used when using OpenTelemetry API, only when using Sentry API. */ @ApiStatus.Internal public final class OtelSpanWrapper implements ISpan { @@ -39,8 +44,8 @@ public final class OtelSpanWrapper implements ISpan { /** The moment in time when span was started. */ private @NotNull SentryDate startTimestamp; - // TODO [POTEL] Set end timestamp in SpanProcessor, read it in exporter - // private @Nullable SentryDate endTimestamp = null; + + private @Nullable SentryDate finishedTimestamp = null; /** * OpenTelemetry span which this wrapper wraps. Needs to be referenced weakly as otherwise we'd @@ -48,68 +53,44 @@ public final class OtelSpanWrapper implements ISpan { * OtelSpanWrapper} and indirectly back to {@link io.opentelemetry.sdk.trace.data.SpanData} via * {@link Span}. Also see {@link SentryWeakSpanStorage}. */ - private final @NotNull WeakReference span; - // private final @NotNull SpanContext context; + private final @NotNull WeakReference span; + + private final @NotNull SpanContext context; // private final @NotNull SpanOptions options; private final @NotNull Contexts contexts = new Contexts(); - // TODO [POTEL] should be on SpanContext and retrieved from there in ctor here - private @NotNull TransactionNameSource nameSource = TransactionNameSource.CUSTOM; - private @NotNull String name = ""; - - // public OtelSpanWrapper( - // final @NotNull SpanBuilder spanBuilder, - // final @NotNull TransactionContext context, - // final @NotNull IScopes scopes, - // final @Nullable SentryDate startTimestamp, - // final @NotNull SpanOptions options) { - //// this.context = Objects.requireNonNull(context, "context is required"); - //// this.transaction = Objects.requireNonNull(transaction, "transaction is required"); - // this.scopes = Objects.requireNonNull(scopes, "scopes are required"); - // // this.spanFinishedCallback = null; - // if (startTimestamp != null) { - // this.startTimestamp = startTimestamp; - // } else { - // this.startTimestamp = scopes.getOptions().getDateProvider().now(); - // } - // spanBuilder.setStartTimestamp(this.startTimestamp.nanoTimestamp(), TimeUnit.NANOSECONDS); - // spanBuilder.setNoParent(); - // // this.options = options; - // this.span = new WeakReference<>(spanBuilder.startSpan()); - // } - - public OtelSpanWrapper(final @NotNull Span span, final @NotNull IScopes scopes) { + private @Nullable String transactionName; + private @Nullable TransactionNameSource transactionNameSource; + + // TODO [POTEL] + // private @Nullable SpanFinishedCallback spanFinishedCallback; + + private final @NotNull Map data = new ConcurrentHashMap<>(); + private final @NotNull Map measurements = new ConcurrentHashMap<>(); + + @SuppressWarnings("Convert2MethodRef") // older AGP versions do not support method references + private final @NotNull LazyEvaluator metricsAggregator = + new LazyEvaluator<>(() -> new LocalMetricsAggregator()); + + /** A throwable thrown during the execution of the span. */ + private @Nullable Throwable throwable; + + public OtelSpanWrapper( + final @NotNull ReadWriteSpan span, + final @NotNull IScopes scopes, + final @NotNull SentryDate startTimestamp, + final @Nullable Span parentSpan) { this.scopes = Objects.requireNonNull(scopes, "scopes are required"); this.span = new WeakReference<>(span); - // TODO [POTEL] how could we make this work? - this.startTimestamp = scopes.getOptions().getDateProvider().now(); - } - - // OtelSpanWrapper( - // final @NotNull SpanBuilder spanBuilder, - // final @NotNull SentryId traceId, - // final @Nullable SpanId parentSpanId, - // final @NotNull String operation, - // final @NotNull IScopes scopes, - // final @Nullable SentryDate startTimestamp, - // final @NotNull SpanOptions options - // /*final @Nullable SpanFinishedCallback spanFinishedCallback*/ ) { - // this.scopes = Objects.requireNonNull(scopes, "scopes are required"); - //// this.context = - //// new SpanContext( - //// traceId, new SpanId(), operation, parentSpanId, - // transaction.getSamplingDecision()); - //// this.transaction = Objects.requireNonNull(transaction, "transaction is required"); - // Objects.requireNonNull(scopes, "Scopes are required"); - // // this.options = options; - // // this.spanFinishedCallback = spanFinishedCallback; - // if (startTimestamp != null) { - // this.startTimestamp = startTimestamp; - // } else { - // this.startTimestamp = scopes.getOptions().getDateProvider().now(); - // } - // spanBuilder.setStartTimestamp(this.startTimestamp.nanoTimestamp(), TimeUnit.NANOSECONDS); - // this.span = new WeakReference<>(spanBuilder.startSpan()); - // } + this.startTimestamp = startTimestamp; + final @NotNull SentryId traceId = new SentryId(span.getSpanContext().getTraceId()); + final @NotNull SpanId spanId = new SpanId(span.getSpanContext().getSpanId()); + final @Nullable SpanId parentSpanId = + parentSpan == null ? null : new SpanId(parentSpan.getSpanContext().getSpanId()); + @NotNull String operation = span.getName(); + + // TODO [POTEL] tracesSamplingDecision + this.context = new SpanContext(traceId, spanId, operation, parentSpanId, null); + } @Override public @NotNull ISpan startChild(@NotNull String operation) { @@ -119,10 +100,14 @@ public OtelSpanWrapper(final @NotNull Span span, final @NotNull IScopes scopes) @Override public @NotNull ISpan startChild( @NotNull String operation, @Nullable String description, @NotNull SpanOptions spanOptions) { - // TODO [POTEL] check finished - // return transaction.startChild(context.getSpanId(), operation, description, spanOptions); - // TODO [POTEL] use description - return scopes.getOptions().getSpanFactory().createSpan(operation, scopes, spanOptions, this); + if (isFinished()) { + return NoOpSpan.getInstance(); + } + + return scopes + .getOptions() + .getSpanFactory() + .createSpan(operation, description, scopes, spanOptions, this); } @Override @@ -141,21 +126,31 @@ public OtelSpanWrapper(final @NotNull Span span, final @NotNull IScopes scopes) @Nullable SentryDate timestamp, @NotNull Instrumenter instrumenter, @NotNull SpanOptions spanOptions) { - // TODO [POTEL] check finished - // return transaction.startChild( - // context.getSpanId(), operation, description, timestamp, instrumenter, spanOptions); - // TODO [POTEL] use description, timestamp, instrumenter - return scopes.getOptions().getSpanFactory().createSpan(operation, scopes, spanOptions, this); + if (isFinished()) { + return NoOpSpan.getInstance(); + } + + if (timestamp != null) { + spanOptions.setStartTimestamp(timestamp); + } + + // TODO [POTEL] use instrumenter + return scopes + .getOptions() + .getSpanFactory() + .createSpan(operation, description, scopes, spanOptions, this); } @Override public @NotNull ISpan startChild(@NotNull String operation, @Nullable String description) { - // TODO [POTEL] check finished - // return transaction.startChild(context.getSpanId(), operation, description); + if (isFinished()) { + return NoOpSpan.getInstance(); + } + return scopes .getOptions() .getSpanFactory() - .createSpan(operation, scopes, new SpanOptions(), this); + .createSpan(operation, description, scopes, new SpanOptions(), this); } @Override @@ -164,15 +159,10 @@ public OtelSpanWrapper(final @NotNull Span span, final @NotNull IScopes scopes) } private @NotNull SpanId getOtelSpanId() { - final @Nullable Span otelSpan = getSpan(); - if (otelSpan != null) { - return new SpanId(otelSpan.getSpanContext().getSpanId()); - } else { - return SpanId.EMPTY_ID; - } + return context.getSpanId(); } - private @Nullable Span getSpan() { + private @Nullable ReadWriteSpan getSpan() { return span.get(); } @@ -192,9 +182,7 @@ public OtelSpanWrapper(final @NotNull Span span, final @NotNull IScopes scopes) @Override public void finish() { - // finish(this.context.getStatus()); - // TODO [POTEL] - finish(SpanStatus.OK); + finish(getStatus()); } @Override @@ -220,102 +208,140 @@ public void finish(@Nullable SpanStatus status, @Nullable SentryDate timestamp) } @Override - public void setOperation(@NotNull String operation) {} + public void setOperation(@NotNull String operation) { + this.context.setOperation(operation); + } @Override public @NotNull String getOperation() { - // TODO [POTEL] - return ""; + return context.getOperation(); } @Override public void setDescription(@Nullable String description) { - // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter - // ^ could go in span attributes + this.context.setDescription(description); } @Override public @Nullable String getDescription() { - // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter - return null; + return this.context.getDescription(); } @Override public void setStatus(@Nullable SpanStatus status) { // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter // ^ could go in span attributes - // this.context.setStatus(status); + this.context.setStatus(status); } @Override public @Nullable SpanStatus getStatus() { // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter - // return context.getStatus(); - return null; + return context.getStatus(); } @Override public void setThrowable(@Nullable Throwable throwable) { - // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter + this.throwable = throwable; } @Override public @Nullable Throwable getThrowable() { - // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter - return null; + return throwable; } @Override public @NotNull SpanContext getSpanContext() { - // TODO [POTEL] usage outside: setSampled, setOrigin, getTraceId, contexts.setTrace(), status, - // getOrigin - // TODO [POTEL] op, util for spanid, parentSpanId - return new SpanContext(getTraceId(), getOtelSpanId(), "TODO op", null, getSamplingDecision()); + return context; } @Override public void setTag(@NotNull String key, @NotNull String value) { - // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter - // context.setTag(key, value); + context.setTag(key, value); } @Override public @Nullable String getTag(@NotNull String key) { - // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter - // return context.getTags().get(key); - return null; + return context.getTags().get(key); + } + + @ApiStatus.Internal + public @NotNull Map getTags() { + return context.getTags(); } @Override public boolean isFinished() { - // TODO [POTEL] find a way to check - return false; + final @Nullable ReadWriteSpan otelSpan = getSpan(); + if (otelSpan != null) { + return otelSpan.hasEnded(); + } + + // if span is no longer available we consider it ended/finished + return true; } @Override public void setData(@NotNull String key, @NotNull Object value) { - // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter + data.put(key, value); } @Override public @Nullable Object getData(@NotNull String key) { - // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter - return null; + return data.get(key); } @Override public void setMeasurement(@NotNull String name, @NotNull Number value) { - // TODO [POTEL] + if (isFinished()) { + scopes + .getOptions() + .getLogger() + .log( + SentryLevel.DEBUG, + "The span is already finished. Measurement %s cannot be set", + name); + return; + } + this.measurements.put(name, new MeasurementValue(value, null)); + + // TODO [POTEL] can't set on transaction + // We set the measurement in the transaction, too, but we have to check if this is the root span + // of the transaction, to avoid an infinite recursion + // if (transaction.getRoot() != this) { + // transaction.setMeasurementFromChild(name, value); + // } } @Override public void setMeasurement( @NotNull String name, @NotNull Number value, @NotNull MeasurementUnit unit) { - // TODO [POTEL] + if (isFinished()) { + scopes + .getOptions() + .getLogger() + .log( + SentryLevel.DEBUG, + "The span is already finished. Measurement %s cannot be set", + name); + return; + } + this.measurements.put(name, new MeasurementValue(value, unit.apiName())); + + // TODO [POTEL] can't set on transaction + // We set the measurement in the transaction, too, but we have to check if this is the root span + // of the transaction, to avoid an infinite recursion + // if (transaction.getRoot() != this) { + // transaction.setMeasurementFromChild(name, value, unit); + // } } @Override public boolean updateEndDate(@NotNull SentryDate date) { + if (this.finishedTimestamp != null) { + this.finishedTimestamp = date; + return true; + } return false; } @@ -326,8 +352,7 @@ public boolean updateEndDate(@NotNull SentryDate date) { @Override public @Nullable SentryDate getFinishDate() { - // TODO [POTEL] cannot access spandata.getEndEpochNanos - return null; + return finishedTimestamp; } @Override @@ -337,7 +362,7 @@ public boolean isNoOp() { @Override public @Nullable LocalMetricsAggregator getLocalMetricsAggregator() { - return null; + return metricsAggregator.getValue(); } @Override @@ -351,74 +376,51 @@ public void setContext(@NotNull String key, @NotNull Object context) { return contexts; } - @Override - public void setName(@NotNull String name) { - setName(name, TransactionNameSource.CUSTOM); + public void setTransactionName(@NotNull String name) { + setTransactionName(name, TransactionNameSource.CUSTOM); } - @Override - public void setName(@NotNull String name, @NotNull TransactionNameSource nameSource) { - this.name = name; - this.nameSource = nameSource; + public void setTransactionName(@NotNull String name, @NotNull TransactionNameSource nameSource) { + this.transactionName = name; + this.transactionNameSource = nameSource; } - @Override - public @NotNull TransactionNameSource getNameSource() { - return nameSource; + @ApiStatus.Internal + public @Nullable TransactionNameSource getTransactionNameSource() { + return transactionNameSource; } - @Override - public @NotNull String getName() { - return this.name; + @ApiStatus.Internal + public @Nullable String getTransactionName() { + return this.transactionName; } @NotNull public SentryId getTraceId() { - final @Nullable Span otelSpan = getSpan(); - if (otelSpan != null) { - return new SentryId(otelSpan.getSpanContext().getTraceId()); - } else { - return SentryId.EMPTY_ID; - } + return context.getTraceId(); } public @NotNull Map getData() { - // return data; - // TODO [POTEL] - return new HashMap<>(); + return data; } @NotNull public Map getMeasurements() { - // return measurements; - // TODO [POTEL] - return new HashMap<>(); + return measurements; } @Override public @Nullable Boolean isSampled() { - final @Nullable Span otelSpan = getSpan(); - if (otelSpan != null) { - return otelSpan.getSpanContext().isSampled(); - } - return null; + return context.getSampled(); } public @Nullable Boolean isProfileSampled() { - // we do not support profiling for OpenTelemetry yet - return false; + return context.getProfileSampled(); } @Override public @Nullable TracesSamplingDecision getSamplingDecision() { - // TODO [POTEL] - - final @Nullable Span otelSpan = getSpan(); - if (otelSpan != null) { - return new TracesSamplingDecision(otelSpan.getSpanContext().isSampled()); - } - - return null; + return context.getSamplingDecision(); } @Override diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java index 25129db7e3..137bb2cdc1 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java @@ -28,9 +28,9 @@ @ApiStatus.Internal public final class OtelTransactionSpanForwarder implements ITransaction { - private final @NotNull ISpan rootSpan; + private final @NotNull OtelSpanWrapper rootSpan; - public OtelTransactionSpanForwarder(final @NotNull ISpan rootSpan) { + public OtelTransactionSpanForwarder(final @NotNull OtelSpanWrapper rootSpan) { this.rootSpan = Objects.requireNonNull(rootSpan, "root span is required"); } @@ -61,9 +61,7 @@ public OtelTransactionSpanForwarder(final @NotNull ISpan rootSpan) { @Nullable SentryDate timestamp, @NotNull Instrumenter instrumenter, @NotNull SpanOptions spanOptions) { - // TODO [POTEL] - // return rootSpan.startChild(operation, description, timestamp, spanOptions); - return rootSpan.startChild(operation, description, timestamp, Instrumenter.SENTRY); + return rootSpan.startChild(operation, description, timestamp, instrumenter, spanOptions); } @Override @@ -213,7 +211,11 @@ public boolean isNoOp() { @Override public @NotNull TransactionNameSource getTransactionNameSource() { - return rootSpan.getNameSource(); + final @Nullable TransactionNameSource nameSource = rootSpan.getTransactionNameSource(); + if (nameSource == null) { + return TransactionNameSource.CUSTOM; + } + return nameSource; } @Override @@ -225,7 +227,6 @@ public boolean isNoOp() { @Override public @NotNull ISpan startChild( @NotNull String operation, @Nullable String description, @Nullable SentryDate timestamp) { - // TODO [POTEL] return rootSpan.startChild(operation, description, timestamp, Instrumenter.SENTRY); } @@ -236,8 +237,7 @@ public boolean isNoOp() { @Override public @Nullable Boolean isProfileSampled() { - // TODO [POTEL] - return null; + return rootSpan.isProfileSampled(); } @Override @@ -284,7 +284,6 @@ public void finish( @Override public void setContext(@NotNull String key, @NotNull Object context) { - // TODO [POTEL] either set on root span or store in global storage or store on scopes // thoughts: // - span would have to save it on global storage too since we can't add complex data to otel // span @@ -300,21 +299,20 @@ public void setContext(@NotNull String key, @NotNull Object context) { @Override public void setName(@NotNull String name) { - rootSpan.setName(name); + rootSpan.setTransactionName(name); } @Override public void setName(@NotNull String name, @NotNull TransactionNameSource nameSource) { - rootSpan.setName(name, nameSource); - } - - @Override - public @NotNull TransactionNameSource getNameSource() { - return rootSpan.getNameSource(); + rootSpan.setTransactionName(name, nameSource); } @Override public @NotNull String getName() { - return rootSpan.getName(); + final @Nullable String name = rootSpan.getTransactionName(); + if (name == null) { + return ""; + } + return name; } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java index 9932065769..e2a5fb75ae 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java @@ -12,7 +12,9 @@ import io.sentry.IScopes; import io.sentry.ScopesAdapter; import io.sentry.Sentry; +import io.sentry.SentryDate; import io.sentry.SentryLevel; +import io.sentry.SentryLongDate; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -45,7 +47,10 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri ? scopesFromContext.forkedCurrentScope("spanprocessor") : Sentry.forkedRootScopes("spanprocessor"); final @NotNull SpanContext spanContext = otelSpan.getSpanContext(); - spanStorage.storeSentrySpan(spanContext, new OtelSpanWrapper(otelSpan, scopes)); + final @NotNull SentryDate startTimestamp = + new SentryLongDate(otelSpan.toSpanData().getStartEpochNanos()); + spanStorage.storeSentrySpan( + spanContext, new OtelSpanWrapper(otelSpan, scopes, startTimestamp, parentSpan)); } @Override @@ -55,6 +60,13 @@ public boolean isStartRequired() { @Override public void onEnd(final @NotNull ReadableSpan spanBeingEnded) { + final @Nullable OtelSpanWrapper sentrySpan = + spanStorage.getSentrySpan(spanBeingEnded.getSpanContext()); + if (sentrySpan != null) { + final @NotNull SentryDate finishDate = + new SentryLongDate(spanBeingEnded.toSpanData().getEndEpochNanos()); + sentrySpan.updateEndDate(finishDate); + } System.out.println("span ended: " + spanBeingEnded.getSpanContext().getSpanId()); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index c0fdc2dcdb..3775eec559 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -28,6 +28,7 @@ import io.sentry.TransactionOptions; import io.sentry.protocol.Contexts; import io.sentry.protocol.SentryId; +import io.sentry.protocol.TransactionNameSource; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; @@ -208,7 +209,7 @@ private List maybeSend(final @NotNull List spans) { private void createAndFinishSpanForOtelSpan( final @NotNull SpanNode spanNode, - final @NotNull ISpan sentrySpan, + final @NotNull ISpan parentSentrySpan, final @NotNull List remaining) { remaining.remove(spanNode); final @Nullable SpanData spanData = spanNode.getSpan(); @@ -216,13 +217,15 @@ private void createAndFinishSpanForOtelSpan( // If this span should be dropped, we still want to create spans for the children of this if (spanData == null) { for (SpanNode childNode : spanNode.getChildren()) { - createAndFinishSpanForOtelSpan(childNode, sentrySpan, remaining); + createAndFinishSpanForOtelSpan(childNode, parentSentrySpan, remaining); } return; } final @NotNull String spanId = spanData.getSpanId(); final @NotNull OtelSpanInfo spanInfo = spanDescriptionExtractor.extractSpanInfo(spanData); + final @Nullable OtelSpanWrapper sentrySpanMaybe = + spanStorage.getSentrySpan(spanData.getSpanContext()); // TODO attributes // TODO cleanup sentry attributes @@ -236,8 +239,9 @@ private void createAndFinishSpanForOtelSpan( spanData.getTraceId(), spanData.getParentSpanId()); final @NotNull SentryDate startDate = new SentryLongDate(spanData.getStartEpochNanos()); + // TODO [POTEL] op and description might have been overriden final @NotNull ISpan sentryChildSpan = - sentrySpan.startChild( + parentSentrySpan.startChild( spanInfo.getOp(), spanInfo.getDescription(), startDate, Instrumenter.OTEL); sentryChildSpan.getSpanContext().setOrigin(TRACE_ORIGN); @@ -245,6 +249,8 @@ private void createAndFinishSpanForOtelSpan( sentryChildSpan.setData(dataField.getKey(), dataField.getValue()); } + transferSpanDetails(sentrySpanMaybe, sentryChildSpan); + for (SpanNode childNode : spanNode.getChildren()) { createAndFinishSpanForOtelSpan(childNode, sentryChildSpan, remaining); } @@ -253,6 +259,35 @@ private void createAndFinishSpanForOtelSpan( mapOtelStatus(spanData), new SentryLongDate(spanData.getEndEpochNanos())); } + private void transferSpanDetails( + final @Nullable OtelSpanWrapper sourceSpanMaybe, final @NotNull ISpan targetSpan) { + if (sourceSpanMaybe != null) { + final @NotNull OtelSpanWrapper sourceSpan = sourceSpanMaybe; + + final @NotNull Contexts contexts = sourceSpan.getContexts(); + targetSpan.getContexts().putAll(contexts); + + final @NotNull Map data = sourceSpan.getData(); + for (Map.Entry entry : data.entrySet()) { + targetSpan.setData(entry.getKey(), entry.getValue()); + } + + // TODO [POTEL] this is not an OtelSpanWrapper since it's created with default span factory + // if (sentryChildSpan instanceof OtelSpanWrapper) { + // final @NotNull OtelSpanWrapper sentryChildSpanWrapper = (OtelSpanWrapper) + // sentryChildSpan; + // final @NotNull Map measurements = + // sentrySpan.getMeasurements(); + // sentryChildSpanWrapper.addAllMeasurements(measurements); + // } + + final @NotNull Map tags = sourceSpan.getTags(); + for (Map.Entry entry : tags.entrySet()) { + targetSpan.setTag(entry.getKey(), entry.getValue()); + } + } + } + private @Nullable ITransaction createTransactionForOtelSpan(final @NotNull SpanData span) { final @NotNull String spanId = span.getSpanId(); final @NotNull String traceId = span.getTraceId(); @@ -287,6 +322,22 @@ private void createAndFinishSpanForOtelSpan( // TODO parentSpanId, parentSamplingDecision, baggage + @NotNull String transactionName = spanInfo.getDescription(); + @NotNull TransactionNameSource transactionNameSource = spanInfo.getTransactionNameSource(); + + if (sentrySpanMaybe != null) { + final @NotNull OtelSpanWrapper sentrySpan = sentrySpanMaybe; + final @Nullable String transactionNameMaybe = sentrySpan.getTransactionName(); + if (transactionNameMaybe != null) { + transactionName = transactionNameMaybe; + } + final @Nullable TransactionNameSource transactionNameSourceMaybe = + sentrySpan.getTransactionNameSource(); + if (transactionNameSourceMaybe != null) { + transactionNameSource = transactionNameSourceMaybe; + } + } + final @NotNull TransactionContext transactionContext = new TransactionContext(new SentryId(traceId), sentrySpanId, null, null, null); // traceData.getSentryTraceHeader() == null @@ -296,8 +347,8 @@ private void createAndFinishSpanForOtelSpan( // PropagationContext.fromHeaders( // traceData.getSentryTraceHeader(), traceData.getBaggage(), spanId)); - transactionContext.setName(spanInfo.getDescription()); - transactionContext.setTransactionNameSource(spanInfo.getTransactionNameSource()); + transactionContext.setName(transactionName); + transactionContext.setTransactionNameSource(transactionNameSource); transactionContext.setOperation(spanInfo.getOp()); transactionContext.setInstrumenter(Instrumenter.OTEL); @@ -316,11 +367,7 @@ private void createAndFinishSpanForOtelSpan( sentryTransaction.setData(dataField.getKey(), dataField.getValue()); } - if (sentrySpanMaybe != null) { - final @NotNull ISpan sentrySpan = sentrySpanMaybe; - final @NotNull Contexts contexts = sentrySpan.getContexts(); - sentryTransaction.getContexts().putAll(contexts); - } + transferSpanDetails(sentrySpanMaybe, sentryTransaction); return sentryTransaction; } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java index d1c48cc8cd..da359bbd25 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java @@ -64,119 +64,7 @@ private OtelSpanInfo extractSpanDescription(SpanData otelSpan) { return descriptionForDbSystem(otelSpan); } - return new OtelSpanInfo(name, name, TransactionNameSource.CUSTOM); - } - - @SuppressWarnings("deprecation") - private OtelSpanInfo descriptionForHttpMethod( - final @NotNull SpanData otelSpan, final @NotNull String httpMethod) { - final @NotNull String name = otelSpan.getName(); - final @NotNull SpanKind kind = otelSpan.getKind(); - final @NotNull StringBuilder opBuilder = new StringBuilder("http"); - final @NotNull Attributes attributes = otelSpan.getAttributes(); - final @NotNull Map dataFields = new HashMap<>(); - dataFields.put("http.request.method", httpMethod); - - if (SpanKind.CLIENT.equals(kind)) { - opBuilder.append(".client"); - } else if (SpanKind.SERVER.equals(kind)) { - opBuilder.append(".server"); - } - final @Nullable String httpTarget = attributes.get(SemanticAttributes.HTTP_TARGET); - final @Nullable String httpRoute = attributes.get(SemanticAttributes.HTTP_ROUTE); - @Nullable String httpPath = httpRoute; - if (httpPath == null) { - httpPath = httpTarget; - } - final @NotNull String op = opBuilder.toString(); - - final @Nullable Long httpStatusCode = - attributes.get(SemanticAttributes.HTTP_RESPONSE_STATUS_CODE); - if (httpStatusCode != null) { - dataFields.put("http.response.status_code", httpStatusCode); - } - - final @Nullable String serverAddress = attributes.get(SemanticAttributes.SERVER_ADDRESS); - if (serverAddress != null) { - dataFields.put("server.address", serverAddress); - } - - final @Nullable String urlFull = attributes.get(SemanticAttributes.URL_FULL); - if (urlFull != null) { - dataFields.put("url.full", urlFull); - if (httpPath == null) { - httpPath = urlFull; - } - } - - if (httpPath == null) { - return new OtelSpanInfo(op, name, TransactionNameSource.CUSTOM, dataFields); - } - - final @NotNull String description = httpMethod + " " + httpPath; - final @NotNull TransactionNameSource transactionNameSource = - httpRoute != null ? TransactionNameSource.ROUTE : TransactionNameSource.URL; - - return new OtelSpanInfo(op, description, transactionNameSource, dataFields); - } - - @SuppressWarnings("deprecation") - private OtelSpanInfo descriptionForDbSystem(final @NotNull SpanData otelSpan) { - final @NotNull Attributes attributes = otelSpan.getAttributes(); - @Nullable String dbStatement = attributes.get(SemanticAttributes.DB_STATEMENT); - @NotNull String description = dbStatement != null ? dbStatement : otelSpan.getName(); - return new OtelSpanInfo("db", description, TransactionNameSource.TASK); - } - - @SuppressWarnings("deprecation") - public @NotNull OtelSpanInfo extractSpanInfo(final @NotNull SpanData otelSpan) { - OtelSpanInfo spanInfo = extractSpanDescription(otelSpan); - - final @Nullable Long threadId = otelSpan.getAttributes().get(SemanticAttributes.THREAD_ID); - if (threadId != null) { - spanInfo.addDataField("thread.id", threadId); - } - - final @Nullable String threadName = - otelSpan.getAttributes().get(SemanticAttributes.THREAD_NAME); - if (threadName != null) { - spanInfo.addDataField("thread.name", threadName); - } - - final @Nullable String dbSystem = otelSpan.getAttributes().get(SemanticAttributes.DB_SYSTEM); - if (dbSystem != null) { - spanInfo.addDataField("db.system", dbSystem); - } - - final @Nullable String dbName = otelSpan.getAttributes().get(SemanticAttributes.DB_NAME); - if (dbName != null) { - spanInfo.addDataField("db.name", dbName); - } - - return spanInfo; - } - - @SuppressWarnings("deprecation") - private OtelSpanInfo extractSpanDescription(SpanData otelSpan) { - final @NotNull String name = otelSpan.getName(); - final @NotNull Attributes attributes = otelSpan.getAttributes(); - - final @Nullable String httpMethod = attributes.get(SemanticAttributes.HTTP_METHOD); - if (httpMethod != null) { - return descriptionForHttpMethod(otelSpan, httpMethod); - } - - final @Nullable String httpRequestMethod = - attributes.get(SemanticAttributes.HTTP_REQUEST_METHOD); - if (httpRequestMethod != null) { - return descriptionForHttpMethod(otelSpan, httpRequestMethod); - } - - final @Nullable String dbSystem = attributes.get(SemanticAttributes.DB_SYSTEM); - if (dbSystem != null) { - return descriptionForDbSystem(otelSpan); - } - + // TODO [POTEL] use sentry span description if available return new OtelSpanInfo(name, name, TransactionNameSource.CUSTOM); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanNode.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanNode.java index 7342ec3f2b..3f95e50b2b 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanNode.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanNode.java @@ -8,8 +8,6 @@ public final class SpanNode { private final @NotNull String id; - - // TODO [POTEL] should this be ReadableSpan? if so weak or strong ref? private @Nullable SpanData span; private @Nullable SpanNode parentNode; private @NotNull List children = new CopyOnWriteArrayList<>(); diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 1d803703ee..f8d3f7b2f8 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -367,7 +367,7 @@ public final class io/sentry/DefaultScopesStorage : io/sentry/IScopesStorage { public final class io/sentry/DefaultSpanFactory : io/sentry/ISpanFactory { public fun ()V - public fun createSpan (Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/ISpan;)Lio/sentry/ISpan; + public fun createSpan (Ljava/lang/String;Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/ISpan;)Lio/sentry/ISpan; public fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; public fun retrieveCurrentSpan (Lio/sentry/IScopes;)Lio/sentry/ISpan; } @@ -991,7 +991,7 @@ public abstract interface class io/sentry/ISpan { } public abstract interface class io/sentry/ISpanFactory { - public abstract fun createSpan (Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/ISpan;)Lio/sentry/ISpan; + public abstract fun createSpan (Ljava/lang/String;Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/ISpan;)Lio/sentry/ISpan; public abstract fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; public abstract fun retrieveCurrentSpan (Lio/sentry/IScopes;)Lio/sentry/ISpan; } @@ -1000,10 +1000,13 @@ public abstract interface class io/sentry/ITransaction : io/sentry/ISpan { public abstract fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;ZLio/sentry/Hint;)V public abstract fun forceFinish (Lio/sentry/SpanStatus;ZLio/sentry/Hint;)V public abstract fun getLatestActiveSpan ()Lio/sentry/ISpan; + public abstract fun getName ()Ljava/lang/String; public abstract fun getSpans ()Ljava/util/List; public abstract fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; public abstract fun isProfileSampled ()Ljava/lang/Boolean; public abstract fun scheduleFinish ()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; } @@ -3218,6 +3221,10 @@ public abstract interface class io/sentry/SpanDataConvention { public static final field THREAD_NAME Ljava/lang/String; } +public abstract interface class io/sentry/SpanFinishedCallback { + public abstract fun execute (Lio/sentry/Span;)V +} + public final class io/sentry/SpanId : io/sentry/JsonSerializable { public static final field EMPTY_ID Lio/sentry/SpanId; public fun ()V @@ -3236,10 +3243,12 @@ public final class io/sentry/SpanId$Deserializer : io/sentry/JsonDeserializer { public class io/sentry/SpanOptions { public fun ()V + public fun getStartTimestamp ()Lio/sentry/SentryDate; public fun isIdle ()Z public fun isTrimEnd ()Z public fun isTrimStart ()Z public fun setIdle (Z)V + public fun setStartTimestamp (Lio/sentry/SentryDate;)V public fun setTrimEnd (Z)V public fun setTrimStart (Z)V } @@ -3371,7 +3380,6 @@ public final class io/sentry/TransactionOptions : io/sentry/SpanOptions { public fun getDeadlineTimeout ()Ljava/lang/Long; public fun getIdleTimeout ()Ljava/lang/Long; public fun getSpanFactory ()Lio/sentry/ISpanFactory; - public fun getStartTimestamp ()Lio/sentry/SentryDate; public fun getTransactionFinishedCallback ()Lio/sentry/TransactionFinishedCallback; public fun isAppStartTransaction ()Z public fun isBindToScope ()Z @@ -3382,7 +3390,6 @@ public final class io/sentry/TransactionOptions : io/sentry/SpanOptions { public fun setDeadlineTimeout (Ljava/lang/Long;)V public fun setIdleTimeout (Ljava/lang/Long;)V public fun setSpanFactory (Lio/sentry/ISpanFactory;)V - public fun setStartTimestamp (Lio/sentry/SentryDate;)V public fun setTransactionFinishedCallback (Lio/sentry/TransactionFinishedCallback;)V public fun setWaitForChildren (Z)V } diff --git a/sentry/src/main/java/io/sentry/DefaultSpanFactory.java b/sentry/src/main/java/io/sentry/DefaultSpanFactory.java index e2d54ff5f7..1c8cf42628 100644 --- a/sentry/src/main/java/io/sentry/DefaultSpanFactory.java +++ b/sentry/src/main/java/io/sentry/DefaultSpanFactory.java @@ -18,6 +18,7 @@ public final class DefaultSpanFactory implements ISpanFactory { @Override public @NotNull ISpan createSpan( @NotNull String name, + @Nullable String description, @NotNull IScopes scopes, @NotNull SpanOptions spanOptions, @Nullable ISpan parentSpan) { diff --git a/sentry/src/main/java/io/sentry/ISpan.java b/sentry/src/main/java/io/sentry/ISpan.java index e54754390f..ec1a9699ac 100644 --- a/sentry/src/main/java/io/sentry/ISpan.java +++ b/sentry/src/main/java/io/sentry/ISpan.java @@ -3,7 +3,6 @@ import io.sentry.metrics.LocalMetricsAggregator; import io.sentry.protocol.Contexts; import io.sentry.protocol.SentryId; -import io.sentry.protocol.TransactionNameSource; import java.util.List; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -272,17 +271,6 @@ ISpan startChild( @NotNull Contexts getContexts(); - void setName(@NotNull String name); - - void setName(@NotNull String name, @NotNull TransactionNameSource nameSource); - - @NotNull - TransactionNameSource getNameSource(); - - // TODO [POTEL] nullable? - @NotNull - String getName(); - @Nullable Boolean isSampled(); diff --git a/sentry/src/main/java/io/sentry/ISpanFactory.java b/sentry/src/main/java/io/sentry/ISpanFactory.java index b89ae5dddf..67e12b4caa 100644 --- a/sentry/src/main/java/io/sentry/ISpanFactory.java +++ b/sentry/src/main/java/io/sentry/ISpanFactory.java @@ -16,6 +16,7 @@ ITransaction createTransaction( @NotNull ISpan createSpan( @NotNull String name, + @Nullable String description, @NotNull IScopes scopes, @NotNull SpanOptions spanOptions, @Nullable ISpan parentSpan); diff --git a/sentry/src/main/java/io/sentry/NoOpSpan.java b/sentry/src/main/java/io/sentry/NoOpSpan.java index d616a2d9d3..8af2aecb46 100644 --- a/sentry/src/main/java/io/sentry/NoOpSpan.java +++ b/sentry/src/main/java/io/sentry/NoOpSpan.java @@ -176,22 +176,6 @@ public void setContext(@NotNull String key, @NotNull Object context) {} return new Contexts(); } - @Override - public void setName(@NotNull String name) {} - - @Override - public void setName(@NotNull String name, @NotNull TransactionNameSource nameSource) {} - - @Override - public @NotNull TransactionNameSource getNameSource() { - return TransactionNameSource.CUSTOM; - } - - @Override - public @NotNull String getName() { - return ""; - } - @Override public @Nullable Boolean isSampled() { return null; diff --git a/sentry/src/main/java/io/sentry/SentryNanotimeDate.java b/sentry/src/main/java/io/sentry/SentryNanotimeDate.java index 093b249cb7..2993eeed6c 100644 --- a/sentry/src/main/java/io/sentry/SentryNanotimeDate.java +++ b/sentry/src/main/java/io/sentry/SentryNanotimeDate.java @@ -5,7 +5,7 @@ import org.jetbrains.annotations.Nullable; /** - * Uses {@link Date} in cominbation with System.nanoTime(). + * Uses {@link Date} in combination with System.nanoTime(). * *

    A single date only offers millisecond precision but diff can be calculated with up to * nanosecond precision. This increased precision can also be used to calculate a new end date for a diff --git a/sentry/src/main/java/io/sentry/Span.java b/sentry/src/main/java/io/sentry/Span.java index 79f4c28c41..686857447c 100644 --- a/sentry/src/main/java/io/sentry/Span.java +++ b/sentry/src/main/java/io/sentry/Span.java @@ -429,28 +429,6 @@ public void setContext(@NotNull String key, @NotNull Object context) { return contexts; } - @Override - public void setName(@NotNull String name) { - // TODO [POTEL] - } - - @Override - public void setName(@NotNull String name, @NotNull TransactionNameSource nameSource) { - // TODO [POTEL] - } - - @Override - public @NotNull TransactionNameSource getNameSource() { - // TODO [POTEL] - return TransactionNameSource.CUSTOM; - } - - @Override - public @NotNull String getName() { - // TODO [POTEL] - return getOperation(); - } - void setSpanFinishedCallback(final @Nullable SpanFinishedCallback callback) { this.spanFinishedCallback = callback; } diff --git a/sentry/src/main/java/io/sentry/SpanFinishedCallback.java b/sentry/src/main/java/io/sentry/SpanFinishedCallback.java index 9ce34dc764..55f5a66f0b 100644 --- a/sentry/src/main/java/io/sentry/SpanFinishedCallback.java +++ b/sentry/src/main/java/io/sentry/SpanFinishedCallback.java @@ -1,8 +1,10 @@ package io.sentry; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -interface SpanFinishedCallback { +@ApiStatus.Internal +public interface SpanFinishedCallback { /** * Called when observed span finishes. * diff --git a/sentry/src/main/java/io/sentry/SpanOptions.java b/sentry/src/main/java/io/sentry/SpanOptions.java index 42fc9906a3..086b435b77 100644 --- a/sentry/src/main/java/io/sentry/SpanOptions.java +++ b/sentry/src/main/java/io/sentry/SpanOptions.java @@ -2,11 +2,34 @@ import com.jakewharton.nopen.annotation.Open; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal @Open public class SpanOptions { + /** The start timestamp of the transaction */ + private @Nullable SentryDate startTimestamp = null; + + // TODO [POTEL] this should also work for non OTel spans + /** + * 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; + } + /** * If `trimStart` is true, sets the start timestamp of the transaction to the lowest start * timestamp of child spans. diff --git a/sentry/src/main/java/io/sentry/TransactionOptions.java b/sentry/src/main/java/io/sentry/TransactionOptions.java index f3301c53e7..782e35a039 100644 --- a/sentry/src/main/java/io/sentry/TransactionOptions.java +++ b/sentry/src/main/java/io/sentry/TransactionOptions.java @@ -18,9 +18,6 @@ public final class TransactionOptions extends SpanOptions { /** Defines if transaction should be bound to scope */ private boolean bindToScope = false; - /** The start timestamp of the transaction */ - private @Nullable SentryDate startTimestamp = null; - /** Defines if transaction refers to the app start process */ private boolean isAppStartTransaction = false; @@ -96,24 +93,6 @@ 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; - } - /** * Checks if waitForChildren is enabled * diff --git a/sentry/src/test/java/io/sentry/ScopesTest.kt b/sentry/src/test/java/io/sentry/ScopesTest.kt index 33a0ce90a8..5bd896a59d 100644 --- a/sentry/src/test/java/io/sentry/ScopesTest.kt +++ b/sentry/src/test/java/io/sentry/ScopesTest.kt @@ -38,6 +38,7 @@ import java.util.UUID import java.util.concurrent.atomic.AtomicReference import kotlin.test.AfterTest import kotlin.test.BeforeTest +import kotlin.test.Ignore import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -1046,6 +1047,8 @@ class ScopesTest { assertEquals("test", scope?.transactionName) } + // TODO [POTEL] how do we handle instrumenter? + @Ignore @Test fun `when startTransaction is called with different instrumenter, no-op is returned`() { val scopes = generateScopes() @@ -1057,6 +1060,8 @@ class ScopesTest { assertTrue(tx is NoOpTransaction) } + // TODO [POTEL] how do we handle instrumenter? + @Ignore @Test fun `when startTransaction is called with different instrumenter, no-op is returned 2`() { val scopes = generateScopes() { diff --git a/sentry/src/test/java/io/sentry/SentryTest.kt b/sentry/src/test/java/io/sentry/SentryTest.kt index ebd5e92c2b..2229e18818 100644 --- a/sentry/src/test/java/io/sentry/SentryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTest.kt @@ -945,9 +945,11 @@ class SentryTest { @Test fun `getSpan calls scopes getSpan`() { val scopes = mock() - Sentry.init({ - it.dsn = dsn - }, false) + val options = SentryOptions().also { it.dsn = dsn } + whenever(scopes.options).thenReturn(options) + + Sentry.init(options) + Sentry.setCurrentScopes(scopes) Sentry.getSpan() verify(scopes).span From 504ef52db0a691b5c83050b341d0780afc02babd Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 18 Jun 2024 07:34:45 +0200 Subject: [PATCH 58/91] POTEL 6 - Use OpenTelemetry span status for Sentry span API (#3439) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API --- .../api/sentry-opentelemetry-bootstrap.api | 6 ++ .../sentry/opentelemetry/OtelSpanContext.java | 88 +++++++++++++++++++ .../sentry/opentelemetry/OtelSpanWrapper.java | 16 +--- .../opentelemetry/SentrySpanExporter.java | 19 +++- sentry/api/sentry.api | 2 + .../src/main/java/io/sentry/SpanContext.java | 9 +- .../src/main/java/io/sentry/SpanStatus.java | 17 +++- 7 files changed, 136 insertions(+), 21 deletions(-) create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api index cff2574792..f8abed919e 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api @@ -16,6 +16,12 @@ public final class io/sentry/opentelemetry/OtelContextScopesStorage : io/sentry/ public fun set (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; } +public final class io/sentry/opentelemetry/OtelSpanContext : io/sentry/SpanContext { + public fun (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/opentelemetry/api/trace/Span;)V + public fun getStatus ()Lio/sentry/SpanStatus; + public fun setStatus (Lio/sentry/SpanStatus;)V +} + public final class io/sentry/opentelemetry/OtelSpanFactory : io/sentry/ISpanFactory { public fun ()V public fun createSpan (Ljava/lang/String;Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/ISpan;)Lio/sentry/ISpan; diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java new file mode 100644 index 0000000000..e33bcf061e --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java @@ -0,0 +1,88 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.sdk.trace.ReadWriteSpan; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.sentry.SpanContext; +import io.sentry.SpanId; +import io.sentry.SpanStatus; +import io.sentry.protocol.SentryId; +import java.lang.ref.WeakReference; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class OtelSpanContext extends SpanContext { + + /** + * OpenTelemetry span which this wrapper wraps. Needs to be referenced weakly as otherwise we'd + * create a circular reference from {@link io.opentelemetry.sdk.trace.data.SpanData} to {@link + * OtelSpanWrapper} and indirectly back to {@link io.opentelemetry.sdk.trace.data.SpanData} via + * {@link Span}. Also see {@link SentryWeakSpanStorage}. + */ + private final @NotNull WeakReference span; + + public OtelSpanContext(final @NotNull ReadWriteSpan span, final @Nullable Span parentSpan) { + // TODO [POTEL] tracesSamplingDecision + super( + new SentryId(span.getSpanContext().getTraceId()), + new SpanId(span.getSpanContext().getSpanId()), + parentSpan == null ? null : new SpanId(parentSpan.getSpanContext().getSpanId()), + span.getName(), + null, + null, + null, + null); + this.span = new WeakReference<>(span); + } + + @Override + public @Nullable SpanStatus getStatus() { + final @Nullable ReadWriteSpan otelSpan = span.get(); + + if (otelSpan != null) { + final @NotNull StatusData otelStatus = otelSpan.toSpanData().getStatus(); + final @NotNull String otelStatusDescription = otelStatus.getDescription(); + if (otelStatusDescription.isEmpty()) { + return otelStatusCodeFallback(otelStatus); + } + final @Nullable SpanStatus spanStatus = SpanStatus.fromApiNameSafely(otelStatusDescription); + if (spanStatus == null) { + return otelStatusCodeFallback(otelStatus); + } + return spanStatus; + } + + return null; + } + + @Override + public void setStatus(@Nullable SpanStatus status) { + if (status != null) { + final @Nullable ReadWriteSpan otelSpan = span.get(); + if (otelSpan != null) { + final @NotNull StatusCode statusCode = translateStatusCode(status); + otelSpan.setStatus(statusCode, status.apiName()); + } + } + } + + private @Nullable SpanStatus otelStatusCodeFallback(final @NotNull StatusData otelStatus) { + if (otelStatus.getStatusCode() == StatusCode.ERROR) { + return SpanStatus.UNKNOWN_ERROR; + } else if (otelStatus.getStatusCode() == StatusCode.OK) { + return SpanStatus.OK; + } + return null; + } + + private @NotNull StatusCode translateStatusCode(final @Nullable SpanStatus status) { + if (status == null) { + return StatusCode.UNSET; + } else if (status == SpanStatus.OK) { + return StatusCode.OK; + } else { + return StatusCode.ERROR; + } + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java index 2edaa88382..2c363555c1 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java @@ -82,14 +82,7 @@ public OtelSpanWrapper( this.scopes = Objects.requireNonNull(scopes, "scopes are required"); this.span = new WeakReference<>(span); this.startTimestamp = startTimestamp; - final @NotNull SentryId traceId = new SentryId(span.getSpanContext().getTraceId()); - final @NotNull SpanId spanId = new SpanId(span.getSpanContext().getSpanId()); - final @Nullable SpanId parentSpanId = - parentSpan == null ? null : new SpanId(parentSpan.getSpanContext().getSpanId()); - @NotNull String operation = span.getName(); - - // TODO [POTEL] tracesSamplingDecision - this.context = new SpanContext(traceId, spanId, operation, parentSpanId, null); + this.context = new OtelSpanContext(span, parentSpan); } @Override @@ -228,15 +221,12 @@ public void setDescription(@Nullable String description) { } @Override - public void setStatus(@Nullable SpanStatus status) { - // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter - // ^ could go in span attributes - this.context.setStatus(status); + public void setStatus(final @Nullable SpanStatus status) { + context.setStatus(status); } @Override public @Nullable SpanStatus getStatus() { - // TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter return context.getStatus(); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index 3775eec559..9387489888 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -198,7 +198,8 @@ private List maybeSend(final @NotNull List spans) { // spanStorage.getScope() // transaction.finishWithScope - transaction.finish(mapOtelStatus(span), new SentryLongDate(span.getEndEpochNanos())); + transaction.finish( + mapOtelStatus(span, transaction), new SentryLongDate(span.getEndEpochNanos())); } return remaining.stream() @@ -244,6 +245,9 @@ private void createAndFinishSpanForOtelSpan( parentSentrySpan.startChild( spanInfo.getOp(), spanInfo.getDescription(), startDate, Instrumenter.OTEL); + // TODO [POTEL] Check if we want to use `instrumentationScopeInfo.name` and append it to + // `auto.otel` + // TODO [POTEL] For spans manually created via Sentry API we should set manual, not auto.otel sentryChildSpan.getSpanContext().setOrigin(TRACE_ORIGN); for (Map.Entry dataField : spanInfo.getDataFields().entrySet()) { sentryChildSpan.setData(dataField.getKey(), dataField.getValue()); @@ -256,7 +260,7 @@ private void createAndFinishSpanForOtelSpan( } sentryChildSpan.finish( - mapOtelStatus(spanData), new SentryLongDate(spanData.getEndEpochNanos())); + mapOtelStatus(spanData, sentryChildSpan), new SentryLongDate(spanData.getEndEpochNanos())); } private void transferSpanDetails( @@ -285,6 +289,8 @@ private void transferSpanDetails( for (Map.Entry entry : tags.entrySet()) { targetSpan.setTag(entry.getKey(), entry.getValue()); } + + targetSpan.setStatus(sourceSpan.getStatus()); } } @@ -463,7 +469,14 @@ private void createOrUpdateSpanNodeAndRefs( } @SuppressWarnings("deprecation") - private SpanStatus mapOtelStatus(final @NotNull SpanData otelSpanData) { + private SpanStatus mapOtelStatus( + final @NotNull SpanData otelSpanData, final @NotNull ISpan sentrySpan) { + final @Nullable SpanStatus existingStatus = sentrySpan.getStatus(); + // TODO [POTEL] do we want the unknown error check here? + if (existingStatus != null && existingStatus != SpanStatus.UNKNOWN_ERROR) { + return existingStatus; + } + final @NotNull StatusData otelStatus = otelSpanData.getStatus(); final @NotNull StatusCode otelStatusCode = otelStatus.getStatusCode(); diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index f8d3f7b2f8..a4cf8b854b 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -3272,6 +3272,8 @@ public final class io/sentry/SpanStatus : java/lang/Enum, io/sentry/JsonSerializ public static final field UNIMPLEMENTED Lio/sentry/SpanStatus; public static final field UNKNOWN Lio/sentry/SpanStatus; public static final field UNKNOWN_ERROR Lio/sentry/SpanStatus; + public fun apiName ()Ljava/lang/String; + public static fun fromApiNameSafely (Ljava/lang/String;)Lio/sentry/SpanStatus; public static fun fromHttpStatusCode (I)Lio/sentry/SpanStatus; public static fun fromHttpStatusCode (Ljava/lang/Integer;Lio/sentry/SpanStatus;)Lio/sentry/SpanStatus; public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V diff --git a/sentry/src/main/java/io/sentry/SpanContext.java b/sentry/src/main/java/io/sentry/SpanContext.java index be428708cb..5d00b8d59b 100644 --- a/sentry/src/main/java/io/sentry/SpanContext.java +++ b/sentry/src/main/java/io/sentry/SpanContext.java @@ -145,6 +145,7 @@ public SpanId getParentSpanId() { } public @NotNull String getOperation() { + // TODO [POTEL] use span name here return op; } @@ -223,12 +224,12 @@ public boolean equals(Object o) { && Objects.equals(parentSpanId, that.parentSpanId) && op.equals(that.op) && Objects.equals(description, that.description) - && status == that.status; + && getStatus() == that.getStatus(); } @Override public int hashCode() { - return Objects.hash(traceId, spanId, parentSpanId, op, description, status); + return Objects.hash(traceId, spanId, parentSpanId, op, description, getStatus()); } // region JsonSerializable @@ -260,8 +261,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger if (description != null) { writer.name(JsonKeys.DESCRIPTION).value(description); } - if (status != null) { - writer.name(JsonKeys.STATUS).value(logger, status); + if (getStatus() != null) { + writer.name(JsonKeys.STATUS).value(logger, getStatus()); } if (origin != null) { writer.name(JsonKeys.ORIGIN).value(logger, origin); diff --git a/sentry/src/main/java/io/sentry/SpanStatus.java b/sentry/src/main/java/io/sentry/SpanStatus.java index b0b1bf78c8..37991abd67 100644 --- a/sentry/src/main/java/io/sentry/SpanStatus.java +++ b/sentry/src/main/java/io/sentry/SpanStatus.java @@ -103,12 +103,27 @@ private boolean matches(int httpStatusCode) { return httpStatusCode >= minHttpStatusCode && httpStatusCode <= maxHttpStatusCode; } + public @NotNull String apiName() { + return name().toLowerCase(Locale.ROOT); + } + + public static @Nullable SpanStatus fromApiNameSafely(final @Nullable String apiName) { + if (apiName == null) { + return null; + } + try { + return SpanStatus.valueOf(apiName.toUpperCase(Locale.ROOT)); + } catch (IllegalArgumentException ex) { + return null; + } + } + // JsonSerializable @Override public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger logger) throws IOException { - writer.value(name().toLowerCase(Locale.ROOT)); + writer.value(apiName()); } public static final class Deserializer implements JsonDeserializer { From 2b8a03740a98f0db4dcc1f520c3e674bd4babad3 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 18 Jun 2024 07:42:25 +0200 Subject: [PATCH 59/91] POTEL 7 - Tracing (#3445) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing --- .../api/sentry-opentelemetry-bootstrap.api | 14 ++-- .../InternalSemanticAttributes.java | 19 +++-- .../sentry/opentelemetry/OtelSpanContext.java | 13 ++- .../sentry/opentelemetry/OtelSpanFactory.java | 81 ++++++++++++++++-- .../sentry/opentelemetry/OtelSpanWrapper.java | 28 +++++-- .../OtelTransactionSpanForwarder.java | 1 + .../opentelemetry/PotelSentryPropagator.java | 72 ++++++---------- .../PotelSentrySpanProcessor.java | 82 +++++++++++++++++-- .../opentelemetry/SentrySpanExporter.java | 3 + sentry/api/sentry.api | 11 ++- .../java/io/sentry/DefaultSpanFactory.java | 24 ++++-- sentry/src/main/java/io/sentry/ISpan.java | 1 + .../src/main/java/io/sentry/ISpanFactory.java | 4 + sentry/src/main/java/io/sentry/Scopes.java | 8 +- .../src/main/java/io/sentry/SentryTracer.java | 1 + .../main/java/io/sentry/TracesSampler.java | 6 +- 16 files changed, 265 insertions(+), 103 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api index f8abed919e..3764cd4ccf 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api @@ -1,11 +1,10 @@ public final class io/sentry/opentelemetry/InternalSemanticAttributes { - public static final field BREADCRUMB_TYPE Lio/opentelemetry/api/common/AttributeKey; public static final field IS_REMOTE_PARENT Lio/opentelemetry/api/common/AttributeKey; - public static final field OP Lio/opentelemetry/api/common/AttributeKey; - public static final field ORIGIN Lio/opentelemetry/api/common/AttributeKey; public static final field PARENT_SAMPLED Lio/opentelemetry/api/common/AttributeKey; + public static final field PROFILE_SAMPLED Lio/opentelemetry/api/common/AttributeKey; + public static final field PROFILE_SAMPLE_RATE Lio/opentelemetry/api/common/AttributeKey; + public static final field SAMPLED Lio/opentelemetry/api/common/AttributeKey; public static final field SAMPLE_RATE Lio/opentelemetry/api/common/AttributeKey; - public static final field SOURCE Lio/opentelemetry/api/common/AttributeKey; public fun ()V } @@ -17,20 +16,21 @@ public final class io/sentry/opentelemetry/OtelContextScopesStorage : io/sentry/ } public final class io/sentry/opentelemetry/OtelSpanContext : io/sentry/SpanContext { - public fun (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/opentelemetry/api/trace/Span;)V + public fun (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/TracesSamplingDecision;Lio/sentry/opentelemetry/OtelSpanWrapper;)V public fun getStatus ()Lio/sentry/SpanStatus; public fun setStatus (Lio/sentry/SpanStatus;)V } public final class io/sentry/opentelemetry/OtelSpanFactory : io/sentry/ISpanFactory { public fun ()V - public fun createSpan (Ljava/lang/String;Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/ISpan;)Lio/sentry/ISpan; + public fun createSpan (Ljava/lang/String;Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/SpanContext;Lio/sentry/ISpan;)Lio/sentry/ISpan; public fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; + public fun retrieveCurrentSpan (Lio/sentry/IScope;)Lio/sentry/ISpan; public fun retrieveCurrentSpan (Lio/sentry/IScopes;)Lio/sentry/ISpan; } public final class io/sentry/opentelemetry/OtelSpanWrapper : io/sentry/ISpan { - public fun (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/IScopes;Lio/sentry/SentryDate;Lio/opentelemetry/api/trace/Span;)V + public fun (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/IScopes;Lio/sentry/SentryDate;Lio/sentry/TracesSamplingDecision;Lio/sentry/opentelemetry/OtelSpanWrapper;)V public fun finish ()V public fun finish (Lio/sentry/SpanStatus;)V public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java index e8d9d34c49..e21db174f1 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java @@ -4,17 +4,22 @@ // TODO [POTEL] context key vs attribute key public final class InternalSemanticAttributes { - public static final AttributeKey ORIGIN = AttributeKey.stringKey("sentry.origin"); - public static final AttributeKey OP = AttributeKey.stringKey("sentry.op"); - public static final AttributeKey SOURCE = AttributeKey.stringKey("sentry.source"); + // public static final AttributeKey ORIGIN = AttributeKey.stringKey("sentry.origin"); + // public static final AttributeKey OP = AttributeKey.stringKey("sentry.op"); + // public static final AttributeKey SOURCE = AttributeKey.stringKey("sentry.source"); + public static final AttributeKey SAMPLED = AttributeKey.booleanKey("sentry.sampled"); public static final AttributeKey SAMPLE_RATE = AttributeKey.doubleKey("sentry.sample_rate"); public static final AttributeKey PARENT_SAMPLED = - AttributeKey.booleanKey("sentry.parentSampled"); + AttributeKey.booleanKey("sentry.parent_sampled"); + public static final AttributeKey PROFILE_SAMPLED = + AttributeKey.booleanKey("sentry.profile_sampled"); + public static final AttributeKey PROFILE_SAMPLE_RATE = + AttributeKey.doubleKey("sentry.profile_sample_rate"); public static final AttributeKey IS_REMOTE_PARENT = - AttributeKey.booleanKey("sentry.isParentRemote"); - public static final AttributeKey BREADCRUMB_TYPE = - AttributeKey.stringKey("sentry.breadcrumb.type"); + AttributeKey.booleanKey("sentry.is_remote_parent"); + // public static final AttributeKey BREADCRUMB_TYPE = + // AttributeKey.stringKey("sentry.breadcrumb.type"); // public static final AttributeKey BREADCRUMB_TYPE = // InternalAttributeKeyImpl.create("sentry.breadcrumb.type", SentryLevel.class); // BREADCRUMB_TYPE("sentry.breadcrumb.type"), diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java index e33bcf061e..2d1bd78b95 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java @@ -7,6 +7,7 @@ import io.sentry.SpanContext; import io.sentry.SpanId; import io.sentry.SpanStatus; +import io.sentry.TracesSamplingDecision; import io.sentry.protocol.SentryId; import java.lang.ref.WeakReference; import org.jetbrains.annotations.NotNull; @@ -22,15 +23,19 @@ public final class OtelSpanContext extends SpanContext { */ private final @NotNull WeakReference span; - public OtelSpanContext(final @NotNull ReadWriteSpan span, final @Nullable Span parentSpan) { - // TODO [POTEL] tracesSamplingDecision + public OtelSpanContext( + final @NotNull ReadWriteSpan span, + final @Nullable TracesSamplingDecision samplingDecision, + final @Nullable OtelSpanWrapper parentSpan) { super( new SentryId(span.getSpanContext().getTraceId()), new SpanId(span.getSpanContext().getSpanId()), - parentSpan == null ? null : new SpanId(parentSpan.getSpanContext().getSpanId()), + parentSpan == null ? null : parentSpan.getSpanContext().getSpanId(), span.getName(), null, - null, + samplingDecision != null + ? samplingDecision + : (parentSpan == null ? null : parentSpan.getSamplingDecision()), null, null); this.span = new WeakReference<>(span); diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java index 0b859c00cc..55b9f8094a 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java @@ -3,8 +3,11 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.api.trace.TraceFlags; +import io.opentelemetry.api.trace.TraceState; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Context; +import io.sentry.IScope; import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.ISpanFactory; @@ -12,10 +15,14 @@ import io.sentry.NoOpSpan; import io.sentry.NoOpTransaction; import io.sentry.SentryDate; +import io.sentry.SpanContext; +import io.sentry.SpanId; import io.sentry.SpanOptions; +import io.sentry.TracesSamplingDecision; import io.sentry.TransactionContext; import io.sentry.TransactionOptions; import io.sentry.TransactionPerformanceCollector; +import io.sentry.protocol.SentryId; import java.util.concurrent.TimeUnit; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -34,7 +41,13 @@ public final class OtelSpanFactory implements ISpanFactory { @Nullable TransactionPerformanceCollector transactionPerformanceCollector) { final @Nullable OtelSpanWrapper span = createSpanInternal( - context.getName(), context.getDescription(), scopes, transactionOptions, null); + context.getName(), + context.getDescription(), + scopes, + transactionOptions, + null, + context.getSamplingDecision(), + context); if (span == null) { return NoOpTransaction.getInstance(); } @@ -47,9 +60,13 @@ public final class OtelSpanFactory implements ISpanFactory { final @Nullable String description, final @NotNull IScopes scopes, final @NotNull SpanOptions spanOptions, + final @NotNull SpanContext spanContext, final @Nullable ISpan parentSpan) { + final @Nullable TracesSamplingDecision samplingDecision = + parentSpan == null ? null : parentSpan.getSamplingDecision(); final @Nullable OtelSpanWrapper span = - createSpanInternal(name, description, scopes, spanOptions, parentSpan); + createSpanInternal( + name, description, scopes, spanOptions, parentSpan, samplingDecision, spanContext); if (span == null) { return NoOpSpan.getInstance(); } @@ -61,10 +78,34 @@ public final class OtelSpanFactory implements ISpanFactory { final @Nullable String description, final @NotNull IScopes scopes, final @NotNull SpanOptions spanOptions, - final @Nullable ISpan parentSpan) { + final @Nullable ISpan parentSpan, + final @Nullable TracesSamplingDecision samplingDecision, + final @NotNull SpanContext spanContext) { final @NotNull SpanBuilder spanBuilder = getTracer().spanBuilder(name); + // TODO [POTEL] If performance is disabled, can we use otel.SamplingDecision.RECORD_ONLY to + // still allow otel to be used for tracing if (parentSpan == null) { - spanBuilder.setNoParent(); + final @NotNull SentryId traceId = spanContext.getTraceId(); + final @Nullable SpanId parentSpanId = spanContext.getParentSpanId(); + if (parentSpanId == null) { + final @NotNull io.opentelemetry.api.trace.SpanContext otelSpanContext = + io.opentelemetry.api.trace.SpanContext.create( + traceId.toString(), + io.opentelemetry.api.trace.SpanId.getInvalid(), + TraceFlags.getSampled(), + TraceState.getDefault()); + final @NotNull Span wrappedSpan = Span.wrap(otelSpanContext); + spanBuilder.setParent(Context.root().with(wrappedSpan)); + } else { + final @NotNull io.opentelemetry.api.trace.SpanContext otelSpanContext = + io.opentelemetry.api.trace.SpanContext.createFromRemoteParent( + traceId.toString(), + parentSpanId.toString(), + TraceFlags.getSampled(), + TraceState.getDefault()); + final @NotNull Span wrappedSpan = Span.wrap(otelSpanContext); + spanBuilder.setParent(Context.root().with(wrappedSpan)); + } } else { if (parentSpan instanceof OtelSpanWrapper) { // TODO [POTEL] retrieve context from span @@ -72,6 +113,8 @@ public final class OtelSpanFactory implements ISpanFactory { } } + // TODO [POTEL] send baggage in (note: won't go through propagators) + final @Nullable SentryDate startTimestampFromOptions = spanOptions.getStartTimestamp(); final @NotNull SentryDate startTimestamp = startTimestampFromOptions == null @@ -79,11 +122,28 @@ public final class OtelSpanFactory implements ISpanFactory { : startTimestampFromOptions; spanBuilder.setStartTimestamp(startTimestamp.nanoTimestamp(), TimeUnit.NANOSECONDS); + if (samplingDecision != null) { + spanBuilder.setAttribute(InternalSemanticAttributes.SAMPLED, samplingDecision.getSampled()); + spanBuilder.setAttribute( + InternalSemanticAttributes.SAMPLE_RATE, samplingDecision.getSampleRate()); + spanBuilder.setAttribute( + InternalSemanticAttributes.PROFILE_SAMPLED, samplingDecision.getProfileSampled()); + spanBuilder.setAttribute( + InternalSemanticAttributes.PROFILE_SAMPLE_RATE, samplingDecision.getProfileSampleRate()); + } + final @NotNull Span otelSpan = spanBuilder.startSpan(); + final @Nullable OtelSpanWrapper sentrySpan = storage.getSentrySpan(otelSpan.getSpanContext()); - if (sentrySpan != null && description != null) { - sentrySpan.setDescription(description); + if (sentrySpan != null) { + if (description != null) { + sentrySpan.setDescription(description); + } + if (samplingDecision != null) { + sentrySpan.getSpanContext().setSamplingDecision(samplingDecision); + } } + return sentrySpan; } @@ -96,6 +156,15 @@ public final class OtelSpanFactory implements ISpanFactory { return storage.getSentrySpan(span.getSpanContext()); } + @Override + public @Nullable ISpan retrieveCurrentSpan(IScope scope) { + final @Nullable Span span = Span.fromContextOrNull(Context.current()); + if (span == null) { + return null; + } + return storage.getSentrySpan(span.getSpanContext()); + } + private @NotNull Tracer getTracer() { return GlobalOpenTelemetry.getTracer( "sentry-instrumentation-scope-name", "sentry-instrumentation-scope-version"); diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java index 2c363555c1..f6272dd10b 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java @@ -28,6 +28,8 @@ import io.sentry.util.LazyEvaluator; import io.sentry.util.Objects; import java.lang.ref.WeakReference; +import java.util.ArrayDeque; +import java.util.Deque; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -74,15 +76,18 @@ public final class OtelSpanWrapper implements ISpan { /** A throwable thrown during the execution of the span. */ private @Nullable Throwable throwable; + private @NotNull Deque tokensToCleanup = new ArrayDeque<>(1); + public OtelSpanWrapper( final @NotNull ReadWriteSpan span, final @NotNull IScopes scopes, final @NotNull SentryDate startTimestamp, - final @Nullable Span parentSpan) { + final @Nullable TracesSamplingDecision samplingDecision, + final @Nullable OtelSpanWrapper parentSpan) { this.scopes = Objects.requireNonNull(scopes, "scopes are required"); this.span = new WeakReference<>(span); this.startTimestamp = startTimestamp; - this.context = new OtelSpanContext(span, parentSpan); + this.context = new OtelSpanContext(span, samplingDecision, parentSpan); } @Override @@ -100,7 +105,7 @@ public OtelSpanWrapper( return scopes .getOptions() .getSpanFactory() - .createSpan(operation, description, scopes, spanOptions, this); + .createSpan(operation, description, scopes, spanOptions, context, this); } @Override @@ -131,7 +136,7 @@ public OtelSpanWrapper( return scopes .getOptions() .getSpanFactory() - .createSpan(operation, description, scopes, spanOptions, this); + .createSpan(operation, description, scopes, spanOptions, context, this); } @Override @@ -143,7 +148,7 @@ public OtelSpanWrapper( return scopes .getOptions() .getSpanFactory() - .createSpan(operation, description, scopes, new SpanOptions(), this); + .createSpan(operation, description, scopes, new SpanOptions(), context, this); } @Override @@ -185,6 +190,10 @@ public void finish(@Nullable SpanStatus status) { if (otelSpan != null) { otelSpan.end(); } + + for (ISentryLifecycleToken token : tokensToCleanup) { + token.close(); + } } @Override @@ -425,12 +434,19 @@ public Map getMeasurements() { } @SuppressWarnings("MustBeClosedChecker") + @ApiStatus.Internal @Override public @NotNull ISentryLifecycleToken makeCurrent() { final @Nullable Span otelSpan = getSpan(); if (otelSpan != null) { final @NotNull Scope otelScope = otelSpan.makeCurrent(); - return new OtelContextSpanStorageToken(otelScope); + // TODO [POTEL] should we keep an ordered list of otel scopes and close them in reverse order + // on finish? + // TODO [POTEL] should we make transaction/span implement ISentryLifecycleToken instead? + final @NotNull OtelContextSpanStorageToken token = new OtelContextSpanStorageToken(otelScope); + // to iterate LIFO when closing + tokensToCleanup.addFirst(token); + return token; } return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java index 137bb2cdc1..5ee8a4916a 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java @@ -255,6 +255,7 @@ public boolean isNoOp() { return rootSpan.getEventId(); } + @ApiStatus.Internal @Override public @NotNull ISentryLifecycleToken makeCurrent() { return rootSpan.makeCurrent(); diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentryPropagator.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentryPropagator.java index 2164950ef8..1cc4b1c411 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentryPropagator.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentryPropagator.java @@ -20,6 +20,7 @@ import io.sentry.exception.InvalidSentryTraceHeaderException; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -28,8 +29,7 @@ public final class PotelSentryPropagator implements TextMapPropagator { private static final @NotNull List FIELDS = Arrays.asList(SentryTraceHeader.SENTRY_TRACE_HEADER, BaggageHeader.BAGGAGE_HEADER); - // private final @NotNull SentryWeakSpanStorage spanStorage = - // SentryWeakSpanStorage.getInstance(); + private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); private final @NotNull IScopes scopes; public PotelSentryPropagator() { @@ -59,40 +59,26 @@ public void inject(final Context context, final C carrier, final TextMapSett return; } - /** - * TODO - * - *

    maybe it could work like this: - * - *

    getIsolationScope() check if there's a PropagationContext on there and use that for - * generating headers and freezing - * - *

    if that's not there check Context for data and attach headers - */ - - // TODO: inject from OTEL SpanContext and TraceState - System.out.println("TODO"); - // TODO how to inject? - // final @Nullable ISpan sentrySpan = spanStorage.get(otelSpanContext.getSpanId()); - // if (sentrySpan == null || sentrySpan.isNoOp()) { - // hub.getOptions() - // .getLogger() - // .log( - // SentryLevel.DEBUG, - // "Not injecting Sentry tracing information for span %s as no Sentry span has been - // found or it is a NoOp (trace %s). This might simply mean this is a request to Sentry.", - // otelSpanContext.getSpanId(), - // otelSpanContext.getTraceId()); - // return; - // } - // - // final @NotNull SentryTraceHeader sentryTraceHeader = sentrySpan.toSentryTrace(); - // setter.set(carrier, sentryTraceHeader.getName(), sentryTraceHeader.getValue()); - // final @Nullable BaggageHeader baggageHeader = - // sentrySpan.toBaggageHeader(Collections.emptyList()); - // if (baggageHeader != null) { - // setter.set(carrier, baggageHeader.getName(), baggageHeader.getValue()); - // } + final @Nullable OtelSpanWrapper sentrySpan = spanStorage.getSentrySpan(otelSpanContext); + if (sentrySpan == null || sentrySpan.isNoOp()) { + scopes + .getOptions() + .getLogger() + .log( + SentryLevel.DEBUG, + "Not injecting Sentry tracing information for span %s as no Sentry span has been found or it is a NoOp (trace %s). This might simply mean this is a request to Sentry.", + otelSpanContext.getSpanId(), + otelSpanContext.getTraceId()); + return; + } + + final @NotNull SentryTraceHeader sentryTraceHeader = sentrySpan.toSentryTrace(); + setter.set(carrier, sentryTraceHeader.getName(), sentryTraceHeader.getValue()); + final @Nullable BaggageHeader baggageHeader = + sentrySpan.toBaggageHeader(Collections.emptyList()); + if (baggageHeader != null) { + setter.set(carrier, baggageHeader.getName(), baggageHeader.getValue()); + } } @Override @@ -107,25 +93,13 @@ public Context extract( final @Nullable String sentryTraceString = getter.get(carrier, SentryTraceHeader.SENTRY_TRACE_HEADER); if (sentryTraceString == null) { - - final @NotNull Context modifiedContext = context.with(SENTRY_SCOPES_KEY, scopesToUse); - // return context.with(SENTRY_SCOPES_KEY, scopesToUse); - return modifiedContext; + return context.with(SENTRY_SCOPES_KEY, scopesToUse); } - // else { - // // TODO clean up code here - // // TODO should we rely on OTEL trace/span ids here? - // scopesToUse.getIsolationScope().setPropagationContext(new PropagationContext()); - // } try { SentryTraceHeader sentryTraceHeader = new SentryTraceHeader(sentryTraceString); final @Nullable String baggageString = getter.get(carrier, BaggageHeader.BAGGAGE_HEADER); - // Baggage baggage = Baggage.fromHeader(baggageString); - - // final @NotNull TraceState traceState = TraceState.builder().put("todo.dsc", - // baggage.).build(); final @NotNull TraceState traceState = TraceState.getDefault(); SpanContext otelSpanContext = diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java index e2a5fb75ae..9105467cef 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java @@ -3,18 +3,24 @@ import static io.sentry.opentelemetry.InternalSemanticAttributes.IS_REMOTE_PARENT; import static io.sentry.opentelemetry.SentryOtelKeys.SENTRY_SCOPES_KEY; -import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.context.Context; import io.opentelemetry.sdk.trace.ReadWriteSpan; import io.opentelemetry.sdk.trace.ReadableSpan; import io.opentelemetry.sdk.trace.SpanProcessor; import io.sentry.IScopes; +import io.sentry.PropagationContext; +import io.sentry.SamplingContext; import io.sentry.ScopesAdapter; import io.sentry.Sentry; import io.sentry.SentryDate; import io.sentry.SentryLevel; import io.sentry.SentryLongDate; +import io.sentry.SpanId; +import io.sentry.TracesSampler; +import io.sentry.TracesSamplingDecision; +import io.sentry.TransactionContext; +import io.sentry.protocol.SentryId; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -22,12 +28,15 @@ public final class PotelSentrySpanProcessor implements SpanProcessor { private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); private final @NotNull IScopes scopes; + private final @NotNull TracesSampler tracesSampler; + public PotelSentrySpanProcessor() { this(ScopesAdapter.getInstance()); } PotelSentrySpanProcessor(final @NotNull IScopes scopes) { this.scopes = scopes; + this.tracesSampler = new TracesSampler(scopes.getOptions()); } @Override @@ -36,21 +45,80 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri return; } - final @Nullable Span parentSpan = Span.fromContextOrNull(parentContext); - if (parentSpan != null) { - otelSpan.setAttribute(IS_REMOTE_PARENT, parentSpan.getSpanContext().isRemote()); - } - final @Nullable IScopes scopesFromContext = parentContext.get(SENTRY_SCOPES_KEY); final @NotNull IScopes scopes = scopesFromContext != null ? scopesFromContext.forkedCurrentScope("spanprocessor") : Sentry.forkedRootScopes("spanprocessor"); + + final @Nullable OtelSpanWrapper sentryParentSpan = + spanStorage.getSentrySpan(otelSpan.getParentSpanContext()); + @Nullable TracesSamplingDecision samplingDecision = null; + otelSpan.setAttribute(IS_REMOTE_PARENT, otelSpan.getParentSpanContext().isRemote()); + if (sentryParentSpan == null) { + final @Nullable Boolean sampled = otelSpan.getAttribute(InternalSemanticAttributes.SAMPLED); + final @Nullable Double sampleRate = + otelSpan.getAttribute(InternalSemanticAttributes.SAMPLE_RATE); + final @Nullable Boolean profileSampled = + otelSpan.getAttribute(InternalSemanticAttributes.PROFILE_SAMPLED); + final @Nullable Double profileSampleRate = + otelSpan.getAttribute(InternalSemanticAttributes.PROFILE_SAMPLE_RATE); + if (sampled != null) { + // span created by Sentry API + + final @NotNull String traceId = otelSpan.getSpanContext().getTraceId(); + final @NotNull String spanId = otelSpan.getSpanContext().getSpanId(); + // TODO [POTEL] parent span id could be invalid + final @NotNull String parentSpanId = otelSpan.getParentSpanContext().getSpanId(); + + final @NotNull PropagationContext propagationContext = + new PropagationContext( + new SentryId(traceId), new SpanId(spanId), new SpanId(parentSpanId), null, sampled); + + scopes.configureScope( + scope -> { + scope.withPropagationContext( + oldPropagationContext -> { + scope.setPropagationContext(propagationContext); + }); + }); + + // TODO [POTEL] can we use OTel Sampler to let OTel know our sampling decision + // Sentry not sampled vs OTel not sampled may mean different things for trace propagation + samplingDecision = + new TracesSamplingDecision( + sampled, + sampleRate, + profileSampled == null ? false : profileSampled, + profileSampleRate); + } else { + // span not created by Sentry API + + final @NotNull String traceId = otelSpan.getSpanContext().getTraceId(); + final @NotNull String spanId = otelSpan.getSpanContext().getSpanId(); + + final @NotNull PropagationContext propagationContext = + new PropagationContext(new SentryId(traceId), new SpanId(spanId), null, null, null); + + scopes.configureScope( + scope -> { + scope.withPropagationContext( + oldPropagationContext -> { + scope.setPropagationContext(propagationContext); + }); + }); + + final @NotNull TransactionContext transactionContext = + TransactionContext.fromPropagationContext(propagationContext); + samplingDecision = tracesSampler.sample(new SamplingContext(transactionContext, null)); + } + } final @NotNull SpanContext spanContext = otelSpan.getSpanContext(); final @NotNull SentryDate startTimestamp = new SentryLongDate(otelSpan.toSpanData().getStartEpochNanos()); spanStorage.storeSentrySpan( - spanContext, new OtelSpanWrapper(otelSpan, scopes, startTimestamp, parentSpan)); + spanContext, + new OtelSpanWrapper(otelSpan, scopes, startTimestamp, samplingDecision, sentryParentSpan)); } @Override diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index 9387489888..d10f931002 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -241,6 +241,8 @@ private void createAndFinishSpanForOtelSpan( spanData.getParentSpanId()); final @NotNull SentryDate startDate = new SentryLongDate(spanData.getStartEpochNanos()); // TODO [POTEL] op and description might have been overriden + // TODO [POTEL] ensure not sampling again + // TODO [POTEL] use OTel span ID so tracing actually has value final @NotNull ISpan sentryChildSpan = parentSentrySpan.startChild( spanInfo.getOp(), spanInfo.getDescription(), startDate, Instrumenter.OTEL); @@ -362,6 +364,7 @@ private void transferSpanDetails( transactionOptions.setStartTimestamp(new SentryLongDate(span.getStartEpochNanos())); transactionOptions.setSpanFactory(new DefaultSpanFactory()); + // TODO [POTEL] do not sample again ITransaction sentryTransaction = scopesToUse.startTransaction(transactionContext, transactionOptions); sentryTransaction.getSpanContext().setOrigin(TRACE_ORIGN); diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index a4cf8b854b..85f95429cc 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -367,8 +367,9 @@ public final class io/sentry/DefaultScopesStorage : io/sentry/IScopesStorage { public final class io/sentry/DefaultSpanFactory : io/sentry/ISpanFactory { public fun ()V - public fun createSpan (Ljava/lang/String;Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/ISpan;)Lio/sentry/ISpan; + public fun createSpan (Ljava/lang/String;Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/SpanContext;Lio/sentry/ISpan;)Lio/sentry/ISpan; public fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; + public fun retrieveCurrentSpan (Lio/sentry/IScope;)Lio/sentry/ISpan; public fun retrieveCurrentSpan (Lio/sentry/IScopes;)Lio/sentry/ISpan; } @@ -991,8 +992,9 @@ public abstract interface class io/sentry/ISpan { } public abstract interface class io/sentry/ISpanFactory { - public abstract fun createSpan (Ljava/lang/String;Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/ISpan;)Lio/sentry/ISpan; + public abstract fun createSpan (Ljava/lang/String;Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/SpanContext;Lio/sentry/ISpan;)Lio/sentry/ISpan; public abstract fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; + public abstract fun retrieveCurrentSpan (Lio/sentry/IScope;)Lio/sentry/ISpan; public abstract fun retrieveCurrentSpan (Lio/sentry/IScopes;)Lio/sentry/ISpan; } @@ -3338,6 +3340,11 @@ public final class io/sentry/TraceContext$JsonKeys { public fun ()V } +public final class io/sentry/TracesSampler { + public fun (Lio/sentry/SentryOptions;)V + public fun sample (Lio/sentry/SamplingContext;)Lio/sentry/TracesSamplingDecision; +} + public final class io/sentry/TracesSamplingDecision { public fun (Ljava/lang/Boolean;)V public fun (Ljava/lang/Boolean;Ljava/lang/Double;)V diff --git a/sentry/src/main/java/io/sentry/DefaultSpanFactory.java b/sentry/src/main/java/io/sentry/DefaultSpanFactory.java index 1c8cf42628..da2b3da979 100644 --- a/sentry/src/main/java/io/sentry/DefaultSpanFactory.java +++ b/sentry/src/main/java/io/sentry/DefaultSpanFactory.java @@ -8,26 +8,32 @@ public final class DefaultSpanFactory implements ISpanFactory { @Override public @NotNull ITransaction createTransaction( - @NotNull TransactionContext context, - @NotNull IScopes scopes, - @NotNull TransactionOptions transactionOptions, - @Nullable TransactionPerformanceCollector transactionPerformanceCollector) { + final @NotNull TransactionContext context, + final @NotNull IScopes scopes, + final @NotNull TransactionOptions transactionOptions, + final @Nullable TransactionPerformanceCollector transactionPerformanceCollector) { return new SentryTracer(context, scopes, transactionOptions, transactionPerformanceCollector); } @Override public @NotNull ISpan createSpan( - @NotNull String name, - @Nullable String description, - @NotNull IScopes scopes, - @NotNull SpanOptions spanOptions, + final @NotNull String name, + final @Nullable String description, + final @NotNull IScopes scopes, + final @NotNull SpanOptions spanOptions, + final @NotNull SpanContext spanContext, @Nullable ISpan parentSpan) { // TODO [POTEL] forward to SentryTracer.createChild? return NoOpSpan.getInstance(); } @Override - public @Nullable ISpan retrieveCurrentSpan(IScopes scopes) { + public @Nullable ISpan retrieveCurrentSpan(final IScopes scopes) { return scopes.getSpan(); } + + @Override + public @Nullable ISpan retrieveCurrentSpan(final IScope scope) { + return scope.getSpan(); + } } diff --git a/sentry/src/main/java/io/sentry/ISpan.java b/sentry/src/main/java/io/sentry/ISpan.java index ec1a9699ac..fedc725455 100644 --- a/sentry/src/main/java/io/sentry/ISpan.java +++ b/sentry/src/main/java/io/sentry/ISpan.java @@ -280,6 +280,7 @@ ISpan startChild( @NotNull SentryId getEventId(); + @ApiStatus.Internal @NotNull ISentryLifecycleToken makeCurrent(); } diff --git a/sentry/src/main/java/io/sentry/ISpanFactory.java b/sentry/src/main/java/io/sentry/ISpanFactory.java index 67e12b4caa..ece928a147 100644 --- a/sentry/src/main/java/io/sentry/ISpanFactory.java +++ b/sentry/src/main/java/io/sentry/ISpanFactory.java @@ -19,8 +19,12 @@ ISpan createSpan( @Nullable String description, @NotNull IScopes scopes, @NotNull SpanOptions spanOptions, + @NotNull SpanContext spanContext, @Nullable ISpan parentSpan); @Nullable ISpan retrieveCurrentSpan(IScopes scopes); + + @Nullable + ISpan retrieveCurrentSpan(IScope scope); } diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index a038be000d..27bfbf72f6 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -817,6 +817,7 @@ public void flush(long timeoutMillis) { final @NotNull TransactionContext transactionContext, final @NotNull TransactionOptions transactionOptions) { Objects.requireNonNull(transactionContext, "transactionContext is required"); + // TODO [POTEL] what if span is already running and someone calls startTransaction? ITransaction transaction; if (!isEnabled()) { @@ -875,8 +876,8 @@ public void flush(long timeoutMillis) { } } if (transactionOptions.isBindToScope()) { + // TODO [POTEL] this causes problems with OTel since it messes up closing of scopes and leaks transaction.makeCurrent(); - // configureScope(scope -> scope.setTransaction(transaction)); } return transaction; } @@ -899,15 +900,14 @@ public void setSpanContext( @Override public @Nullable ISpan getSpan() { - ISpan span = null; if (!isEnabled()) { getOptions() .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'getSpan' call is a no-op."); } else { - span = getCombinedScopeView().getSpan(); + return getOptions().getSpanFactory().retrieveCurrentSpan(getCombinedScopeView()); } - return span; + return null; } @Override diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index 32a2a94df4..ba1a952c98 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -859,6 +859,7 @@ public void setName(@NotNull String name, @NotNull TransactionNameSource transac return eventId; } + @ApiStatus.Internal @Override public @NotNull ISentryLifecycleToken makeCurrent() { scopes.configureScope( diff --git a/sentry/src/main/java/io/sentry/TracesSampler.java b/sentry/src/main/java/io/sentry/TracesSampler.java index 3b83a815cf..f85aba1a9b 100644 --- a/sentry/src/main/java/io/sentry/TracesSampler.java +++ b/sentry/src/main/java/io/sentry/TracesSampler.java @@ -2,11 +2,13 @@ import io.sentry.util.Objects; import java.security.SecureRandom; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; -final class TracesSampler { +@ApiStatus.Internal +public final class TracesSampler { private static final @NotNull Double DEFAULT_TRACES_SAMPLE_RATE = 1.0; private final @NotNull SentryOptions options; @@ -23,7 +25,7 @@ public TracesSampler(final @NotNull SentryOptions options) { } @NotNull - TracesSamplingDecision sample(final @NotNull SamplingContext samplingContext) { + public TracesSamplingDecision sample(final @NotNull SamplingContext samplingContext) { final TracesSamplingDecision samplingContextSamplingDecision = samplingContext.getTransactionContext().getSamplingDecision(); if (samplingContextSamplingDecision != null) { From aa70b169b3c2370c7765b336e5ad0c461a208207 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 18 Jun 2024 07:44:48 +0200 Subject: [PATCH 60/91] POTEL 8 - Inherit OTel span ID and do not sample again when sending to Sentry (#3451) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing * Inherit OTel span IDs when sending to sentry --- .../SentryFragmentLifecycleCallbacksTest.kt | 12 +-- .../api/sentry-opentelemetry-bootstrap.api | 4 +- .../sentry/opentelemetry/OtelSpanFactory.java | 18 ++-- .../sentry/opentelemetry/OtelSpanWrapper.java | 54 +++++++----- .../OtelTransactionSpanForwarder.java | 6 ++ .../opentelemetry/SentrySpanExporter.java | 26 ++++-- sentry/api/sentry.api | 15 +++- .../java/io/sentry/DefaultSpanFactory.java | 2 - sentry/src/main/java/io/sentry/ISpan.java | 4 + .../src/main/java/io/sentry/ISpanFactory.java | 2 - sentry/src/main/java/io/sentry/NoOpSpan.java | 6 ++ .../main/java/io/sentry/NoOpTransaction.java | 6 ++ .../src/main/java/io/sentry/SentryTracer.java | 84 +++++++++++++++---- sentry/src/main/java/io/sentry/Span.java | 55 ++++++++---- .../src/main/java/io/sentry/SpanContext.java | 33 +++++++- .../java/io/sentry/TransactionContext.java | 9 -- sentry/src/test/java/io/sentry/SpanTest.kt | 29 +++++-- 17 files changed, 260 insertions(+), 105 deletions(-) diff --git a/sentry-android-fragment/src/test/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacksTest.kt b/sentry-android-fragment/src/test/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacksTest.kt index 26cb5b211a..c12cd199a9 100644 --- a/sentry-android-fragment/src/test/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacksTest.kt +++ b/sentry-android-fragment/src/test/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacksTest.kt @@ -53,7 +53,7 @@ class SentryFragmentLifecycleCallbacksTest { whenever(span.spanContext).thenReturn( SpanContext(SentryId.EMPTY_ID, SpanId.EMPTY_ID, "op", null, null) ) - whenever(transaction.startChild(any(), any())).thenReturn(span) + whenever(transaction.startChild(any(), any())).thenReturn(span) whenever(scope.transaction).thenReturn(transaction) whenever(scopes.configureScope(any())).thenAnswer { (it.arguments[0] as ScopeCallback).run(scope) @@ -190,7 +190,7 @@ class SentryFragmentLifecycleCallbacksTest { sut.onFragmentCreated(fixture.fragmentManager, fixture.fragment, savedInstanceState = null) - verify(fixture.transaction, never()).startChild(any(), any()) + verify(fixture.transaction, never()).startChild(any(), any()) } @Test @@ -200,10 +200,10 @@ class SentryFragmentLifecycleCallbacksTest { sut.onFragmentCreated(fixture.fragmentManager, fixture.fragment, savedInstanceState = null) verify(fixture.transaction).startChild( - check { + check { assertEquals(SentryFragmentLifecycleCallbacks.FRAGMENT_LOAD_OP, it) }, - check { + check { assertEquals("androidx.fragment.app.Fragment", it) } ) @@ -215,7 +215,7 @@ class SentryFragmentLifecycleCallbacksTest { sut.onFragmentCreated(fixture.fragmentManager, fixture.fragment, savedInstanceState = null) - verify(fixture.transaction, never()).startChild(any(), any()) + verify(fixture.transaction, never()).startChild(any(), any()) } @Test @@ -225,7 +225,7 @@ class SentryFragmentLifecycleCallbacksTest { sut.onFragmentCreated(fixture.fragmentManager, fixture.fragment, savedInstanceState = null) sut.onFragmentCreated(fixture.fragmentManager, fixture.fragment, savedInstanceState = null) - verify(fixture.transaction).startChild(any(), any()) + verify(fixture.transaction).startChild(any(), any()) } @Test diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api index 3764cd4ccf..b5135d9d32 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api @@ -23,7 +23,7 @@ public final class io/sentry/opentelemetry/OtelSpanContext : io/sentry/SpanConte public final class io/sentry/opentelemetry/OtelSpanFactory : io/sentry/ISpanFactory { public fun ()V - public fun createSpan (Ljava/lang/String;Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/SpanContext;Lio/sentry/ISpan;)Lio/sentry/ISpan; + public fun createSpan (Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/SpanContext;Lio/sentry/ISpan;)Lio/sentry/ISpan; public fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; public fun retrieveCurrentSpan (Lio/sentry/IScope;)Lio/sentry/ISpan; public fun retrieveCurrentSpan (Lio/sentry/IScopes;)Lio/sentry/ISpan; @@ -70,6 +70,7 @@ public final class io/sentry/opentelemetry/OtelSpanWrapper : io/sentry/ISpan { public fun setThrowable (Ljava/lang/Throwable;)V public fun setTransactionName (Ljava/lang/String;)V public fun setTransactionName (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V + public fun startChild (Lio/sentry/SpanContext;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; 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/Instrumenter;)Lio/sentry/ISpan; @@ -122,6 +123,7 @@ public final class io/sentry/opentelemetry/OtelTransactionSpanForwarder : io/sen public fun setStatus (Lio/sentry/SpanStatus;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V public fun setThrowable (Ljava/lang/Throwable;)V + public fun startChild (Lio/sentry/SpanContext;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; 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; diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java index 55b9f8094a..3467789c63 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java @@ -39,15 +39,10 @@ public final class OtelSpanFactory implements ISpanFactory { @NotNull IScopes scopes, @NotNull TransactionOptions transactionOptions, @Nullable TransactionPerformanceCollector transactionPerformanceCollector) { + // TODO [POTEL] name vs. op for transaction final @Nullable OtelSpanWrapper span = createSpanInternal( - context.getName(), - context.getDescription(), - scopes, - transactionOptions, - null, - context.getSamplingDecision(), - context); + scopes, transactionOptions, null, context.getSamplingDecision(), context); if (span == null) { return NoOpTransaction.getInstance(); } @@ -56,8 +51,6 @@ public final class OtelSpanFactory implements ISpanFactory { @Override public @NotNull ISpan createSpan( - final @NotNull String name, - final @Nullable String description, final @NotNull IScopes scopes, final @NotNull SpanOptions spanOptions, final @NotNull SpanContext spanContext, @@ -65,8 +58,7 @@ public final class OtelSpanFactory implements ISpanFactory { final @Nullable TracesSamplingDecision samplingDecision = parentSpan == null ? null : parentSpan.getSamplingDecision(); final @Nullable OtelSpanWrapper span = - createSpanInternal( - name, description, scopes, spanOptions, parentSpan, samplingDecision, spanContext); + createSpanInternal(scopes, spanOptions, parentSpan, samplingDecision, spanContext); if (span == null) { return NoOpSpan.getInstance(); } @@ -74,13 +66,12 @@ public final class OtelSpanFactory implements ISpanFactory { } private @Nullable OtelSpanWrapper createSpanInternal( - final @NotNull String name, - final @Nullable String description, final @NotNull IScopes scopes, final @NotNull SpanOptions spanOptions, final @Nullable ISpan parentSpan, final @Nullable TracesSamplingDecision samplingDecision, final @NotNull SpanContext spanContext) { + final @NotNull String name = spanContext.getOperation(); final @NotNull SpanBuilder spanBuilder = getTracer().spanBuilder(name); // TODO [POTEL] If performance is disabled, can we use otel.SamplingDecision.RECORD_ONLY to // still allow otel to be used for tracing @@ -136,6 +127,7 @@ public final class OtelSpanFactory implements ISpanFactory { final @Nullable OtelSpanWrapper sentrySpan = storage.getSentrySpan(otelSpan.getSpanContext()); if (sentrySpan != null) { + final @Nullable String description = spanContext.getDescription(); if (description != null) { sentrySpan.setDescription(description); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java index f6272dd10b..8ec6c6bb0b 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java @@ -101,11 +101,21 @@ public OtelSpanWrapper( if (isFinished()) { return NoOpSpan.getInstance(); } + final @NotNull SpanContext spanContext = + context.copyForChild(operation, getSpanContext().getSpanId(), null); + spanContext.setDescription(description); - return scopes - .getOptions() - .getSpanFactory() - .createSpan(operation, description, scopes, spanOptions, context, this); + return startChild(spanContext, spanOptions); + } + + @Override + public @NotNull ISpan startChild( + @NotNull SpanContext spanContext, @NotNull SpanOptions spanOptions) { + if (isFinished()) { + return NoOpSpan.getInstance(); + } + + return scopes.getOptions().getSpanFactory().createSpan(scopes, spanOptions, spanContext, this); } @Override @@ -114,7 +124,15 @@ public OtelSpanWrapper( @Nullable String description, @Nullable SentryDate timestamp, @NotNull Instrumenter instrumenter) { - return startChild(operation, description, timestamp, instrumenter, new SpanOptions()); + final @NotNull SpanContext spanContext = + context.copyForChild(operation, getSpanContext().getSpanId(), null); + spanContext.setDescription(description); + spanContext.setInstrumenter(instrumenter); + + final @NotNull SpanOptions spanOptions = new SpanOptions(); + spanOptions.setStartTimestamp(timestamp); + + return startChild(spanContext, spanOptions); } @Override @@ -124,31 +142,25 @@ public OtelSpanWrapper( @Nullable SentryDate timestamp, @NotNull Instrumenter instrumenter, @NotNull SpanOptions spanOptions) { - if (isFinished()) { - return NoOpSpan.getInstance(); - } - if (timestamp != null) { spanOptions.setStartTimestamp(timestamp); } - // TODO [POTEL] use instrumenter - return scopes - .getOptions() - .getSpanFactory() - .createSpan(operation, description, scopes, spanOptions, context, this); + final @NotNull SpanContext spanContext = + context.copyForChild(operation, getSpanContext().getSpanId(), null); + spanContext.setDescription(description); + spanContext.setInstrumenter(instrumenter); + + return startChild(spanContext, spanOptions); } @Override public @NotNull ISpan startChild(@NotNull String operation, @Nullable String description) { - if (isFinished()) { - return NoOpSpan.getInstance(); - } + final @NotNull SpanContext spanContext = + context.copyForChild(operation, getSpanContext().getSpanId(), null); + spanContext.setDescription(description); - return scopes - .getOptions() - .getSpanFactory() - .createSpan(operation, description, scopes, new SpanOptions(), context, this); + return startChild(spanContext, new SpanOptions()); } @Override diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java index 5ee8a4916a..fd7110d8ec 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java @@ -45,6 +45,12 @@ public OtelTransactionSpanForwarder(final @NotNull OtelSpanWrapper rootSpan) { return rootSpan.startChild(operation, description, spanOptions); } + @Override + public @NotNull ISpan startChild( + @NotNull SpanContext spanContext, @NotNull SpanOptions spanOptions) { + return rootSpan.startChild(spanContext, spanOptions); + } + @Override public @NotNull ISpan startChild( @NotNull String operation, diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index d10f931002..2c62ab45e6 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -23,6 +23,7 @@ import io.sentry.SentryLevel; import io.sentry.SentryLongDate; import io.sentry.SpanId; +import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.TransactionContext; import io.sentry.TransactionOptions; @@ -241,11 +242,23 @@ private void createAndFinishSpanForOtelSpan( spanData.getParentSpanId()); final @NotNull SentryDate startDate = new SentryLongDate(spanData.getStartEpochNanos()); // TODO [POTEL] op and description might have been overriden - // TODO [POTEL] ensure not sampling again - // TODO [POTEL] use OTel span ID so tracing actually has value - final @NotNull ISpan sentryChildSpan = - parentSentrySpan.startChild( - spanInfo.getOp(), spanInfo.getDescription(), startDate, Instrumenter.OTEL); + final @NotNull io.sentry.SpanContext spanContext = + parentSentrySpan + .getSpanContext() + .copyForChild( + spanInfo.getOp(), + parentSentrySpan.getSpanContext().getSpanId(), + new SpanId(spanId)); + spanContext.setDescription(spanInfo.getDescription()); + spanContext.setInstrumenter(Instrumenter.OTEL); + if (sentrySpanMaybe != null) { + spanContext.setSamplingDecision(sentrySpanMaybe.getSamplingDecision()); + } + + final @NotNull SpanOptions spanOptions = new SpanOptions(); + spanOptions.setStartTimestamp(startDate); + + final @NotNull ISpan sentryChildSpan = parentSentrySpan.startChild(spanContext, spanOptions); // TODO [POTEL] Check if we want to use `instrumentationScopeInfo.name` and append it to // `auto.otel` @@ -359,6 +372,9 @@ private void transferSpanDetails( transactionContext.setTransactionNameSource(transactionNameSource); transactionContext.setOperation(spanInfo.getOp()); transactionContext.setInstrumenter(Instrumenter.OTEL); + if (sentrySpanMaybe != null) { + transactionContext.setSamplingDecision(sentrySpanMaybe.getSamplingDecision()); + } TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setStartTimestamp(new SentryLongDate(span.getStartEpochNanos())); diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 85f95429cc..44117d931a 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -367,7 +367,7 @@ public final class io/sentry/DefaultScopesStorage : io/sentry/IScopesStorage { public final class io/sentry/DefaultSpanFactory : io/sentry/ISpanFactory { public fun ()V - public fun createSpan (Ljava/lang/String;Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/SpanContext;Lio/sentry/ISpan;)Lio/sentry/ISpan; + public fun createSpan (Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/SpanContext;Lio/sentry/ISpan;)Lio/sentry/ISpan; public fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; public fun retrieveCurrentSpan (Lio/sentry/IScope;)Lio/sentry/ISpan; public fun retrieveCurrentSpan (Lio/sentry/IScopes;)Lio/sentry/ISpan; @@ -980,6 +980,7 @@ public abstract interface class io/sentry/ISpan { public abstract fun setStatus (Lio/sentry/SpanStatus;)V public abstract fun setTag (Ljava/lang/String;Ljava/lang/String;)V public abstract fun setThrowable (Ljava/lang/Throwable;)V + public abstract fun startChild (Lio/sentry/SpanContext;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; public abstract fun startChild (Ljava/lang/String;)Lio/sentry/ISpan; public abstract fun startChild (Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ISpan; public abstract fun startChild (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryDate;Lio/sentry/Instrumenter;)Lio/sentry/ISpan; @@ -992,7 +993,7 @@ public abstract interface class io/sentry/ISpan { } public abstract interface class io/sentry/ISpanFactory { - public abstract fun createSpan (Ljava/lang/String;Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/SpanContext;Lio/sentry/ISpan;)Lio/sentry/ISpan; + public abstract fun createSpan (Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/SpanContext;Lio/sentry/ISpan;)Lio/sentry/ISpan; public abstract fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; public abstract fun retrieveCurrentSpan (Lio/sentry/IScope;)Lio/sentry/ISpan; public abstract fun retrieveCurrentSpan (Lio/sentry/IScopes;)Lio/sentry/ISpan; @@ -1581,6 +1582,7 @@ public final class io/sentry/NoOpSpan : io/sentry/ISpan { public fun setStatus (Lio/sentry/SpanStatus;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V public fun setThrowable (Ljava/lang/Throwable;)V + public fun startChild (Lio/sentry/SpanContext;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; 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/Instrumenter;)Lio/sentry/ISpan; @@ -1634,6 +1636,7 @@ public final class io/sentry/NoOpTransaction : io/sentry/ITransaction { public fun setStatus (Lio/sentry/SpanStatus;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V public fun setThrowable (Ljava/lang/Throwable;)V + public fun startChild (Lio/sentry/SpanContext;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; 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; @@ -3005,6 +3008,7 @@ public final class io/sentry/SentryTracer : io/sentry/ITransaction { public fun setStatus (Lio/sentry/SpanStatus;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V public fun setThrowable (Ljava/lang/Throwable;)V + public fun startChild (Lio/sentry/SpanContext;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; 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; @@ -3136,6 +3140,7 @@ public final class io/sentry/Span : io/sentry/ISpan { public fun setStatus (Lio/sentry/SpanStatus;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V public fun setThrowable (Ljava/lang/Throwable;)V + public fun startChild (Lio/sentry/SpanContext;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; 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/Instrumenter;)Lio/sentry/ISpan; @@ -3148,6 +3153,7 @@ public final class io/sentry/Span : io/sentry/ISpan { } public class io/sentry/SpanContext : io/sentry/JsonSerializable, io/sentry/JsonUnknown { + public static final field DEFAULT_ORIGIN Ljava/lang/String; public static final field TYPE Ljava/lang/String; protected field description Ljava/lang/String; protected field op Ljava/lang/String; @@ -3159,8 +3165,10 @@ public class io/sentry/SpanContext : io/sentry/JsonSerializable, io/sentry/JsonU public fun (Lio/sentry/protocol/SentryId;Lio/sentry/SpanId;Ljava/lang/String;Lio/sentry/SpanId;Lio/sentry/TracesSamplingDecision;)V public fun (Ljava/lang/String;)V public fun (Ljava/lang/String;Lio/sentry/TracesSamplingDecision;)V + public fun copyForChild (Ljava/lang/String;Lio/sentry/SpanId;Lio/sentry/SpanId;)Lio/sentry/SpanContext; public fun equals (Ljava/lang/Object;)Z public fun getDescription ()Ljava/lang/String; + public fun getInstrumenter ()Lio/sentry/Instrumenter; public fun getOperation ()Ljava/lang/String; public fun getOrigin ()Ljava/lang/String; public fun getParentSpanId ()Lio/sentry/SpanId; @@ -3175,6 +3183,7 @@ public class io/sentry/SpanContext : io/sentry/JsonSerializable, io/sentry/JsonU public fun hashCode ()I public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V public fun setDescription (Ljava/lang/String;)V + public fun setInstrumenter (Lio/sentry/Instrumenter;)V public fun setOperation (Ljava/lang/String;)V public fun setOrigin (Ljava/lang/String;)V public fun setSampled (Ljava/lang/Boolean;)V @@ -3364,14 +3373,12 @@ public final class io/sentry/TransactionContext : io/sentry/SpanContext { public static fun fromPropagationContext (Lio/sentry/PropagationContext;)Lio/sentry/TransactionContext; public static fun fromSentryTrace (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryTraceHeader;)Lio/sentry/TransactionContext; public fun getBaggage ()Lio/sentry/Baggage; - public fun getInstrumenter ()Lio/sentry/Instrumenter; public fun getName ()Ljava/lang/String; public fun getParentSampled ()Ljava/lang/Boolean; public fun getParentSamplingDecision ()Lio/sentry/TracesSamplingDecision; public fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; public fun isForNextAppStart ()Z public fun setForNextAppStart (Z)V - public fun setInstrumenter (Lio/sentry/Instrumenter;)V public fun setName (Ljava/lang/String;)V public fun setParentSampled (Ljava/lang/Boolean;)V public fun setParentSampled (Ljava/lang/Boolean;Ljava/lang/Boolean;)V diff --git a/sentry/src/main/java/io/sentry/DefaultSpanFactory.java b/sentry/src/main/java/io/sentry/DefaultSpanFactory.java index da2b3da979..faefc5c75f 100644 --- a/sentry/src/main/java/io/sentry/DefaultSpanFactory.java +++ b/sentry/src/main/java/io/sentry/DefaultSpanFactory.java @@ -17,8 +17,6 @@ public final class DefaultSpanFactory implements ISpanFactory { @Override public @NotNull ISpan createSpan( - final @NotNull String name, - final @Nullable String description, final @NotNull IScopes scopes, final @NotNull SpanOptions spanOptions, final @NotNull SpanContext spanContext, diff --git a/sentry/src/main/java/io/sentry/ISpan.java b/sentry/src/main/java/io/sentry/ISpan.java index fedc725455..7fc56cc01f 100644 --- a/sentry/src/main/java/io/sentry/ISpan.java +++ b/sentry/src/main/java/io/sentry/ISpan.java @@ -24,6 +24,10 @@ public interface ISpan { ISpan startChild( @NotNull String operation, @Nullable String description, @NotNull SpanOptions spanOptions); + @ApiStatus.Internal + @NotNull + ISpan startChild(@NotNull SpanContext spanContext, @NotNull SpanOptions spanOptions); + @ApiStatus.Internal @NotNull ISpan startChild( diff --git a/sentry/src/main/java/io/sentry/ISpanFactory.java b/sentry/src/main/java/io/sentry/ISpanFactory.java index ece928a147..53bffbb57b 100644 --- a/sentry/src/main/java/io/sentry/ISpanFactory.java +++ b/sentry/src/main/java/io/sentry/ISpanFactory.java @@ -15,8 +15,6 @@ ITransaction createTransaction( @NotNull ISpan createSpan( - @NotNull String name, - @Nullable String description, @NotNull IScopes scopes, @NotNull SpanOptions spanOptions, @NotNull SpanContext spanContext, diff --git a/sentry/src/main/java/io/sentry/NoOpSpan.java b/sentry/src/main/java/io/sentry/NoOpSpan.java index 8af2aecb46..494d274a42 100644 --- a/sentry/src/main/java/io/sentry/NoOpSpan.java +++ b/sentry/src/main/java/io/sentry/NoOpSpan.java @@ -29,6 +29,12 @@ public static NoOpSpan getInstance() { return NoOpSpan.getInstance(); } + @Override + public @NotNull ISpan startChild( + @NotNull SpanContext spanContext, @NotNull SpanOptions spanOptions) { + return NoOpSpan.getInstance(); + } + @Override public @NotNull ISpan startChild( @NotNull String operation, diff --git a/sentry/src/main/java/io/sentry/NoOpTransaction.java b/sentry/src/main/java/io/sentry/NoOpTransaction.java index 2984af7316..2693e47aed 100644 --- a/sentry/src/main/java/io/sentry/NoOpTransaction.java +++ b/sentry/src/main/java/io/sentry/NoOpTransaction.java @@ -53,6 +53,12 @@ public void setName(@NotNull String name, @NotNull TransactionNameSource transac return NoOpSpan.getInstance(); } + @Override + public @NotNull ISpan startChild( + @NotNull SpanContext spanContext, @NotNull SpanOptions spanOptions) { + return NoOpSpan.getInstance(); + } + @Override public @NotNull ISpan startChild( @NotNull String operation, diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index ba1a952c98..c39036e6db 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -373,8 +373,15 @@ ISpan startChild( final @Nullable String description, final @Nullable SentryDate timestamp, final @NotNull Instrumenter instrumenter) { - return createChild( - parentSpanId, operation, description, timestamp, instrumenter, new SpanOptions()); + final @NotNull SpanContext spanContext = + getSpanContext().copyForChild(operation, parentSpanId, null); + spanContext.setDescription(description); + spanContext.setInstrumenter(instrumenter); + + final @NotNull SpanOptions spanOptions = new SpanOptions(); + spanOptions.setStartTimestamp(timestamp); + + return createChild(spanContext, spanOptions); } @NotNull @@ -385,7 +392,14 @@ ISpan startChild( final @Nullable SentryDate timestamp, final @NotNull Instrumenter instrumenter, final @NotNull SpanOptions spanOptions) { - return createChild(parentSpanId, operation, description, timestamp, instrumenter, spanOptions); + final @NotNull SpanContext spanContext = + getSpanContext().copyForChild(operation, parentSpanId, null); + spanContext.setDescription(description); + spanContext.setInstrumenter(instrumenter); + + spanOptions.setStartTimestamp(timestamp); + + return createChild(spanContext, spanOptions); } /** @@ -403,37 +417,38 @@ private ISpan createChild( final @NotNull String operation, final @Nullable String description, final @NotNull SpanOptions options) { - return createChild(parentSpanId, operation, description, null, Instrumenter.SENTRY, options); + final @NotNull SpanContext spanContext = + getSpanContext().copyForChild(operation, parentSpanId, null); + spanContext.setDescription(description); + spanContext.setInstrumenter(Instrumenter.SENTRY); + + return createChild(spanContext, options); } @NotNull private ISpan createChild( - final @NotNull SpanId parentSpanId, - final @NotNull String operation, - final @Nullable String description, - @Nullable SentryDate timestamp, - final @NotNull Instrumenter instrumenter, - final @NotNull SpanOptions spanOptions) { + final @NotNull SpanContext spanContext, final @NotNull SpanOptions spanOptions) { if (root.isFinished()) { return NoOpSpan.getInstance(); } - if (!this.instrumenter.equals(instrumenter)) { + if (!this.instrumenter.equals(spanContext.getInstrumenter())) { return NoOpSpan.getInstance(); } + final @Nullable SpanId parentSpanId = spanContext.getParentSpanId(); + final @NotNull String operation = spanContext.getOperation(); + final @Nullable String description = spanContext.getDescription(); + if (children.size() < scopes.getOptions().getMaxSpans()) { Objects.requireNonNull(parentSpanId, "parentSpanId is required"); - Objects.requireNonNull(operation, "operation is required"); + // Objects.requireNonNull(operation, "operation is required"); cancelIdleTimer(); final Span span = new Span( - root.getTraceId(), - parentSpanId, this, - operation, - this.scopes, - timestamp, + scopes, + spanContext, spanOptions, finishingSpan -> { if (transactionPerformanceCollector != null) { @@ -451,7 +466,34 @@ private ISpan createChild( finish(finishStatus.spanStatus); } }); - span.setDescription(description); + // final Span span = + // new Span( + // root.getTraceId(), + // parentSpanId, + // this, + // operation, + // this.scopes, + // timestamp, + // spanOptions, + // finishingSpan -> { + // if (transactionPerformanceCollector != null) { + // transactionPerformanceCollector.onSpanFinished(finishingSpan); + // } + // final FinishStatus finishStatus = this.finishStatus; + // if (transactionOptions.getIdleTimeout() != null) { + // // if it's an idle transaction, no matter the status, we'll reset the + // timeout here + // // so the transaction will either idle and finish itself, or a new child + // will be + // // added and we'll wait for it again + // if (!transactionOptions.isWaitForChildren() || hasAllChildrenFinished()) { + // scheduleFinish(); + // } + // } else if (finishStatus.isFinishing) { + // finish(finishStatus.spanStatus); + // } + // }); + // span.setDescription(description); span.setData(SpanDataConvention.THREAD_ID, String.valueOf(Thread.currentThread().getId())); span.setData( SpanDataConvention.THREAD_NAME, @@ -520,6 +562,12 @@ private ISpan createChild( return createChild(operation, description, null, Instrumenter.SENTRY, spanOptions); } + @Override + public @NotNull ISpan startChild( + @NotNull SpanContext spanContext, @NotNull SpanOptions spanOptions) { + return createChild(spanContext, spanOptions); + } + private @NotNull ISpan createChild( final @NotNull String operation, final @Nullable String description, diff --git a/sentry/src/main/java/io/sentry/Span.java b/sentry/src/main/java/io/sentry/Span.java index 686857447c..2d8e043dc8 100644 --- a/sentry/src/main/java/io/sentry/Span.java +++ b/sentry/src/main/java/io/sentry/Span.java @@ -54,31 +54,50 @@ public final class Span implements ISpan { private final @NotNull LazyEvaluator metricsAggregator = new LazyEvaluator<>(() -> new LocalMetricsAggregator()); - Span( - final @NotNull SentryId traceId, - final @Nullable SpanId parentSpanId, - final @NotNull SentryTracer transaction, - final @NotNull String operation, - final @NotNull IScopes scopes) { - this(traceId, parentSpanId, transaction, operation, scopes, null, new SpanOptions(), null); - } + // Span( + // final @NotNull SentryId traceId, + // final @Nullable SpanId parentSpanId, + // final @NotNull SentryTracer transaction, + // final @NotNull String operation, + // final @NotNull IScopes scopes) { + // this(traceId, parentSpanId, transaction, operation, scopes, null, new SpanOptions(), null); + // } + + // Span( + // final @NotNull SentryId traceId, + // final @Nullable SpanId parentSpanId, + // final @NotNull SentryTracer transaction, + // final @NotNull String operation, + // final @NotNull IScopes scopes, + // final @Nullable SentryDate startTimestamp, + // final @NotNull SpanOptions options, + // final @Nullable SpanFinishedCallback spanFinishedCallback) { + // this.context = + // new SpanContext( + // traceId, new SpanId(), operation, parentSpanId, transaction.getSamplingDecision()); + // this.transaction = Objects.requireNonNull(transaction, "transaction is required"); + // this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); + // this.options = options; + // this.spanFinishedCallback = spanFinishedCallback; + // if (startTimestamp != null) { + // this.startTimestamp = startTimestamp; + // } else { + // this.startTimestamp = scopes.getOptions().getDateProvider().now(); + // } + // } Span( - final @NotNull SentryId traceId, - final @Nullable SpanId parentSpanId, final @NotNull SentryTracer transaction, - final @NotNull String operation, final @NotNull IScopes scopes, - final @Nullable SentryDate startTimestamp, + final @NotNull SpanContext spanContext, final @NotNull SpanOptions options, final @Nullable SpanFinishedCallback spanFinishedCallback) { - this.context = - new SpanContext( - traceId, new SpanId(), operation, parentSpanId, transaction.getSamplingDecision()); + this.context = spanContext; this.transaction = Objects.requireNonNull(transaction, "transaction is required"); this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); this.options = options; this.spanFinishedCallback = spanFinishedCallback; + final @Nullable SentryDate startTimestamp = options.getStartTimestamp(); if (startTimestamp != null) { this.startTimestamp = startTimestamp; } else { @@ -153,6 +172,12 @@ public Span( return transaction.startChild(context.getSpanId(), operation, description, spanOptions); } + @Override + public @NotNull ISpan startChild( + @NotNull SpanContext spanContext, @NotNull SpanOptions spanOptions) { + return transaction.startChild(spanContext, spanOptions); + } + @Override public @NotNull ISpan startChild( @NotNull String operation, diff --git a/sentry/src/main/java/io/sentry/SpanContext.java b/sentry/src/main/java/io/sentry/SpanContext.java index 5d00b8d59b..1b11d10e5c 100644 --- a/sentry/src/main/java/io/sentry/SpanContext.java +++ b/sentry/src/main/java/io/sentry/SpanContext.java @@ -16,6 +16,7 @@ @Open public class SpanContext implements JsonUnknown, JsonSerializable { public static final String TYPE = "trace"; + public static final String DEFAULT_ORIGIN = "manual"; /** Determines which trace the Span belongs to. */ private final @NotNull SentryId traceId; @@ -24,7 +25,7 @@ public class SpanContext implements JsonUnknown, JsonSerializable { private final @NotNull SpanId spanId; /** Id of a parent span. */ - private final @Nullable SpanId parentSpanId; + private @Nullable SpanId parentSpanId; private transient @Nullable TracesSamplingDecision samplingDecision; @@ -44,10 +45,12 @@ public class SpanContext implements JsonUnknown, JsonSerializable { protected @NotNull Map tags = new ConcurrentHashMap<>(); /** Describes the status of the Transaction. */ - protected @Nullable String origin = "manual"; + protected @Nullable String origin = DEFAULT_ORIGIN; private @Nullable Map unknown; + private @NotNull Instrumenter instrumenter = Instrumenter.SENTRY; + public SpanContext( final @NotNull String operation, final @Nullable TracesSamplingDecision samplingDecision) { this(new SentryId(), new SpanId(), operation, null, samplingDecision); @@ -68,7 +71,7 @@ public SpanContext( final @NotNull String operation, final @Nullable SpanId parentSpanId, final @Nullable TracesSamplingDecision samplingDecision) { - this(traceId, spanId, parentSpanId, operation, null, samplingDecision, null, "manual"); + this(traceId, spanId, parentSpanId, operation, null, samplingDecision, null, DEFAULT_ORIGIN); } @ApiStatus.Internal @@ -214,6 +217,30 @@ public void setOrigin(final @Nullable String origin) { this.origin = origin; } + public @NotNull Instrumenter getInstrumenter() { + return instrumenter; + } + + public void setInstrumenter(final @NotNull Instrumenter instrumenter) { + this.instrumenter = instrumenter; + } + + @ApiStatus.Internal + public SpanContext copyForChild( + final @NotNull String operation, + final @Nullable SpanId parentSpanId, + final @Nullable SpanId spanId) { + return new SpanContext( + traceId, + spanId == null ? new SpanId() : spanId, + parentSpanId, + operation, + null, + samplingDecision, + null, + DEFAULT_ORIGIN); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/sentry/src/main/java/io/sentry/TransactionContext.java b/sentry/src/main/java/io/sentry/TransactionContext.java index 9dfebd3453..6d63942baf 100644 --- a/sentry/src/main/java/io/sentry/TransactionContext.java +++ b/sentry/src/main/java/io/sentry/TransactionContext.java @@ -17,7 +17,6 @@ public final class TransactionContext extends SpanContext { private @NotNull TransactionNameSource transactionNameSource; private @Nullable TracesSamplingDecision parentSamplingDecision; private @Nullable Baggage baggage; - private @NotNull Instrumenter instrumenter = Instrumenter.SENTRY; private boolean isForNextAppStart = false; /** @@ -186,14 +185,6 @@ public void setParentSampled( return transactionNameSource; } - public @NotNull Instrumenter getInstrumenter() { - return instrumenter; - } - - public void setInstrumenter(final @NotNull Instrumenter instrumenter) { - this.instrumenter = instrumenter; - } - public void setName(final @NotNull String name) { this.name = Objects.requireNonNull(name, "name is required"); } diff --git a/sentry/src/test/java/io/sentry/SpanTest.kt b/sentry/src/test/java/io/sentry/SpanTest.kt index fd36c31933..1cde4a8f0e 100644 --- a/sentry/src/test/java/io/sentry/SpanTest.kt +++ b/sentry/src/test/java/io/sentry/SpanTest.kt @@ -33,13 +33,20 @@ class SpanTest { } fun getSut(options: SpanOptions = SpanOptions()): Span { - return Span( + val context = SpanContext( SentryId(), SpanId(), - SentryTracer(TransactionContext("name", "op"), scopes), + SpanId(), "op", - scopes, null, + null, + null, + null + ) + return Span( + SentryTracer(TransactionContext("name", "op"), scopes), + scopes, + context, options, null ) @@ -101,15 +108,25 @@ class SpanTest { fun `converts to Sentry trace header`() { val traceId = SentryId() val parentSpanId = SpanId() - val span = Span( + val spanContext = SpanContext( traceId, + SpanId(), parentSpanId, + "op", + null, + TracesSamplingDecision(true), + null, + null + ) + val span = Span( SentryTracer( TransactionContext("name", "op", TracesSamplingDecision(true)), fixture.scopes ), - "op", - fixture.scopes + fixture.scopes, + spanContext, + SpanOptions(), + null ) val sentryTrace = span.toSentryTrace() From 210c9924e769bb65229391c39843f845168a7499 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 18 Jun 2024 07:47:39 +0200 Subject: [PATCH 61/91] POTEL 9 - Tracing Fixes and Baggage (#3455) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing * Inherit OTel span IDs when sending to sentry * Fix tracing; parse incoming baggage; add baggage to outgoing --- .../api/sentry-opentelemetry-bootstrap.api | 6 +- .../InternalSemanticAttributes.java | 3 + .../sentry/opentelemetry/OtelSpanContext.java | 5 +- .../sentry/opentelemetry/OtelSpanFactory.java | 8 ++- .../sentry/opentelemetry/OtelSpanWrapper.java | 56 +++++++++++++++++-- .../OtelTransactionSpanForwarder.java | 3 - .../opentelemetry/PotelSentryPropagator.java | 8 ++- .../PotelSentrySpanProcessor.java | 35 +++++++++++- .../opentelemetry/SentrySpanExporter.java | 31 +++------- sentry/api/sentry.api | 5 +- sentry/src/main/java/io/sentry/Baggage.java | 13 ++--- .../src/main/java/io/sentry/SentryTracer.java | 7 ++- .../src/main/java/io/sentry/SpanContext.java | 6 ++ .../java/io/sentry/TransactionContext.java | 5 -- .../sentry/TraceContextSerializationTest.kt | 7 ++- 15 files changed, 142 insertions(+), 56 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api index b5135d9d32..110faac564 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api @@ -1,4 +1,6 @@ public final class io/sentry/opentelemetry/InternalSemanticAttributes { + public static final field BAGGAGE Lio/opentelemetry/api/common/AttributeKey; + public static final field BAGGAGE_MUTABLE Lio/opentelemetry/api/common/AttributeKey; public static final field IS_REMOTE_PARENT Lio/opentelemetry/api/common/AttributeKey; public static final field PARENT_SAMPLED Lio/opentelemetry/api/common/AttributeKey; public static final field PROFILE_SAMPLED Lio/opentelemetry/api/common/AttributeKey; @@ -16,7 +18,7 @@ public final class io/sentry/opentelemetry/OtelContextScopesStorage : io/sentry/ } public final class io/sentry/opentelemetry/OtelSpanContext : io/sentry/SpanContext { - public fun (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/TracesSamplingDecision;Lio/sentry/opentelemetry/OtelSpanWrapper;)V + public fun (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/TracesSamplingDecision;Lio/sentry/opentelemetry/OtelSpanWrapper;Lio/sentry/Baggage;)V public fun getStatus ()Lio/sentry/SpanStatus; public fun setStatus (Lio/sentry/SpanStatus;)V } @@ -30,7 +32,7 @@ public final class io/sentry/opentelemetry/OtelSpanFactory : io/sentry/ISpanFact } public final class io/sentry/opentelemetry/OtelSpanWrapper : io/sentry/ISpan { - public fun (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/IScopes;Lio/sentry/SentryDate;Lio/sentry/TracesSamplingDecision;Lio/sentry/opentelemetry/OtelSpanWrapper;)V + public fun (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/IScopes;Lio/sentry/SentryDate;Lio/sentry/TracesSamplingDecision;Lio/sentry/opentelemetry/OtelSpanWrapper;Lio/sentry/Baggage;)V public fun finish ()V public fun finish (Lio/sentry/SpanStatus;)V public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java index e21db174f1..f9d04c3724 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java @@ -18,6 +18,9 @@ public final class InternalSemanticAttributes { AttributeKey.doubleKey("sentry.profile_sample_rate"); public static final AttributeKey IS_REMOTE_PARENT = AttributeKey.booleanKey("sentry.is_remote_parent"); + public static final AttributeKey BAGGAGE = AttributeKey.stringKey("sentry.baggage"); + public static final AttributeKey BAGGAGE_MUTABLE = + AttributeKey.booleanKey("sentry.baggage_mutable"); // public static final AttributeKey BREADCRUMB_TYPE = // AttributeKey.stringKey("sentry.breadcrumb.type"); // public static final AttributeKey BREADCRUMB_TYPE = diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java index 2d1bd78b95..127dbfd57e 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java @@ -4,6 +4,7 @@ import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.sdk.trace.ReadWriteSpan; import io.opentelemetry.sdk.trace.data.StatusData; +import io.sentry.Baggage; import io.sentry.SpanContext; import io.sentry.SpanId; import io.sentry.SpanStatus; @@ -26,7 +27,8 @@ public final class OtelSpanContext extends SpanContext { public OtelSpanContext( final @NotNull ReadWriteSpan span, final @Nullable TracesSamplingDecision samplingDecision, - final @Nullable OtelSpanWrapper parentSpan) { + final @Nullable OtelSpanWrapper parentSpan, + final @Nullable Baggage baggage) { super( new SentryId(span.getSpanContext().getTraceId()), new SpanId(span.getSpanContext().getSpanId()), @@ -39,6 +41,7 @@ public OtelSpanContext( null, null); this.span = new WeakReference<>(span); + this.baggage = baggage; } @Override diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java index 3467789c63..8c8fca4ba4 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java @@ -7,6 +7,7 @@ import io.opentelemetry.api.trace.TraceState; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Context; +import io.sentry.Baggage; import io.sentry.IScope; import io.sentry.IScopes; import io.sentry.ISpan; @@ -104,7 +105,12 @@ public final class OtelSpanFactory implements ISpanFactory { } } - // TODO [POTEL] send baggage in (note: won't go through propagators) + // note: won't go through propagators + final @Nullable Baggage baggage = spanContext.getBaggage(); + if (baggage != null) { + spanBuilder.setAttribute(InternalSemanticAttributes.BAGGAGE_MUTABLE, baggage.isMutable()); + spanBuilder.setAttribute(InternalSemanticAttributes.BAGGAGE, baggage.toHeaderString(null)); + } final @Nullable SentryDate startTimestampFromOptions = spanOptions.getStartTimestamp(); final @NotNull SentryDate startTimestamp = diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java index 8ec6c6bb0b..a0d6c0a9d4 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java @@ -3,6 +3,7 @@ import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Scope; import io.opentelemetry.sdk.trace.ReadWriteSpan; +import io.sentry.Baggage; import io.sentry.BaggageHeader; import io.sentry.IScopes; import io.sentry.ISentryLifecycleToken; @@ -25,6 +26,7 @@ import io.sentry.protocol.MeasurementValue; import io.sentry.protocol.SentryId; import io.sentry.protocol.TransactionNameSource; +import io.sentry.protocol.User; import io.sentry.util.LazyEvaluator; import io.sentry.util.Objects; import java.lang.ref.WeakReference; @@ -34,6 +36,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -62,6 +65,7 @@ public final class OtelSpanWrapper implements ISpan { private final @NotNull Contexts contexts = new Contexts(); private @Nullable String transactionName; private @Nullable TransactionNameSource transactionNameSource; + private final @Nullable Baggage baggage; // TODO [POTEL] // private @Nullable SpanFinishedCallback spanFinishedCallback; @@ -78,16 +82,28 @@ public final class OtelSpanWrapper implements ISpan { private @NotNull Deque tokensToCleanup = new ArrayDeque<>(1); + // TODO [POTEL] reference root span? for getting root baggage public OtelSpanWrapper( final @NotNull ReadWriteSpan span, final @NotNull IScopes scopes, final @NotNull SentryDate startTimestamp, final @Nullable TracesSamplingDecision samplingDecision, - final @Nullable OtelSpanWrapper parentSpan) { + final @Nullable OtelSpanWrapper parentSpan, + final @Nullable Baggage baggage) { this.scopes = Objects.requireNonNull(scopes, "scopes are required"); this.span = new WeakReference<>(span); this.startTimestamp = startTimestamp; - this.context = new OtelSpanContext(span, samplingDecision, parentSpan); + + if (parentSpan != null) { + this.baggage = parentSpan.getSpanContext().getBaggage(); + } else if (baggage != null) { + this.baggage = baggage; + } else { + this.baggage = null; + // this.baggage = new Baggage(scopes.getOptions().getLogger()); + } + + this.context = new OtelSpanContext(span, samplingDecision, parentSpan, this.baggage); } @Override @@ -178,15 +194,43 @@ public OtelSpanWrapper( @Override public @Nullable TraceContext traceContext() { - // return transaction.traceContext(); - // TODO [POTEL] + if (scopes.getOptions().isTraceSampling()) { + if (baggage != null) { + updateBaggageValues(); + return baggage.toTraceContext(); + } + } return null; } + private void updateBaggageValues() { + synchronized (this) { + if (baggage != null && baggage.isMutable()) { + final AtomicReference userAtomicReference = new AtomicReference<>(); + scopes.configureScope( + scope -> { + userAtomicReference.set(scope.getUser()); + }); + baggage.setValuesFromTransaction( + getSpanContext().getTraceId(), + userAtomicReference.get(), + scopes.getOptions(), + this.getSamplingDecision(), + getTransactionName(), + getTransactionNameSource()); + baggage.freeze(); + } + } + } + @Override public @Nullable BaggageHeader toBaggageHeader(@Nullable List thirdPartyBaggageHeaders) { - // return transaction.toBaggageHeader(thirdPartyBaggageHeaders); - // TODO [POTEL] + if (scopes.getOptions().isTraceSampling()) { + if (baggage != null) { + updateBaggageValues(); + return BaggageHeader.fromBaggageAndOutgoingHeader(baggage, thirdPartyBaggageHeaders); + } + } return null; } diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java index fd7110d8ec..beba25374a 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java @@ -77,19 +77,16 @@ public OtelTransactionSpanForwarder(final @NotNull OtelSpanWrapper rootSpan) { @Override public @NotNull SentryTraceHeader toSentryTrace() { - // TODO [POTEL] root span? return rootSpan.toSentryTrace(); } @Override public @Nullable TraceContext traceContext() { - // TODO [POTEL] root span? return rootSpan.traceContext(); } @Override public @Nullable BaggageHeader toBaggageHeader(@Nullable List thirdPartyBaggageHeaders) { - // TODO [POTEL] root span? return rootSpan.toBaggageHeader(thirdPartyBaggageHeaders); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentryPropagator.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentryPropagator.java index 1cc4b1c411..3a9d0718f4 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentryPropagator.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentryPropagator.java @@ -10,6 +10,7 @@ import io.opentelemetry.context.propagation.TextMapGetter; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.context.propagation.TextMapSetter; +import io.sentry.Baggage; import io.sentry.BaggageHeader; import io.sentry.IScopes; import io.sentry.PropagationContext; @@ -100,6 +101,7 @@ public Context extract( SentryTraceHeader sentryTraceHeader = new SentryTraceHeader(sentryTraceString); final @Nullable String baggageString = getter.get(carrier, BaggageHeader.BAGGAGE_HEADER); + final Baggage baggage = Baggage.fromHeader(baggageString); final @NotNull TraceState traceState = TraceState.getDefault(); SpanContext otelSpanContext = @@ -112,7 +114,11 @@ public Context extract( Span wrappedSpan = Span.wrap(otelSpanContext); final @NotNull Context modifiedContext = - context.with(wrappedSpan).with(SENTRY_SCOPES_KEY, scopesToUse); + context + .with(wrappedSpan) + .with(SENTRY_SCOPES_KEY, scopesToUse) + .with(SentryOtelKeys.SENTRY_TRACE_KEY, sentryTraceHeader) + .with(SentryOtelKeys.SENTRY_BAGGAGE_KEY, baggage); scopes .getOptions() diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java index 9105467cef..2ce773fc38 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java @@ -8,6 +8,7 @@ import io.opentelemetry.sdk.trace.ReadWriteSpan; import io.opentelemetry.sdk.trace.ReadableSpan; import io.opentelemetry.sdk.trace.SpanProcessor; +import io.sentry.Baggage; import io.sentry.IScopes; import io.sentry.PropagationContext; import io.sentry.SamplingContext; @@ -16,6 +17,7 @@ import io.sentry.SentryDate; import io.sentry.SentryLevel; import io.sentry.SentryLongDate; +import io.sentry.SentryTraceHeader; import io.sentry.SpanId; import io.sentry.TracesSampler; import io.sentry.TracesSamplingDecision; @@ -54,8 +56,20 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri final @Nullable OtelSpanWrapper sentryParentSpan = spanStorage.getSentrySpan(otelSpan.getParentSpanContext()); @Nullable TracesSamplingDecision samplingDecision = null; + // TODO [POTEL] baggage from propagator should be honored + @Nullable Baggage baggage = null; otelSpan.setAttribute(IS_REMOTE_PARENT, otelSpan.getParentSpanContext().isRemote()); if (sentryParentSpan == null) { + final @Nullable Boolean baggageMutable = + otelSpan.getAttribute(InternalSemanticAttributes.BAGGAGE_MUTABLE); + final @Nullable String baggageString = + otelSpan.getAttribute(InternalSemanticAttributes.BAGGAGE); + if (baggageString != null) { + baggage = Baggage.fromHeader(baggageString); + if (baggageMutable == true) { + baggage.freeze(); + } + } final @Nullable Boolean sampled = otelSpan.getAttribute(InternalSemanticAttributes.SAMPLED); final @Nullable Double sampleRate = otelSpan.getAttribute(InternalSemanticAttributes.SAMPLE_RATE); @@ -73,7 +87,11 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri final @NotNull PropagationContext propagationContext = new PropagationContext( - new SentryId(traceId), new SpanId(spanId), new SpanId(parentSpanId), null, sampled); + new SentryId(traceId), + new SpanId(spanId), + new SpanId(parentSpanId), + baggage, + sampled); scopes.configureScope( scope -> { @@ -96,9 +114,19 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri final @NotNull String traceId = otelSpan.getSpanContext().getTraceId(); final @NotNull String spanId = otelSpan.getSpanContext().getSpanId(); + final @NotNull SpanId sentrySpanId = new SpanId(spanId); + + @Nullable + SentryTraceHeader sentryTraceHeader = parentContext.get(SentryOtelKeys.SENTRY_TRACE_KEY); + @Nullable Baggage baggageFromContext = parentContext.get(SentryOtelKeys.SENTRY_BAGGAGE_KEY); + if (sentryTraceHeader != null) { + baggage = baggageFromContext; + } final @NotNull PropagationContext propagationContext = - new PropagationContext(new SentryId(traceId), new SpanId(spanId), null, null, null); + sentryTraceHeader == null + ? new PropagationContext(new SentryId(traceId), sentrySpanId, null, baggage, null) + : PropagationContext.fromHeaders(sentryTraceHeader, baggage, sentrySpanId); scopes.configureScope( scope -> { @@ -118,7 +146,8 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri new SentryLongDate(otelSpan.toSpanData().getStartEpochNanos()); spanStorage.storeSentrySpan( spanContext, - new OtelSpanWrapper(otelSpan, scopes, startTimestamp, samplingDecision, sentryParentSpan)); + new OtelSpanWrapper( + otelSpan, scopes, startTimestamp, samplingDecision, sentryParentSpan, baggage)); } @Override diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index 2c62ab45e6..9b96010c75 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -10,6 +10,7 @@ import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.sdk.trace.export.SpanExporter; import io.opentelemetry.semconv.SemanticAttributes; +import io.sentry.Baggage; import io.sentry.DateUtils; import io.sentry.DefaultSpanFactory; import io.sentry.DsnUtil; @@ -22,6 +23,7 @@ import io.sentry.SentryInstantDate; import io.sentry.SentryLevel; import io.sentry.SentryLongDate; +import io.sentry.SpanContext; import io.sentry.SpanId; import io.sentry.SpanOptions; import io.sentry.SpanStatus; @@ -197,8 +199,6 @@ private List maybeSend(final @NotNull List spans) { createAndFinishSpanForOtelSpan(childNode, transaction, remaining); } - // spanStorage.getScope() - // transaction.finishWithScope transaction.finish( mapOtelStatus(span, transaction), new SentryLongDate(span.getEndEpochNanos())); } @@ -312,7 +312,6 @@ private void transferSpanDetails( private @Nullable ITransaction createTransactionForOtelSpan(final @NotNull SpanData span) { final @NotNull String spanId = span.getSpanId(); final @NotNull String traceId = span.getTraceId(); - // final @Nullable IScope scope = spanStorage.getScope(spanId); final @Nullable OtelSpanWrapper sentrySpanMaybe = spanStorage.getSentrySpan(span.getSpanContext()); @@ -322,15 +321,6 @@ private void transferSpanDetails( scopesMaybe == null ? ScopesAdapter.getInstance() : scopesMaybe; final @NotNull OtelSpanInfo spanInfo = spanDescriptionExtractor.extractSpanInfo(span); - // final @Nullable Boolean parentSampled = - // span.getAttributes().get(InternalSemanticAttributes.PARENT_SAMPLED); - // TODO DSC - // TODO op, desc, tags, data, origin, source - // TODO metadata - - // TODO we'll have to copy some of otel span attributes over to our transaction/span, e.g. - // thread info is wrong because it's created here in the exporter - scopesToUse .getOptions() .getLogger() @@ -341,10 +331,10 @@ private void transferSpanDetails( traceId); final SpanId sentrySpanId = new SpanId(spanId); - // TODO parentSpanId, parentSamplingDecision, baggage - @NotNull String transactionName = spanInfo.getDescription(); @NotNull TransactionNameSource transactionNameSource = spanInfo.getTransactionNameSource(); + @Nullable SpanId parentSpanId = null; + @Nullable Baggage baggage = null; if (sentrySpanMaybe != null) { final @NotNull OtelSpanWrapper sentrySpan = sentrySpanMaybe; @@ -357,16 +347,14 @@ private void transferSpanDetails( if (transactionNameSourceMaybe != null) { transactionNameSource = transactionNameSourceMaybe; } + final @NotNull SpanContext spanContext = sentrySpan.getSpanContext(); + parentSpanId = spanContext.getParentSpanId(); + baggage = spanContext.getBaggage(); } + // TODO [POTEL] parentSamplingDecision? final @NotNull TransactionContext transactionContext = - new TransactionContext(new SentryId(traceId), sentrySpanId, null, null, null); - // traceData.getSentryTraceHeader() == null - // ? new TransactionContext( - // new SentryId(traceData.getTraceId()), spanId, null, null, null) - // : TransactionContext.fromPropagationContext( - // PropagationContext.fromHeaders( - // traceData.getSentryTraceHeader(), traceData.getBaggage(), spanId)); + new TransactionContext(new SentryId(traceId), sentrySpanId, parentSpanId, null, baggage); transactionContext.setName(transactionName); transactionContext.setTransactionNameSource(transactionNameSource); @@ -380,7 +368,6 @@ private void transferSpanDetails( transactionOptions.setStartTimestamp(new SentryLongDate(span.getStartEpochNanos())); transactionOptions.setSpanFactory(new DefaultSpanFactory()); - // TODO [POTEL] do not sample again ITransaction sentryTransaction = scopesToUse.startTransaction(transactionContext, transactionOptions); sentryTransaction.getSpanContext().setOrigin(TRACE_ORIGN); diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 44117d931a..5602ecd097 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -66,7 +66,7 @@ public final class io/sentry/Baggage { public fun setUserId (Ljava/lang/String;)V public fun setUserSegment (Ljava/lang/String;)V public fun setValuesFromScope (Lio/sentry/IScope;Lio/sentry/SentryOptions;)V - public fun setValuesFromTransaction (Lio/sentry/ITransaction;Lio/sentry/protocol/User;Lio/sentry/SentryOptions;Lio/sentry/TracesSamplingDecision;)V + public fun setValuesFromTransaction (Lio/sentry/protocol/SentryId;Lio/sentry/protocol/User;Lio/sentry/SentryOptions;Lio/sentry/TracesSamplingDecision;Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V public fun toHeaderString (Ljava/lang/String;)Ljava/lang/String; public fun toTraceContext ()Lio/sentry/TraceContext; } @@ -3155,6 +3155,7 @@ public final class io/sentry/Span : io/sentry/ISpan { public class io/sentry/SpanContext : io/sentry/JsonSerializable, io/sentry/JsonUnknown { public static final field DEFAULT_ORIGIN Ljava/lang/String; public static final field TYPE Ljava/lang/String; + protected field baggage Lio/sentry/Baggage; protected field description Ljava/lang/String; protected field op Ljava/lang/String; protected field origin Ljava/lang/String; @@ -3167,6 +3168,7 @@ public class io/sentry/SpanContext : io/sentry/JsonSerializable, io/sentry/JsonU public fun (Ljava/lang/String;Lio/sentry/TracesSamplingDecision;)V public fun copyForChild (Ljava/lang/String;Lio/sentry/SpanId;Lio/sentry/SpanId;)Lio/sentry/SpanContext; public fun equals (Ljava/lang/Object;)Z + public fun getBaggage ()Lio/sentry/Baggage; public fun getDescription ()Ljava/lang/String; public fun getInstrumenter ()Lio/sentry/Instrumenter; public fun getOperation ()Ljava/lang/String; @@ -3372,7 +3374,6 @@ public final class io/sentry/TransactionContext : io/sentry/SpanContext { public fun (Ljava/lang/String;Ljava/lang/String;Lio/sentry/TracesSamplingDecision;)V public static fun fromPropagationContext (Lio/sentry/PropagationContext;)Lio/sentry/TransactionContext; public static fun fromSentryTrace (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryTraceHeader;)Lio/sentry/TransactionContext; - public fun getBaggage ()Lio/sentry/Baggage; public fun getName ()Ljava/lang/String; public fun getParentSampled ()Ljava/lang/Boolean; public fun getParentSamplingDecision ()Lio/sentry/TracesSamplingDecision; diff --git a/sentry/src/main/java/io/sentry/Baggage.java b/sentry/src/main/java/io/sentry/Baggage.java index 4a637bacdf..34ab1f0317 100644 --- a/sentry/src/main/java/io/sentry/Baggage.java +++ b/sentry/src/main/java/io/sentry/Baggage.java @@ -371,19 +371,18 @@ public void set(final @NotNull String key, final @Nullable String value) { @ApiStatus.Internal public void setValuesFromTransaction( - final @NotNull ITransaction transaction, + final @NotNull SentryId traceId, final @Nullable User user, final @NotNull SentryOptions sentryOptions, - final @Nullable TracesSamplingDecision samplingDecision) { - setTraceId(transaction.getSpanContext().getTraceId().toString()); + final @Nullable TracesSamplingDecision samplingDecision, + final @Nullable String transactionName, + final @Nullable TransactionNameSource transactionNameSource) { + setTraceId(traceId.toString()); setPublicKey(new Dsn(sentryOptions.getDsn()).getPublicKey()); setRelease(sentryOptions.getRelease()); setEnvironment(sentryOptions.getEnvironment()); setUserSegment(user != null ? getSegment(user) : null); - setTransaction( - isHighQualityTransactionName(transaction.getTransactionNameSource()) - ? transaction.getName() - : null); + setTransaction(isHighQualityTransactionName(transactionNameSource) ? transactionName : null); setSampleRate(sampleRateToString(sampleRate(samplingDecision))); setSampled(StringUtils.toString(sampled(samplingDecision))); } diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index c39036e6db..43cd3cf188 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -638,7 +638,12 @@ private void updateBaggageValues() { userAtomicReference.set(scope.getUser()); }); baggage.setValuesFromTransaction( - this, userAtomicReference.get(), scopes.getOptions(), this.getSamplingDecision()); + getSpanContext().getTraceId(), + userAtomicReference.get(), + scopes.getOptions(), + this.getSamplingDecision(), + getName(), + getTransactionNameSource()); baggage.freeze(); } } diff --git a/sentry/src/main/java/io/sentry/SpanContext.java b/sentry/src/main/java/io/sentry/SpanContext.java index 1b11d10e5c..2d1b8c5fe7 100644 --- a/sentry/src/main/java/io/sentry/SpanContext.java +++ b/sentry/src/main/java/io/sentry/SpanContext.java @@ -51,6 +51,8 @@ public class SpanContext implements JsonUnknown, JsonSerializable { private @NotNull Instrumenter instrumenter = Instrumenter.SENTRY; + protected @Nullable Baggage baggage; + public SpanContext( final @NotNull String operation, final @Nullable TracesSamplingDecision samplingDecision) { this(new SentryId(), new SpanId(), operation, null, samplingDecision); @@ -225,6 +227,10 @@ public void setInstrumenter(final @NotNull Instrumenter instrumenter) { this.instrumenter = instrumenter; } + public @Nullable Baggage getBaggage() { + return baggage; + } + @ApiStatus.Internal public SpanContext copyForChild( final @NotNull String operation, diff --git a/sentry/src/main/java/io/sentry/TransactionContext.java b/sentry/src/main/java/io/sentry/TransactionContext.java index 6d63942baf..8fd4779c26 100644 --- a/sentry/src/main/java/io/sentry/TransactionContext.java +++ b/sentry/src/main/java/io/sentry/TransactionContext.java @@ -16,7 +16,6 @@ public final class TransactionContext extends SpanContext { private @NotNull String name; private @NotNull TransactionNameSource transactionNameSource; private @Nullable TracesSamplingDecision parentSamplingDecision; - private @Nullable Baggage baggage; private boolean isForNextAppStart = false; /** @@ -157,10 +156,6 @@ public TransactionContext( return parentSamplingDecision; } - public @Nullable Baggage getBaggage() { - return baggage; - } - public void setParentSampled(final @Nullable Boolean parentSampled) { if (parentSampled == null) { this.parentSamplingDecision = null; diff --git a/sentry/src/test/java/io/sentry/TraceContextSerializationTest.kt b/sentry/src/test/java/io/sentry/TraceContextSerializationTest.kt index 0847c9448f..66f34f41c4 100644 --- a/sentry/src/test/java/io/sentry/TraceContextSerializationTest.kt +++ b/sentry/src/test/java/io/sentry/TraceContextSerializationTest.kt @@ -1,6 +1,7 @@ package io.sentry import io.sentry.protocol.SentryId +import io.sentry.protocol.TransactionNameSource import io.sentry.protocol.User import org.junit.Test import org.mockito.kotlin.mock @@ -57,7 +58,7 @@ class TraceContextSerializationTest { val scopes: IScopes = mock() whenever(scopes.options).thenReturn(SentryOptions()) baggage.setValuesFromTransaction( - SentryTracer(TransactionContext("name", "op"), scopes), + SentryId(), User().apply { id = "user-id" others = mapOf("segment" to "pro") @@ -68,7 +69,9 @@ class TraceContextSerializationTest { release = "1.0.17" tracesSampleRate = sRate }, - TracesSamplingDecision(sRate > 0.5, sRate) + TracesSamplingDecision(sRate > 0.5, sRate), + "name", + TransactionNameSource.ROUTE ) return baggage.toTraceContext()!! } From 67490cdded314189fd629389b3f3941d316a2a71 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 18 Jun 2024 07:49:41 +0200 Subject: [PATCH 62/91] POTEL 10 - Cleanup (#3460) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing * Inherit OTel span IDs when sending to sentry * Fix tracing; parse incoming baggage; add baggage to outgoing * Cleanup --- .../api/sentry-android-core.api | 8 ++-- .../android/core/ActivityFramesTracker.java | 7 ++-- .../core/AndroidOptionsInitializer.java | 6 +-- .../io/sentry/android/core/LoadClass.java | 37 ++++++++----------- .../io/sentry/android/core/SentryAndroid.java | 4 +- .../core/UserInteractionIntegration.java | 2 +- .../InternalSemanticAttributes.java | 14 ------- .../OtelContextScopesStorage.java | 4 +- .../sentry/opentelemetry/OtelSpanWrapper.java | 9 +---- .../PotelSentrySpanProcessor.java | 1 - .../opentelemetry/SentrySpanExporter.java | 4 +- sentry/api/sentry.api | 7 +++- .../src/main/java/io/sentry/HubAdapter.java | 2 +- sentry/src/main/java/io/sentry/NoOpHub.java | 6 +-- .../src/main/java/io/sentry/NoOpScopes.java | 6 +-- .../io/sentry/NoOpScopesLifecycleToken.java | 15 ++++++++ .../java/io/sentry/NoOpScopesStorage.java | 15 -------- sentry/src/main/java/io/sentry/NoOpSpan.java | 2 +- .../main/java/io/sentry/NoOpTransaction.java | 2 +- sentry/src/main/java/io/sentry/Scopes.java | 5 +-- .../main/java/io/sentry/ScopesAdapter.java | 2 +- sentry/src/main/java/io/sentry/Sentry.java | 4 +- .../src/main/java/io/sentry/SentryTracer.java | 2 +- sentry/src/main/java/io/sentry/Span.java | 2 +- .../main/java/io/sentry/util/LoadClass.java | 5 ++- 25 files changed, 74 insertions(+), 97 deletions(-) create mode 100644 sentry/src/main/java/io/sentry/NoOpScopesLifecycleToken.java diff --git a/sentry-android-core/api/sentry-android-core.api b/sentry-android-core/api/sentry-android-core.api index 9ee8843eea..d3eb019484 100644 --- a/sentry-android-core/api/sentry-android-core.api +++ b/sentry-android-core/api/sentry-android-core.api @@ -12,8 +12,8 @@ public final class io/sentry/android/core/ActivityBreadcrumbsIntegration : andro } public final class io/sentry/android/core/ActivityFramesTracker { - public fun (Lio/sentry/android/core/LoadClass;Lio/sentry/android/core/SentryAndroidOptions;)V - public fun (Lio/sentry/android/core/LoadClass;Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/android/core/MainLooperHandler;)V + public fun (Lio/sentry/util/LoadClass;Lio/sentry/android/core/SentryAndroidOptions;)V + public fun (Lio/sentry/util/LoadClass;Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/android/core/MainLooperHandler;)V public fun addActivity (Landroid/app/Activity;)V public fun isFrameMetricsAggregatorAvailable ()Z public fun setMetrics (Landroid/app/Activity;Lio/sentry/protocol/SentryId;)V @@ -209,7 +209,7 @@ public final class io/sentry/android/core/InternalSentrySdk { public static fun serializeScope (Landroid/content/Context;Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/IScope;)Ljava/util/Map; } -public final class io/sentry/android/core/LoadClass { +public final class io/sentry/android/core/LoadClass : io/sentry/util/LoadClass { public fun ()V public fun isClassAvailable (Ljava/lang/String;Lio/sentry/ILogger;)Z public fun isClassAvailable (Ljava/lang/String;Lio/sentry/SentryOptions;)Z @@ -374,7 +374,7 @@ public final class io/sentry/android/core/TempSensorBreadcrumbsIntegration : and } public final class io/sentry/android/core/UserInteractionIntegration : android/app/Application$ActivityLifecycleCallbacks, io/sentry/Integration, java/io/Closeable { - public fun (Landroid/app/Application;Lio/sentry/android/core/LoadClass;)V + public fun (Landroid/app/Application;Lio/sentry/util/LoadClass;)V public fun close ()V public fun onActivityCreated (Landroid/app/Activity;Landroid/os/Bundle;)V public fun onActivityDestroyed (Landroid/app/Activity;)V diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java index 93f50b3ec1..99d230b305 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java @@ -39,7 +39,7 @@ public final class ActivityFramesTracker { private final @NotNull MainLooperHandler handler; public ActivityFramesTracker( - final @NotNull LoadClass loadClass, + final @NotNull io.sentry.util.LoadClass loadClass, final @NotNull SentryAndroidOptions options, final @NotNull MainLooperHandler handler) { @@ -54,13 +54,14 @@ public ActivityFramesTracker( } public ActivityFramesTracker( - final @NotNull LoadClass loadClass, final @NotNull SentryAndroidOptions options) { + final @NotNull io.sentry.util.LoadClass loadClass, + final @NotNull SentryAndroidOptions options) { this(loadClass, options, new MainLooperHandler()); } @TestOnly ActivityFramesTracker( - final @NotNull LoadClass loadClass, + final @NotNull io.sentry.util.LoadClass loadClass, final @NotNull SentryAndroidOptions options, final @NotNull MainLooperHandler handler, final @Nullable FrameMetricsAggregator frameMetricsAggregator) { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java index 605de4c0a8..dd5c3c5254 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java @@ -119,7 +119,7 @@ static void loadDefaultAndMetadataOptions( static void initializeIntegrationsAndProcessors( final @NotNull SentryAndroidOptions options, final @NotNull Context context, - final @NotNull LoadClass loadClass, + final @NotNull io.sentry.util.LoadClass loadClass, final @NotNull ActivityFramesTracker activityFramesTracker) { initializeIntegrationsAndProcessors( options, @@ -133,7 +133,7 @@ static void initializeIntegrationsAndProcessors( final @NotNull SentryAndroidOptions options, final @NotNull Context context, final @NotNull BuildInfoProvider buildInfoProvider, - final @NotNull LoadClass loadClass, + final @NotNull io.sentry.util.LoadClass loadClass, final @NotNull ActivityFramesTracker activityFramesTracker) { if (options.getCacheDirPath() != null @@ -237,7 +237,7 @@ static void installDefaultIntegrations( final @NotNull Context context, final @NotNull SentryAndroidOptions options, final @NotNull BuildInfoProvider buildInfoProvider, - final @NotNull LoadClass loadClass, + final @NotNull io.sentry.util.LoadClass loadClass, final @NotNull ActivityFramesTracker activityFramesTracker, final boolean isFragmentAvailable, final boolean isTimberAvailable) { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/LoadClass.java b/sentry-android-core/src/main/java/io/sentry/android/core/LoadClass.java index 6401945cab..34b8d1d5f1 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/LoadClass.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/LoadClass.java @@ -1,13 +1,23 @@ package io.sentry.android.core; import io.sentry.ILogger; -import io.sentry.SentryLevel; import io.sentry.SentryOptions; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** An Adapter for making Class.forName testable */ -public final class LoadClass { +/** + * An Adapter for making Class.forName testable + * + * @deprecated please use {@link io.sentry.util.LoadClass} instead. + */ +@Deprecated +public final class LoadClass extends io.sentry.util.LoadClass { + + private final io.sentry.util.LoadClass delegate; + + public LoadClass() { + delegate = new io.sentry.util.LoadClass(); + } /** * Try to load a class via reflection @@ -17,30 +27,15 @@ public final class LoadClass { * @return a Class if it's available, or null */ public @Nullable Class loadClass(final @NotNull String clazz, final @Nullable ILogger logger) { - try { - return Class.forName(clazz); - } catch (ClassNotFoundException e) { - if (logger != null) { - logger.log(SentryLevel.DEBUG, "Class not available:" + clazz, e); - } - } catch (UnsatisfiedLinkError e) { - if (logger != null) { - logger.log(SentryLevel.ERROR, "Failed to load (UnsatisfiedLinkError) " + clazz, e); - } - } catch (Throwable e) { - if (logger != null) { - logger.log(SentryLevel.ERROR, "Failed to initialize " + clazz, e); - } - } - return null; + return delegate.loadClass(clazz, logger); } public boolean isClassAvailable(final @NotNull String clazz, final @Nullable ILogger logger) { - return loadClass(clazz, logger) != null; + return delegate.isClassAvailable(clazz, logger); } public boolean isClassAvailable( final @NotNull String clazz, final @Nullable SentryOptions options) { - return isClassAvailable(clazz, options != null ? options.getLogger() : null); + return delegate.isClassAvailable(clazz, options); } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java index 424de4d82e..55ec471fb1 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java @@ -87,7 +87,7 @@ public static synchronized void init( Sentry.init( OptionsContainer.create(SentryAndroidOptions.class), options -> { - final LoadClass classLoader = new LoadClass(); + final io.sentry.util.LoadClass classLoader = new io.sentry.util.LoadClass(); final boolean isTimberUpstreamAvailable = classLoader.isClassAvailable(TIMBER_CLASS_NAME, options); final boolean isFragmentUpstreamAvailable = @@ -101,7 +101,7 @@ public static synchronized void init( && classLoader.isClassAvailable(SENTRY_TIMBER_INTEGRATION_CLASS_NAME, options)); final BuildInfoProvider buildInfoProvider = new BuildInfoProvider(logger); - final LoadClass loadClass = new LoadClass(); + final io.sentry.util.LoadClass loadClass = new io.sentry.util.LoadClass(); final ActivityFramesTracker activityFramesTracker = new ActivityFramesTracker(loadClass, options); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/UserInteractionIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/UserInteractionIntegration.java index 712651b460..02a707173a 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/UserInteractionIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/UserInteractionIntegration.java @@ -29,7 +29,7 @@ public final class UserInteractionIntegration private final boolean isAndroidXAvailable; public UserInteractionIntegration( - final @NotNull Application application, final @NotNull LoadClass classLoader) { + final @NotNull Application application, final @NotNull io.sentry.util.LoadClass classLoader) { this.application = Objects.requireNonNull(application, "Application is required"); isAndroidXAvailable = classLoader.isClassAvailable("androidx.core.view.GestureDetectorCompat", options); diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java index f9d04c3724..d186d2c634 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java @@ -2,11 +2,7 @@ import io.opentelemetry.api.common.AttributeKey; -// TODO [POTEL] context key vs attribute key public final class InternalSemanticAttributes { - // public static final AttributeKey ORIGIN = AttributeKey.stringKey("sentry.origin"); - // public static final AttributeKey OP = AttributeKey.stringKey("sentry.op"); - // public static final AttributeKey SOURCE = AttributeKey.stringKey("sentry.source"); public static final AttributeKey SAMPLED = AttributeKey.booleanKey("sentry.sampled"); public static final AttributeKey SAMPLE_RATE = AttributeKey.doubleKey("sentry.sample_rate"); @@ -21,14 +17,4 @@ public final class InternalSemanticAttributes { public static final AttributeKey BAGGAGE = AttributeKey.stringKey("sentry.baggage"); public static final AttributeKey BAGGAGE_MUTABLE = AttributeKey.booleanKey("sentry.baggage_mutable"); - // public static final AttributeKey BREADCRUMB_TYPE = - // AttributeKey.stringKey("sentry.breadcrumb.type"); - // public static final AttributeKey BREADCRUMB_TYPE = - // InternalAttributeKeyImpl.create("sentry.breadcrumb.type", SentryLevel.class); - // BREADCRUMB_TYPE("sentry.breadcrumb.type"), - // BREADCRUMB_LEVEL("sentry.breadcrumb.level"), - // BREADCRUMB_EVENT_ID("sentry.breadcrumb.event_id"), - // BREADCRUMB_CATEGORY("sentry.breadcrumb.category"), - // BREADCRUMB_DATA("sentry.breadcrumb.data"); - } diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java index 09014a77bb..b6d7d1bc83 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java @@ -26,9 +26,7 @@ public ISentryLifecycleToken set(@Nullable IScopes scopes) { } @Override - public void close() { - // TODO [POTEL] can we do something here? - } + public void close() {} static final class OtelContextScopesStorageToken implements ISentryLifecycleToken { diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java index a0d6c0a9d4..3c226abbd9 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java @@ -10,7 +10,7 @@ import io.sentry.ISpan; import io.sentry.Instrumenter; import io.sentry.MeasurementUnit; -import io.sentry.NoOpScopesStorage; +import io.sentry.NoOpScopesLifecycleToken; import io.sentry.NoOpSpan; import io.sentry.SentryDate; import io.sentry.SentryLevel; @@ -82,7 +82,6 @@ public final class OtelSpanWrapper implements ISpan { private @NotNull Deque tokensToCleanup = new ArrayDeque<>(1); - // TODO [POTEL] reference root span? for getting root baggage public OtelSpanWrapper( final @NotNull ReadWriteSpan span, final @NotNull IScopes scopes, @@ -100,7 +99,6 @@ public OtelSpanWrapper( this.baggage = baggage; } else { this.baggage = null; - // this.baggage = new Baggage(scopes.getOptions().getLogger()); } this.context = new OtelSpanContext(span, samplingDecision, parentSpan, this.baggage); @@ -496,15 +494,12 @@ public Map getMeasurements() { final @Nullable Span otelSpan = getSpan(); if (otelSpan != null) { final @NotNull Scope otelScope = otelSpan.makeCurrent(); - // TODO [POTEL] should we keep an ordered list of otel scopes and close them in reverse order - // on finish? - // TODO [POTEL] should we make transaction/span implement ISentryLifecycleToken instead? final @NotNull OtelContextSpanStorageToken token = new OtelContextSpanStorageToken(otelScope); // to iterate LIFO when closing tokensToCleanup.addFirst(token); return token; } - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + return NoOpScopesLifecycleToken.getInstance(); } // TODO [POTEL] extract generic diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java index 2ce773fc38..1a672ca562 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java @@ -56,7 +56,6 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri final @Nullable OtelSpanWrapper sentryParentSpan = spanStorage.getSentrySpan(otelSpan.getParentSpanContext()); @Nullable TracesSamplingDecision samplingDecision = null; - // TODO [POTEL] baggage from propagator should be honored @Nullable Baggage baggage = null; otelSpan.setAttribute(IS_REMOTE_PARENT, otelSpan.getParentSpanContext().isRemote()); if (sentryParentSpan == null) { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index 9b96010c75..64cc71bd2c 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -46,9 +46,7 @@ public final class SentrySpanExporter implements SpanExporter { private volatile boolean stopped = false; - // TODO is a strong ref problematic here? - // TODO [POTEL] a weak ref could mean spans are gone before we had a chance to attach them - // somewhere + // TODO [POTEL] should we clear out old finished spans after a while? private final List finishedSpans = new CopyOnWriteArrayList<>(); private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); private final @NotNull SpanDescriptionExtractor spanDescriptionExtractor = diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 5602ecd097..3601236557 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -1535,6 +1535,11 @@ public final class io/sentry/NoOpScopes : io/sentry/IScopes { public fun withScope (Lio/sentry/ScopeCallback;)V } +public final class io/sentry/NoOpScopesLifecycleToken : io/sentry/ISentryLifecycleToken { + public fun close ()V + public static fun getInstance ()Lio/sentry/NoOpScopesLifecycleToken; +} + public final class io/sentry/NoOpScopesStorage : io/sentry/IScopesStorage { public fun close ()V public fun get ()Lio/sentry/IScopes; @@ -5554,7 +5559,7 @@ public final class io/sentry/util/LifecycleHelper { public static fun close (Ljava/lang/Object;)V } -public final class io/sentry/util/LoadClass { +public class io/sentry/util/LoadClass { public fun ()V public fun isClassAvailable (Ljava/lang/String;Lio/sentry/ILogger;)Z public fun isClassAvailable (Ljava/lang/String;Lio/sentry/SentryOptions;)Z diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index 9c09be075c..8ba2ae9193 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -230,7 +230,7 @@ public void flush(long timeoutMillis) { @Override public @NotNull ISentryLifecycleToken makeCurrent() { - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + return NoOpScopesLifecycleToken.getInstance(); } @Override diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index 580cd74284..55893746d8 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -128,12 +128,12 @@ public void removeExtra(@NotNull String key) {} @Override public @NotNull ISentryLifecycleToken pushScope() { - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + return NoOpScopesLifecycleToken.getInstance(); } @Override public @NotNull ISentryLifecycleToken pushIsolationScope() { - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + return NoOpScopesLifecycleToken.getInstance(); } /** @@ -190,7 +190,7 @@ public void flush(long timeoutMillis) {} @Override public @NotNull ISentryLifecycleToken makeCurrent() { - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + return NoOpScopesLifecycleToken.getInstance(); } @Override diff --git a/sentry/src/main/java/io/sentry/NoOpScopes.java b/sentry/src/main/java/io/sentry/NoOpScopes.java index 4528a285ca..58c1207809 100644 --- a/sentry/src/main/java/io/sentry/NoOpScopes.java +++ b/sentry/src/main/java/io/sentry/NoOpScopes.java @@ -123,12 +123,12 @@ public void removeExtra(@NotNull String key) {} @Override public @NotNull ISentryLifecycleToken pushScope() { - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + return NoOpScopesLifecycleToken.getInstance(); } @Override public @NotNull ISentryLifecycleToken pushIsolationScope() { - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + return NoOpScopesLifecycleToken.getInstance(); } /** @@ -190,7 +190,7 @@ public void flush(long timeoutMillis) {} @Override public @NotNull ISentryLifecycleToken makeCurrent() { - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + return NoOpScopesLifecycleToken.getInstance(); } @Override diff --git a/sentry/src/main/java/io/sentry/NoOpScopesLifecycleToken.java b/sentry/src/main/java/io/sentry/NoOpScopesLifecycleToken.java new file mode 100644 index 0000000000..4fc589d5b3 --- /dev/null +++ b/sentry/src/main/java/io/sentry/NoOpScopesLifecycleToken.java @@ -0,0 +1,15 @@ +package io.sentry; + +public final class NoOpScopesLifecycleToken implements ISentryLifecycleToken { + + private static final NoOpScopesLifecycleToken instance = new NoOpScopesLifecycleToken(); + + private NoOpScopesLifecycleToken() {} + + public static NoOpScopesLifecycleToken getInstance() { + return instance; + } + + @Override + public void close() {} +} diff --git a/sentry/src/main/java/io/sentry/NoOpScopesStorage.java b/sentry/src/main/java/io/sentry/NoOpScopesStorage.java index dcf4d7c816..9e2d7f82d6 100644 --- a/sentry/src/main/java/io/sentry/NoOpScopesStorage.java +++ b/sentry/src/main/java/io/sentry/NoOpScopesStorage.java @@ -23,19 +23,4 @@ public ISentryLifecycleToken set(@Nullable IScopes scopes) { @Override public void close() {} - - // TODO [POTEL] extract into its own class - public static final class NoOpScopesLifecycleToken implements ISentryLifecycleToken { - - private static final NoOpScopesLifecycleToken instance = new NoOpScopesLifecycleToken(); - - private NoOpScopesLifecycleToken() {} - - public static NoOpScopesLifecycleToken getInstance() { - return instance; - } - - @Override - public void close() {} - } } diff --git a/sentry/src/main/java/io/sentry/NoOpSpan.java b/sentry/src/main/java/io/sentry/NoOpSpan.java index 494d274a42..4acce66ec6 100644 --- a/sentry/src/main/java/io/sentry/NoOpSpan.java +++ b/sentry/src/main/java/io/sentry/NoOpSpan.java @@ -199,6 +199,6 @@ public void setContext(@NotNull String key, @NotNull Object context) {} @Override public @NotNull ISentryLifecycleToken makeCurrent() { - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + return NoOpScopesLifecycleToken.getInstance(); } } diff --git a/sentry/src/main/java/io/sentry/NoOpTransaction.java b/sentry/src/main/java/io/sentry/NoOpTransaction.java index 2693e47aed..747621ee06 100644 --- a/sentry/src/main/java/io/sentry/NoOpTransaction.java +++ b/sentry/src/main/java/io/sentry/NoOpTransaction.java @@ -112,7 +112,7 @@ public void setName(@NotNull String name, @NotNull TransactionNameSource transac @Override public @NotNull ISentryLifecycleToken makeCurrent() { - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + return NoOpScopesLifecycleToken.getInstance(); } @Override diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 27bfbf72f6..d8e3fefa79 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -588,7 +588,7 @@ public ISentryLifecycleToken pushScope() { getOptions() .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'pushScope' call is a no-op."); - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + return NoOpScopesLifecycleToken.getInstance(); } else { final @NotNull IScopes scopes = this.forkedCurrentScope("pushScope"); return scopes.makeCurrent(); @@ -603,7 +603,7 @@ public ISentryLifecycleToken pushIsolationScope() { .log( SentryLevel.WARNING, "Instance is disabled and this 'pushIsolationScope' call is a no-op."); - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + return NoOpScopesLifecycleToken.getInstance(); } else { final @NotNull IScopes scopes = this.forkedScopes("pushIsolationScope"); return scopes.makeCurrent(); @@ -876,7 +876,6 @@ public void flush(long timeoutMillis) { } } if (transactionOptions.isBindToScope()) { - // TODO [POTEL] this causes problems with OTel since it messes up closing of scopes and leaks transaction.makeCurrent(); } return transaction; diff --git a/sentry/src/main/java/io/sentry/ScopesAdapter.java b/sentry/src/main/java/io/sentry/ScopesAdapter.java index 3a0669eb35..92387dc602 100644 --- a/sentry/src/main/java/io/sentry/ScopesAdapter.java +++ b/sentry/src/main/java/io/sentry/ScopesAdapter.java @@ -227,7 +227,7 @@ public void flush(long timeoutMillis) { @Override public @NotNull ISentryLifecycleToken makeCurrent() { - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + return NoOpScopesLifecycleToken.getInstance(); } @Override diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 34eb7f87fa..7e4c63b962 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -830,7 +830,7 @@ public static void removeExtra(final @NotNull String key) { if (!globalHubMode) { return getCurrentScopes().pushScope(); } - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + return NoOpScopesLifecycleToken.getInstance(); } /** Pushes a new isolation and current scope while inheriting the current scope's data. */ @@ -839,7 +839,7 @@ public static void removeExtra(final @NotNull String key) { if (!globalHubMode) { return getCurrentScopes().pushIsolationScope(); } - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + return NoOpScopesLifecycleToken.getInstance(); } /** diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index 43cd3cf188..a84f2c20c4 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -921,7 +921,7 @@ public void setName(@NotNull String name, @NotNull TransactionNameSource transac }); // TODO [POTEL] can we return an actual token here - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + return NoOpScopesLifecycleToken.getInstance(); } @NotNull diff --git a/sentry/src/main/java/io/sentry/Span.java b/sentry/src/main/java/io/sentry/Span.java index 2d8e043dc8..35bbc42890 100644 --- a/sentry/src/main/java/io/sentry/Span.java +++ b/sentry/src/main/java/io/sentry/Span.java @@ -483,6 +483,6 @@ private List getDirectChildren() { @Override public @NotNull ISentryLifecycleToken makeCurrent() { - return NoOpScopesStorage.NoOpScopesLifecycleToken.getInstance(); + return NoOpScopesLifecycleToken.getInstance(); } } diff --git a/sentry/src/main/java/io/sentry/util/LoadClass.java b/sentry/src/main/java/io/sentry/util/LoadClass.java index b41f64fe14..11fef9ea01 100644 --- a/sentry/src/main/java/io/sentry/util/LoadClass.java +++ b/sentry/src/main/java/io/sentry/util/LoadClass.java @@ -1,5 +1,6 @@ package io.sentry.util; +import com.jakewharton.nopen.annotation.Open; import io.sentry.ILogger; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -7,8 +8,8 @@ import org.jetbrains.annotations.Nullable; /** An Adapter for making Class.forName testable */ -// TODO [POTEL] deduplicate -public final class LoadClass { +@Open +public class LoadClass { /** * Try to load a class via reflection From e50d95541fa0e41b79c61a50a6af6812ac5aaa03 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 18 Jun 2024 07:50:39 +0200 Subject: [PATCH 63/91] POTEL 11 - Move sampling logic into OTel Sampler (#3462) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing * Inherit OTel span IDs when sending to sentry * Fix tracing; parse incoming baggage; add baggage to outgoing * Cleanup * Move sampling logic to OTel Sampler --- ...ryAutoConfigurationCustomizerProvider.java | 1 + .../api/sentry-opentelemetry-core.api | 20 +++ .../opentelemetry/OtelSamplingUtil.java | 38 ++++++ .../PotelSentrySpanProcessor.java | 116 ++++++------------ .../sentry/opentelemetry/SentrySampler.java | 105 ++++++++++++++++ .../opentelemetry/SentrySamplingResult.java | 38 ++++++ 6 files changed, 242 insertions(+), 76 deletions(-) create mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSamplingUtil.java create mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java create mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySamplingResult.java diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java index c914ee9f0b..6e776f94ed 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java @@ -151,6 +151,7 @@ private SdkTracerProviderBuilder configureSdkTracerProvider( // TODO [POTEL] configurable or separate packages for old vs new way // return tracerProvider.addSpanProcessor(new SentrySpanProcessor()); return tracerProvider + .setSampler(new SentrySampler()) .addSpanProcessor(new PotelSentrySpanProcessor()) .addSpanProcessor(BatchSpanProcessor.builder(new SentrySpanExporter()).build()); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api index 88a7c23530..6112e9e3cd 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api +++ b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api @@ -4,6 +4,12 @@ public final class io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } +public final class io/sentry/opentelemetry/OtelSamplingUtil { + public fun ()V + public static fun extractSamplingDecision (Lio/opentelemetry/api/common/Attributes;)Lio/sentry/TracesSamplingDecision; + public static fun extractSamplingDecisionOrDefault (Lio/opentelemetry/api/common/Attributes;)Lio/sentry/TracesSamplingDecision; +} + public final class io/sentry/opentelemetry/OtelSpanInfo { public fun (Ljava/lang/String;Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V public fun (Ljava/lang/String;Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;Ljava/util/Map;)V @@ -36,6 +42,20 @@ public final class io/sentry/opentelemetry/SentryPropagator : io/opentelemetry/c public fun inject (Lio/opentelemetry/context/Context;Ljava/lang/Object;Lio/opentelemetry/context/propagation/TextMapSetter;)V } +public final class io/sentry/opentelemetry/SentrySampler : io/opentelemetry/sdk/trace/samplers/Sampler { + public fun ()V + public fun (Lio/sentry/IScopes;)V + public fun getDescription ()Ljava/lang/String; + public fun shouldSample (Lio/opentelemetry/context/Context;Ljava/lang/String;Ljava/lang/String;Lio/opentelemetry/api/trace/SpanKind;Lio/opentelemetry/api/common/Attributes;Ljava/util/List;)Lio/opentelemetry/sdk/trace/samplers/SamplingResult; +} + +public final class io/sentry/opentelemetry/SentrySamplingResult : io/opentelemetry/sdk/trace/samplers/SamplingResult { + public fun (Lio/sentry/TracesSamplingDecision;)V + public fun getAttributes ()Lio/opentelemetry/api/common/Attributes; + public fun getDecision ()Lio/opentelemetry/sdk/trace/samplers/SamplingDecision; + public fun getSentryDecision ()Lio/sentry/TracesSamplingDecision; +} + public final class io/sentry/opentelemetry/SentrySpanExporter : io/opentelemetry/sdk/trace/export/SpanExporter { public fun ()V public fun (Lio/sentry/IScopes;)V diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSamplingUtil.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSamplingUtil.java new file mode 100644 index 0000000000..4a6124df11 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSamplingUtil.java @@ -0,0 +1,38 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.api.common.Attributes; +import io.sentry.TracesSamplingDecision; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@ApiStatus.Internal +public final class OtelSamplingUtil { + + public static @Nullable TracesSamplingDecision extractSamplingDecisionOrDefault( + final @NotNull Attributes attributes) { + final @Nullable TracesSamplingDecision decision = extractSamplingDecision(attributes); + if (decision != null) { + return decision; + } else { + return new TracesSamplingDecision(false); + } + } + + public static @Nullable TracesSamplingDecision extractSamplingDecision( + final @NotNull Attributes attributes) { + final @Nullable Boolean sampled = attributes.get(InternalSemanticAttributes.SAMPLED); + if (sampled != null) { + final @Nullable Double sampleRate = attributes.get(InternalSemanticAttributes.SAMPLE_RATE); + final @Nullable Boolean profileSampled = + attributes.get(InternalSemanticAttributes.PROFILE_SAMPLED); + final @Nullable Double profileSampleRate = + attributes.get(InternalSemanticAttributes.PROFILE_SAMPLE_RATE); + + return new TracesSamplingDecision( + sampled, sampleRate, profileSampled == null ? false : profileSampled, profileSampleRate); + } else { + return null; + } + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java index 1a672ca562..4d54f03be1 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java @@ -11,7 +11,6 @@ import io.sentry.Baggage; import io.sentry.IScopes; import io.sentry.PropagationContext; -import io.sentry.SamplingContext; import io.sentry.ScopesAdapter; import io.sentry.Sentry; import io.sentry.SentryDate; @@ -19,9 +18,7 @@ import io.sentry.SentryLongDate; import io.sentry.SentryTraceHeader; import io.sentry.SpanId; -import io.sentry.TracesSampler; import io.sentry.TracesSamplingDecision; -import io.sentry.TransactionContext; import io.sentry.protocol.SentryId; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,15 +27,12 @@ public final class PotelSentrySpanProcessor implements SpanProcessor { private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); private final @NotNull IScopes scopes; - private final @NotNull TracesSampler tracesSampler; - public PotelSentrySpanProcessor() { this(ScopesAdapter.getInstance()); } PotelSentrySpanProcessor(final @NotNull IScopes scopes) { this.scopes = scopes; - this.tracesSampler = new TracesSampler(scopes.getOptions()); } @Override @@ -55,10 +49,26 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri final @Nullable OtelSpanWrapper sentryParentSpan = spanStorage.getSentrySpan(otelSpan.getParentSpanContext()); - @Nullable TracesSamplingDecision samplingDecision = null; + @NotNull + TracesSamplingDecision samplingDecision = + OtelSamplingUtil.extractSamplingDecisionOrDefault(otelSpan.toSpanData().getAttributes()); @Nullable Baggage baggage = null; otelSpan.setAttribute(IS_REMOTE_PARENT, otelSpan.getParentSpanContext().isRemote()); if (sentryParentSpan == null) { + final @NotNull String traceId = otelSpan.getSpanContext().getTraceId(); + final @NotNull String spanId = otelSpan.getSpanContext().getSpanId(); + final @NotNull SpanId sentrySpanId = new SpanId(spanId); + final @NotNull String parentSpanId = otelSpan.getParentSpanContext().getSpanId(); + final @Nullable SpanId sentryParentSpanId = + io.opentelemetry.api.trace.SpanId.isValid(parentSpanId) ? new SpanId(parentSpanId) : null; + + @Nullable + SentryTraceHeader sentryTraceHeader = parentContext.get(SentryOtelKeys.SENTRY_TRACE_KEY); + @Nullable Baggage baggageFromContext = parentContext.get(SentryOtelKeys.SENTRY_BAGGAGE_KEY); + if (sentryTraceHeader != null) { + baggage = baggageFromContext; + } + final @Nullable Boolean baggageMutable = otelSpan.getAttribute(InternalSemanticAttributes.BAGGAGE_MUTABLE); final @Nullable String baggageString = @@ -69,77 +79,20 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri baggage.freeze(); } } - final @Nullable Boolean sampled = otelSpan.getAttribute(InternalSemanticAttributes.SAMPLED); - final @Nullable Double sampleRate = - otelSpan.getAttribute(InternalSemanticAttributes.SAMPLE_RATE); - final @Nullable Boolean profileSampled = - otelSpan.getAttribute(InternalSemanticAttributes.PROFILE_SAMPLED); - final @Nullable Double profileSampleRate = - otelSpan.getAttribute(InternalSemanticAttributes.PROFILE_SAMPLE_RATE); - if (sampled != null) { - // span created by Sentry API - - final @NotNull String traceId = otelSpan.getSpanContext().getTraceId(); - final @NotNull String spanId = otelSpan.getSpanContext().getSpanId(); - // TODO [POTEL] parent span id could be invalid - final @NotNull String parentSpanId = otelSpan.getParentSpanContext().getSpanId(); - - final @NotNull PropagationContext propagationContext = - new PropagationContext( - new SentryId(traceId), - new SpanId(spanId), - new SpanId(parentSpanId), - baggage, - sampled); - - scopes.configureScope( - scope -> { - scope.withPropagationContext( - oldPropagationContext -> { - scope.setPropagationContext(propagationContext); - }); - }); - - // TODO [POTEL] can we use OTel Sampler to let OTel know our sampling decision - // Sentry not sampled vs OTel not sampled may mean different things for trace propagation - samplingDecision = - new TracesSamplingDecision( - sampled, - sampleRate, - profileSampled == null ? false : profileSampled, - profileSampleRate); - } else { - // span not created by Sentry API - - final @NotNull String traceId = otelSpan.getSpanContext().getTraceId(); - final @NotNull String spanId = otelSpan.getSpanContext().getSpanId(); - final @NotNull SpanId sentrySpanId = new SpanId(spanId); - - @Nullable - SentryTraceHeader sentryTraceHeader = parentContext.get(SentryOtelKeys.SENTRY_TRACE_KEY); - @Nullable Baggage baggageFromContext = parentContext.get(SentryOtelKeys.SENTRY_BAGGAGE_KEY); - if (sentryTraceHeader != null) { - baggage = baggageFromContext; - } - final @NotNull PropagationContext propagationContext = - sentryTraceHeader == null - ? new PropagationContext(new SentryId(traceId), sentrySpanId, null, baggage, null) - : PropagationContext.fromHeaders(sentryTraceHeader, baggage, sentrySpanId); - - scopes.configureScope( - scope -> { - scope.withPropagationContext( - oldPropagationContext -> { - scope.setPropagationContext(propagationContext); - }); - }); - - final @NotNull TransactionContext transactionContext = - TransactionContext.fromPropagationContext(propagationContext); - samplingDecision = tracesSampler.sample(new SamplingContext(transactionContext, null)); - } + // TODO [POTEL] what do we use as fallback here? could happen if misconfigured (i.e. sampler + // not in place) + final boolean sampled = samplingDecision != null ? samplingDecision.getSampled() : true; + + final @NotNull PropagationContext propagationContext = + sentryTraceHeader == null + ? new PropagationContext( + new SentryId(traceId), sentrySpanId, sentryParentSpanId, baggage, sampled) + : PropagationContext.fromHeaders(sentryTraceHeader, baggage, sentrySpanId); + + updatePropagationContext(scopes, propagationContext); } + final @NotNull SpanContext spanContext = otelSpan.getSpanContext(); final @NotNull SentryDate startTimestamp = new SentryLongDate(otelSpan.toSpanData().getStartEpochNanos()); @@ -149,6 +102,17 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri otelSpan, scopes, startTimestamp, samplingDecision, sentryParentSpan, baggage)); } + private static void updatePropagationContext( + IScopes scopes, PropagationContext propagationContext) { + scopes.configureScope( + scope -> { + scope.withPropagationContext( + oldPropagationContext -> { + scope.setPropagationContext(propagationContext); + }); + }); + } + @Override public boolean isStartRequired() { return true; diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java new file mode 100644 index 0000000000..0d54cd9947 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java @@ -0,0 +1,105 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.trace.data.LinkData; +import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.sdk.trace.samplers.SamplingResult; +import io.sentry.Baggage; +import io.sentry.IScopes; +import io.sentry.PropagationContext; +import io.sentry.SamplingContext; +import io.sentry.ScopesAdapter; +import io.sentry.SentryTraceHeader; +import io.sentry.SpanId; +import io.sentry.TracesSampler; +import io.sentry.TracesSamplingDecision; +import io.sentry.TransactionContext; +import io.sentry.protocol.SentryId; +import java.util.List; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class SentrySampler implements Sampler { + + private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); + private final @NotNull TracesSampler tracesSampler; + + public SentrySampler(final @NotNull IScopes scopes) { + this.tracesSampler = new TracesSampler(scopes.getOptions()); + } + + public SentrySampler() { + this(ScopesAdapter.getInstance()); + } + + @Override + public SamplingResult shouldSample( + final @NotNull Context parentContext, + final @NotNull String traceId, + final @NotNull String name, + final @NotNull SpanKind spanKind, + final @NotNull Attributes attributes, + final @NotNull List parentLinks) { + // note: parentLinks seems to usually be empty + final @Nullable Span parentOtelSpan = Span.fromContextOrNull(parentContext); + final @Nullable OtelSpanWrapper parentSentrySpan = + parentOtelSpan != null ? spanStorage.getSentrySpan(parentOtelSpan.getSpanContext()) : null; + + if (parentSentrySpan != null) { + return copyParentSentryDecision(parentSentrySpan); + } else { + final @Nullable TracesSamplingDecision samplingDecision = + OtelSamplingUtil.extractSamplingDecision(attributes); + if (samplingDecision != null) { + return new SentrySamplingResult(samplingDecision); + } else { + return handleRootOtelSpan(traceId, parentContext); + } + } + } + + private @NotNull SentrySamplingResult handleRootOtelSpan( + final @NotNull String traceId, final @NotNull Context parentContext) { + @Nullable Baggage baggage = null; + @Nullable + SentryTraceHeader sentryTraceHeader = parentContext.get(SentryOtelKeys.SENTRY_TRACE_KEY); + @Nullable Baggage baggageFromContext = parentContext.get(SentryOtelKeys.SENTRY_BAGGAGE_KEY); + if (sentryTraceHeader != null) { + baggage = baggageFromContext; + } + + // there's no way to get the span id here, so we just use a random id for sampling + SpanId randomSpanId = new SpanId(); + final @NotNull PropagationContext propagationContext = + sentryTraceHeader == null + ? new PropagationContext(new SentryId(traceId), randomSpanId, null, baggage, null) + : PropagationContext.fromHeaders(sentryTraceHeader, baggage, randomSpanId); + + final @NotNull TransactionContext transactionContext = + TransactionContext.fromPropagationContext(propagationContext); + final @NotNull TracesSamplingDecision sentryDecision = + tracesSampler.sample(new SamplingContext(transactionContext, null)); + return new SentrySamplingResult(sentryDecision); + } + + private @NotNull SentrySamplingResult copyParentSentryDecision( + final @NotNull OtelSpanWrapper parentSentrySpan) { + final @Nullable TracesSamplingDecision parentSamplingDecision = + parentSentrySpan.getSamplingDecision(); + if (parentSamplingDecision != null) { + return new SentrySamplingResult(parentSamplingDecision); + } else { + // this should never happen and only serve to calm the compiler + // TODO [POTEL] log + return new SentrySamplingResult(new TracesSamplingDecision(true)); + } + } + + @Override + public String getDescription() { + return "SentrySampler"; + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySamplingResult.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySamplingResult.java new file mode 100644 index 0000000000..c8049f3067 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySamplingResult.java @@ -0,0 +1,38 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.trace.samplers.SamplingDecision; +import io.opentelemetry.sdk.trace.samplers.SamplingResult; +import io.sentry.TracesSamplingDecision; +import org.jetbrains.annotations.NotNull; + +public final class SentrySamplingResult implements SamplingResult { + private final TracesSamplingDecision sentryDecision; + + public SentrySamplingResult(final @NotNull TracesSamplingDecision sentryDecision) { + this.sentryDecision = sentryDecision; + } + + @Override + public SamplingDecision getDecision() { + if (sentryDecision.getSampled()) { + return SamplingDecision.RECORD_AND_SAMPLE; + } else { + return SamplingDecision.RECORD_ONLY; + } + } + + @Override + public Attributes getAttributes() { + return Attributes.builder() + .put(InternalSemanticAttributes.SAMPLED, sentryDecision.getSampled()) + .put(InternalSemanticAttributes.SAMPLE_RATE, sentryDecision.getSampleRate()) + .put(InternalSemanticAttributes.PROFILE_SAMPLED, sentryDecision.getProfileSampled()) + .put(InternalSemanticAttributes.PROFILE_SAMPLE_RATE, sentryDecision.getProfileSampleRate()) + .build(); + } + + public TracesSamplingDecision getSentryDecision() { + return sentryDecision; + } +} From dd6307a75092d151dc08e7cb1ece3cf921ea8a66 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 18 Jun 2024 07:51:37 +0200 Subject: [PATCH 64/91] POTEL 12 - Remove internal span attributes so they are not sent to Sentry (#3463) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing * Inherit OTel span IDs when sending to sentry * Fix tracing; parse incoming baggage; add baggage to outgoing * Cleanup * Move sampling logic to OTel Sampler * Remove internal span attributes so they are not sent to Sentry --- .../opentelemetry/SentrySpanExporter.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index 64cc71bd2c..6125d8c477 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -55,6 +55,17 @@ public final class SentrySpanExporter implements SpanExporter { private final @NotNull List spanKindsConsideredForSentryRequests = Arrays.asList(SpanKind.CLIENT, SpanKind.INTERNAL); + + private final @NotNull List attributeKeysToRemove = + Arrays.asList( + InternalSemanticAttributes.IS_REMOTE_PARENT.getKey(), + InternalSemanticAttributes.BAGGAGE.getKey(), + InternalSemanticAttributes.BAGGAGE_MUTABLE.getKey(), + InternalSemanticAttributes.SAMPLED.getKey(), + InternalSemanticAttributes.SAMPLE_RATE.getKey(), + InternalSemanticAttributes.PROFILE_SAMPLED.getKey(), + InternalSemanticAttributes.PROFILE_SAMPLE_RATE.getKey(), + InternalSemanticAttributes.PARENT_SAMPLED.getKey()); private static final @NotNull Long SPAN_TIMEOUT = DateUtils.secondsToNanos(5 * 60); private static final String TRACE_ORIGN = "auto.potel"; @@ -516,7 +527,10 @@ private SpanStatus mapOtelStatus( attributes.forEach( (key, value) -> { if (key != null) { - mapWithStringKeys.put(key.getKey(), value); + final @NotNull String stringKey = key.getKey(); + if (!isSentryInternalKey(stringKey)) { + mapWithStringKeys.put(stringKey, value); + } } }); } @@ -524,6 +538,10 @@ private SpanStatus mapOtelStatus( return mapWithStringKeys; } + private boolean isSentryInternalKey(final @NotNull String key) { + return attributeKeysToRemove.contains(key); + } + @Override public CompletableResultCode flush() { scopes.flush(10000); From 94ba63ce0ceac17e16971604789fdfe6dba00b98 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 18 Jun 2024 07:52:12 +0200 Subject: [PATCH 65/91] POTEL 13 - Use transaction name (#3464) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing * Inherit OTel span IDs when sending to sentry * Fix tracing; parse incoming baggage; add baggage to outgoing * Cleanup * Move sampling logic to OTel Sampler * Remove internal span attributes so they are not sent to Sentry * Use transaction name * remove obsolete comment --- .../java/io/sentry/opentelemetry/OtelSpanFactory.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java index 8c8fca4ba4..d75a0272a7 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java @@ -40,7 +40,6 @@ public final class OtelSpanFactory implements ISpanFactory { @NotNull IScopes scopes, @NotNull TransactionOptions transactionOptions, @Nullable TransactionPerformanceCollector transactionPerformanceCollector) { - // TODO [POTEL] name vs. op for transaction final @Nullable OtelSpanWrapper span = createSpanInternal( scopes, transactionOptions, null, context.getSamplingDecision(), context); @@ -137,8 +136,14 @@ public final class OtelSpanFactory implements ISpanFactory { if (description != null) { sentrySpan.setDescription(description); } - if (samplingDecision != null) { - sentrySpan.getSpanContext().setSamplingDecision(samplingDecision); + // TODO [POTEL] do we need this? + // if (samplingDecision != null) { + // sentrySpan.getSpanContext().setSamplingDecision(samplingDecision); + // } + if (spanContext instanceof TransactionContext) { + final @NotNull TransactionContext transactionContext = (TransactionContext) spanContext; + sentrySpan.setTransactionName( + transactionContext.getName(), transactionContext.getTransactionNameSource()); } } From 5c9fb87594c4957484a0c0efd5c8e059a1bf41c2 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 18 Jun 2024 07:53:15 +0200 Subject: [PATCH 66/91] POTEL 14 - Keep Sentry span `op` and OTel span `name` in sync (#3468) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing * Inherit OTel span IDs when sending to sentry * Fix tracing; parse incoming baggage; add baggage to outgoing * Cleanup * Move sampling logic to OTel Sampler * Remove internal span attributes so they are not sent to Sentry * Use transaction name * remove obsolete comment * Keep OTel and Sentry span name/op in sync --- .../api/sentry-opentelemetry-bootstrap.api | 2 ++ .../sentry/opentelemetry/OtelSpanContext.java | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api index 110faac564..e7d2348771 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api @@ -19,7 +19,9 @@ public final class io/sentry/opentelemetry/OtelContextScopesStorage : io/sentry/ public final class io/sentry/opentelemetry/OtelSpanContext : io/sentry/SpanContext { public fun (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/TracesSamplingDecision;Lio/sentry/opentelemetry/OtelSpanWrapper;Lio/sentry/Baggage;)V + public fun getOperation ()Ljava/lang/String; public fun getStatus ()Lio/sentry/SpanStatus; + public fun setOperation (Ljava/lang/String;)V public fun setStatus (Lio/sentry/SpanStatus;)V } diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java index 127dbfd57e..5f75a6f10e 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java @@ -75,6 +75,23 @@ public void setStatus(@Nullable SpanStatus status) { } } + @Override + public @NotNull String getOperation() { + final @Nullable ReadWriteSpan otelSpan = span.get(); + if (otelSpan != null) { + return otelSpan.getName(); + } + return ""; + } + + @Override + public void setOperation(@NotNull String operation) { + final @Nullable ReadWriteSpan otelSpan = span.get(); + if (otelSpan != null) { + otelSpan.updateName(operation); + } + } + private @Nullable SpanStatus otelStatusCodeFallback(final @NotNull StatusData otelStatus) { if (otelStatus.getStatusCode() == StatusCode.ERROR) { return SpanStatus.UNKNOWN_ERROR; From ecfcb2b1edbaa7d7fc1059cb8e521944127c2da5 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 18 Jun 2024 07:55:49 +0200 Subject: [PATCH 67/91] POTEL 15 - More cleanup (#3469) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing * Inherit OTel span IDs when sending to sentry * Fix tracing; parse incoming baggage; add baggage to outgoing * Cleanup * Move sampling logic to OTel Sampler * Remove internal span attributes so they are not sent to Sentry * Use transaction name * remove obsolete comment * Keep OTel and Sentry span name/op in sync * more cleanup --- .../OtelContextScopesStorage.java | 16 +------------- .../sentry/opentelemetry/OtelSpanFactory.java | 4 ---- .../sentry/opentelemetry/OtelSpanWrapper.java | 17 +-------------- .../opentelemetry/OtelStorageToken.java | 21 +++++++++++++++++++ .../OtelTransactionSpanForwarder.java | 4 +++- .../api/sentry-opentelemetry-core.api | 2 +- .../io/sentry/opentelemetry/OtelSpanInfo.java | 9 ++++---- .../opentelemetry/SentrySpanExporter.java | 10 ++++++--- .../opentelemetry/SentrySpanProcessor.java | 10 ++++++--- .../SpanDescriptionExtractor.java | 14 +++++++------ sentry/api/sentry.api | 1 + sentry/src/main/java/io/sentry/Span.java | 3 +-- .../java/io/sentry/TransactionContext.java | 4 ++-- 13 files changed, 58 insertions(+), 57 deletions(-) create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelStorageToken.java diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java index b6d7d1bc83..d824776dab 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java @@ -17,7 +17,7 @@ public final class OtelContextScopesStorage implements IScopesStorage { public ISentryLifecycleToken set(@Nullable IScopes scopes) { final @NotNull Scope otelScope = Context.current().with(SENTRY_SCOPES_KEY, scopes).makeCurrent(); - return new OtelContextScopesStorageToken(otelScope); + return new OtelStorageToken(otelScope); } @Override @@ -27,18 +27,4 @@ public ISentryLifecycleToken set(@Nullable IScopes scopes) { @Override public void close() {} - - static final class OtelContextScopesStorageToken implements ISentryLifecycleToken { - - private final @NotNull Scope otelScope; - - OtelContextScopesStorageToken(final @NotNull Scope otelScope) { - this.otelScope = otelScope; - } - - @Override - public void close() { - otelScope.close(); - } - } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java index d75a0272a7..78c9f2a5e3 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java @@ -136,10 +136,6 @@ public final class OtelSpanFactory implements ISpanFactory { if (description != null) { sentrySpan.setDescription(description); } - // TODO [POTEL] do we need this? - // if (samplingDecision != null) { - // sentrySpan.getSpanContext().setSamplingDecision(samplingDecision); - // } if (spanContext instanceof TransactionContext) { final @NotNull TransactionContext transactionContext = (TransactionContext) spanContext; sentrySpan.setTransactionName( diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java index 3c226abbd9..a7f6b86eb7 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java @@ -494,26 +494,11 @@ public Map getMeasurements() { final @Nullable Span otelSpan = getSpan(); if (otelSpan != null) { final @NotNull Scope otelScope = otelSpan.makeCurrent(); - final @NotNull OtelContextSpanStorageToken token = new OtelContextSpanStorageToken(otelScope); + final @NotNull OtelStorageToken token = new OtelStorageToken(otelScope); // to iterate LIFO when closing tokensToCleanup.addFirst(token); return token; } return NoOpScopesLifecycleToken.getInstance(); } - - // TODO [POTEL] extract generic - static final class OtelContextSpanStorageToken implements ISentryLifecycleToken { - - private final @NotNull Scope otelScope; - - OtelContextSpanStorageToken(final @NotNull Scope otelScope) { - this.otelScope = otelScope; - } - - @Override - public void close() { - otelScope.close(); - } - } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelStorageToken.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelStorageToken.java new file mode 100644 index 0000000000..f9c7ccafad --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelStorageToken.java @@ -0,0 +1,21 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.context.Scope; +import io.sentry.ISentryLifecycleToken; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +@ApiStatus.Internal +final class OtelStorageToken implements ISentryLifecycleToken { + + private final @NotNull Scope otelScope; + + OtelStorageToken(final @NotNull Scope otelScope) { + this.otelScope = otelScope; + } + + @Override + public void close() { + otelScope.close(); + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java index beba25374a..eaa3fce56e 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java @@ -1,5 +1,7 @@ package io.sentry.opentelemetry; +import static io.sentry.TransactionContext.DEFAULT_TRANSACTION_NAME; + import io.sentry.BaggageHeader; import io.sentry.Hint; import io.sentry.ISentryLifecycleToken; @@ -315,7 +317,7 @@ public void setName(@NotNull String name, @NotNull TransactionNameSource nameSou public @NotNull String getName() { final @Nullable String name = rootSpan.getTransactionName(); if (name == null) { - return ""; + return DEFAULT_TRANSACTION_NAME; } return name; } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api index 6112e9e3cd..0a16e9204e 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api +++ b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api @@ -74,7 +74,7 @@ public final class io/sentry/opentelemetry/SentrySpanProcessor : io/opentelemetr public final class io/sentry/opentelemetry/SpanDescriptionExtractor { public fun ()V - public fun extractSpanInfo (Lio/opentelemetry/sdk/trace/data/SpanData;)Lio/sentry/opentelemetry/OtelSpanInfo; + public fun extractSpanInfo (Lio/opentelemetry/sdk/trace/data/SpanData;Lio/sentry/opentelemetry/OtelSpanWrapper;)Lio/sentry/opentelemetry/OtelSpanInfo; } public final class io/sentry/opentelemetry/SpanNode { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanInfo.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanInfo.java index 0cc0cd0236..3a0032d16e 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanInfo.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanInfo.java @@ -5,19 +5,20 @@ import java.util.Map; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class OtelSpanInfo { private final @NotNull String op; - private final @NotNull String description; + private final @Nullable String description; private final @NotNull TransactionNameSource transactionNameSource; private final @NotNull Map dataFields; public OtelSpanInfo( final @NotNull String op, - final @NotNull String description, + final @Nullable String description, final @NotNull TransactionNameSource transactionNameSource, final @NotNull Map dataFields) { this.op = op; @@ -28,7 +29,7 @@ public OtelSpanInfo( public OtelSpanInfo( final @NotNull String op, - final @NotNull String description, + final @Nullable String description, final @NotNull TransactionNameSource transactionNameSource) { this.op = op; this.description = description; @@ -40,7 +41,7 @@ public OtelSpanInfo( return op; } - public @NotNull String getDescription() { + public @Nullable String getDescription() { return description; } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index 6125d8c477..2b5d2c3b36 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -1,5 +1,6 @@ package io.sentry.opentelemetry; +import static io.sentry.TransactionContext.DEFAULT_TRANSACTION_NAME; import static io.sentry.opentelemetry.InternalSemanticAttributes.IS_REMOTE_PARENT; import io.opentelemetry.api.common.Attributes; @@ -234,9 +235,10 @@ private void createAndFinishSpanForOtelSpan( } final @NotNull String spanId = spanData.getSpanId(); - final @NotNull OtelSpanInfo spanInfo = spanDescriptionExtractor.extractSpanInfo(spanData); final @Nullable OtelSpanWrapper sentrySpanMaybe = spanStorage.getSentrySpan(spanData.getSpanContext()); + final @NotNull OtelSpanInfo spanInfo = + spanDescriptionExtractor.extractSpanInfo(spanData, sentrySpanMaybe); // TODO attributes // TODO cleanup sentry attributes @@ -328,7 +330,8 @@ private void transferSpanDetails( sentrySpanMaybe != null ? sentrySpanMaybe.getScopes() : null; final @NotNull IScopes scopesToUse = scopesMaybe == null ? ScopesAdapter.getInstance() : scopesMaybe; - final @NotNull OtelSpanInfo spanInfo = spanDescriptionExtractor.extractSpanInfo(span); + final @NotNull OtelSpanInfo spanInfo = + spanDescriptionExtractor.extractSpanInfo(span, sentrySpanMaybe); scopesToUse .getOptions() @@ -365,7 +368,8 @@ private void transferSpanDetails( final @NotNull TransactionContext transactionContext = new TransactionContext(new SentryId(traceId), sentrySpanId, parentSpanId, null, baggage); - transactionContext.setName(transactionName); + transactionContext.setName( + transactionName == null ? DEFAULT_TRANSACTION_NAME : transactionName); transactionContext.setTransactionNameSource(transactionNameSource); transactionContext.setOperation(spanInfo.getOp()); transactionContext.setInstrumenter(Instrumenter.OTEL); diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java index 9e78927980..47268de115 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java @@ -1,5 +1,7 @@ package io.sentry.opentelemetry; +import static io.sentry.TransactionContext.DEFAULT_TRANSACTION_NAME; + import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.api.trace.SpanKind; @@ -282,10 +284,12 @@ private boolean isSentryRequest(final @NotNull ReadableSpan otelSpan) { private void updateTransactionWithOtelData( final @NotNull ITransaction sentryTransaction, final @NotNull ReadableSpan otelSpan) { final @NotNull OtelSpanInfo otelSpanInfo = - spanDescriptionExtractor.extractSpanInfo(otelSpan.toSpanData()); + spanDescriptionExtractor.extractSpanInfo(otelSpan.toSpanData(), null); sentryTransaction.setOperation(otelSpanInfo.getOp()); + String transactionName = otelSpanInfo.getDescription(); sentryTransaction.setName( - otelSpanInfo.getDescription(), otelSpanInfo.getTransactionNameSource()); + transactionName == null ? DEFAULT_TRANSACTION_NAME : transactionName, + otelSpanInfo.getTransactionNameSource()); final @NotNull Map otelContext = toOtelContext(otelSpan); sentryTransaction.setContext("otel", otelContext); @@ -317,7 +321,7 @@ private void updateSpanWithOtelData( }); final @NotNull OtelSpanInfo otelSpanInfo = - spanDescriptionExtractor.extractSpanInfo(otelSpan.toSpanData()); + spanDescriptionExtractor.extractSpanInfo(otelSpan.toSpanData(), null); sentrySpan.setOperation(otelSpanInfo.getOp()); sentrySpan.setDescription(otelSpanInfo.getDescription()); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java index da359bbd25..5e2c07bbdc 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java @@ -16,8 +16,9 @@ public final class SpanDescriptionExtractor { // TODO [POTEL] remove these method overloads and pass in SpanData instead (span.toSpanData()) @SuppressWarnings("deprecation") - public @NotNull OtelSpanInfo extractSpanInfo(final @NotNull SpanData otelSpan) { - OtelSpanInfo spanInfo = extractSpanDescription(otelSpan); + public @NotNull OtelSpanInfo extractSpanInfo( + final @NotNull SpanData otelSpan, final @Nullable OtelSpanWrapper sentrySpan) { + OtelSpanInfo spanInfo = extractSpanDescription(otelSpan, sentrySpan); final @Nullable Long threadId = otelSpan.getAttributes().get(SemanticAttributes.THREAD_ID); if (threadId != null) { @@ -44,8 +45,8 @@ public final class SpanDescriptionExtractor { } @SuppressWarnings("deprecation") - private OtelSpanInfo extractSpanDescription(SpanData otelSpan) { - final @NotNull String name = otelSpan.getName(); + private OtelSpanInfo extractSpanDescription( + final @NotNull SpanData otelSpan, final @Nullable OtelSpanWrapper sentrySpan) { final @NotNull Attributes attributes = otelSpan.getAttributes(); final @Nullable String httpMethod = attributes.get(SemanticAttributes.HTTP_METHOD); @@ -64,8 +65,9 @@ private OtelSpanInfo extractSpanDescription(SpanData otelSpan) { return descriptionForDbSystem(otelSpan); } - // TODO [POTEL] use sentry span description if available - return new OtelSpanInfo(name, name, TransactionNameSource.CUSTOM); + final @NotNull String name = otelSpan.getName(); + final @Nullable String description = sentrySpan != null ? sentrySpan.getDescription() : name; + return new OtelSpanInfo(name, description, TransactionNameSource.CUSTOM); } @SuppressWarnings("deprecation") diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 3601236557..c56fc7ed04 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -3372,6 +3372,7 @@ public final class io/sentry/TracesSamplingDecision { } public final class io/sentry/TransactionContext : io/sentry/SpanContext { + public static final field DEFAULT_TRANSACTION_NAME Ljava/lang/String; public fun (Lio/sentry/protocol/SentryId;Lio/sentry/SpanId;Lio/sentry/SpanId;Lio/sentry/TracesSamplingDecision;Lio/sentry/Baggage;)V public fun (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;Ljava/lang/String;)V public fun (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;Ljava/lang/String;Lio/sentry/TracesSamplingDecision;)V diff --git a/sentry/src/main/java/io/sentry/Span.java b/sentry/src/main/java/io/sentry/Span.java index 35bbc42890..51d9b25ba2 100644 --- a/sentry/src/main/java/io/sentry/Span.java +++ b/sentry/src/main/java/io/sentry/Span.java @@ -336,8 +336,7 @@ public boolean isFinished() { @Override public @NotNull SentryId getEventId() { - // TODO [POTEL] - return new SentryId(); + return new SentryId(getSpanId().toString()); } @Override diff --git a/sentry/src/main/java/io/sentry/TransactionContext.java b/sentry/src/main/java/io/sentry/TransactionContext.java index 8fd4779c26..d81aa1059d 100644 --- a/sentry/src/main/java/io/sentry/TransactionContext.java +++ b/sentry/src/main/java/io/sentry/TransactionContext.java @@ -9,7 +9,7 @@ import org.jetbrains.annotations.Nullable; public final class TransactionContext extends SpanContext { - private static final @NotNull String DEFAULT_NAME = ""; + public static final @NotNull String DEFAULT_TRANSACTION_NAME = ""; private static final @NotNull TransactionNameSource DEFAULT_NAME_SOURCE = TransactionNameSource.CUSTOM; private static final @NotNull String DEFAULT_OPERATION = "default"; @@ -134,7 +134,7 @@ public TransactionContext( final @Nullable TracesSamplingDecision parentSamplingDecision, final @Nullable Baggage baggage) { super(traceId, spanId, DEFAULT_OPERATION, parentSpanId, null); - this.name = DEFAULT_NAME; + this.name = DEFAULT_TRANSACTION_NAME; this.parentSamplingDecision = parentSamplingDecision; this.transactionNameSource = DEFAULT_NAME_SOURCE; this.baggage = baggage; From 19d0b3fdd7391325eb77113fa5648eddbe55b5fc Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 18 Jun 2024 12:22:10 +0200 Subject: [PATCH 68/91] POTEL 16 - Add `ignoredSpanOrigins` option (#3477) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing * Inherit OTel span IDs when sending to sentry * Fix tracing; parse incoming baggage; add baggage to outgoing * Cleanup * Move sampling logic to OTel Sampler * Remove internal span attributes so they are not sent to Sentry * Use transaction name * remove obsolete comment * Keep OTel and Sentry span name/op in sync * more cleanup * Make it possible to ignore span origins * Format code * Changelog --------- Co-authored-by: Sentry Github Bot --- CHANGELOG.md | 16 +++++ .../core/ActivityLifecycleIntegration.java | 1 + .../gestures/SentryGestureListener.java | 3 +- .../SentryGestureListenerTracingTest.kt | 17 +++--- .../sentry/graphql/SentryInstrumentation.java | 9 +-- .../sentry/jdbc/SentryJdbcEventListener.java | 6 +- .../sentry/openfeign/SentryFeignClient.java | 6 +- ...ryAutoConfigurationCustomizerProvider.java | 5 ++ .../sentry/opentelemetry/OtelSpanFactory.java | 6 ++ .../api/sentry-opentelemetry-core.api | 1 + .../PotelSentrySpanProcessor.java | 7 ++- .../opentelemetry/SentrySpanExporter.java | 18 +++--- .../opentelemetry/SentrySpanProcessor.java | 8 ++- .../test/kotlin/SentrySpanProcessorTest.kt | 6 +- .../api/sentry-spring-jakarta.api | 4 +- .../jakarta/tracing/SentrySpanAdvice.java | 6 +- ...entrySpanClientHttpRequestInterceptor.java | 7 ++- .../SentrySpanClientWebRequestFilter.java | 7 ++- .../jakarta/tracing/SentryTracingFilter.java | 14 ++--- .../tracing/SentryTransactionAdvice.java | 2 +- .../webflux/AbstractSentryWebFilter.java | 10 +++- .../jakarta/webflux/SentryWebFilter.java | 6 +- ...entryWebFilterWithThreadLocalAccessor.java | 5 +- .../tracing/SentryTracingFilterTest.kt | 2 +- .../tracing/SentryTransactionAdviceTest.kt | 11 +++- .../webflux/SentryWebFluxTracingFilterTest.kt | 2 +- .../spring/tracing/SentrySpanAdvice.java | 6 +- ...entrySpanClientHttpRequestInterceptor.java | 7 ++- .../SentrySpanClientWebRequestFilter.java | 7 ++- .../spring/tracing/SentryTracingFilter.java | 14 ++--- .../tracing/SentryTransactionAdvice.java | 2 +- .../spring/webflux/SentryWebFilter.java | 5 +- .../spring/tracing/SentryTracingFilterTest.kt | 2 +- .../tracing/SentryTransactionAdviceTest.kt | 11 +++- .../webflux/SentryWebFluxTracingFilterTest.kt | 2 +- sentry/api/sentry.api | 11 ++++ sentry/src/main/java/io/sentry/NoOpSpan.java | 1 - sentry/src/main/java/io/sentry/Scopes.java | 14 +++++ .../main/java/io/sentry/SentryOptions.java | 25 ++++++++ .../src/main/java/io/sentry/SentryTracer.java | 7 +++ sentry/src/main/java/io/sentry/Span.java | 35 +---------- .../src/main/java/io/sentry/SpanOptions.java | 12 ++++ .../main/java/io/sentry/util/SpanUtils.java | 60 +++++++++++++++++++ sentry/src/test/java/io/sentry/ScopesTest.kt | 35 +++++++++++ .../test/java/io/sentry/SentryTracerTest.kt | 16 +++++ sentry/src/test/java/io/sentry/SpanTest.kt | 32 ++++++++++ 46 files changed, 360 insertions(+), 129 deletions(-) create mode 100644 sentry/src/main/java/io/sentry/util/SpanUtils.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 57f12ff897..a78866de0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,22 @@ ### Features +- Our `sentry-opentelemetry-agent` has been completely reworked and now plays nicely with the rest of the Java SDK + - NOTE: Not all features have been implemented yet for the OpenTelemetry agent. + - You can add `sentry-opentelemetry-agent` to your setup by downloading the latest release and using it when starting up your application + - `SENTRY_PROPERTIES_FILE=sentry.properties java -javaagent:sentry-opentelemetry-agent-x.x.x.jar -jar your-application.jar` + - Please use `sentry.properties` or environment variables to configure the SDK as the agent is now in charge of initializing the SDK and options coming from things like logging integrations or our Spring Boot integration will not take effect. + - You may find the [docs page](https://docs.sentry.io/platforms/java/tracing/instrumentation/opentelemetry/#using-sentry-opentelemetry-agent-with-auto-initialization) useful. While we haven't updated it yet to reflect the changes described here, the section about using the agent with auto init should still be vaild. + - What's new about the Agent + - When the OpenTelemetry Agent is used, Sentry API creates OpenTelemetry spans under the hood, handing back a wrapper object which bridges the gap between traditional Sentry API and OpenTelemetry. We might be replacing some of the Sentry performance API in the future. + - This is achieved by configuring the SDK to use `OtelSpanFactory` instead of `DefaultSpanFactory` which is done automatically by the auto init of the Java Agent. + - OpenTelemetry spans are now only turned into Sentry spans when they are finished so they can be sent to the Sentry server. + - Now registers an OpenTelemetry `Sampler` which uses Sentry sampling configuration + - Other Performance integrations automatically stop creating spans to avoid duplicate spans + - The Sentry SDK now makes use of OpenTelemetry `Context` for storing Sentry `Scopes` (which is similar to what used to be called `Hub`) and thus relies on OpenTelemetry for `Context` propagation. + - Classes used for the previous version of our OpenTelemetry support have been deprecated but can still be used manually. We're not planning to keep the old agent around in favor of less complexity in the SDK. +- Add `ignoredSpanOrigins` option for ignoring spans coming from certain integrations + - We pre-configure this to ignore Performance instrumentation for Spring and other integrations when using our OpenTelemetry Agent to avoid duplicate spans - Publish Gradle module metadata ([#3422](https://github.com/getsentry/sentry-java/pull/3422)) - Add data fetching environment hint to breadcrumb for GraphQL (#3413) ([#3431](https://github.com/getsentry/sentry-java/pull/3431)) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java index 76bedae5d0..749b9d8f19 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java @@ -290,6 +290,7 @@ private void startTracing(final @NotNull Activity activity) { private void setSpanOrigin(ISpan span) { if (span != null) { + // TODO [POTEL] replace with transactionOptions.setOrigin span.getSpanContext().setOrigin(TRACE_ORIGIN); } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java index 9154f3e7c6..cd80f5ced7 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java @@ -251,13 +251,12 @@ private void startTracing(final @NotNull UiElement target, final @NotNull Gestur TransactionOptions.DEFAULT_DEADLINE_TIMEOUT_AUTO_TRANSACTION); transactionOptions.setIdleTimeout(options.getIdleTimeout()); transactionOptions.setTrimEnd(true); + transactionOptions.setOrigin(TRACE_ORIGIN + "." + target.getOrigin()); final ITransaction transaction = scopes.startTransaction( new TransactionContext(name, TransactionNameSource.COMPONENT, op), transactionOptions); - transaction.getSpanContext().setOrigin(TRACE_ORIGIN + "." + target.getOrigin()); - scopes.configureScope( scope -> { applyScope(scope, transaction); diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerTracingTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerTracingTest.kt index d3f6647c2a..8f3e824a2b 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerTracingTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerTracingTest.kt @@ -349,14 +349,15 @@ class SentryGestureListenerTracingTest { ) } - @Test - fun `captures transaction and sets trace origin`() { - val sut = fixture.getSut() - - sut.onSingleTapUp(fixture.event) - - assertEquals("auto.ui.gesture_listener.old_view_system", fixture.transaction.spanContext.origin) - } + // TODO [POTEL] rewrite +// @Test +// fun `captures transaction and sets trace origin`() { +// val sut = fixture.getSut() +// +// sut.onSingleTapUp(fixture.event) +// +// assertEquals("auto.ui.gesture_listener.old_view_system", fixture.transaction.spanContext.origin) +// } @Test fun `preserves existing transaction status`() { diff --git a/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java b/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java index 2de9b82f75..9a853faf38 100644 --- a/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java +++ b/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java @@ -24,6 +24,7 @@ import io.sentry.NoOpScopes; import io.sentry.Sentry; import io.sentry.SentryIntegrationPackageStorage; +import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.TypeCheckHint; import io.sentry.util.StringUtils; @@ -402,13 +403,13 @@ private void finish( } else { parent = (GraphQLObjectType) type; } - + final @NotNull SpanOptions spanOptions = new SpanOptions(); + spanOptions.setOrigin(TRACE_ORIGIN); final @NotNull ISpan span = transaction.startChild( "graphql", - parent.getName() + "." + parameters.getExecutionStepInfo().getPath().getSegmentName()); - - span.getSpanContext().setOrigin(TRACE_ORIGIN); + parent.getName() + "." + parameters.getExecutionStepInfo().getPath().getSegmentName(), + spanOptions); return span; } diff --git a/sentry-jdbc/src/main/java/io/sentry/jdbc/SentryJdbcEventListener.java b/sentry-jdbc/src/main/java/io/sentry/jdbc/SentryJdbcEventListener.java index 4cb21188e4..4f45a67cc9 100644 --- a/sentry-jdbc/src/main/java/io/sentry/jdbc/SentryJdbcEventListener.java +++ b/sentry-jdbc/src/main/java/io/sentry/jdbc/SentryJdbcEventListener.java @@ -11,6 +11,7 @@ import io.sentry.ScopesAdapter; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.Span; +import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; import java.sql.SQLException; @@ -40,9 +41,10 @@ public SentryJdbcEventListener() { public void onBeforeAnyExecute(final @NotNull StatementInformation statementInformation) { final ISpan parent = scopes.getSpan(); if (parent != null && !parent.isNoOp()) { - final ISpan span = parent.startChild("db.query", statementInformation.getSql()); + final @NotNull SpanOptions spanOptions = new SpanOptions(); + spanOptions.setOrigin(TRACE_ORIGIN); + final ISpan span = parent.startChild("db.query", statementInformation.getSql(), spanOptions); CURRENT_SPAN.set(span); - span.getSpanContext().setOrigin(TRACE_ORIGIN); } } diff --git a/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java b/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java index 037768c7ad..a57380b29a 100644 --- a/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java +++ b/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java @@ -12,6 +12,7 @@ import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.SpanDataConvention; +import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; import io.sentry.util.TracingUtils; @@ -54,8 +55,9 @@ public Response execute(final @NotNull Request request, final @NotNull Request.O return delegate.execute(modifiedRequest, options); } - ISpan span = activeSpan.startChild("http.client"); - span.getSpanContext().setOrigin(TRACE_ORIGIN); + final @NotNull SpanOptions spanOptions = new SpanOptions(); + spanOptions.setOrigin(TRACE_ORIGIN); + ISpan span = activeSpan.startChild("http.client", null, spanOptions); final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(request.url()); final @NotNull String method = request.httpMethod().name(); span.setDescription(method + " " + urlDetails.getUrlOrFallback()); diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java index 6e776f94ed..8ea2f3bab5 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java @@ -12,6 +12,7 @@ import io.sentry.SentryOptions; import io.sentry.protocol.SdkVersion; import io.sentry.protocol.SentryPackage; +import io.sentry.util.SpanUtils; import java.io.IOException; import java.net.URL; import java.util.ArrayList; @@ -37,10 +38,14 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) { Sentry.init( options -> { options.setEnableExternalConfiguration(true); + // TODO [POTEL] deprecate options.setInstrumenter(Instrumenter.OTEL); + // TODO [POTEL] do we still need this? options.addEventProcessor(new OpenTelemetryLinkErrorEventProcessor()); + options.setIgnoredSpanOrigins(SpanUtils.ignoredSpanOriginsForOpenTelemetry()); options.setSpanFactory(new OtelSpanFactory()); final @Nullable SdkVersion sdkVersion = createSdkVersion(options, versionInfoHolder); + // TODO [POTEL] is detecting a version mismatch between application and agent possible? if (sdkVersion != null) { options.setSdkVersion(sdkVersion); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java index 78c9f2a5e3..7d060bb16a 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java @@ -24,6 +24,7 @@ import io.sentry.TransactionOptions; import io.sentry.TransactionPerformanceCollector; import io.sentry.protocol.SentryId; +import io.sentry.util.SpanUtils; import java.util.concurrent.TimeUnit; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -55,6 +56,10 @@ public final class OtelSpanFactory implements ISpanFactory { final @NotNull SpanOptions spanOptions, final @NotNull SpanContext spanContext, final @Nullable ISpan parentSpan) { + if (SpanUtils.isIgnored(scopes.getOptions().getIgnoredSpanOrigins(), spanOptions.getOrigin())) { + return NoOpSpan.getInstance(); + } + final @Nullable TracesSamplingDecision samplingDecision = parentSpan == null ? null : parentSpan.getSamplingDecision(); final @Nullable OtelSpanWrapper span = @@ -141,6 +146,7 @@ public final class OtelSpanFactory implements ISpanFactory { sentrySpan.setTransactionName( transactionContext.getName(), transactionContext.getTransactionNameSource()); } + sentrySpan.getSpanContext().setOrigin(spanOptions.getOrigin()); } return sentrySpan; diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api index 0a16e9204e..72f8400fed 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api +++ b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api @@ -57,6 +57,7 @@ public final class io/sentry/opentelemetry/SentrySamplingResult : io/opentelemet } public final class io/sentry/opentelemetry/SentrySpanExporter : io/opentelemetry/sdk/trace/export/SpanExporter { + public static final field TRACE_ORIGIN Ljava/lang/String; public fun ()V public fun (Lio/sentry/IScopes;)V public fun export (Ljava/util/Collection;)Lio/opentelemetry/sdk/common/CompletableResultCode; diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java index 4d54f03be1..5ba54602b4 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java @@ -96,10 +96,11 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri final @NotNull SpanContext spanContext = otelSpan.getSpanContext(); final @NotNull SentryDate startTimestamp = new SentryLongDate(otelSpan.toSpanData().getStartEpochNanos()); - spanStorage.storeSentrySpan( - spanContext, + final @NotNull OtelSpanWrapper sentrySpan = new OtelSpanWrapper( - otelSpan, scopes, startTimestamp, samplingDecision, sentryParentSpan, baggage)); + otelSpan, scopes, startTimestamp, samplingDecision, sentryParentSpan, baggage); + sentrySpan.getSpanContext().setOrigin(SentrySpanExporter.TRACE_ORIGIN); + spanStorage.storeSentrySpan(spanContext, sentrySpan); } private static void updatePropagationContext( diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index 2b5d2c3b36..50439a8700 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -69,7 +69,7 @@ public final class SentrySpanExporter implements SpanExporter { InternalSemanticAttributes.PARENT_SAMPLED.getKey()); private static final @NotNull Long SPAN_TIMEOUT = DateUtils.secondsToNanos(5 * 60); - private static final String TRACE_ORIGN = "auto.potel"; + public static final String TRACE_ORIGIN = "auto.potel"; public SentrySpanExporter() { this(ScopesAdapter.getInstance()); @@ -252,6 +252,7 @@ private void createAndFinishSpanForOtelSpan( spanData.getTraceId(), spanData.getParentSpanId()); final @NotNull SentryDate startDate = new SentryLongDate(spanData.getStartEpochNanos()); + final @NotNull SpanOptions spanOptions = new SpanOptions(); // TODO [POTEL] op and description might have been overriden final @NotNull io.sentry.SpanContext spanContext = parentSentrySpan @@ -264,17 +265,17 @@ private void createAndFinishSpanForOtelSpan( spanContext.setInstrumenter(Instrumenter.OTEL); if (sentrySpanMaybe != null) { spanContext.setSamplingDecision(sentrySpanMaybe.getSamplingDecision()); + spanOptions.setOrigin(sentrySpanMaybe.getSpanContext().getOrigin()); + } else { + // TODO [POTEL] Check if we want to use `instrumentationScopeInfo.name` and append it to + // `auto.otel` + spanOptions.setOrigin(TRACE_ORIGIN); } - final @NotNull SpanOptions spanOptions = new SpanOptions(); spanOptions.setStartTimestamp(startDate); final @NotNull ISpan sentryChildSpan = parentSentrySpan.startChild(spanContext, spanOptions); - // TODO [POTEL] Check if we want to use `instrumentationScopeInfo.name` and append it to - // `auto.otel` - // TODO [POTEL] For spans manually created via Sentry API we should set manual, not auto.otel - sentryChildSpan.getSpanContext().setOrigin(TRACE_ORIGN); for (Map.Entry dataField : spanInfo.getDataFields().entrySet()) { sentryChildSpan.setData(dataField.getKey(), dataField.getValue()); } @@ -368,6 +369,8 @@ private void transferSpanDetails( final @NotNull TransactionContext transactionContext = new TransactionContext(new SentryId(traceId), sentrySpanId, parentSpanId, null, baggage); + TransactionOptions transactionOptions = new TransactionOptions(); + transactionContext.setName( transactionName == null ? DEFAULT_TRANSACTION_NAME : transactionName); transactionContext.setTransactionNameSource(transactionNameSource); @@ -375,15 +378,14 @@ private void transferSpanDetails( transactionContext.setInstrumenter(Instrumenter.OTEL); if (sentrySpanMaybe != null) { transactionContext.setSamplingDecision(sentrySpanMaybe.getSamplingDecision()); + transactionOptions.setOrigin(sentrySpanMaybe.getSpanContext().getOrigin()); } - TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setStartTimestamp(new SentryLongDate(span.getStartEpochNanos())); transactionOptions.setSpanFactory(new DefaultSpanFactory()); ITransaction sentryTransaction = scopesToUse.startTransaction(transactionContext, transactionOptions); - sentryTransaction.getSpanContext().setOrigin(TRACE_ORIGN); final @NotNull Map otelContext = toOtelContext(span); sentryTransaction.setContext("otel", otelContext); diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java index 47268de115..60f379ceae 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java @@ -27,6 +27,7 @@ import io.sentry.SentrySpanStorage; import io.sentry.SentryTraceHeader; import io.sentry.SpanId; +import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.TransactionContext; import io.sentry.TransactionOptions; @@ -92,10 +93,11 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri traceData.getParentSpanId()); final @NotNull SentryDate startDate = new SentryLongDate(otelSpan.toSpanData().getStartEpochNanos()); + final @NotNull SpanOptions spanOptions = new SpanOptions(); + spanOptions.setOrigin(TRACE_ORIGN); final @NotNull ISpan sentryChildSpan = sentryParentSpan.startChild( - otelSpan.getName(), otelSpan.getName(), startDate, Instrumenter.OTEL); - sentryChildSpan.getSpanContext().setOrigin(TRACE_ORIGN); + otelSpan.getName(), otelSpan.getName(), startDate, Instrumenter.OTEL, spanOptions); spanStorage.store(traceData.getSpanId(), sentryChildSpan); } else { scopes @@ -127,9 +129,9 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setStartTimestamp( new SentryLongDate(otelSpan.toSpanData().getStartEpochNanos())); + transactionOptions.setOrigin(TRACE_ORIGN); ISpan sentryTransaction = scopes.startTransaction(transactionContext, transactionOptions); - sentryTransaction.getSpanContext().setOrigin(TRACE_ORIGN); spanStorage.store(traceData.getSpanId(), sentryTransaction); } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SentrySpanProcessorTest.kt b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SentrySpanProcessorTest.kt index 50d70f34f5..acead00460 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SentrySpanProcessorTest.kt +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SentrySpanProcessorTest.kt @@ -29,6 +29,7 @@ import io.sentry.SentryDate import io.sentry.SentryEvent import io.sentry.SentryOptions import io.sentry.SentryTraceHeader +import io.sentry.SpanOptions import io.sentry.SpanStatus import io.sentry.TransactionContext import io.sentry.TransactionOptions @@ -91,7 +92,7 @@ class SentrySpanProcessorTest { whenever(span.toBaggageHeader(any())).thenReturn(baggageHeader) whenever(transaction.toBaggageHeader(any())).thenReturn(baggageHeader) - whenever(transaction.startChild(any(), anyOrNull(), anyOrNull(), eq(Instrumenter.OTEL))).thenReturn(span) + whenever(transaction.startChild(any(), anyOrNull(), anyOrNull(), eq(Instrumenter.OTEL), any())).thenReturn(span) val sdkTracerProvider = SdkTracerProvider.builder() .addSpanProcessor(SentrySpanProcessor(scopes)) @@ -462,7 +463,8 @@ class SentrySpanProcessorTest { eq("childspan"), eq("childspan"), any(), - eq(Instrumenter.OTEL) + eq(Instrumenter.OTEL), + any() ) } diff --git a/sentry-spring-jakarta/api/sentry-spring-jakarta.api b/sentry-spring-jakarta/api/sentry-spring-jakarta.api index 4eed502841..e445aac284 100644 --- a/sentry-spring-jakarta/api/sentry-spring-jakarta.api +++ b/sentry-spring-jakarta/api/sentry-spring-jakarta.api @@ -281,9 +281,9 @@ public abstract class io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter : protected fun doFinally (Lorg/springframework/web/server/ServerWebExchange;Lio/sentry/IScopes;Lio/sentry/ITransaction;)V protected fun doFirst (Lorg/springframework/web/server/ServerWebExchange;Lio/sentry/IScopes;)V protected fun doOnError (Lio/sentry/ITransaction;Ljava/lang/Throwable;)V - protected fun maybeStartTransaction (Lio/sentry/IScopes;Lorg/springframework/http/server/reactive/ServerHttpRequest;)Lio/sentry/ITransaction; + protected fun maybeStartTransaction (Lio/sentry/IScopes;Lorg/springframework/http/server/reactive/ServerHttpRequest;Ljava/lang/String;)Lio/sentry/ITransaction; protected fun shouldTraceRequest (Lio/sentry/IScopes;Lorg/springframework/http/server/reactive/ServerHttpRequest;)Z - protected fun startTransaction (Lio/sentry/IScopes;Lorg/springframework/http/server/reactive/ServerHttpRequest;Lio/sentry/TransactionContext;)Lio/sentry/ITransaction; + protected fun startTransaction (Lio/sentry/IScopes;Lorg/springframework/http/server/reactive/ServerHttpRequest;Lio/sentry/TransactionContext;Ljava/lang/String;)Lio/sentry/ITransaction; } public final class io/sentry/spring/jakarta/webflux/ReactorUtils { diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanAdvice.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanAdvice.java index e8de36487f..668c8d1b0b 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanAdvice.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanAdvice.java @@ -4,6 +4,7 @@ import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.ScopesAdapter; +import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; import java.lang.reflect.Method; @@ -51,8 +52,9 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl mostSpecificMethod.getDeclaringClass(), SentrySpan.class); } final String operation = resolveSpanOperation(targetClass, mostSpecificMethod, sentrySpan); - final ISpan span = activeSpan.startChild(operation); - span.getSpanContext().setOrigin(TRACE_ORIGIN); + SpanOptions spanOptions = new SpanOptions(); + spanOptions.setOrigin(TRACE_ORIGIN); + final ISpan span = activeSpan.startChild(operation, null, spanOptions); if (sentrySpan != null && !StringUtils.isEmpty(sentrySpan.description())) { span.setDescription(sentrySpan.description()); } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java index 7a787fb29d..6feac7eec7 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java @@ -11,6 +11,7 @@ import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.SpanDataConvention; +import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; import io.sentry.util.TracingUtils; @@ -55,9 +56,9 @@ public SentrySpanClientHttpRequestInterceptor( maybeAddTracingHeaders(request, null); return execution.execute(request, body); } - - final ISpan span = activeSpan.startChild("http.client"); - span.getSpanContext().setOrigin(traceOrigin); + final @NotNull SpanOptions spanOptions = new SpanOptions(); + spanOptions.setOrigin(traceOrigin); + final ISpan span = activeSpan.startChild("http.client", null, spanOptions); final String methodName = request.getMethod() != null ? request.getMethod().name() : "unknown"; final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(request.getURI().toString()); diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java index bc5c0edfab..6744cf174e 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java @@ -10,6 +10,7 @@ import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.SpanDataConvention; +import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; import io.sentry.util.TracingUtils; @@ -40,9 +41,9 @@ public SentrySpanClientWebRequestFilter(final @NotNull IScopes scopes) { addBreadcrumb(modifiedRequest, null); return next.exchange(modifiedRequest); } - - final ISpan span = activeSpan.startChild("http.client"); - span.getSpanContext().setOrigin(TRACE_ORIGIN); + final @NotNull SpanOptions spanOptions = new SpanOptions(); + spanOptions.setOrigin(TRACE_ORIGIN); + final ISpan span = activeSpan.startChild("http.client", null, spanOptions); final @NotNull String method = request.method().name(); span.setDescription(method + " " + request.url()); span.setData(SpanDataConvention.HTTP_METHOD_KEY, method.toUpperCase(Locale.ROOT)); diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java index 097528ac44..fdfef1030c 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java @@ -102,7 +102,6 @@ private void doFilterWithTransaction( throws IOException, ServletException { // at this stage we are not able to get real transaction name final ITransaction transaction = startTransaction(httpRequest, transactionContext); - transaction.getSpanContext().setOrigin(TRACE_ORIGIN); try { filterChain.doFilter(httpRequest, httpResponse); @@ -144,22 +143,19 @@ private ITransaction startTransaction( final CustomSamplingContext customSamplingContext = new CustomSamplingContext(); customSamplingContext.set("request", request); + final TransactionOptions transactionOptions = new TransactionOptions(); + transactionOptions.setCustomSamplingContext(customSamplingContext); + transactionOptions.setBindToScope(true); + transactionOptions.setOrigin(TRACE_ORIGIN); + if (transactionContext != null) { transactionContext.setName(name); transactionContext.setTransactionNameSource(TransactionNameSource.URL); transactionContext.setOperation("http.server"); - final TransactionOptions transactionOptions = new TransactionOptions(); - transactionOptions.setCustomSamplingContext(customSamplingContext); - transactionOptions.setBindToScope(true); - return scopes.startTransaction(transactionContext, transactionOptions); } - final TransactionOptions transactionOptions = new TransactionOptions(); - transactionOptions.setCustomSamplingContext(customSamplingContext); - transactionOptions.setBindToScope(true); - return scopes.startTransaction( new TransactionContext(name, TransactionNameSource.URL, "http.server"), transactionOptions); } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java index c85831ae8f..95618f76fd 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java @@ -74,11 +74,11 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) { final TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setBindToScope(true); + transactionOptions.setOrigin(TRACE_ORIGIN); final ITransaction transaction = forkedScopes.startTransaction( new TransactionContext(nameAndSource.name, nameAndSource.source, operation), transactionOptions); - transaction.getSpanContext().setOrigin(TRACE_ORIGIN); try { final Object result = invocation.proceed(); transaction.setStatus(SpanStatus.OK); diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java index e626e69d3b..2d43b41f85 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java @@ -49,7 +49,9 @@ public AbstractSentryWebFilter(final @NotNull IScopes scopes) { } protected @Nullable ITransaction maybeStartTransaction( - final @NotNull IScopes requestScopes, final @NotNull ServerHttpRequest request) { + final @NotNull IScopes requestScopes, + final @NotNull ServerHttpRequest request, + final @NotNull String origin) { if (requestScopes.isEnabled()) { final @NotNull HttpHeaders headers = request.getHeaders(); final @Nullable String sentryTraceHeader = @@ -60,7 +62,7 @@ public AbstractSentryWebFilter(final @NotNull IScopes scopes) { if (requestScopes.getOptions().isTracingEnabled() && shouldTraceRequest(requestScopes, request)) { - return startTransaction(requestScopes, request, transactionContext); + return startTransaction(requestScopes, request, transactionContext, origin); } } @@ -135,7 +137,8 @@ private void finishTransaction(ServerWebExchange exchange, ITransaction transact protected @NotNull ITransaction startTransaction( final @NotNull IScopes scopes, final @NotNull ServerHttpRequest request, - final @Nullable TransactionContext transactionContext) { + final @Nullable TransactionContext transactionContext, + final @NotNull String origin) { final @NotNull String name = request.getMethod() + " " + request.getURI().getPath(); final @NotNull CustomSamplingContext customSamplingContext = new CustomSamplingContext(); customSamplingContext.set("request", request); @@ -143,6 +146,7 @@ private void finishTransaction(ServerWebExchange exchange, ITransaction transact final TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setCustomSamplingContext(customSamplingContext); transactionOptions.setBindToScope(true); + transactionOptions.setOrigin(origin); if (transactionContext != null) { transactionContext.setName(name); diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilter.java index 0a6b767ec4..0ec4b44a0e 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilter.java @@ -30,10 +30,8 @@ public Mono filter( final @NotNull WebFilterChain webFilterChain) { @NotNull IScopes requestScopes = Sentry.forkedRootScopes("request.webflux"); final ServerHttpRequest request = serverWebExchange.getRequest(); - final @Nullable ITransaction transaction = maybeStartTransaction(requestScopes, request); - if (transaction != null) { - transaction.getSpanContext().setOrigin(TRACE_ORIGIN); - } + final @Nullable ITransaction transaction = + maybeStartTransaction(requestScopes, request, TRACE_ORIGIN); return webFilterChain .filter(serverWebExchange) .doFinally(__ -> doFinally(serverWebExchange, requestScopes, transaction)) diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor.java index c38e322731..5408f6dbec 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor.java @@ -41,11 +41,8 @@ public Mono filter( doFirst(serverWebExchange, Sentry.getCurrentScopes()); final ITransaction transaction = maybeStartTransaction( - Sentry.getCurrentScopes(), serverWebExchange.getRequest()); + Sentry.getCurrentScopes(), serverWebExchange.getRequest(), TRACE_ORIGIN); transactionContainer.transaction = transaction; - if (transaction != null) { - transaction.getSpanContext().setOrigin(TRACE_ORIGIN); - } })); } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTracingFilterTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTracingFilterTest.kt index 265d607b70..678dd238cf 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTracingFilterTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTracingFilterTest.kt @@ -92,6 +92,7 @@ class SentryTracingFilterTest { assertNotNull(it.customSamplingContext?.get("request")) assertTrue(it.customSamplingContext?.get("request") is HttpServletRequest) assertTrue(it.isBindToScope) + assertThat(it.origin).isEqualTo("auto.http.spring_jakarta.webmvc") } ) verify(fixture.chain).doFilter(fixture.request, fixture.response) @@ -100,7 +101,6 @@ class SentryTracingFilterTest { assertThat(it.transaction).isEqualTo("POST /product/{id}") assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.OK) assertThat(it.contexts.trace!!.operation).isEqualTo("http.server") - assertThat(it.contexts.trace!!.origin).isEqualTo("auto.http.spring_jakarta.webmvc") }, anyOrNull(), anyOrNull(), diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt index 978c5baa63..545a5e1390 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt @@ -52,7 +52,15 @@ class SentryTransactionAdviceTest { @BeforeTest fun setup() { reset(scopes) - whenever(scopes.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } + whenever( + scopes.startTransaction( + any(), + check { + assertTrue(it.isBindToScope) + assertThat(it.origin).isEqualTo("auto.function.spring_jakarta.advice") + } + ) + ).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } whenever(scopes.options).thenReturn( SentryOptions().apply { dsn = "https://key@sentry.io/proj" @@ -70,7 +78,6 @@ class SentryTransactionAdviceTest { assertThat(it.transaction).isEqualTo("customName") assertThat(it.contexts.trace!!.operation).isEqualTo("bean") assertThat(it.status).isEqualTo(SpanStatus.OK) - assertThat(it.contexts.trace!!.origin).isEqualTo("auto.function.spring_jakarta.advice") }, anyOrNull(), anyOrNull(), diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt index 76e4e0e2b6..2b18b38651 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt @@ -107,6 +107,7 @@ class SentryWebFluxTracingFilterTest { assertNotNull(it.customSamplingContext?.get("request")) assertTrue(it.customSamplingContext?.get("request") is ServerHttpRequest) assertTrue(it.isBindToScope) + assertThat(it.origin).isEqualTo("auto.spring_jakarta.webflux") } ) verify(fixture.chain).filter(fixture.exchange) @@ -115,7 +116,6 @@ class SentryWebFluxTracingFilterTest { assertThat(it.transaction).isEqualTo("POST /product/{id}") assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.OK) assertThat(it.contexts.trace!!.operation).isEqualTo("http.server") - assertThat(it.contexts.trace!!.origin).isEqualTo("auto.spring_jakarta.webflux") assertThat(it.contexts.response!!.statusCode).isEqualTo(200) }, anyOrNull(), diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanAdvice.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanAdvice.java index c1b7305d4f..af57e999f9 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanAdvice.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanAdvice.java @@ -4,6 +4,7 @@ import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.ScopesAdapter; +import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; import java.lang.reflect.Method; @@ -51,8 +52,9 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl mostSpecificMethod.getDeclaringClass(), SentrySpan.class); } final String operation = resolveSpanOperation(targetClass, mostSpecificMethod, sentrySpan); - final ISpan span = activeSpan.startChild(operation); - span.getSpanContext().setOrigin(TRACE_ORIGIN); + final @NotNull SpanOptions spanOptions = new SpanOptions(); + spanOptions.setOrigin(TRACE_ORIGIN); + final ISpan span = activeSpan.startChild(operation, null, spanOptions); if (sentrySpan != null && !StringUtils.isEmpty(sentrySpan.description())) { span.setDescription(sentrySpan.description()); } diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java index bdf8417642..91ca625d41 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java @@ -11,6 +11,7 @@ import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.SpanDataConvention; +import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; import io.sentry.util.TracingUtils; @@ -47,9 +48,9 @@ public SentrySpanClientHttpRequestInterceptor(final @NotNull IScopes scopes) { maybeAddTracingHeaders(request, null); return execution.execute(request, body); } - - final ISpan span = activeSpan.startChild("http.client"); - span.getSpanContext().setOrigin(TRACE_ORIGIN); + final @NotNull SpanOptions spanOptions = new SpanOptions(); + spanOptions.setOrigin(TRACE_ORIGIN); + final ISpan span = activeSpan.startChild("http.client", null, spanOptions); final String methodName = request.getMethod() != null ? request.getMethod().name() : "unknown"; final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(request.getURI().toString()); diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java index c526cb64cc..d401041544 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java @@ -10,6 +10,7 @@ import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.SpanDataConvention; +import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; import io.sentry.util.TracingUtils; @@ -40,9 +41,9 @@ public SentrySpanClientWebRequestFilter(final @NotNull IScopes scopes) { addBreadcrumb(request, null); return next.exchange(maybeAddHeaders(request, null)); } - - final ISpan span = activeSpan.startChild("http.client"); - span.getSpanContext().setOrigin(TRACE_ORIGIN); + final @NotNull SpanOptions spanOptions = new SpanOptions(); + spanOptions.setOrigin(TRACE_ORIGIN); + final ISpan span = activeSpan.startChild("http.client", null, spanOptions); final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(request.url().toString()); final @NotNull String method = request.method().name(); span.setDescription(method + " " + urlDetails.getUrlOrFallback()); diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java index b2519ba14e..1e5cffc568 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java @@ -101,7 +101,6 @@ private void doFilterWithTransaction( throws IOException, ServletException { // at this stage we are not able to get real transaction name final ITransaction transaction = startTransaction(httpRequest, transactionContext); - transaction.getSpanContext().setOrigin(TRACE_ORIGIN); try { filterChain.doFilter(httpRequest, httpResponse); @@ -143,22 +142,19 @@ private ITransaction startTransaction( final CustomSamplingContext customSamplingContext = new CustomSamplingContext(); customSamplingContext.set("request", request); + final TransactionOptions transactionOptions = new TransactionOptions(); + transactionOptions.setCustomSamplingContext(customSamplingContext); + transactionOptions.setBindToScope(true); + transactionOptions.setOrigin(TRACE_ORIGIN); + if (transactionContext != null) { transactionContext.setName(name); transactionContext.setTransactionNameSource(TransactionNameSource.URL); transactionContext.setOperation("http.server"); - final TransactionOptions transactionOptions = new TransactionOptions(); - transactionOptions.setCustomSamplingContext(customSamplingContext); - transactionOptions.setBindToScope(true); - return scopes.startTransaction(transactionContext, transactionOptions); } - final TransactionOptions transactionOptions = new TransactionOptions(); - transactionOptions.setCustomSamplingContext(customSamplingContext); - transactionOptions.setBindToScope(true); - return scopes.startTransaction( new TransactionContext(name, TransactionNameSource.URL, "http.server"), transactionOptions); } diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java index e293eb0b9c..180d5df00a 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java @@ -73,11 +73,11 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) { final TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setBindToScope(true); + transactionOptions.setOrigin(TRACE_ORIGIN); final ITransaction transaction = forkedScopes.startTransaction( new TransactionContext(nameAndSource.name, nameAndSource.source, operation), transactionOptions); - transaction.getSpanContext().setOrigin(TRACE_ORIGIN); try { final Object result = invocation.proceed(); transaction.setStatus(SpanStatus.OK); diff --git a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java index 3549b95c81..ee5a5a7094 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java @@ -73,10 +73,6 @@ isTracingEnabled && shouldTraceRequest(requestScopes, request) ? startTransaction(requestScopes, request, transactionContext) : null; - if (transaction != null) { - transaction.getSpanContext().setOrigin(TRACE_ORIGIN); - } - return webFilterChain .filter(serverWebExchange) .doFinally( @@ -128,6 +124,7 @@ private boolean shouldTraceRequest( final TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setCustomSamplingContext(customSamplingContext); transactionOptions.setBindToScope(true); + transactionOptions.setOrigin(TRACE_ORIGIN); if (transactionContext != null) { transactionContext.setName(name); diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTracingFilterTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTracingFilterTest.kt index aca54809cc..ca89c09a12 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTracingFilterTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTracingFilterTest.kt @@ -92,6 +92,7 @@ class SentryTracingFilterTest { assertNotNull(it.customSamplingContext?.get("request")) assertTrue(it.customSamplingContext?.get("request") is HttpServletRequest) assertTrue(it.isBindToScope) + assertThat(it.origin).isEqualTo("auto.http.spring.webmvc") } ) verify(fixture.chain).doFilter(fixture.request, fixture.response) @@ -100,7 +101,6 @@ class SentryTracingFilterTest { assertThat(it.transaction).isEqualTo("POST /product/{id}") assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.OK) assertThat(it.contexts.trace!!.operation).isEqualTo("http.server") - assertThat(it.contexts.trace!!.origin).isEqualTo("auto.http.spring.webmvc") }, anyOrNull(), anyOrNull(), diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt index 3c35bad8e4..dd1dabf572 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt @@ -52,7 +52,15 @@ class SentryTransactionAdviceTest { @BeforeTest fun setup() { reset(scopes) - whenever(scopes.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } + whenever( + scopes.startTransaction( + any(), + check { + assertTrue(it.isBindToScope) + assertThat(it.origin).isEqualTo("auto.function.spring.advice") + } + ) + ).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } whenever(scopes.options).thenReturn( SentryOptions().apply { dsn = "https://key@sentry.io/proj" @@ -70,7 +78,6 @@ class SentryTransactionAdviceTest { assertThat(it.transaction).isEqualTo("customName") assertThat(it.contexts.trace!!.operation).isEqualTo("bean") assertThat(it.status).isEqualTo(SpanStatus.OK) - assertThat(it.contexts.trace!!.origin).isEqualTo("auto.function.spring.advice") }, anyOrNull(), anyOrNull(), diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt index cb764f31e9..0b0d7e6496 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt @@ -108,6 +108,7 @@ class SentryWebFluxTracingFilterTest { assertNotNull(it.customSamplingContext?.get("request")) assertTrue(it.customSamplingContext?.get("request") is ServerHttpRequest) assertTrue(it.isBindToScope) + assertThat(it.origin).isEqualTo("auto.spring.webflux") } ) verify(fixture.chain).filter(fixture.exchange) @@ -116,7 +117,6 @@ class SentryWebFluxTracingFilterTest { assertThat(it.transaction).isEqualTo("POST /product/{id}") assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.OK) assertThat(it.contexts.trace!!.operation).isEqualTo("http.server") - assertThat(it.contexts.trace!!.origin).isEqualTo("auto.spring.webflux") assertThat(it.contexts.response!!.statusCode).isEqualTo(200) }, anyOrNull(), diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index c56fc7ed04..dd682fa196 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -2692,6 +2692,7 @@ public class io/sentry/SentryOptions { public fun getIdleTimeout ()Ljava/lang/Long; public fun getIgnoredCheckIns ()Ljava/util/List; public fun getIgnoredExceptionsForType ()Ljava/util/Set; + public fun getIgnoredSpanOrigins ()Ljava/util/List; public fun getInAppExcludes ()Ljava/util/List; public fun getInAppIncludes ()Ljava/util/List; public fun getInstrumenter ()Lio/sentry/Instrumenter; @@ -2818,6 +2819,7 @@ public class io/sentry/SentryOptions { public fun setGestureTargetLocators (Ljava/util/List;)V public fun setIdleTimeout (Ljava/lang/Long;)V public fun setIgnoredCheckIns (Ljava/util/List;)V + public fun setIgnoredSpanOrigins (Ljava/util/List;)V public fun setInstrumenter (Lio/sentry/Instrumenter;)V public fun setLogger (Lio/sentry/ILogger;)V public fun setMainThreadChecker (Lio/sentry/util/thread/IMainThreadChecker;)V @@ -3260,12 +3262,15 @@ public final class io/sentry/SpanId$Deserializer : io/sentry/JsonDeserializer { } public class io/sentry/SpanOptions { + protected field origin Ljava/lang/String; public fun ()V + public fun getOrigin ()Ljava/lang/String; public fun getStartTimestamp ()Lio/sentry/SentryDate; public fun isIdle ()Z public fun isTrimEnd ()Z public fun isTrimStart ()Z public fun setIdle (Z)V + public fun setOrigin (Ljava/lang/String;)V public fun setStartTimestamp (Lio/sentry/SentryDate;)V public fun setTrimEnd (Z)V public fun setTrimStart (Z)V @@ -5635,6 +5640,12 @@ public final class io/sentry/util/SampleRateUtils { public static fun isValidTracesSampleRate (Ljava/lang/Double;Z)Z } +public final class io/sentry/util/SpanUtils { + public fun ()V + public static fun ignoredSpanOriginsForOpenTelemetry ()Ljava/util/List; + public static fun isIgnored (Ljava/util/List;Ljava/lang/String;)Z +} + public final class io/sentry/util/StringUtils { public static fun byteCountToString (J)Ljava/lang/String; public static fun calculateStringHash (Ljava/lang/String;Lio/sentry/ILogger;)Ljava/lang/String; diff --git a/sentry/src/main/java/io/sentry/NoOpSpan.java b/sentry/src/main/java/io/sentry/NoOpSpan.java index 4acce66ec6..533bbf0074 100644 --- a/sentry/src/main/java/io/sentry/NoOpSpan.java +++ b/sentry/src/main/java/io/sentry/NoOpSpan.java @@ -3,7 +3,6 @@ import io.sentry.metrics.LocalMetricsAggregator; import io.sentry.protocol.Contexts; import io.sentry.protocol.SentryId; -import io.sentry.protocol.TransactionNameSource; import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index d8e3fefa79..1ebfa93e9b 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -11,6 +11,7 @@ import io.sentry.transport.RateLimiter; import io.sentry.util.HintUtils; import io.sentry.util.Objects; +import io.sentry.util.SpanUtils; import io.sentry.util.TracingUtils; import java.io.Closeable; import java.io.IOException; @@ -819,6 +820,8 @@ public void flush(long timeoutMillis) { Objects.requireNonNull(transactionContext, "transactionContext is required"); // TODO [POTEL] what if span is already running and someone calls startTransaction? + transactionContext.setOrigin(transactionOptions.getOrigin()); + ITransaction transaction; if (!isEnabled()) { getOptions() @@ -827,6 +830,17 @@ public void flush(long timeoutMillis) { SentryLevel.WARNING, "Instance is disabled and this 'startTransaction' returns a no-op."); transaction = NoOpTransaction.getInstance(); + } else if (SpanUtils.isIgnored( + getOptions().getIgnoredSpanOrigins(), transactionContext.getOrigin())) { + // TODO [POTEL] may not have been set yet? + getOptions() + .getLogger() + .log( + SentryLevel.DEBUG, + "Returning no-op for span origin %s as the SDK has been configured to use ignore it", + transactionContext.getOrigin()); + transaction = NoOpTransaction.getInstance(); + // } else if (!getOptions().getInstrumenter().equals(transactionContext.getInstrumenter())) // { // getOptions() diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 386bf6fee1..1f143cc3b7 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -455,6 +455,10 @@ public class SentryOptions { /** Contains a list of monitor slugs for which check-ins should not be sent. */ @ApiStatus.Experimental private @Nullable List ignoredCheckIns = null; + /** Contains a list of span origins for which spans / transactions should not be created. */ + @ApiStatus.Experimental private @Nullable List ignoredSpanOrigins = null; + + @ApiStatus.Experimental private @NotNull IBackpressureMonitor backpressureMonitor = NoOpBackpressureMonitor.getInstance(); private boolean enableBackpressureHandling = true; @@ -2198,6 +2202,27 @@ public void setIgnoredCheckIns(final @Nullable List ignoredCheckIns) { } } + @ApiStatus.Experimental + public @Nullable List getIgnoredSpanOrigins() { + return ignoredSpanOrigins; + } + + @ApiStatus.Experimental + public void setIgnoredSpanOrigins(final @Nullable List ignoredSpanOrigins) { + if (ignoredSpanOrigins == null) { + this.ignoredSpanOrigins = null; + } else { + @NotNull final List filtered = new ArrayList<>(); + for (String origin : ignoredSpanOrigins) { + if (!origin.isEmpty()) { + filtered.add(origin); + } + } + + this.ignoredSpanOrigins = filtered; + } + } + @ApiStatus.Experimental public @Nullable List getIgnoredCheckIns() { return ignoredCheckIns; diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index a84f2c20c4..73cf5afdee 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -440,6 +440,13 @@ private ISpan createChild( final @NotNull String operation = spanContext.getOperation(); final @Nullable String description = spanContext.getDescription(); + // TODO [POTEL] how should this work? return a noop? shouldn't block nested code from actually + // creating spans + // if (SpanUtils.isIgnored(scopes.getOptions().getIgnoredSpanOrigins(), + // spanOptions.getOrigin())) { + // return this; + // } + if (children.size() < scopes.getOptions().getMaxSpans()) { Objects.requireNonNull(parentSpanId, "parentSpanId is required"); // Objects.requireNonNull(operation, "operation is required"); diff --git a/sentry/src/main/java/io/sentry/Span.java b/sentry/src/main/java/io/sentry/Span.java index 51d9b25ba2..4667c61ab6 100644 --- a/sentry/src/main/java/io/sentry/Span.java +++ b/sentry/src/main/java/io/sentry/Span.java @@ -4,7 +4,6 @@ import io.sentry.protocol.Contexts; import io.sentry.protocol.MeasurementValue; import io.sentry.protocol.SentryId; -import io.sentry.protocol.TransactionNameSource; import io.sentry.util.LazyEvaluator; import io.sentry.util.Objects; import java.util.ArrayList; @@ -54,38 +53,6 @@ public final class Span implements ISpan { private final @NotNull LazyEvaluator metricsAggregator = new LazyEvaluator<>(() -> new LocalMetricsAggregator()); - // Span( - // final @NotNull SentryId traceId, - // final @Nullable SpanId parentSpanId, - // final @NotNull SentryTracer transaction, - // final @NotNull String operation, - // final @NotNull IScopes scopes) { - // this(traceId, parentSpanId, transaction, operation, scopes, null, new SpanOptions(), null); - // } - - // Span( - // final @NotNull SentryId traceId, - // final @Nullable SpanId parentSpanId, - // final @NotNull SentryTracer transaction, - // final @NotNull String operation, - // final @NotNull IScopes scopes, - // final @Nullable SentryDate startTimestamp, - // final @NotNull SpanOptions options, - // final @Nullable SpanFinishedCallback spanFinishedCallback) { - // this.context = - // new SpanContext( - // traceId, new SpanId(), operation, parentSpanId, transaction.getSamplingDecision()); - // this.transaction = Objects.requireNonNull(transaction, "transaction is required"); - // this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); - // this.options = options; - // this.spanFinishedCallback = spanFinishedCallback; - // if (startTimestamp != null) { - // this.startTimestamp = startTimestamp; - // } else { - // this.startTimestamp = scopes.getOptions().getDateProvider().now(); - // } - // } - Span( final @NotNull SentryTracer transaction, final @NotNull IScopes scopes, @@ -93,6 +60,7 @@ public final class Span implements ISpan { final @NotNull SpanOptions options, final @Nullable SpanFinishedCallback spanFinishedCallback) { this.context = spanContext; + this.context.setOrigin(options.getOrigin()); this.transaction = Objects.requireNonNull(transaction, "transaction is required"); this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); this.options = options; @@ -112,6 +80,7 @@ public Span( final @Nullable SentryDate startTimestamp, final @NotNull SpanOptions options) { this.context = Objects.requireNonNull(context, "context is required"); + this.context.setOrigin(options.getOrigin()); this.transaction = Objects.requireNonNull(sentryTracer, "sentryTracer is required"); this.scopes = Objects.requireNonNull(scopes, "scopes are required"); this.spanFinishedCallback = null; diff --git a/sentry/src/main/java/io/sentry/SpanOptions.java b/sentry/src/main/java/io/sentry/SpanOptions.java index 086b435b77..29ee134c1a 100644 --- a/sentry/src/main/java/io/sentry/SpanOptions.java +++ b/sentry/src/main/java/io/sentry/SpanOptions.java @@ -1,5 +1,7 @@ package io.sentry; +import static io.sentry.SpanContext.DEFAULT_ORIGIN; + import com.jakewharton.nopen.annotation.Open; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -50,6 +52,8 @@ public void setStartTimestamp(@Nullable SentryDate startTimestamp) { */ private boolean isIdle = false; + protected @Nullable String origin = DEFAULT_ORIGIN; + public boolean isTrimStart() { return trimStart; } @@ -73,4 +77,12 @@ public void setTrimEnd(boolean trimEnd) { public void setIdle(boolean idle) { isIdle = idle; } + + public @Nullable String getOrigin() { + return origin; + } + + public void setOrigin(final @Nullable String origin) { + this.origin = origin; + } } diff --git a/sentry/src/main/java/io/sentry/util/SpanUtils.java b/sentry/src/main/java/io/sentry/util/SpanUtils.java new file mode 100644 index 0000000000..6c6a83182f --- /dev/null +++ b/sentry/src/main/java/io/sentry/util/SpanUtils.java @@ -0,0 +1,60 @@ +package io.sentry.util; + +import java.util.ArrayList; +import java.util.List; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class SpanUtils { + + /** + * A list of span origins that are ignored by default when using OpenTelemetry. + * + * @return a list of span origins to be ignored + */ + public static @NotNull List ignoredSpanOriginsForOpenTelemetry() { + final @NotNull List origins = new ArrayList<>(); + + origins.add("auto.http.spring_jakarta.webmvc"); + origins.add("auto.http.spring.webmvc"); + origins.add("auto.spring_jakarta.webflux"); + origins.add("auto.spring.webflux"); + origins.add("auto.http.spring_jakarta.webclient"); + origins.add("auto.http.spring.webclient"); + origins.add("auto.http.spring_jakarta.restclient"); + origins.add("auto.http.spring.restclient"); + origins.add("auto.http.spring_jakarta.resttemplate"); + origins.add("auto.http.spring.resttemplate"); + origins.add("auto.http.openfeign"); + origins.add("auto.graphql.graphql"); + origins.add("auto.db.jdbc"); + + return origins; + } + + /** Checks if a span origin has been ignored. */ + @ApiStatus.Internal + public static boolean isIgnored( + final @Nullable List ignoredOrigins, final @Nullable String origin) { + if (origin == null || ignoredOrigins == null || ignoredOrigins.isEmpty()) { + return false; + } + + for (final String ignoredOrigin : ignoredOrigins) { + if (ignoredOrigin.equalsIgnoreCase(origin)) { + return true; + } + + try { + if (origin.matches(ignoredOrigin)) { + return true; + } + } catch (Throwable t) { + // ignore invalid regex + } + } + + return false; + } +} diff --git a/sentry/src/test/java/io/sentry/ScopesTest.kt b/sentry/src/test/java/io/sentry/ScopesTest.kt index 5bd896a59d..8428b2c19e 100644 --- a/sentry/src/test/java/io/sentry/ScopesTest.kt +++ b/sentry/src/test/java/io/sentry/ScopesTest.kt @@ -16,6 +16,7 @@ import io.sentry.test.createSentryClientMock import io.sentry.test.createTestScopes import io.sentry.util.HintUtils import io.sentry.util.StringUtils +import junit.framework.TestCase.assertSame import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.argWhere @@ -2198,6 +2199,40 @@ class ScopesTest { assertFalse(scopes.isEnabled) } + @Test + fun `creating a transaction with an ignored origin noops`() { + val scopes = generateScopes { + it.ignoredSpanOrigins = listOf("ignored.span.origin") + } + + val transactionContext = TransactionContext("transaction-name", "transaction-op") + val transactionOptions = TransactionOptions().also { + it.origin = "ignored.span.origin" + it.isBindToScope = true + } + + val transaction = scopes.startTransaction(transactionContext, transactionOptions) + assertTrue(transaction.isNoOp) + scopes.configureScope { assertNull(it.transaction) } + } + + @Test + fun `creating a transaction with a non ignored origin creates the transaction`() { + val scopes = generateScopes { + it.ignoredSpanOrigins = listOf("ignored.span.origin") + } + + val transactionContext = TransactionContext("transaction-name", "transaction-op") + val transactionOptions = TransactionOptions().also { + it.origin = "other.span.origin" + it.isBindToScope = true + } + + val transaction = scopes.startTransaction(transactionContext, transactionOptions) + assertFalse(transaction.isNoOp) + scopes.configureScope { assertSame(transaction, it.transaction) } + } + private val dsnTest = "https://key@sentry.io/proj" private fun generateScopes(optionsConfiguration: Sentry.OptionsConfiguration? = null): IScopes { diff --git a/sentry/src/test/java/io/sentry/SentryTracerTest.kt b/sentry/src/test/java/io/sentry/SentryTracerTest.kt index 03e8149464..9e7c0a013c 100644 --- a/sentry/src/test/java/io/sentry/SentryTracerTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTracerTest.kt @@ -67,6 +67,22 @@ class SentryTracerTest { private val fixture = Fixture() + @Test + fun `transfer origin from transaction options to transaction context`() { + fixture.getSut() + val transactionOptions = TransactionOptions().also { + it.origin = "new-origin" + } + val transactionContext = TransactionContext("name", "op", null).also { + it.origin = "old-origin" + } + + val transaction = SentryTracer(transactionContext, fixture.scopes, transactionOptions, null) + assertEquals("new-origin", transaction.spanContext.origin) + } + + // TODO [POTEL] test child creation is ignored because of span origin + @Test fun `does not add more spans than configured in options`() { val tracer = fixture.getSut({ diff --git a/sentry/src/test/java/io/sentry/SpanTest.kt b/sentry/src/test/java/io/sentry/SpanTest.kt index 1cde4a8f0e..77914a53df 100644 --- a/sentry/src/test/java/io/sentry/SpanTest.kt +++ b/sentry/src/test/java/io/sentry/SpanTest.kt @@ -137,6 +137,38 @@ class SpanTest { } } + @Test + fun `transfers span origin from options to span context`() { + val traceId = SentryId() + val parentSpanId = SpanId() + val spanContext = SpanContext( + traceId, + SpanId(), + parentSpanId, + "op", + null, + TracesSamplingDecision(true), + null, + "old-origin" + ) + + val spanOptions = SpanOptions() + spanOptions.origin = "new-origin" + + val span = Span( + SentryTracer( + TransactionContext("name", "op", TracesSamplingDecision(true)), + fixture.scopes + ), + fixture.scopes, + spanContext, + spanOptions, + null + ) + + assertEquals("new-origin", span.spanContext.origin) + } + @Test fun `starting a child with details adds span to transaction`() { val transaction = getTransaction() From 967fa5ffe536b15e075fc648a18ed7e54f1d2bb7 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 18 Jun 2024 12:30:20 +0200 Subject: [PATCH 69/91] Reuse `TracesSampler` instance (#3479) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing * Inherit OTel span IDs when sending to sentry * Fix tracing; parse incoming baggage; add baggage to outgoing * Cleanup * Move sampling logic to OTel Sampler * Remove internal span attributes so they are not sent to Sentry * Use transaction name * remove obsolete comment * Keep OTel and Sentry span name/op in sync * more cleanup * Make it possible to ignore span origins * Reuse TracesSampler instance * changelog --- CHANGELOG.md | 1 + .../io/sentry/opentelemetry/SentrySampler.java | 2 +- sentry/api/sentry.api | 1 + sentry/src/main/java/io/sentry/Scopes.java | 3 +-- sentry/src/main/java/io/sentry/Sentry.java | 2 +- sentry/src/main/java/io/sentry/SentryOptions.java | 14 ++++++++++++++ 6 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a78866de0a..eed836ac23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ ### Fixes +- `TracesSampler` is now only created once in `SentryOptions` instead of creating a new one for every `Hub` (which is now `Scopes`). This means we're now creating fewwer `SecureRandom` instances. - Fix faulty `span.frame_delay` calculation for early app start spans ([#3427](https://github.com/getsentry/sentry-java/pull/3427)) ### Dependencies diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java index 0d54cd9947..e2b5aee454 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java @@ -28,7 +28,7 @@ public final class SentrySampler implements Sampler { private final @NotNull TracesSampler tracesSampler; public SentrySampler(final @NotNull IScopes scopes) { - this.tracesSampler = new TracesSampler(scopes.getOptions()); + this.tracesSampler = scopes.getOptions().getInternalTracesSampler(); } public SentrySampler() { diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index dd682fa196..026acab24c 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -2697,6 +2697,7 @@ public class io/sentry/SentryOptions { public fun getInAppIncludes ()Ljava/util/List; public fun getInstrumenter ()Lio/sentry/Instrumenter; public fun getIntegrations ()Ljava/util/List; + public fun getInternalTracesSampler ()Lio/sentry/TracesSampler; public fun getLogger ()Lio/sentry/ILogger; public fun getMainThreadChecker ()Lio/sentry/util/thread/IMainThreadChecker; public fun getMaxAttachmentSize ()J diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 1ebfa93e9b..0e37ec572a 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -32,7 +32,6 @@ public final class Scopes implements IScopes, MetricsApi.IMetricsInterface { private final @Nullable Scopes parentScopes; private final @NotNull String creator; - private final @NotNull TracesSampler tracesSampler; private final @NotNull TransactionPerformanceCollector transactionPerformanceCollector; private final @NotNull MetricsApi metricsApi; @@ -61,7 +60,6 @@ private Scopes( final @NotNull SentryOptions options = getOptions(); validateOptions(options); - this.tracesSampler = new TracesSampler(options); this.transactionPerformanceCollector = options.getTransactionPerformanceCollector(); this.metricsApi = new MetricsApi(this); } @@ -861,6 +859,7 @@ public void flush(long timeoutMillis) { } else { final SamplingContext samplingContext = new SamplingContext(transactionContext, transactionOptions.getCustomSamplingContext()); + final @NotNull TracesSampler tracesSampler = getOptions().getInternalTracesSampler(); @NotNull TracesSamplingDecision samplingDecision = tracesSampler.sample(samplingContext); transactionContext.setSamplingDecision(samplingDecision); diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 7e4c63b962..89f96b0d11 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -370,7 +370,7 @@ private static void handleAppStartProfilingConfig( TransactionContext appStartTransactionContext = new TransactionContext("app.launch", "profile"); appStartTransactionContext.setForNextAppStart(true); SamplingContext appStartSamplingContext = new SamplingContext(appStartTransactionContext, null); - return new TracesSampler(options).sample(appStartSamplingContext); + return options.getInternalTracesSampler().sample(appStartSamplingContext); } @SuppressWarnings("FutureReturnValueIgnored") diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 1f143cc3b7..688e5436da 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -203,6 +203,8 @@ public class SentryOptions { */ private @Nullable TracesSamplerCallback tracesSampler; + private volatile @Nullable TracesSampler internalTracesSampler; + /** * A list of string prefixes of module names that do not belong to the app, but rather third-party * packages. Modules considered not to be part of the app will be hidden from stack traces by @@ -962,6 +964,18 @@ public void setTracesSampler(final @Nullable TracesSamplerCallback tracesSampler this.tracesSampler = tracesSampler; } + @ApiStatus.Internal + public @NotNull TracesSampler getInternalTracesSampler() { + if (internalTracesSampler == null) { + synchronized (this) { + if (internalTracesSampler == null) { + internalTracesSampler = new TracesSampler(this); + } + } + } + return internalTracesSampler; + } + /** * the list of inApp excludes * From 394677f1508512a6d06681fad0fed8d3ea603010 Mon Sep 17 00:00:00 2001 From: Pedro Aguiar <72931357+codespearhead@users.noreply.github.com> Date: Fri, 14 Jun 2024 13:53:17 -0400 Subject: [PATCH 70/91] chore: bump Spring Boot Jakarta from 3.2.0 to 3.3.0 Package sentry-spring-boot-starter-jakarta is currently listed as incompatible with 3.3.0, which was released on May 23, 2024, according to Spring Initializr [1]. [1] https://start.spring.io/ --- CHANGELOG.md | 4 ++++ buildSrc/src/main/java/Config.kt | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c7c07d8d9..fd1866bf5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ - Remove profiling timeout logic and disable profiling on API 21 ([#3478](https://github.com/getsentry/sentry-java/pull/3478)) - Properly reset metric flush flag on metric emission ([#3493](https://github.com/getsentry/sentry-java/pull/3493)) +### Dependencies + +- Bump Spring Boot Jakarta from 3.2.0 to 3.3.0 + ## 7.10.0 ### Features diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index 7a0081d5f4..697f509ebb 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -7,7 +7,7 @@ object Config { val kotlinStdLib = "stdlib-jdk8" val springBootVersion = "2.7.5" - val springBoot3Version = "3.2.0" + val springBoot3Version = "3.3.0" val kotlinCompatibleLanguageVersion = "1.4" val composeVersion = "1.5.3" From eff639915b99ae5fb286ad20953bb60027b997cf Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 24 Jun 2024 07:20:14 +0200 Subject: [PATCH 71/91] Catch exceptions when closing integrations (#3488) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing * Inherit OTel span IDs when sending to sentry * Fix tracing; parse incoming baggage; add baggage to outgoing * Cleanup * Move sampling logic to OTel Sampler * Remove internal span attributes so they are not sent to Sentry * Use transaction name * remove obsolete comment * Keep OTel and Sentry span name/op in sync * more cleanup * Make it possible to ignore span origins * Reuse TracesSampler instance * Catch exceptions thrown by integration.close --- sentry/src/main/java/io/sentry/Scopes.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 0e37ec572a..f305411aa6 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -14,7 +14,6 @@ import io.sentry.util.SpanUtils; import io.sentry.util.TracingUtils; import java.io.Closeable; -import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -401,7 +400,7 @@ public void close(final boolean isRestarting) { if (integration instanceof Closeable) { try { ((Closeable) integration).close(); - } catch (IOException e) { + } catch (Throwable e) { getOptions() .getLogger() .log(SentryLevel.WARNING, "Failed to close the integration {}.", integration, e); From 9f7e431320bd5cab0cda7ceb1449a1c3165c7c0a Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 24 Jun 2024 07:21:07 +0200 Subject: [PATCH 72/91] POTEL 17 - Use `NoOpSpanFactory` for `SentryOptions.empty` (#3489) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing * Inherit OTel span IDs when sending to sentry * Fix tracing; parse incoming baggage; add baggage to outgoing * Cleanup * Move sampling logic to OTel Sampler * Remove internal span attributes so they are not sent to Sentry * Use transaction name * remove obsolete comment * Keep OTel and Sentry span name/op in sync * more cleanup * Make it possible to ignore span origins * Reuse TracesSampler instance * Catch exceptions thrown by integration.close * Set NoOpSpanFactory as property default --- sentry/api/sentry.api | 8 ++++ .../java/io/sentry/DefaultSpanFactory.java | 7 ++- .../main/java/io/sentry/NoOpSpanFactory.java | 45 +++++++++++++++++++ .../main/java/io/sentry/SentryOptions.java | 3 +- 4 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 sentry/src/main/java/io/sentry/NoOpSpanFactory.java diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index f5a2a77c60..472b389dfe 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -1586,6 +1586,14 @@ public final class io/sentry/NoOpSpan : io/sentry/ISpan { public fun updateEndDate (Lio/sentry/SentryDate;)Z } +public final class io/sentry/NoOpSpanFactory : io/sentry/ISpanFactory { + public fun createSpan (Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/SpanContext;Lio/sentry/ISpan;)Lio/sentry/ISpan; + public fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; + public static fun getInstance ()Lio/sentry/NoOpSpanFactory; + public fun retrieveCurrentSpan (Lio/sentry/IScope;)Lio/sentry/ISpan; + public fun retrieveCurrentSpan (Lio/sentry/IScopes;)Lio/sentry/ISpan; +} + public final class io/sentry/NoOpTransaction : io/sentry/ITransaction { public fun finish ()V public fun finish (Lio/sentry/SpanStatus;)V diff --git a/sentry/src/main/java/io/sentry/DefaultSpanFactory.java b/sentry/src/main/java/io/sentry/DefaultSpanFactory.java index faefc5c75f..3282d32949 100644 --- a/sentry/src/main/java/io/sentry/DefaultSpanFactory.java +++ b/sentry/src/main/java/io/sentry/DefaultSpanFactory.java @@ -21,8 +21,11 @@ public final class DefaultSpanFactory implements ISpanFactory { final @NotNull SpanOptions spanOptions, final @NotNull SpanContext spanContext, @Nullable ISpan parentSpan) { - // TODO [POTEL] forward to SentryTracer.createChild? - return NoOpSpan.getInstance(); + if (parentSpan == null) { + // TODO [POTEL] We could create a transaction here + return NoOpSpan.getInstance(); + } + return parentSpan.startChild(spanContext, spanOptions); } @Override diff --git a/sentry/src/main/java/io/sentry/NoOpSpanFactory.java b/sentry/src/main/java/io/sentry/NoOpSpanFactory.java new file mode 100644 index 0000000000..282f2a5ab2 --- /dev/null +++ b/sentry/src/main/java/io/sentry/NoOpSpanFactory.java @@ -0,0 +1,45 @@ +package io.sentry; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@ApiStatus.Internal +public final class NoOpSpanFactory implements ISpanFactory { + + private static final NoOpSpanFactory instance = new NoOpSpanFactory(); + + private NoOpSpanFactory() {} + + public static NoOpSpanFactory getInstance() { + return instance; + } + + @Override + public @NotNull ITransaction createTransaction( + @NotNull TransactionContext context, + @NotNull IScopes scopes, + @NotNull TransactionOptions transactionOptions, + @Nullable TransactionPerformanceCollector transactionPerformanceCollector) { + return NoOpTransaction.getInstance(); + } + + @Override + public @NotNull ISpan createSpan( + @NotNull IScopes scopes, + @NotNull SpanOptions spanOptions, + @NotNull SpanContext spanContext, + @Nullable ISpan parentSpan) { + return NoOpSpan.getInstance(); + } + + @Override + public @Nullable ISpan retrieveCurrentSpan(IScopes scopes) { + return NoOpSpan.getInstance(); + } + + @Override + public @Nullable ISpan retrieveCurrentSpan(IScope scope) { + return NoOpSpan.getInstance(); + } +} diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 688e5436da..c199533d20 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -476,7 +476,7 @@ public class SentryOptions { private @Nullable BeforeEmitMetricCallback beforeEmitMetricCallback = null; - private @NotNull ISpanFactory spanFactory = new DefaultSpanFactory(); + private @NotNull ISpanFactory spanFactory = NoOpSpanFactory.getInstance(); /** * Profiling traces rate. 101 hz means 101 traces in 1 second. Defaults to 101 to avoid possible @@ -2558,6 +2558,7 @@ public SentryOptions() { */ private SentryOptions(final boolean empty) { if (!empty) { + setSpanFactory(new DefaultSpanFactory()); // SentryExecutorService should be initialized before any // SendCachedEventFireAndForgetIntegration executorService = new SentryExecutorService(); From 738c5faec20a91050ab7e82e88a40287859b1c5f Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 24 Jun 2024 14:11:35 +0200 Subject: [PATCH 73/91] POTEL 18 - Use correct `SentryOptions` for `SentryClient` constructor (#3490) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing * Inherit OTel span IDs when sending to sentry * Fix tracing; parse incoming baggage; add baggage to outgoing * Cleanup * Move sampling logic to OTel Sampler * Remove internal span attributes so they are not sent to Sentry * Use transaction name * remove obsolete comment * Keep OTel and Sentry span name/op in sync * more cleanup * Make it possible to ignore span origins * Reuse TracesSampler instance * Catch exceptions thrown by integration.close * Set NoOpSpanFactory as property default * Use correct SentryOptions for SentryClient ctor --- .../io/sentry/android/core/InternalSentrySdkTest.kt | 12 ++++-------- sentry/src/main/java/io/sentry/Sentry.java | 5 +---- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt index 332898e760..79e965986a 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt @@ -199,20 +199,16 @@ class InternalSentrySdkTest { @Test fun `current scope returns obj when scopes is active`() { - val options = SentryOptions().apply { - dsn = "https://key@uri/1234567" - } - Sentry.setCurrentScopes(createTestScopes(options)) + val fixture = Fixture() + fixture.init(context) val scope = InternalSentrySdk.getCurrentScope() assertNotNull(scope) } @Test fun `current scope returns a copy of the scope`() { - val options = SentryOptions().apply { - dsn = "https://key@uri/1234567" - } - Sentry.setCurrentScopes(createTestScopes(options)) + val fixture = Fixture() + fixture.init(context) Sentry.addBreadcrumb("test") Sentry.configureScope(ScopeType.CURRENT) { scope -> scope.addBreadcrumb(Breadcrumb("currentBreadcrumb")) } Sentry.configureScope(ScopeType.ISOLATION) { scope -> scope.addBreadcrumb(Breadcrumb("isolationBreadcrumb")) } diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 89f96b0d11..3091a925c4 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -284,10 +284,7 @@ private static synchronized void init( final IScope rootIsolationScope = new Scope(options); scopes.close(true); - globalScope.replaceOptions(options); - rootScopes = new Scopes(rootScope, rootIsolationScope, globalScope, "Sentry.init"); - getScopesStorage().set(rootScopes); - globalScope.bindClient(new SentryClient(options)); + globalScope.bindClient(new SentryClient(rootScopes.getOptions())); // If the executorService passed in the init is the same that was previously closed, we have to // set a new one From 54e65680d433e1e90f59fff82b3964c3824f15e1 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 24 Jun 2024 14:18:14 +0200 Subject: [PATCH 74/91] POTEL 19 - Review feedback (#3491) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing * Inherit OTel span IDs when sending to sentry * Fix tracing; parse incoming baggage; add baggage to outgoing * Cleanup * Move sampling logic to OTel Sampler * Remove internal span attributes so they are not sent to Sentry * Use transaction name * remove obsolete comment * Keep OTel and Sentry span name/op in sync * more cleanup * Make it possible to ignore span origins * Reuse TracesSampler instance * Catch exceptions thrown by integration.close * Set NoOpSpanFactory as property default * Use correct SentryOptions for SentryClient ctor * PR review feedback * more --- ...ryAutoConfigurationCustomizerProvider.java | 7 ----- .../api/sentry-opentelemetry-bootstrap.api | 1 - .../OtelContextScopesStorage.java | 2 +- .../sentry/opentelemetry/OtelSpanWrapper.java | 6 ----- .../OtelTransactionSpanForwarder.java | 3 ++- .../opentelemetry/SentryWeakSpanStorage.java | 10 ++----- .../OpenTelemetryLinkErrorEventProcessor.java | 10 +++++-- .../opentelemetry/OtelSamplingUtil.java | 2 +- .../PotelSentrySpanProcessor.java | 8 +++--- .../opentelemetry/SentryPropagator.java | 11 ++++++-- .../sentry/opentelemetry/SentrySampler.java | 14 ++++++---- .../opentelemetry/SentrySpanExporter.java | 26 +++++++------------ .../opentelemetry/SentrySpanProcessor.java | 11 ++++++-- .../io/sentry/opentelemetry/TraceData.java | 1 + .../boot/jakarta/SentryAutoConfiguration.java | 9 ++++--- .../spring/boot/SentryAutoConfiguration.java | 9 ++++--- sentry/api/sentry.api | 1 + sentry/src/main/java/io/sentry/ISpan.java | 5 ++-- .../src/main/java/io/sentry/ITransaction.java | 4 +++ sentry/src/main/java/io/sentry/NoOpSpan.java | 5 ---- sentry/src/main/java/io/sentry/Scopes.java | 2 +- .../main/java/io/sentry/SentryOptions.java | 6 ++++- .../java/io/sentry/SentrySpanStorage.java | 3 +++ sentry/src/main/java/io/sentry/Span.java | 5 ++++ 24 files changed, 87 insertions(+), 74 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java index 8ea2f3bab5..cff1035457 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java @@ -6,7 +6,6 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; -import io.sentry.Instrumenter; import io.sentry.Sentry; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryOptions; @@ -38,10 +37,6 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) { Sentry.init( options -> { options.setEnableExternalConfiguration(true); - // TODO [POTEL] deprecate - options.setInstrumenter(Instrumenter.OTEL); - // TODO [POTEL] do we still need this? - options.addEventProcessor(new OpenTelemetryLinkErrorEventProcessor()); options.setIgnoredSpanOrigins(SpanUtils.ignoredSpanOriginsForOpenTelemetry()); options.setSpanFactory(new OtelSpanFactory()); final @Nullable SdkVersion sdkVersion = createSdkVersion(options, versionInfoHolder); @@ -153,8 +148,6 @@ private static class VersionInfoHolder { private SdkTracerProviderBuilder configureSdkTracerProvider( SdkTracerProviderBuilder tracerProvider, ConfigProperties config) { - // TODO [POTEL] configurable or separate packages for old vs new way - // return tracerProvider.addSpanProcessor(new SentrySpanProcessor()); return tracerProvider .setSampler(new SentrySampler()) .addSpanProcessor(new PotelSentrySpanProcessor()) diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api index e7d2348771..d9907781b5 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api @@ -42,7 +42,6 @@ public final class io/sentry/opentelemetry/OtelSpanWrapper : io/sentry/ISpan { public fun getData ()Ljava/util/Map; public fun getData (Ljava/lang/String;)Ljava/lang/Object; public fun getDescription ()Ljava/lang/String; - public fun getEventId ()Lio/sentry/protocol/SentryId; public fun getFinishDate ()Lio/sentry/SentryDate; public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; public fun getMeasurements ()Ljava/util/Map; diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java index d824776dab..299e91dae9 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java @@ -14,7 +14,7 @@ public final class OtelContextScopesStorage implements IScopesStorage { @Override - public ISentryLifecycleToken set(@Nullable IScopes scopes) { + public @NotNull ISentryLifecycleToken set(@Nullable IScopes scopes) { final @NotNull Scope otelScope = Context.current().with(SENTRY_SCOPES_KEY, scopes).makeCurrent(); return new OtelStorageToken(otelScope); diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java index a7f6b86eb7..2a023e115b 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java @@ -476,12 +476,6 @@ public Map getMeasurements() { return context.getSamplingDecision(); } - @Override - public @NotNull SentryId getEventId() { - // TODO [POTEL] - return new SentryId(getOtelSpanId().toString()); - } - @ApiStatus.Internal public @NotNull IScopes getScopes() { return scopes; diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java index eaa3fce56e..373a4e1909 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java @@ -257,7 +257,8 @@ public boolean isNoOp() { @Override public @NotNull SentryId getEventId() { - return rootSpan.getEventId(); + // TODO [POTEL] + return new SentryId(); } @ApiStatus.Internal diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryWeakSpanStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryWeakSpanStorage.java index ebcf89ce89..af9413f74f 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryWeakSpanStorage.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryWeakSpanStorage.java @@ -7,14 +7,8 @@ import org.jetbrains.annotations.Nullable; /** - * This class may have to be moved to a new gradle module to include it in the bootstrap - * classloader. - * - *

    This uses multiple maps instead of a single one with a wrapper object as porting this to - * Android would mean there's no access to methods like compute etc. before API level 24. There's - * also no easy way to pre-initialize the map for all keys as spans are used as keys. For span IDs - * it would also not work as they are random. For client report storage we know beforehand what keys - * can exist. + * Weakly references wrappers for OpenTelemetry spans meaning they'll be cleaned up when the + * OpenTelemetry span is garbage collected. */ @ApiStatus.Internal public final class SentryWeakSpanStorage { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java index 9ed17b06dd..cf1e530cd9 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java @@ -11,17 +11,23 @@ import io.sentry.ScopesAdapter; import io.sentry.SentryEvent; import io.sentry.SentryLevel; -import io.sentry.SentrySpanStorage; import io.sentry.SpanContext; import io.sentry.protocol.SentryId; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; +/** + * @deprecated this is no longer needed for the latest version of our OpenTelemetry integration. + */ +@Deprecated public final class OpenTelemetryLinkErrorEventProcessor implements EventProcessor { private final @NotNull IScopes scopes; - private final @NotNull SentrySpanStorage spanStorage = SentrySpanStorage.getInstance(); + + @SuppressWarnings("deprecation") + private final @NotNull io.sentry.SentrySpanStorage spanStorage = + io.sentry.SentrySpanStorage.getInstance(); public OpenTelemetryLinkErrorEventProcessor() { this(ScopesAdapter.getInstance()); diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSamplingUtil.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSamplingUtil.java index 4a6124df11..45a8922741 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSamplingUtil.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSamplingUtil.java @@ -9,7 +9,7 @@ @ApiStatus.Internal public final class OtelSamplingUtil { - public static @Nullable TracesSamplingDecision extractSamplingDecisionOrDefault( + public static @NotNull TracesSamplingDecision extractSamplingDecisionOrDefault( final @NotNull Attributes attributes) { final @Nullable TracesSamplingDecision decision = extractSamplingDecision(attributes); if (decision != null) { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java index 5ba54602b4..9ee3f8e854 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java @@ -80,9 +80,10 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri } } - // TODO [POTEL] what do we use as fallback here? could happen if misconfigured (i.e. sampler - // not in place) - final boolean sampled = samplingDecision != null ? samplingDecision.getSampled() : true; + final boolean sampled = + samplingDecision != null + ? samplingDecision.getSampled() + : otelSpan.getSpanContext().isSampled(); final @NotNull PropagationContext propagationContext = sentryTraceHeader == null @@ -128,7 +129,6 @@ public void onEnd(final @NotNull ReadableSpan spanBeingEnded) { new SentryLongDate(spanBeingEnded.toSpanData().getEndEpochNanos()); sentrySpan.updateEndDate(finishDate); } - System.out.println("span ended: " + spanBeingEnded.getSpanContext().getSpanId()); } @Override diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java index ed3e243f4d..da411c3126 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java @@ -14,7 +14,6 @@ import io.sentry.ISpan; import io.sentry.ScopesAdapter; import io.sentry.SentryLevel; -import io.sentry.SentrySpanStorage; import io.sentry.SentryTraceHeader; import io.sentry.exception.InvalidSentryTraceHeaderException; import java.util.Arrays; @@ -24,11 +23,19 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +/** + * @deprecated please use {@link PotelSentryPropagator} instead + */ +@Deprecated public final class SentryPropagator implements TextMapPropagator { private static final @NotNull List FIELDS = Arrays.asList(SentryTraceHeader.SENTRY_TRACE_HEADER, BaggageHeader.BAGGAGE_HEADER); - private final @NotNull SentrySpanStorage spanStorage = SentrySpanStorage.getInstance(); + + @SuppressWarnings("deprecation") + private final @NotNull io.sentry.SentrySpanStorage spanStorage = + io.sentry.SentrySpanStorage.getInstance(); + private final @NotNull IScopes scopes; public SentryPropagator() { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java index e2b5aee454..f4b6233487 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java @@ -6,15 +6,16 @@ import io.opentelemetry.context.Context; import io.opentelemetry.sdk.trace.data.LinkData; import io.opentelemetry.sdk.trace.samplers.Sampler; +import io.opentelemetry.sdk.trace.samplers.SamplingDecision; import io.opentelemetry.sdk.trace.samplers.SamplingResult; import io.sentry.Baggage; import io.sentry.IScopes; import io.sentry.PropagationContext; import io.sentry.SamplingContext; import io.sentry.ScopesAdapter; +import io.sentry.SentryOptions; import io.sentry.SentryTraceHeader; import io.sentry.SpanId; -import io.sentry.TracesSampler; import io.sentry.TracesSamplingDecision; import io.sentry.TransactionContext; import io.sentry.protocol.SentryId; @@ -25,10 +26,10 @@ public final class SentrySampler implements Sampler { private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); - private final @NotNull TracesSampler tracesSampler; + private final @NotNull SentryOptions options; public SentrySampler(final @NotNull IScopes scopes) { - this.tracesSampler = scopes.getOptions().getInternalTracesSampler(); + this.options = scopes.getOptions(); } public SentrySampler() { @@ -61,8 +62,11 @@ public SamplingResult shouldSample( } } - private @NotNull SentrySamplingResult handleRootOtelSpan( + private @NotNull SamplingResult handleRootOtelSpan( final @NotNull String traceId, final @NotNull Context parentContext) { + if (!options.isTraceSampling()) { + return SamplingResult.create(SamplingDecision.DROP); + } @Nullable Baggage baggage = null; @Nullable SentryTraceHeader sentryTraceHeader = parentContext.get(SentryOtelKeys.SENTRY_TRACE_KEY); @@ -81,7 +85,7 @@ public SamplingResult shouldSample( final @NotNull TransactionContext transactionContext = TransactionContext.fromPropagationContext(propagationContext); final @NotNull TracesSamplingDecision sentryDecision = - tracesSampler.sample(new SamplingContext(transactionContext, null)); + options.getInternalTracesSampler().sample(new SamplingContext(transactionContext, null)); return new SentrySamplingResult(sentryDecision); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index 50439a8700..ec7ae4918b 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -157,21 +157,15 @@ private boolean isSentryRequest(final @NotNull SpanData spanData) { // TODO [POTEL] should check if enabled but multi init with different options makes testing hard // atm // if (scopes.getOptions().isEnableSpotlight()) { - final @Nullable String spotlightUrl = scopes.getOptions().getSpotlightConnectionUrl(); - if (spotlightUrl != null) { - if (containsSpotlightUrl(fullUrl, spotlightUrl)) { - return true; - } - if (containsSpotlightUrl(httpUrl, spotlightUrl)) { - return true; - } - } else { - if (containsSpotlightUrl(fullUrl, "http://localhost:8969/stream")) { - return true; - } - if (containsSpotlightUrl(httpUrl, "http://localhost:8969/stream")) { - return true; - } + final @Nullable String optionsSpotlightUrl = scopes.getOptions().getSpotlightConnectionUrl(); + final @NotNull String spotlightUrl = + optionsSpotlightUrl != null ? optionsSpotlightUrl : "http://localhost:8969/stream"; + + if (containsSpotlightUrl(fullUrl, spotlightUrl)) { + return true; + } + if (containsSpotlightUrl(httpUrl, spotlightUrl)) { + return true; } // } @@ -344,7 +338,7 @@ private void transferSpanDetails( traceId); final SpanId sentrySpanId = new SpanId(spanId); - @NotNull String transactionName = spanInfo.getDescription(); + @Nullable String transactionName = spanInfo.getDescription(); @NotNull TransactionNameSource transactionNameSource = spanInfo.getTransactionNameSource(); @Nullable SpanId parentSpanId = null; @Nullable Baggage baggage = null; diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java index 60f379ceae..4f91d29427 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java @@ -24,7 +24,6 @@ import io.sentry.SentryDate; import io.sentry.SentryLevel; import io.sentry.SentryLongDate; -import io.sentry.SentrySpanStorage; import io.sentry.SentryTraceHeader; import io.sentry.SpanId; import io.sentry.SpanOptions; @@ -40,6 +39,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +/** + * @deprecated please use {@link PotelSentrySpanProcessor} instead. + */ +@Deprecated public final class SentrySpanProcessor implements SpanProcessor { private static final String TRACE_ORIGN = "auto.otel"; @@ -48,7 +51,11 @@ public final class SentrySpanProcessor implements SpanProcessor { Arrays.asList(SpanKind.CLIENT, SpanKind.INTERNAL); private final @NotNull SpanDescriptionExtractor spanDescriptionExtractor = new SpanDescriptionExtractor(); - private final @NotNull SentrySpanStorage spanStorage = SentrySpanStorage.getInstance(); + + @SuppressWarnings("deprecation") + private final @NotNull io.sentry.SentrySpanStorage spanStorage = + io.sentry.SentrySpanStorage.getInstance(); + private final @NotNull IScopes scopes; public SentrySpanProcessor() { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/TraceData.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/TraceData.java index 08751b5609..5904db39e7 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/TraceData.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/TraceData.java @@ -6,6 +6,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +@Deprecated @ApiStatus.Internal public final class TraceData { diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java index d7f5099aa2..f38682cf6d 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java @@ -11,7 +11,6 @@ import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryOptions; import io.sentry.graphql.SentryGraphqlExceptionHandler; -import io.sentry.opentelemetry.OpenTelemetryLinkErrorEventProcessor; import io.sentry.protocol.SdkVersion; import io.sentry.quartz.SentryJobListener; import io.sentry.spring.boot.jakarta.graphql.SentryGraphqlAutoConfiguration; @@ -158,14 +157,16 @@ static class ContextTagsEventProcessorConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(name = "sentry.auto-init", havingValue = "false") - @ConditionalOnClass(OpenTelemetryLinkErrorEventProcessor.class) + @ConditionalOnClass(io.sentry.opentelemetry.OpenTelemetryLinkErrorEventProcessor.class) + @SuppressWarnings("deprecation") @Open static class OpenTelemetryLinkErrorEventProcessorConfiguration { @Bean @ConditionalOnMissingBean - public @NotNull OpenTelemetryLinkErrorEventProcessor openTelemetryLinkErrorEventProcessor() { - return new OpenTelemetryLinkErrorEventProcessor(); + public @NotNull io.sentry.opentelemetry.OpenTelemetryLinkErrorEventProcessor + openTelemetryLinkErrorEventProcessor() { + return new io.sentry.opentelemetry.OpenTelemetryLinkErrorEventProcessor(); } } diff --git a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java index df3ca097bc..62f8e8457d 100644 --- a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java +++ b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java @@ -11,7 +11,6 @@ import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryOptions; import io.sentry.graphql.SentryGraphqlExceptionHandler; -import io.sentry.opentelemetry.OpenTelemetryLinkErrorEventProcessor; import io.sentry.protocol.SdkVersion; import io.sentry.quartz.SentryJobListener; import io.sentry.spring.ContextTagsEventProcessor; @@ -156,14 +155,16 @@ static class ContextTagsEventProcessorConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(name = "sentry.auto-init", havingValue = "false") - @ConditionalOnClass(OpenTelemetryLinkErrorEventProcessor.class) + @ConditionalOnClass(io.sentry.opentelemetry.OpenTelemetryLinkErrorEventProcessor.class) + @SuppressWarnings("deprecation") @Open static class OpenTelemetryLinkErrorEventProcessorConfiguration { @Bean @ConditionalOnMissingBean - public @NotNull OpenTelemetryLinkErrorEventProcessor openTelemetryLinkErrorEventProcessor() { - return new OpenTelemetryLinkErrorEventProcessor(); + public @NotNull io.sentry.opentelemetry.OpenTelemetryLinkErrorEventProcessor + openTelemetryLinkErrorEventProcessor() { + return new io.sentry.opentelemetry.OpenTelemetryLinkErrorEventProcessor(); } } diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 472b389dfe..f0514f13e5 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -998,6 +998,7 @@ public abstract interface class io/sentry/ISpanFactory { public abstract interface class io/sentry/ITransaction : io/sentry/ISpan { public abstract fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;ZLio/sentry/Hint;)V public abstract fun forceFinish (Lio/sentry/SpanStatus;ZLio/sentry/Hint;)V + public abstract fun getEventId ()Lio/sentry/protocol/SentryId; public abstract fun getLatestActiveSpan ()Lio/sentry/ISpan; public abstract fun getName ()Ljava/lang/String; public abstract fun getSpans ()Ljava/util/List; diff --git a/sentry/src/main/java/io/sentry/ISpan.java b/sentry/src/main/java/io/sentry/ISpan.java index 7fc56cc01f..f624bb79ef 100644 --- a/sentry/src/main/java/io/sentry/ISpan.java +++ b/sentry/src/main/java/io/sentry/ISpan.java @@ -2,7 +2,6 @@ import io.sentry.metrics.LocalMetricsAggregator; import io.sentry.protocol.Contexts; -import io.sentry.protocol.SentryId; import java.util.List; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -281,8 +280,8 @@ ISpan startChild( @Nullable TracesSamplingDecision getSamplingDecision(); - @NotNull - SentryId getEventId(); + // @NotNull + // SentryId getEventId(); @ApiStatus.Internal @NotNull diff --git a/sentry/src/main/java/io/sentry/ITransaction.java b/sentry/src/main/java/io/sentry/ITransaction.java index 9504e5b7a8..403b21187b 100644 --- a/sentry/src/main/java/io/sentry/ITransaction.java +++ b/sentry/src/main/java/io/sentry/ITransaction.java @@ -1,5 +1,6 @@ package io.sentry; +import io.sentry.protocol.SentryId; import io.sentry.protocol.TransactionNameSource; import java.util.List; import org.jetbrains.annotations.ApiStatus; @@ -89,4 +90,7 @@ void finish( @Nullable SentryDate timestamp, boolean dropIfNoChildren, @Nullable Hint hint); + + @NotNull + SentryId getEventId(); } diff --git a/sentry/src/main/java/io/sentry/NoOpSpan.java b/sentry/src/main/java/io/sentry/NoOpSpan.java index 533bbf0074..6658308d76 100644 --- a/sentry/src/main/java/io/sentry/NoOpSpan.java +++ b/sentry/src/main/java/io/sentry/NoOpSpan.java @@ -191,11 +191,6 @@ public void setContext(@NotNull String key, @NotNull Object context) {} return null; } - @Override - public @NotNull SentryId getEventId() { - return SentryId.EMPTY_ID; - } - @Override public @NotNull ISentryLifecycleToken makeCurrent() { return NoOpScopesLifecycleToken.getInstance(); diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index f305411aa6..526c0f19d4 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -834,7 +834,7 @@ public void flush(long timeoutMillis) { .getLogger() .log( SentryLevel.DEBUG, - "Returning no-op for span origin %s as the SDK has been configured to use ignore it", + "Returning no-op for span origin %s as the SDK has been configured to ignore it", transactionContext.getOrigin()); transaction = NoOpTransaction.getInstance(); diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index c199533d20..d46eb8771c 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -1954,7 +1954,11 @@ public void setEnableUserInteractionBreadcrumbs(boolean enableUserInteractionBre * startTransaction(...), nor will it create child spans if you call startChild(...) * * @param instrumenter - the instrumenter to use + * @deprecated this should no longer be needed with our current OpenTelmetry integration. Use + * {@link SentryOptions#setIgnoredSpanOrigins(List)} instead if you need fine grained control + * over what integrations can create spans. */ + @Deprecated public void setInstrumenter(final @NotNull Instrumenter instrumenter) { this.instrumenter = instrumenter; } @@ -2228,7 +2232,7 @@ public void setIgnoredSpanOrigins(final @Nullable List ignoredSpanOrigin } else { @NotNull final List filtered = new ArrayList<>(); for (String origin : ignoredSpanOrigins) { - if (!origin.isEmpty()) { + if (origin != null && !origin.isEmpty()) { filtered.add(origin); } } diff --git a/sentry/src/main/java/io/sentry/SentrySpanStorage.java b/sentry/src/main/java/io/sentry/SentrySpanStorage.java index b260f06e5c..eb7379741c 100644 --- a/sentry/src/main/java/io/sentry/SentrySpanStorage.java +++ b/sentry/src/main/java/io/sentry/SentrySpanStorage.java @@ -9,7 +9,10 @@ /** * Has been moved to `sentry` gradle module to include it in the bootstrap classloader without * having to introduce yet another module for OpenTelemetry support. + * + * @deprecated please use SentryWeakSpanStorage (from sentry-opentelemetry-bootstrap) instead. */ +@Deprecated @ApiStatus.Internal public final class SentrySpanStorage { private static volatile @Nullable SentrySpanStorage INSTANCE; diff --git a/sentry/src/main/java/io/sentry/Span.java b/sentry/src/main/java/io/sentry/Span.java index d85cbe43a9..73de808595 100644 --- a/sentry/src/main/java/io/sentry/Span.java +++ b/sentry/src/main/java/io/sentry/Span.java @@ -306,6 +306,11 @@ public boolean isFinished() { return context.getSamplingDecision(); } + // @Override + // public @NotNull SentryId getEventId() { + // return new SentryId(UUID.nameUUIDFromBytes(getSpanId().toString().getBytes())); + // } + @Override public @NotNull SentryId getEventId() { return new SentryId(getSpanId().toString()); From d90e0f9c3cc9d76e1ac856800b5f78366d65886d Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 24 Jun 2024 14:20:36 +0200 Subject: [PATCH 75/91] POTEL 20 - Use `SpanOptions.startTimestamp` in `Span` constructor (#3498) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing * Inherit OTel span IDs when sending to sentry * Fix tracing; parse incoming baggage; add baggage to outgoing * Cleanup * Move sampling logic to OTel Sampler * Remove internal span attributes so they are not sent to Sentry * Use transaction name * remove obsolete comment * Keep OTel and Sentry span name/op in sync * more cleanup * Make it possible to ignore span origins * Reuse TracesSampler instance * Catch exceptions thrown by integration.close * Set NoOpSpanFactory as property default * Use correct SentryOptions for SentryClient ctor * PR review feedback * more * Use SpanOptions.startTimestamp in Span ctor --- .../test/java/io/sentry/okhttp/SentryOkHttpEventTest.kt | 1 - .../main/java/io/sentry/opentelemetry/SentrySampler.java | 2 ++ sentry/api/sentry.api | 2 +- sentry/src/main/java/io/sentry/SentryTracer.java | 3 +-- sentry/src/main/java/io/sentry/Span.java | 7 +------ sentry/src/main/java/io/sentry/SpanOptions.java | 1 - sentry/src/test/java/io/sentry/protocol/SentrySpanTest.kt | 3 +-- sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt | 1 - 8 files changed, 6 insertions(+), 14 deletions(-) diff --git a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventTest.kt b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventTest.kt index 23688d9d85..37482b824f 100644 --- a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventTest.kt +++ b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventTest.kt @@ -67,7 +67,6 @@ class SentryOkHttpEventTest { TransactionContext("name", "op", TracesSamplingDecision(true)), SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), scopes), scopes, - null, SpanOptions() ) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java index f4b6233487..2ae611044b 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java @@ -44,6 +44,7 @@ public SamplingResult shouldSample( final @NotNull SpanKind spanKind, final @NotNull Attributes attributes, final @NotNull List parentLinks) { + // TODO [POTEL] use SamplingDecision.DROP sentry internal spans // note: parentLinks seems to usually be empty final @Nullable Span parentOtelSpan = Span.fromContextOrNull(parentContext); final @Nullable OtelSpanWrapper parentSentrySpan = @@ -65,6 +66,7 @@ public SamplingResult shouldSample( private @NotNull SamplingResult handleRootOtelSpan( final @NotNull String traceId, final @NotNull Context parentContext) { if (!options.isTraceSampling()) { + // TODO [POTEL] should this return RECORD_ONLY to allow tracing without performance return SamplingResult.create(SamplingDecision.DROP); } @Nullable Baggage baggage = null; diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index f0514f13e5..6057a37f14 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -3104,7 +3104,7 @@ public final class io/sentry/ShutdownHookIntegration : io/sentry/Integration, ja } public final class io/sentry/Span : io/sentry/ISpan { - public fun (Lio/sentry/TransactionContext;Lio/sentry/SentryTracer;Lio/sentry/IScopes;Lio/sentry/SentryDate;Lio/sentry/SpanOptions;)V + public fun (Lio/sentry/TransactionContext;Lio/sentry/SentryTracer;Lio/sentry/IScopes;Lio/sentry/SpanOptions;)V public fun finish ()V public fun finish (Lio/sentry/SpanStatus;)V public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index 08703d283c..5b4f9f273f 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -71,8 +71,7 @@ public SentryTracer( Objects.requireNonNull(context, "context is required"); Objects.requireNonNull(scopes, "scopes are required"); - this.root = - new Span(context, this, scopes, transactionOptions.getStartTimestamp(), transactionOptions); + this.root = new Span(context, this, scopes, transactionOptions); this.name = context.getName(); this.instrumenter = context.getInstrumenter(); diff --git a/sentry/src/main/java/io/sentry/Span.java b/sentry/src/main/java/io/sentry/Span.java index 73de808595..9a17a1a83a 100644 --- a/sentry/src/main/java/io/sentry/Span.java +++ b/sentry/src/main/java/io/sentry/Span.java @@ -79,13 +79,13 @@ public Span( final @NotNull TransactionContext context, final @NotNull SentryTracer sentryTracer, final @NotNull IScopes scopes, - final @Nullable SentryDate startTimestamp, final @NotNull SpanOptions options) { this.context = Objects.requireNonNull(context, "context is required"); this.context.setOrigin(options.getOrigin()); this.transaction = Objects.requireNonNull(sentryTracer, "sentryTracer is required"); this.scopes = Objects.requireNonNull(scopes, "scopes are required"); this.spanFinishedCallback = null; + final @Nullable SentryDate startTimestamp = options.getStartTimestamp(); if (startTimestamp != null) { this.startTimestamp = startTimestamp; } else { @@ -311,11 +311,6 @@ public boolean isFinished() { // return new SentryId(UUID.nameUUIDFromBytes(getSpanId().toString().getBytes())); // } - @Override - public @NotNull SentryId getEventId() { - return new SentryId(getSpanId().toString()); - } - @Override public void setThrowable(final @Nullable Throwable throwable) { this.throwable = throwable; diff --git a/sentry/src/main/java/io/sentry/SpanOptions.java b/sentry/src/main/java/io/sentry/SpanOptions.java index 29ee134c1a..41ac313ae5 100644 --- a/sentry/src/main/java/io/sentry/SpanOptions.java +++ b/sentry/src/main/java/io/sentry/SpanOptions.java @@ -13,7 +13,6 @@ public class SpanOptions { /** The start timestamp of the transaction */ private @Nullable SentryDate startTimestamp = null; - // TODO [POTEL] this should also work for non OTel spans /** * Gets the startTimestamp * diff --git a/sentry/src/test/java/io/sentry/protocol/SentrySpanTest.kt b/sentry/src/test/java/io/sentry/protocol/SentrySpanTest.kt index d67b0186be..504f5bcccc 100644 --- a/sentry/src/test/java/io/sentry/protocol/SentrySpanTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/SentrySpanTest.kt @@ -20,8 +20,7 @@ class SentrySpanTest { TransactionContext("name", "op"), mock(), mock(), - SentryLongDate(1000000), - SpanOptions() + SpanOptions().also { it.startTimestamp = SentryLongDate(1000000) } ) val sentrySpan = SentrySpan(span) diff --git a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt index b3f640aeec..62e2ba0055 100644 --- a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt @@ -41,7 +41,6 @@ class TracingUtilsTest { TransactionContext("name", "op", TracesSamplingDecision(true)), SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), scopes), scopes, - null, SpanOptions() ) } From 407a8770c439a57e550e083ed6be2f3b03eb661b Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Mon, 24 Jun 2024 14:24:29 +0200 Subject: [PATCH 76/91] POTEL 21 - Drop OpenTelemetry spans for internal Sentry requests (#3508) * replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing * Inherit OTel span IDs when sending to sentry * Fix tracing; parse incoming baggage; add baggage to outgoing * Cleanup * Move sampling logic to OTel Sampler * Remove internal span attributes so they are not sent to Sentry * Use transaction name * remove obsolete comment * Keep OTel and Sentry span name/op in sync * more cleanup * Make it possible to ignore span origins * Reuse TracesSampler instance * Catch exceptions thrown by integration.close * Set NoOpSpanFactory as property default * Use correct SentryOptions for SentryClient ctor * PR review feedback * more * Use SpanOptions.startTimestamp in Span ctor * Drop internal Sentry spans in SentrySampler --- .../api/sentry-opentelemetry-core.api | 5 ++ .../OtelInternalSpanDetectionUtil.java | 66 +++++++++++++++++++ .../sentry/opentelemetry/SentrySampler.java | 18 +++-- .../opentelemetry/SentrySpanExporter.java | 55 ++-------------- 4 files changed, 87 insertions(+), 57 deletions(-) create mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelInternalSpanDetectionUtil.java diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api index 72f8400fed..f4b4c273f1 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api +++ b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api @@ -4,6 +4,11 @@ public final class io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } +public final class io/sentry/opentelemetry/OtelInternalSpanDetectionUtil { + public fun ()V + public static fun isSentryRequest (Lio/sentry/IScopes;Lio/opentelemetry/api/trace/SpanKind;Lio/opentelemetry/api/common/Attributes;)Z +} + public final class io/sentry/opentelemetry/OtelSamplingUtil { public fun ()V public static fun extractSamplingDecision (Lio/opentelemetry/api/common/Attributes;)Lio/sentry/TracesSamplingDecision; diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelInternalSpanDetectionUtil.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelInternalSpanDetectionUtil.java new file mode 100644 index 0000000000..7009ed144c --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelInternalSpanDetectionUtil.java @@ -0,0 +1,66 @@ +package io.sentry.opentelemetry; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.semconv.SemanticAttributes; +import io.sentry.DsnUtil; +import io.sentry.IScopes; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@ApiStatus.Internal +public final class OtelInternalSpanDetectionUtil { + + private static final @NotNull List spanKindsConsideredForSentryRequests = + Arrays.asList(SpanKind.CLIENT, SpanKind.INTERNAL); + + @SuppressWarnings("deprecation") + public static boolean isSentryRequest( + final @NotNull IScopes scopes, + final @NotNull SpanKind spanKind, + final @NotNull Attributes attributes) { + if (!spanKindsConsideredForSentryRequests.contains(spanKind)) { + return false; + } + + final @Nullable String httpUrl = attributes.get(SemanticAttributes.HTTP_URL); + if (DsnUtil.urlContainsDsnHost(scopes.getOptions(), httpUrl)) { + return true; + } + + final @Nullable String fullUrl = attributes.get(SemanticAttributes.URL_FULL); + if (DsnUtil.urlContainsDsnHost(scopes.getOptions(), fullUrl)) { + return true; + } + + // TODO [POTEL] should check if enabled but multi init with different options makes testing hard + // atm + // if (scopes.getOptions().isEnableSpotlight()) { + final @Nullable String optionsSpotlightUrl = scopes.getOptions().getSpotlightConnectionUrl(); + final @NotNull String spotlightUrl = + optionsSpotlightUrl != null ? optionsSpotlightUrl : "http://localhost:8969/stream"; + + if (containsSpotlightUrl(fullUrl, spotlightUrl)) { + return true; + } + if (containsSpotlightUrl(httpUrl, spotlightUrl)) { + return true; + } + // } + + return false; + } + + private static boolean containsSpotlightUrl( + final @Nullable String requestUrl, final @NotNull String spotlightUrl) { + if (requestUrl == null) { + return false; + } + + return requestUrl.toLowerCase(Locale.ROOT).contains(spotlightUrl.toLowerCase(Locale.ROOT)); + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java index 2ae611044b..37df216ebb 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java @@ -1,5 +1,7 @@ package io.sentry.opentelemetry; +import static io.sentry.opentelemetry.OtelInternalSpanDetectionUtil.isSentryRequest; + import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanKind; @@ -13,7 +15,6 @@ import io.sentry.PropagationContext; import io.sentry.SamplingContext; import io.sentry.ScopesAdapter; -import io.sentry.SentryOptions; import io.sentry.SentryTraceHeader; import io.sentry.SpanId; import io.sentry.TracesSamplingDecision; @@ -26,10 +27,10 @@ public final class SentrySampler implements Sampler { private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); - private final @NotNull SentryOptions options; + private final @NotNull IScopes scopes; public SentrySampler(final @NotNull IScopes scopes) { - this.options = scopes.getOptions(); + this.scopes = scopes; } public SentrySampler() { @@ -44,7 +45,9 @@ public SamplingResult shouldSample( final @NotNull SpanKind spanKind, final @NotNull Attributes attributes, final @NotNull List parentLinks) { - // TODO [POTEL] use SamplingDecision.DROP sentry internal spans + if (isSentryRequest(scopes, spanKind, attributes)) { + return SamplingResult.drop(); + } // note: parentLinks seems to usually be empty final @Nullable Span parentOtelSpan = Span.fromContextOrNull(parentContext); final @Nullable OtelSpanWrapper parentSentrySpan = @@ -65,7 +68,7 @@ public SamplingResult shouldSample( private @NotNull SamplingResult handleRootOtelSpan( final @NotNull String traceId, final @NotNull Context parentContext) { - if (!options.isTraceSampling()) { + if (!scopes.getOptions().isTraceSampling()) { // TODO [POTEL] should this return RECORD_ONLY to allow tracing without performance return SamplingResult.create(SamplingDecision.DROP); } @@ -87,7 +90,10 @@ public SamplingResult shouldSample( final @NotNull TransactionContext transactionContext = TransactionContext.fromPropagationContext(propagationContext); final @NotNull TracesSamplingDecision sentryDecision = - options.getInternalTracesSampler().sample(new SamplingContext(transactionContext, null)); + scopes + .getOptions() + .getInternalTracesSampler() + .sample(new SamplingContext(transactionContext, null)); return new SentrySamplingResult(sentryDecision); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index ec7ae4918b..2d82bd23b7 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -2,9 +2,9 @@ import static io.sentry.TransactionContext.DEFAULT_TRANSACTION_NAME; import static io.sentry.opentelemetry.InternalSemanticAttributes.IS_REMOTE_PARENT; +import static io.sentry.opentelemetry.OtelInternalSpanDetectionUtil.isSentryRequest; import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.trace.data.SpanData; @@ -14,7 +14,6 @@ import io.sentry.Baggage; import io.sentry.DateUtils; import io.sentry.DefaultSpanFactory; -import io.sentry.DsnUtil; import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.ITransaction; @@ -37,7 +36,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Predicate; @@ -54,9 +52,6 @@ public final class SentrySpanExporter implements SpanExporter { new SpanDescriptionExtractor(); private final @NotNull IScopes scopes; - private final @NotNull List spanKindsConsideredForSentryRequests = - Arrays.asList(SpanKind.CLIENT, SpanKind.INTERNAL); - private final @NotNull List attributeKeysToRemove = Arrays.asList( InternalSemanticAttributes.IS_REMOTE_PARENT.getKey(), @@ -134,51 +129,9 @@ private boolean isSpanTooOld(final @NotNull SpanData span, final @NotNull Sentry } private @NotNull List filterOutSentrySpans(final @NotNull Collection spans) { - return spans.stream().filter((span) -> !isSentryRequest(span)).collect(Collectors.toList()); - } - - @SuppressWarnings("deprecation") - private boolean isSentryRequest(final @NotNull SpanData spanData) { - final @NotNull SpanKind kind = spanData.getKind(); - if (!spanKindsConsideredForSentryRequests.contains(kind)) { - return false; - } - - final @Nullable String httpUrl = spanData.getAttributes().get(SemanticAttributes.HTTP_URL); - if (DsnUtil.urlContainsDsnHost(scopes.getOptions(), httpUrl)) { - return true; - } - - final @Nullable String fullUrl = spanData.getAttributes().get(SemanticAttributes.URL_FULL); - if (DsnUtil.urlContainsDsnHost(scopes.getOptions(), fullUrl)) { - return true; - } - - // TODO [POTEL] should check if enabled but multi init with different options makes testing hard - // atm - // if (scopes.getOptions().isEnableSpotlight()) { - final @Nullable String optionsSpotlightUrl = scopes.getOptions().getSpotlightConnectionUrl(); - final @NotNull String spotlightUrl = - optionsSpotlightUrl != null ? optionsSpotlightUrl : "http://localhost:8969/stream"; - - if (containsSpotlightUrl(fullUrl, spotlightUrl)) { - return true; - } - if (containsSpotlightUrl(httpUrl, spotlightUrl)) { - return true; - } - // } - - return false; - } - - private boolean containsSpotlightUrl( - final @Nullable String requestUrl, final @NotNull String spotlightUrl) { - if (requestUrl == null) { - return false; - } - - return requestUrl.toLowerCase(Locale.ROOT).contains(spotlightUrl.toLowerCase(Locale.ROOT)); + return spans.stream() + .filter((span) -> !isSentryRequest(scopes, span.getKind(), span.getAttributes())) + .collect(Collectors.toList()); } private List maybeSend(final @NotNull List spans) { From 42273e87d7e610cf40069bdf3386129fc9085bb8 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 25 Jun 2024 16:14:19 +0200 Subject: [PATCH 77/91] POTEL 22 - Improve Changelog (#3513) --- CHANGELOG.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 715567cb42..c015f1e3c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,11 +11,15 @@ ### Features - Our `sentry-opentelemetry-agent` has been completely reworked and now plays nicely with the rest of the Java SDK - - NOTE: Not all features have been implemented yet for the OpenTelemetry agent. - - You can add `sentry-opentelemetry-agent` to your setup by downloading the latest release and using it when starting up your application - - `SENTRY_PROPERTIES_FILE=sentry.properties java -javaagent:sentry-opentelemetry-agent-x.x.x.jar -jar your-application.jar` - - Please use `sentry.properties` or environment variables to configure the SDK as the agent is now in charge of initializing the SDK and options coming from things like logging integrations or our Spring Boot integration will not take effect. - - You may find the [docs page](https://docs.sentry.io/platforms/java/tracing/instrumentation/opentelemetry/#using-sentry-opentelemetry-agent-with-auto-initialization) useful. While we haven't updated it yet to reflect the changes described here, the section about using the agent with auto init should still be vaild. + - You may also want to give this new agent a try even if you haven't used OpenTelemetry (with Sentry) before. It offers support for [many more libraries and frameworks](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md), improving on our trace propagation, `Scopes` (used to be `Hub`) propagation as well as performance instrumentation (i.e. more spans). + - If you are using a framework we did not support before and currently resort to manual instrumentation, please give the agent a try. See [here for a list of supported libraries, frameworks and application servers](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md). + - NOTE: Not all features have been implemented yet for the OpenTelemetry agent. Features of note that are not working yet: + - Metrics + - Measurements + - `forceFinish` on transaction + - `scheduleFinish` on transaction + - see [#3436](https://github.com/getsentry/sentry-java/issues/3436) for a more up-to-date list of features we have (not) implemented + - Please see "Installing `sentry-opentelemetry-agent`" for more details on how to set up the agent. - What's new about the Agent - When the OpenTelemetry Agent is used, Sentry API creates OpenTelemetry spans under the hood, handing back a wrapper object which bridges the gap between traditional Sentry API and OpenTelemetry. We might be replacing some of the Sentry performance API in the future. - This is achieved by configuring the SDK to use `OtelSpanFactory` instead of `DefaultSpanFactory` which is done automatically by the auto init of the Java Agent. @@ -30,13 +34,49 @@ ### Fixes -- `TracesSampler` is now only created once in `SentryOptions` instead of creating a new one for every `Hub` (which is now `Scopes`). This means we're now creating fewwer `SecureRandom` instances. +- `TracesSampler` is now only created once in `SentryOptions` instead of creating a new one for every `Hub` (which is now `Scopes`). This means we're now creating fewer `SecureRandom` instances. - Move onFinishCallback before span or transaction is finished ([#3459](https://github.com/getsentry/sentry-java/pull/3459)) - Add timestamp when a profile starts ([#3442](https://github.com/getsentry/sentry-java/pull/3442)) - Move fragment auto span finish to onFragmentStarted ([#3424](https://github.com/getsentry/sentry-java/pull/3424)) - Remove profiling timeout logic and disable profiling on API 21 ([#3478](https://github.com/getsentry/sentry-java/pull/3478)) - Properly reset metric flush flag on metric emission ([#3493](https://github.com/getsentry/sentry-java/pull/3493)) +### Migration Guide / Deprecations + +- Classes used for the previous version of the Sentry OpenTelemetry Java Agent have been deprecated (`SentrySpanProcessor`, `SentryPropagator`, `OpenTelemetryLinkErrorEventProcessor`) +- Sentry OpenTelemetry Java Agent has been reworked and now allows you to manually create spans using Sentry API as well. +- Please see "Installing `sentry-opentelemetry-agent`" for more details on how to set up the agent. + +### Installing `sentry-opentelemetry-agent` + +### Upgrading from a previous agent +If you've been using the previous version of `sentry-opentelemetry-agent`, simply replace the agent JAR with the [latest release](https://central.sonatype.com/artifact/io.sentry/sentry-opentelemetry-agent?smo=true) and start your application. That should be it. + +### New to the agent +If you've not been using OpenTelemetry before, you can add `sentry-opentelemetry-agent` to your setup by downloading the latest release and using it when starting up your application + - `SENTRY_PROPERTIES_FILE=sentry.properties java -javaagent:sentry-opentelemetry-agent-x.x.x.jar -jar your-application.jar` + - Please use `sentry.properties` or environment variables to configure the SDK as the agent is now in charge of initializing the SDK and options coming from things like logging integrations or our Spring Boot integration will not take effect. + - You may find the [docs page](https://docs.sentry.io/platforms/java/tracing/instrumentation/opentelemetry/#using-sentry-opentelemetry-agent-with-auto-initialization) useful. While we haven't updated it yet to reflect the changes described here, the section about using the agent with auto init should still be valid. + +If you want to skip auto initialization of the SDK performed by the agent, please follow the steps above and set the environment variable `SENTRY_AUTO_INIT` to `false` then add the following to your `Sentry.init`: + +``` +Sentry.init(options -> { + options.setDsn("https://3d2ac63d6e1a4c6e9214443678f119a3@o87286.ingest.us.sentry.io/1801383"); + OpenTelemetryUtil.applyOpenTelemetryOptions(options); + ... +}); +``` + +If you're using our Spring (Boot) integration with auto init, use the following: +``` +@Bean +Sentry.OptionsConfiguration optionsConfiguration() { + return (options) -> { + OpenTelemetryUtil.applyOpenTelemetryOptions(options); + }; +} +``` ### Dependencies From 8354619685f5de54a9769a4216bd4aaec8d0146e Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 25 Jun 2024 16:14:54 +0200 Subject: [PATCH 78/91] POTEL 23 - Bump OTel versions (#3514) * improve changelog * bump otel versions --- buildSrc/src/main/java/Config.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index a3ccdc3af5..9bed7be1b2 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -151,9 +151,9 @@ object Config { val apolloKotlin = "com.apollographql.apollo3:apollo-runtime:3.8.2" object OpenTelemetry { - val otelVersion = "1.37.0" + val otelVersion = "1.39.0" val otelAlphaVersion = "$otelVersion-alpha" - val otelJavaagentVersion = "2.3.0" + val otelJavaagentVersion = "2.5.0" val otelJavaagentAlphaVersion = "$otelJavaagentVersion-alpha" val otelSemanticConvetionsVersion = "1.23.1-alpha" From 6c89ff75f8c7227f2f023be8d6f57c943f766d3f Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 25 Jun 2024 16:15:12 +0200 Subject: [PATCH 79/91] POTEL 24 - Fixes after merge and some more PR comments have been addressed (#3515) * improve changelog * bump otel versions * merge fix; pr comments --- .../SentryAutoConfigurationCustomizerProvider.java | 2 -- sentry/api/sentry.api | 3 --- sentry/src/main/java/io/sentry/ISpan.java | 3 --- sentry/src/main/java/io/sentry/Sentry.java | 4 ++++ sentry/src/main/java/io/sentry/Span.java | 5 ----- 5 files changed, 4 insertions(+), 13 deletions(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java index cff1035457..2ae24c2f6b 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java @@ -56,8 +56,6 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) { } } - ContextStorage.addWrapper((storage) -> new SentryContextStorage(storage)); - autoConfiguration .addTracerProviderCustomizer(this::configureSdkTracerProvider) .addPropertiesSupplier(this::getDefaultProperties); diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 6057a37f14..146d17461f 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -953,7 +953,6 @@ public abstract interface class io/sentry/ISpan { public abstract fun getContexts ()Lio/sentry/protocol/Contexts; public abstract fun getData (Ljava/lang/String;)Ljava/lang/Object; public abstract fun getDescription ()Ljava/lang/String; - public abstract fun getEventId ()Lio/sentry/protocol/SentryId; public abstract fun getFinishDate ()Lio/sentry/SentryDate; public abstract fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; public abstract fun getOperation ()Ljava/lang/String; @@ -1551,7 +1550,6 @@ public final class io/sentry/NoOpSpan : io/sentry/ISpan { public fun getContexts ()Lio/sentry/protocol/Contexts; public fun getData (Ljava/lang/String;)Ljava/lang/Object; public fun getDescription ()Ljava/lang/String; - public fun getEventId ()Lio/sentry/protocol/SentryId; public fun getFinishDate ()Lio/sentry/SentryDate; public static fun getInstance ()Lio/sentry/NoOpSpan; public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; @@ -3112,7 +3110,6 @@ public final class io/sentry/Span : io/sentry/ISpan { public fun getData ()Ljava/util/Map; public fun getData (Ljava/lang/String;)Ljava/lang/Object; public fun getDescription ()Ljava/lang/String; - public fun getEventId ()Lio/sentry/protocol/SentryId; public fun getFinishDate ()Lio/sentry/SentryDate; public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; public fun getMeasurements ()Ljava/util/Map; diff --git a/sentry/src/main/java/io/sentry/ISpan.java b/sentry/src/main/java/io/sentry/ISpan.java index f624bb79ef..0b8f931033 100644 --- a/sentry/src/main/java/io/sentry/ISpan.java +++ b/sentry/src/main/java/io/sentry/ISpan.java @@ -280,9 +280,6 @@ ISpan startChild( @Nullable TracesSamplingDecision getSamplingDecision(); - // @NotNull - // SentryId getEventId(); - @ApiStatus.Internal @NotNull ISentryLifecycleToken makeCurrent(); diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 3091a925c4..7206df9b37 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -278,10 +278,14 @@ private static synchronized void init( options.getLogger().log(SentryLevel.INFO, "GlobalHubMode: '%s'", String.valueOf(globalHubMode)); Sentry.globalHubMode = globalHubMode; + globalScope.replaceOptions(options); final IScopes scopes = getCurrentScopes(); final IScope rootScope = new Scope(options); final IScope rootIsolationScope = new Scope(options); + rootScopes = new Scopes(rootScope, rootIsolationScope, globalScope, "Sentry.init"); + + getScopesStorage().set(rootScopes); scopes.close(true); globalScope.bindClient(new SentryClient(rootScopes.getOptions())); diff --git a/sentry/src/main/java/io/sentry/Span.java b/sentry/src/main/java/io/sentry/Span.java index 9a17a1a83a..12fcf87c20 100644 --- a/sentry/src/main/java/io/sentry/Span.java +++ b/sentry/src/main/java/io/sentry/Span.java @@ -306,11 +306,6 @@ public boolean isFinished() { return context.getSamplingDecision(); } - // @Override - // public @NotNull SentryId getEventId() { - // return new SentryId(UUID.nameUUIDFromBytes(getSpanId().toString().getBytes())); - // } - @Override public void setThrowable(final @Nullable Throwable throwable) { this.throwable = throwable; From 1a4c9e8702544bc42adac52c4123f625011b76c9 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 25 Jun 2024 16:15:30 +0200 Subject: [PATCH 80/91] POTEL 25 - Workaround for `sentry-opentelemetry-agent` with `SENTRY_AUTO_INIT=false` (#3516) * improve changelog * bump otel versions * merge fix; pr comments * workaround for agent non auto init --- ...ryAutoConfigurationCustomizerProvider.java | 5 +++- .../api/sentry-opentelemetry-bootstrap.api | 5 ++++ .../opentelemetry/OpenTelemetryUtil.java | 18 ++++++++++++ .../sentry/opentelemetry/OtelSpanWrapper.java | 2 +- sentry/api/sentry.api | 6 ++++ .../io/sentry/SentrySpanFactoryHolder.java | 29 +++++++++++++++++++ .../src/main/java/io/sentry/SentryTracer.java | 1 + 7 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OpenTelemetryUtil.java create mode 100644 sentry/src/main/java/io/sentry/SentrySpanFactoryHolder.java diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java index 2ae24c2f6b..2b1b79c521 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java @@ -9,6 +9,7 @@ import io.sentry.Sentry; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryOptions; +import io.sentry.SentrySpanFactoryHolder; import io.sentry.protocol.SdkVersion; import io.sentry.protocol.SentryPackage; import io.sentry.util.SpanUtils; @@ -32,13 +33,15 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) { final @Nullable VersionInfoHolder versionInfoHolder = createVersionInfo(); ContextStorage.addWrapper((storage) -> new SentryContextStorage(storage)); + final @NotNull OtelSpanFactory spanFactory = new OtelSpanFactory(); + SentrySpanFactoryHolder.setSpanFactory(spanFactory); if (isSentryAutoInitEnabled()) { Sentry.init( options -> { options.setEnableExternalConfiguration(true); options.setIgnoredSpanOrigins(SpanUtils.ignoredSpanOriginsForOpenTelemetry()); - options.setSpanFactory(new OtelSpanFactory()); + options.setSpanFactory(spanFactory); final @Nullable SdkVersion sdkVersion = createSdkVersion(options, versionInfoHolder); // TODO [POTEL] is detecting a version mismatch between application and agent possible? if (sdkVersion != null) { diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api index d9907781b5..31aaa4ba9a 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api @@ -10,6 +10,11 @@ public final class io/sentry/opentelemetry/InternalSemanticAttributes { public fun ()V } +public final class io/sentry/opentelemetry/OpenTelemetryUtil { + public fun ()V + public static fun applyOpenTelemetryOptions (Lio/sentry/SentryOptions;)V +} + public final class io/sentry/opentelemetry/OtelContextScopesStorage : io/sentry/IScopesStorage { public fun ()V public fun close ()V diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OpenTelemetryUtil.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OpenTelemetryUtil.java new file mode 100644 index 0000000000..02fc706e61 --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OpenTelemetryUtil.java @@ -0,0 +1,18 @@ +package io.sentry.opentelemetry; + +import io.sentry.SentryOptions; +import io.sentry.SentrySpanFactoryHolder; +import io.sentry.util.SpanUtils; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +@ApiStatus.Experimental +public final class OpenTelemetryUtil { + + public static void applyOpenTelemetryOptions(final @Nullable SentryOptions options) { + if (options != null) { + options.setSpanFactory(SentrySpanFactoryHolder.getSpanFactory()); + options.setIgnoredSpanOrigins(SpanUtils.ignoredSpanOriginsForOpenTelemetry()); + } + } +} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java index 2a023e115b..b5ee906e19 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java @@ -58,7 +58,7 @@ public final class OtelSpanWrapper implements ISpan { * OtelSpanWrapper} and indirectly back to {@link io.opentelemetry.sdk.trace.data.SpanData} via * {@link Span}. Also see {@link SentryWeakSpanStorage}. */ - private final @NotNull WeakReference span; + private final @NotNull WeakReference span; // TODO [POTEL] bootstrap proxy private final @NotNull SpanContext context; // private final @NotNull SpanOptions options; diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 146d17461f..a106b61618 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -2936,6 +2936,12 @@ public abstract interface class io/sentry/SentryOptions$TracesSamplerCallback { public abstract fun sample (Lio/sentry/SamplingContext;)Ljava/lang/Double; } +public final class io/sentry/SentrySpanFactoryHolder { + public fun ()V + public static fun getSpanFactory ()Lio/sentry/ISpanFactory; + public static fun setSpanFactory (Lio/sentry/ISpanFactory;)V +} + public final class io/sentry/SentrySpanStorage { public fun get (Ljava/lang/String;)Lio/sentry/ISpan; public static fun getInstance ()Lio/sentry/SentrySpanStorage; diff --git a/sentry/src/main/java/io/sentry/SentrySpanFactoryHolder.java b/sentry/src/main/java/io/sentry/SentrySpanFactoryHolder.java new file mode 100644 index 0000000000..cde9bc2a4f --- /dev/null +++ b/sentry/src/main/java/io/sentry/SentrySpanFactoryHolder.java @@ -0,0 +1,29 @@ +package io.sentry; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +/** + * NOTE: This just exists as a workaround for a bug. + * + *

    What bug? When using sentry-opentelemetry-agent with SENTRY_AUTO_INIT=false a global storage + * for spans does not work correctly since it's loaded multiple times. Once for bootstrap + * classloader (a.k.a null) and once for the agent classloader. Since the agent is currently loading + * these classes into the agent classloader, there should not be a noticable problem, when using the + * default of SENTRY_AUTO_INIT=true. In the future we plan to have the agent also load the classes + * into the bootstrap classloader, then this hack should no longer be necessary. + */ +@ApiStatus.Experimental +public final class SentrySpanFactoryHolder { + + private static ISpanFactory spanFactory = new DefaultSpanFactory(); + + public static ISpanFactory getSpanFactory() { + return spanFactory; + } + + @ApiStatus.Internal + public static void setSpanFactory(final @NotNull ISpanFactory factory) { + spanFactory = factory; + } +} diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index 5b4f9f273f..ce739986ca 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -486,6 +486,7 @@ private ISpan createChild( finish(finishStatus.spanStatus); } }); + // TODO [POTEL] missing features // final Span span = // new Span( // root.getTraceId(), From d924cd1d79dd78fdf1003a5958ec7095da1658ac Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Wed, 26 Jun 2024 07:19:39 +0200 Subject: [PATCH 81/91] POTEL 26 - Customize OpenTelemetry `Scope.close` behaviour (#3517) * improve changelog * bump otel versions * merge fix; pr comments * workaround for agent non auto init * Customize OTel ThreadLocal storage behaviour * fix changelog --- CHANGELOG.md | 4 +- .../build.gradle.kts | 1 + ...ryAutoConfigurationCustomizerProvider.java | 12 ++- .../api/sentry-opentelemetry-bootstrap.api | 6 ++ .../SentryOtelThreadLocalStorage.java | 85 +++++++++++++++++++ 5 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelThreadLocalStorage.java diff --git a/CHANGELOG.md b/CHANGELOG.md index c015f1e3c3..de32df6a2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,10 +49,10 @@ ### Installing `sentry-opentelemetry-agent` -### Upgrading from a previous agent +#### Upgrading from a previous agent If you've been using the previous version of `sentry-opentelemetry-agent`, simply replace the agent JAR with the [latest release](https://central.sonatype.com/artifact/io.sentry/sentry-opentelemetry-agent?smo=true) and start your application. That should be it. -### New to the agent +#### New to the agent If you've not been using OpenTelemetry before, you can add `sentry-opentelemetry-agent` to your setup by downloading the latest release and using it when starting up your application - `SENTRY_PROPERTIES_FILE=sentry.properties java -javaagent:sentry-opentelemetry-agent-x.x.x.jar -jar your-application.jar` - Please use `sentry.properties` or environment variables to configure the SDK as the agent is now in charge of initializing the SDK and options coming from things like logging integrations or our Spring Boot integration will not take effect. diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts index 475796d246..c0e002ee31 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts @@ -24,6 +24,7 @@ dependencies { exclude(group = "io.opentelemetry") exclude(group = "io.opentelemetry.javaagent") } +// compileOnly(projects.sentryOpentelemetry.sentryOpentelemetryBootstrap) implementation(projects.sentryOpentelemetry.sentryOpentelemetryBootstrap) compileOnly(Config.Libs.OpenTelemetry.otelSdk) diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java index 2b1b79c521..66b55a4f0b 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java @@ -32,9 +32,19 @@ public final class SentryAutoConfigurationCustomizerProvider public void customize(AutoConfigurationCustomizer autoConfiguration) { final @Nullable VersionInfoHolder versionInfoHolder = createVersionInfo(); - ContextStorage.addWrapper((storage) -> new SentryContextStorage(storage)); final @NotNull OtelSpanFactory spanFactory = new OtelSpanFactory(); SentrySpanFactoryHolder.setSpanFactory(spanFactory); + /** + * We're currently overriding the storage mechanism to allow for cleanup of non closed OTel + * scopes. These happen when using e.g. Sentry static API due to getCurrentScopes() invoking + * Context.makeCurrent and then ignoring the returned lifecycle token (OTel Scope). After fixing + * the classloader problem (sentry bootstrap dependency is currently in agent classloader) we + * can revisit and try again to set the storage instead of overriding it in the wrapper. We + * should try to use OTels StorageProvider mechanism instead. + */ + // ContextStorage.addWrapper((storage) -> new SentryContextStorage(storage)); + ContextStorage.addWrapper( + (storage) -> new SentryContextStorage(new SentryOtelThreadLocalStorage())); if (isSentryAutoInitEnabled()) { Sentry.init( diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api index 31aaa4ba9a..0511fed18d 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api @@ -164,6 +164,12 @@ public final class io/sentry/opentelemetry/SentryOtelKeys { public fun ()V } +public final class io/sentry/opentelemetry/SentryOtelThreadLocalStorage : io/opentelemetry/context/ContextStorage { + public fun ()V + public fun attach (Lio/opentelemetry/context/Context;)Lio/opentelemetry/context/Scope; + public fun current ()Lio/opentelemetry/context/Context; +} + public final class io/sentry/opentelemetry/SentryWeakSpanStorage { public static fun getInstance ()Lio/sentry/opentelemetry/SentryWeakSpanStorage; public fun getSentrySpan (Lio/opentelemetry/api/trace/SpanContext;)Lio/sentry/opentelemetry/OtelSpanWrapper; diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelThreadLocalStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelThreadLocalStorage.java new file mode 100644 index 0000000000..e44afc5a2d --- /dev/null +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelThreadLocalStorage.java @@ -0,0 +1,85 @@ +/* + * Adapted from https://github.com/open-telemetry/opentelemetry-java/blob/0aacc55d1e3f5cc6dbb4f8fa26bcb657b01a7bc9/context/src/main/java/io/opentelemetry/context/ThreadLocalContextStorage.java + * + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.sentry.opentelemetry; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextStorage; +import io.opentelemetry.context.Scope; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +/** + * Workaround to make OpenTelemetry context storage work for Sentry since Sentry sometimes forks + * Context without cleaning up. We are not yet sure if this is something we can easliy fix, since + * Sentry static API makes heavy use of getCurrentScopes and there is no easy way of knowing when to + * restore previous Context. + */ +@ApiStatus.Experimental +@ApiStatus.Internal +public final class SentryOtelThreadLocalStorage implements ContextStorage { + private static final Logger logger = + Logger.getLogger(SentryOtelThreadLocalStorage.class.getName()); + + private static final ThreadLocal THREAD_LOCAL_STORAGE = new ThreadLocal<>(); + + @Override + public Scope attach(Context toAttach) { + if (toAttach == null) { + // Null context not allowed so ignore it. + return NoopScope.INSTANCE; + } + + Context beforeAttach = current(); + if (toAttach == beforeAttach) { + return NoopScope.INSTANCE; + } + + THREAD_LOCAL_STORAGE.set(toAttach); + + return new SentryScopeImpl(beforeAttach); + } + + private static class SentryScopeImpl implements Scope { + @Nullable private final Context beforeAttach; + private boolean closed; + + private SentryScopeImpl(@Nullable Context beforeAttach) { + this.beforeAttach = beforeAttach; + } + + @Override + public void close() { + // if (!closed && current() == toAttach) { + // Used to make OTel thread local storage compatible with Sentry where cleanup isn't always + // performed correctly + if (!closed) { + closed = true; + THREAD_LOCAL_STORAGE.set(beforeAttach); + } else { + logger.log( + Level.FINE, + " Trying to close scope which does not represent current context. Ignoring the call."); + } + } + } + + @Override + @Nullable + public Context current() { + return THREAD_LOCAL_STORAGE.get(); + } + + enum NoopScope implements Scope { + INSTANCE; + + @Override + public void close() {} + } +} From af66eb24d1129a4d79b31319c2c36815bfc4af80 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Wed, 26 Jun 2024 07:20:14 +0200 Subject: [PATCH 82/91] POTEL 27 - Rename classes and mark some classes internal (#3518) * improve changelog * bump otel versions * merge fix; pr comments * workaround for agent non auto init * Customize OTel ThreadLocal storage behaviour * fix changelog * rename classes; mark classes internal * revert printlns --- ...ryAutoConfigurationCustomizerProvider.java | 2 +- .../SentryPropagatorProvider.java | 3 +-- .../InternalSemanticAttributes.java | 2 ++ .../OtelContextScopesStorage.java | 2 ++ .../sentry/opentelemetry/OtelSpanContext.java | 2 ++ .../opentelemetry/SentryContextStorage.java | 2 ++ .../opentelemetry/SentryContextWrapper.java | 2 ++ .../api/sentry-opentelemetry-core.api | 24 +++++++++---------- ...pagator.java => OtelSentryPropagator.java} | 6 ++--- ...ssor.java => OtelSentrySpanProcessor.java} | 6 ++--- .../opentelemetry/SentryPropagator.java | 2 +- .../opentelemetry/SentrySamplingResult.java | 2 ++ .../opentelemetry/SentrySpanExporter.java | 2 +- .../opentelemetry/SentrySpanProcessor.java | 2 +- .../io/sentry/opentelemetry/SpanNode.java | 2 ++ .../java/io/sentry/DefaultScopesStorage.java | 2 ++ .../main/java/io/sentry/IScopesStorage.java | 2 ++ .../java/io/sentry/ScopesStorageFactory.java | 2 ++ .../main/java/io/sentry/SentryOptions.java | 2 ++ .../io/sentry/SentrySpanFactoryHolder.java | 1 + 20 files changed, 46 insertions(+), 24 deletions(-) rename sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/{PotelSentryPropagator.java => OtelSentryPropagator.java} (96%) rename sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/{PotelSentrySpanProcessor.java => OtelSentrySpanProcessor.java} (97%) diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java index 66b55a4f0b..019541e7e3 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java @@ -161,7 +161,7 @@ private SdkTracerProviderBuilder configureSdkTracerProvider( SdkTracerProviderBuilder tracerProvider, ConfigProperties config) { return tracerProvider .setSampler(new SentrySampler()) - .addSpanProcessor(new PotelSentrySpanProcessor()) + .addSpanProcessor(new OtelSentrySpanProcessor()) .addSpanProcessor(BatchSpanProcessor.builder(new SentrySpanExporter()).build()); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryPropagatorProvider.java b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryPropagatorProvider.java index ac507badb4..6aa04f31e9 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryPropagatorProvider.java +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryPropagatorProvider.java @@ -7,8 +7,7 @@ public final class SentryPropagatorProvider implements ConfigurablePropagatorProvider { @Override public TextMapPropagator getPropagator(ConfigProperties config) { - // return new SentryPropagator(); - return new PotelSentryPropagator(); + return new OtelSentryPropagator(); } @Override diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java index d186d2c634..cb64d7bfff 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java @@ -1,7 +1,9 @@ package io.sentry.opentelemetry; import io.opentelemetry.api.common.AttributeKey; +import org.jetbrains.annotations.ApiStatus; +@ApiStatus.Internal public final class InternalSemanticAttributes { public static final AttributeKey SAMPLED = AttributeKey.booleanKey("sentry.sampled"); public static final AttributeKey SAMPLE_RATE = diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java index 299e91dae9..810c6c08cc 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java @@ -7,9 +7,11 @@ import io.sentry.IScopes; import io.sentry.IScopesStorage; import io.sentry.ISentryLifecycleToken; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +@ApiStatus.Internal @SuppressWarnings("MustBeClosedChecker") public final class OtelContextScopesStorage implements IScopesStorage { diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java index 5f75a6f10e..7b279802c9 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java @@ -11,9 +11,11 @@ import io.sentry.TracesSamplingDecision; import io.sentry.protocol.SentryId; import java.lang.ref.WeakReference; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +@ApiStatus.Internal public final class OtelSpanContext extends SpanContext { /** diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextStorage.java index 34b71b5426..6ce6f88816 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextStorage.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextStorage.java @@ -5,8 +5,10 @@ import io.opentelemetry.context.Scope; import java.util.logging.Level; import java.util.logging.Logger; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +@ApiStatus.Internal public final class SentryContextStorage implements ContextStorage { private final @NotNull Logger logger = Logger.getLogger(SentryContextStorage.class.getName()); diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextWrapper.java index cf5a6495f0..e2a5efaf89 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextWrapper.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextWrapper.java @@ -7,9 +7,11 @@ import io.opentelemetry.context.ContextKey; import io.sentry.IScopes; import io.sentry.Sentry; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +@ApiStatus.Internal public final class SentryContextWrapper implements Context { private final @NotNull Context delegate; diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api index f4b4c273f1..6c6fc52bad 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api +++ b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api @@ -15,24 +15,14 @@ public final class io/sentry/opentelemetry/OtelSamplingUtil { public static fun extractSamplingDecisionOrDefault (Lio/opentelemetry/api/common/Attributes;)Lio/sentry/TracesSamplingDecision; } -public final class io/sentry/opentelemetry/OtelSpanInfo { - public fun (Ljava/lang/String;Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V - public fun (Ljava/lang/String;Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;Ljava/util/Map;)V - public fun addDataField (Ljava/lang/String;Ljava/lang/Object;)V - public fun getDataFields ()Ljava/util/Map; - public fun getDescription ()Ljava/lang/String; - public fun getOp ()Ljava/lang/String; - public fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; -} - -public final class io/sentry/opentelemetry/PotelSentryPropagator : io/opentelemetry/context/propagation/TextMapPropagator { +public final class io/sentry/opentelemetry/OtelSentryPropagator : io/opentelemetry/context/propagation/TextMapPropagator { public fun ()V public fun extract (Lio/opentelemetry/context/Context;Ljava/lang/Object;Lio/opentelemetry/context/propagation/TextMapGetter;)Lio/opentelemetry/context/Context; public fun fields ()Ljava/util/Collection; public fun inject (Lio/opentelemetry/context/Context;Ljava/lang/Object;Lio/opentelemetry/context/propagation/TextMapSetter;)V } -public final class io/sentry/opentelemetry/PotelSentrySpanProcessor : io/opentelemetry/sdk/trace/SpanProcessor { +public final class io/sentry/opentelemetry/OtelSentrySpanProcessor : io/opentelemetry/sdk/trace/SpanProcessor { public fun ()V public fun isEndRequired ()Z public fun isStartRequired ()Z @@ -40,6 +30,16 @@ public final class io/sentry/opentelemetry/PotelSentrySpanProcessor : io/opentel public fun onStart (Lio/opentelemetry/context/Context;Lio/opentelemetry/sdk/trace/ReadWriteSpan;)V } +public final class io/sentry/opentelemetry/OtelSpanInfo { + public fun (Ljava/lang/String;Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V + public fun (Ljava/lang/String;Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;Ljava/util/Map;)V + public fun addDataField (Ljava/lang/String;Ljava/lang/Object;)V + public fun getDataFields ()Ljava/util/Map; + public fun getDescription ()Ljava/lang/String; + public fun getOp ()Ljava/lang/String; + public fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; +} + public final class io/sentry/opentelemetry/SentryPropagator : io/opentelemetry/context/propagation/TextMapPropagator { public fun ()V public fun extract (Lio/opentelemetry/context/Context;Ljava/lang/Object;Lio/opentelemetry/context/propagation/TextMapGetter;)Lio/opentelemetry/context/Context; diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentryPropagator.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentryPropagator.java similarity index 96% rename from sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentryPropagator.java rename to sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentryPropagator.java index 3a9d0718f4..0b9ee88e08 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentryPropagator.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentryPropagator.java @@ -26,18 +26,18 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public final class PotelSentryPropagator implements TextMapPropagator { +public final class OtelSentryPropagator implements TextMapPropagator { private static final @NotNull List FIELDS = Arrays.asList(SentryTraceHeader.SENTRY_TRACE_HEADER, BaggageHeader.BAGGAGE_HEADER); private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); private final @NotNull IScopes scopes; - public PotelSentryPropagator() { + public OtelSentryPropagator() { this(ScopesAdapter.getInstance()); } - PotelSentryPropagator(final @NotNull IScopes scopes) { + OtelSentryPropagator(final @NotNull IScopes scopes) { this.scopes = scopes; } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentrySpanProcessor.java similarity index 97% rename from sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java rename to sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentrySpanProcessor.java index 9ee3f8e854..8dd4bd160a 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentrySpanProcessor.java @@ -23,15 +23,15 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public final class PotelSentrySpanProcessor implements SpanProcessor { +public final class OtelSentrySpanProcessor implements SpanProcessor { private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); private final @NotNull IScopes scopes; - public PotelSentrySpanProcessor() { + public OtelSentrySpanProcessor() { this(ScopesAdapter.getInstance()); } - PotelSentrySpanProcessor(final @NotNull IScopes scopes) { + OtelSentrySpanProcessor(final @NotNull IScopes scopes) { this.scopes = scopes; } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java index da411c3126..ffcab9ed54 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java @@ -24,7 +24,7 @@ import org.jetbrains.annotations.Nullable; /** - * @deprecated please use {@link PotelSentryPropagator} instead + * @deprecated please use {@link OtelSentryPropagator} instead */ @Deprecated public final class SentryPropagator implements TextMapPropagator { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySamplingResult.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySamplingResult.java index c8049f3067..69acf52134 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySamplingResult.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySamplingResult.java @@ -4,8 +4,10 @@ import io.opentelemetry.sdk.trace.samplers.SamplingDecision; import io.opentelemetry.sdk.trace.samplers.SamplingResult; import io.sentry.TracesSamplingDecision; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +@ApiStatus.Internal public final class SentrySamplingResult implements SamplingResult { private final TracesSamplingDecision sentryDecision; diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index 2d82bd23b7..d3b1cd087c 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -64,7 +64,7 @@ public final class SentrySpanExporter implements SpanExporter { InternalSemanticAttributes.PARENT_SAMPLED.getKey()); private static final @NotNull Long SPAN_TIMEOUT = DateUtils.secondsToNanos(5 * 60); - public static final String TRACE_ORIGIN = "auto.potel"; + public static final String TRACE_ORIGIN = "auto.otel"; public SentrySpanExporter() { this(ScopesAdapter.getInstance()); diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java index 4f91d29427..f09c082d72 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java @@ -40,7 +40,7 @@ import org.jetbrains.annotations.Nullable; /** - * @deprecated please use {@link PotelSentrySpanProcessor} instead. + * @deprecated please use {@link OtelSentrySpanProcessor} instead. */ @Deprecated public final class SentrySpanProcessor implements SpanProcessor { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanNode.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanNode.java index 3f95e50b2b..e74747d8a1 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanNode.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanNode.java @@ -3,9 +3,11 @@ import io.opentelemetry.sdk.trace.data.SpanData; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +@ApiStatus.Internal public final class SpanNode { private final @NotNull String id; private @Nullable SpanData span; diff --git a/sentry/src/main/java/io/sentry/DefaultScopesStorage.java b/sentry/src/main/java/io/sentry/DefaultScopesStorage.java index 1ed80ceea8..6884370c9f 100644 --- a/sentry/src/main/java/io/sentry/DefaultScopesStorage.java +++ b/sentry/src/main/java/io/sentry/DefaultScopesStorage.java @@ -1,8 +1,10 @@ package io.sentry; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +@ApiStatus.Internal public final class DefaultScopesStorage implements IScopesStorage { private static final @NotNull ThreadLocal currentScopes = new ThreadLocal<>(); diff --git a/sentry/src/main/java/io/sentry/IScopesStorage.java b/sentry/src/main/java/io/sentry/IScopesStorage.java index d067d6bafd..394510a528 100644 --- a/sentry/src/main/java/io/sentry/IScopesStorage.java +++ b/sentry/src/main/java/io/sentry/IScopesStorage.java @@ -1,8 +1,10 @@ package io.sentry; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +@ApiStatus.Internal public interface IScopesStorage { @NotNull diff --git a/sentry/src/main/java/io/sentry/ScopesStorageFactory.java b/sentry/src/main/java/io/sentry/ScopesStorageFactory.java index abaf557f87..ab136b2ac9 100644 --- a/sentry/src/main/java/io/sentry/ScopesStorageFactory.java +++ b/sentry/src/main/java/io/sentry/ScopesStorageFactory.java @@ -2,9 +2,11 @@ import io.sentry.util.LoadClass; import java.lang.reflect.InvocationTargetException; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +@ApiStatus.Internal public final class ScopesStorageFactory { private static final String OTEL_SCOPES_STORAGE = diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index d46eb8771c..b8e54952c8 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -2727,11 +2727,13 @@ private void addPackageInfo() { .addPackage("maven:io.sentry:sentry", BuildConfig.VERSION_NAME); } + @ApiStatus.Internal public @NotNull ISpanFactory getSpanFactory() { // TODO [POTEL] use a util for checking if OTel is active or similar return spanFactory; } + @ApiStatus.Internal public void setSpanFactory(final @NotNull ISpanFactory spanFactory) { this.spanFactory = spanFactory; } diff --git a/sentry/src/main/java/io/sentry/SentrySpanFactoryHolder.java b/sentry/src/main/java/io/sentry/SentrySpanFactoryHolder.java index cde9bc2a4f..19f79d7e50 100644 --- a/sentry/src/main/java/io/sentry/SentrySpanFactoryHolder.java +++ b/sentry/src/main/java/io/sentry/SentrySpanFactoryHolder.java @@ -14,6 +14,7 @@ * into the bootstrap classloader, then this hack should no longer be necessary. */ @ApiStatus.Experimental +@ApiStatus.Internal public final class SentrySpanFactoryHolder { private static ISpanFactory spanFactory = new DefaultSpanFactory(); From 98cd975f5e3797e6eb7c0b39d377176d46f5a6df Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Wed, 26 Jun 2024 11:06:50 +0200 Subject: [PATCH 83/91] POTEL 28 - Use `auto.opentelemetry` for POTel span origin (#3523) --- .../main/java/io/sentry/opentelemetry/SentrySpanExporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index d3b1cd087c..5e7c610cc0 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -64,7 +64,7 @@ public final class SentrySpanExporter implements SpanExporter { InternalSemanticAttributes.PARENT_SAMPLED.getKey()); private static final @NotNull Long SPAN_TIMEOUT = DateUtils.secondsToNanos(5 * 60); - public static final String TRACE_ORIGIN = "auto.otel"; + public static final String TRACE_ORIGIN = "auto.opentelemetry"; public SentrySpanExporter() { this(ScopesAdapter.getInstance()); From 783e11254cbb122b2dfd7986eb4cc41ea4e64d90 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Wed, 26 Jun 2024 15:46:52 +0200 Subject: [PATCH 84/91] Remove sentry-native submodule again; ignore failing test (#3525) * Change POTel span origin * remove sentry-native again * ignore test * ignore another test --- .../androidTest/java/io/sentry/uitest/android/SdkInitTests.kt | 3 +++ sentry-android-ndk/sentry-native | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) delete mode 160000 sentry-android-ndk/sentry-native diff --git a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt index b615406a3d..a40bc301a1 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt +++ b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt @@ -10,6 +10,7 @@ import io.sentry.android.core.SentryAndroidOptions import io.sentry.assertEnvelopeTransaction import io.sentry.protocol.SentryTransaction import org.junit.runner.RunWith +import kotlin.test.Ignore import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -35,6 +36,7 @@ class SdkInitTests : BaseUiTest() { transaction2.finish() } + @Ignore("TODO [POTEL] reinit should be discussed with mobile team") @Test fun doubleInitWithSameOptionsDoesNotThrow() { val options = SentryAndroidOptions() @@ -93,6 +95,7 @@ class SdkInitTests : BaseUiTest() { } } + @Ignore("TODO [POTEL] reinit should be discussed with mobile team") @Test fun doubleInitDoesNotWait() { relayIdlingResource.increment() diff --git a/sentry-android-ndk/sentry-native b/sentry-android-ndk/sentry-native deleted file mode 160000 index 4ec95c0725..0000000000 --- a/sentry-android-ndk/sentry-native +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4ec95c0725df5f34440db8fa8d37b4c519fce74e From 8268911044ed030a7c7b64c561d88a85b85ae776 Mon Sep 17 00:00:00 2001 From: getsentry-bot Date: Wed, 26 Jun 2024 13:47:34 +0000 Subject: [PATCH 85/91] release: 8.0.0-alpha.2 --- CHANGELOG.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de32df6a2e..9051f46587 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 8.0.0-alpha.2 ### Behavioural Changes diff --git a/gradle.properties b/gradle.properties index a4760b08dd..0a136e76c0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ android.useAndroidX=true android.defaults.buildfeatures.buildconfig=true # Release information -versionName=8.0.0-alpha.1 +versionName=8.0.0-alpha.2 # Override the SDK name on native crashes on Android sentryAndroidSdkName=sentry.native.android From 935bb1de14df61731836349d07c77072fca4ab59 Mon Sep 17 00:00:00 2001 From: Stefano Date: Mon, 1 Jul 2024 11:05:50 +0200 Subject: [PATCH 86/91] Removed sentry-android-okhttp module (#3510) * removed sentry-android-okhttp module --- .craft.yml | 1 - .github/ISSUE_TEMPLATE/bug_report_android.yml | 4 +- .github/workflows/system-tests-backend.yml | 2 +- CHANGELOG.md | 6 + README.md | 1 - build.gradle.kts | 2 - .../api/sentry-android-okhttp.api | 62 ------ sentry-android-okhttp/build.gradle.kts | 83 -------- sentry-android-okhttp/proguard-rules.pro | 13 -- .../okhttp/SentryOkHttpEventListener.kt | 201 ------------------ .../android/okhttp/SentryOkHttpInterceptor.kt | 79 ------- .../src/main/res/values/public.xml | 4 - .../sentry-samples-android/build.gradle.kts | 2 +- settings.gradle.kts | 1 - 14 files changed, 10 insertions(+), 451 deletions(-) delete mode 100644 sentry-android-okhttp/api/sentry-android-okhttp.api delete mode 100644 sentry-android-okhttp/build.gradle.kts delete mode 100644 sentry-android-okhttp/proguard-rules.pro delete mode 100644 sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpEventListener.kt delete mode 100644 sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpInterceptor.kt delete mode 100644 sentry-android-okhttp/src/main/res/values/public.xml diff --git a/.craft.yml b/.craft.yml index d50705f433..c08a321343 100644 --- a/.craft.yml +++ b/.craft.yml @@ -37,7 +37,6 @@ targets: maven:io.sentry:sentry-android-core: maven:io.sentry:sentry-android-ndk: maven:io.sentry:sentry-android-timber: - maven:io.sentry:sentry-android-okhttp: maven:io.sentry:sentry-kotlin-extensions: maven:io.sentry:sentry-android-fragment: maven:io.sentry:sentry-bom: diff --git a/.github/ISSUE_TEMPLATE/bug_report_android.yml b/.github/ISSUE_TEMPLATE/bug_report_android.yml index 9b6bfc9ff6..20db87e363 100644 --- a/.github/ISSUE_TEMPLATE/bug_report_android.yml +++ b/.github/ISSUE_TEMPLATE/bug_report_android.yml @@ -10,13 +10,13 @@ body: options: - sentry-android - sentry-android-ndk - - sentry-android-okhttp - sentry-android-timber - sentry-android-fragment - sentry-android-sqlite - sentry-apollo - - sentry-compose - sentry-apollo-3 + - sentry-compose + - sentry-okhttp - other validations: required: true diff --git a/.github/workflows/system-tests-backend.yml b/.github/workflows/system-tests-backend.yml index a916d8f9ac..6584228d51 100644 --- a/.github/workflows/system-tests-backend.yml +++ b/.github/workflows/system-tests-backend.yml @@ -46,7 +46,7 @@ jobs: - name: Exclude android modules from build run: | - sed -i -e '/.*"sentry-android-ndk",/d' -e '/.*"sentry-android",/d' -e '/.*"sentry-compose",/d' -e '/.*"sentry-android-core",/d' -e '/.*"sentry-android-fragment",/d' -e '/.*"sentry-android-navigation",/d' -e '/.*"sentry-android-okhttp",/d' -e '/.*"sentry-android-sqlite",/d' -e '/.*"sentry-android-timber",/d' -e '/.*"sentry-android-integration-tests:sentry-uitest-android-benchmark",/d' -e '/.*"sentry-android-integration-tests:sentry-uitest-android",/d' -e '/.*"sentry-android-integration-tests:test-app-sentry",/d' -e '/.*"sentry-samples:sentry-samples-android",/d' settings.gradle.kts + sed -i -e '/.*"sentry-android-ndk",/d' -e '/.*"sentry-android",/d' -e '/.*"sentry-compose",/d' -e '/.*"sentry-android-core",/d' -e '/.*"sentry-android-fragment",/d' -e '/.*"sentry-android-navigation",/d' -e '/.*"sentry-android-sqlite",/d' -e '/.*"sentry-android-timber",/d' -e '/.*"sentry-android-integration-tests:sentry-uitest-android-benchmark",/d' -e '/.*"sentry-android-integration-tests:sentry-uitest-android",/d' -e '/.*"sentry-android-integration-tests:test-app-sentry",/d' -e '/.*"sentry-samples:sentry-samples-android",/d' settings.gradle.kts - name: Exclude android modules from ignore list run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 9051f46587..59b6d78694 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Breaking Changes + +- `sentry-android-okhttp` has been removed in favor of `sentry-okhttp`, removing android dependency from the module ([#3510](https://github.com/getsentry/sentry-java/pull/3510)) + ## 8.0.0-alpha.2 ### Behavioural Changes diff --git a/README.md b/README.md index 338a59eba5..d6107cd267 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,6 @@ Sentry SDK for Java and Android | sentry-android | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android) | 19 | | sentry-android-core | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-core) | 19 | | sentry-android-ndk | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-ndk/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-ndk) | 19 | -| sentry-android-okhttp | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-okhttp/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-okhttp) | 21 | | sentry-android-timber | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-timber/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-timber) | 19 | | sentry-android-fragment | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-fragment/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-fragment) | 19 | | sentry-android-navigation | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-navigation/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-navigation) | 19 | diff --git a/build.gradle.kts b/build.gradle.kts index 03b5723da0..4b84c17aba 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -107,7 +107,6 @@ subprojects { "sentry-android-fragment", "sentry-android-navigation", "sentry-android-ndk", - "sentry-android-okhttp", "sentry-android-sqlite", "sentry-android-timber" ) @@ -291,7 +290,6 @@ private val androidLibs = setOf( "sentry-android-ndk", "sentry-android-fragment", "sentry-android-navigation", - "sentry-android-okhttp", "sentry-android-timber", "sentry-compose-android" ) diff --git a/sentry-android-okhttp/api/sentry-android-okhttp.api b/sentry-android-okhttp/api/sentry-android-okhttp.api deleted file mode 100644 index 35f42842dd..0000000000 --- a/sentry-android-okhttp/api/sentry-android-okhttp.api +++ /dev/null @@ -1,62 +0,0 @@ -public final class io/sentry/android/okhttp/BuildConfig { - public static final field BUILD_TYPE Ljava/lang/String; - public static final field DEBUG Z - public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String; - public static final field VERSION_NAME Ljava/lang/String; - public fun ()V -} - -public final class io/sentry/android/okhttp/SentryOkHttpEventListener : okhttp3/EventListener { - public fun ()V - public fun (Lio/sentry/IScopes;Lkotlin/jvm/functions/Function1;)V - public synthetic fun (Lio/sentry/IScopes;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Lio/sentry/IScopes;Lokhttp3/EventListener$Factory;)V - public synthetic fun (Lio/sentry/IScopes;Lokhttp3/EventListener$Factory;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Lio/sentry/IScopes;Lokhttp3/EventListener;)V - public synthetic fun (Lio/sentry/IScopes;Lokhttp3/EventListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Lokhttp3/EventListener$Factory;)V - public fun (Lokhttp3/EventListener;)V - public fun cacheConditionalHit (Lokhttp3/Call;Lokhttp3/Response;)V - public fun cacheHit (Lokhttp3/Call;Lokhttp3/Response;)V - public fun cacheMiss (Lokhttp3/Call;)V - public fun callEnd (Lokhttp3/Call;)V - public fun callFailed (Lokhttp3/Call;Ljava/io/IOException;)V - public fun callStart (Lokhttp3/Call;)V - public fun canceled (Lokhttp3/Call;)V - public fun connectEnd (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;Lokhttp3/Protocol;)V - public fun connectFailed (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;Lokhttp3/Protocol;Ljava/io/IOException;)V - public fun connectStart (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;)V - public fun connectionAcquired (Lokhttp3/Call;Lokhttp3/Connection;)V - public fun connectionReleased (Lokhttp3/Call;Lokhttp3/Connection;)V - public fun dnsEnd (Lokhttp3/Call;Ljava/lang/String;Ljava/util/List;)V - public fun dnsStart (Lokhttp3/Call;Ljava/lang/String;)V - public fun proxySelectEnd (Lokhttp3/Call;Lokhttp3/HttpUrl;Ljava/util/List;)V - public fun proxySelectStart (Lokhttp3/Call;Lokhttp3/HttpUrl;)V - public fun requestBodyEnd (Lokhttp3/Call;J)V - public fun requestBodyStart (Lokhttp3/Call;)V - public fun requestFailed (Lokhttp3/Call;Ljava/io/IOException;)V - public fun requestHeadersEnd (Lokhttp3/Call;Lokhttp3/Request;)V - public fun requestHeadersStart (Lokhttp3/Call;)V - public fun responseBodyEnd (Lokhttp3/Call;J)V - public fun responseBodyStart (Lokhttp3/Call;)V - public fun responseFailed (Lokhttp3/Call;Ljava/io/IOException;)V - public fun responseHeadersEnd (Lokhttp3/Call;Lokhttp3/Response;)V - public fun responseHeadersStart (Lokhttp3/Call;)V - public fun satisfactionFailure (Lokhttp3/Call;Lokhttp3/Response;)V - public fun secureConnectEnd (Lokhttp3/Call;Lokhttp3/Handshake;)V - public fun secureConnectStart (Lokhttp3/Call;)V -} - -public final class io/sentry/android/okhttp/SentryOkHttpInterceptor : okhttp3/Interceptor { - public fun ()V - public fun (Lio/sentry/IScopes;)V - public fun (Lio/sentry/IScopes;Lio/sentry/android/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;ZLjava/util/List;Ljava/util/List;)V - public synthetic fun (Lio/sentry/IScopes;Lio/sentry/android/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;ZLjava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Lio/sentry/android/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;)V - public fun intercept (Lokhttp3/Interceptor$Chain;)Lokhttp3/Response; -} - -public abstract interface class io/sentry/android/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback { - public abstract fun execute (Lio/sentry/ISpan;Lokhttp3/Request;Lokhttp3/Response;)Lio/sentry/ISpan; -} - diff --git a/sentry-android-okhttp/build.gradle.kts b/sentry-android-okhttp/build.gradle.kts deleted file mode 100644 index f3eaa59303..0000000000 --- a/sentry-android-okhttp/build.gradle.kts +++ /dev/null @@ -1,83 +0,0 @@ -import io.gitlab.arturbosch.detekt.Detekt -import org.jetbrains.kotlin.config.KotlinCompilerVersion - -plugins { - id("com.android.library") - kotlin("android") - jacoco - id(Config.QualityPlugins.jacocoAndroid) - id(Config.QualityPlugins.gradleVersions) - id(Config.QualityPlugins.detektPlugin) -} - -android { - compileSdk = Config.Android.compileSdkVersion - namespace = "io.sentry.android.okhttp" - - defaultConfig { - targetSdk = Config.Android.targetSdkVersion - minSdk = Config.Android.minSdkVersionOkHttp - - // for AGP 4.1 - buildConfigField("String", "VERSION_NAME", "\"${project.version}\"") - } - - buildTypes { - getByName("debug") - getByName("release") - } - - kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8.toString() - kotlinOptions.languageVersion = Config.kotlinCompatibleLanguageVersion - } - - testOptions { - animationsDisabled = true - unitTests.apply { - isReturnDefaultValues = true - isIncludeAndroidResources = true - } - } - - lint { - warningsAsErrors = true - checkDependencies = true - - // We run a full lint analysis as build part in CI, so skip vital checks for assemble tasks. - checkReleaseBuilds = false - } - - variantFilter { - if (Config.Android.shouldSkipDebugVariant(buildType.name)) { - ignore = true - } - } -} - -kotlin { - explicitApi() -} - -dependencies { - api(projects.sentry) - api(projects.sentryOkhttp) - - compileOnly(Config.Libs.okhttp) - - implementation(kotlin(Config.kotlinStdLib, KotlinCompilerVersion.VERSION)) - - // tests - testImplementation(projects.sentryTestSupport) - testImplementation(Config.Libs.okhttp) - testImplementation(Config.TestLibs.kotlinTestJunit) - testImplementation(Config.TestLibs.androidxJunit) - testImplementation(Config.TestLibs.mockitoKotlin) - testImplementation(Config.TestLibs.mockitoInline) - testImplementation(Config.TestLibs.mockWebserver) -} - -tasks.withType { - // Target version of the generated JVM bytecode. It is used for type resolution. - jvmTarget = JavaVersion.VERSION_1_8.toString() -} diff --git a/sentry-android-okhttp/proguard-rules.pro b/sentry-android-okhttp/proguard-rules.pro deleted file mode 100644 index 3f9ea4feb2..0000000000 --- a/sentry-android-okhttp/proguard-rules.pro +++ /dev/null @@ -1,13 +0,0 @@ -##---------------Begin: proguard configuration for OkHttp ---------- - -# To ensure that stack traces is unambiguous -# https://developer.android.com/studio/build/shrink-code#decode-stack-trace --keepattributes LineNumberTable,SourceFile - -# https://square.github.io/okhttp/features/r8_proguard/ -# If you use OkHttp as a dependency in an Android project which uses R8 as a default compiler you -# don’t have to do anything. The specific rules are already bundled into the JAR which can -# be interpreted by R8 automatically. -# https://raw.githubusercontent.com/square/okhttp/master/okhttp/src/jvmMain/resources/META-INF/proguard/okhttp3.pro - -##---------------End: proguard configuration for OkHttp ---------- diff --git a/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpEventListener.kt b/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpEventListener.kt deleted file mode 100644 index f99106e8d9..0000000000 --- a/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpEventListener.kt +++ /dev/null @@ -1,201 +0,0 @@ -package io.sentry.android.okhttp - -import io.sentry.IScopes -import io.sentry.ScopesAdapter -import okhttp3.Call -import okhttp3.Connection -import okhttp3.EventListener -import okhttp3.Handshake -import okhttp3.HttpUrl -import okhttp3.Protocol -import okhttp3.Request -import okhttp3.Response -import java.io.IOException -import java.net.InetAddress -import java.net.InetSocketAddress -import java.net.Proxy - -/** - * Logs network performance event metrics to Sentry - * - * Usage - add instance of [SentryOkHttpEventListener] in [okhttp3.OkHttpClient.Builder.eventListener] - * - * ``` - * val client = OkHttpClient.Builder() - * .eventListener(SentryOkHttpEventListener()) - * .addInterceptor(SentryOkHttpInterceptor()) - * .build() - * ``` - * - * If you already use a [okhttp3.EventListener], you can pass it in the constructor. - * - * ``` - * val client = OkHttpClient.Builder() - * .eventListener(SentryOkHttpEventListener(myEventListener)) - * .addInterceptor(SentryOkHttpInterceptor()) - * .build() - * ``` - */ -@Deprecated( - "Use SentryOkHttpEventListener from sentry-okhttp instead", - ReplaceWith("SentryOkHttpEventListener", "io.sentry.okhttp.SentryOkHttpEventListener") -) -@Suppress("TooManyFunctions") -class SentryOkHttpEventListener( - scopes: IScopes = ScopesAdapter.getInstance(), - originalEventListenerCreator: ((call: Call) -> EventListener)? = null -) : EventListener() { - constructor() : this( - ScopesAdapter.getInstance(), - originalEventListenerCreator = null - ) - - constructor(originalEventListener: EventListener) : this( - ScopesAdapter.getInstance(), - originalEventListenerCreator = { originalEventListener } - ) - - constructor(originalEventListenerFactory: Factory) : this( - ScopesAdapter.getInstance(), - originalEventListenerCreator = { originalEventListenerFactory.create(it) } - ) - - constructor(scopes: IScopes = ScopesAdapter.getInstance(), originalEventListener: EventListener) : this( - scopes, - originalEventListenerCreator = { originalEventListener } - ) - - constructor(scopes: IScopes = ScopesAdapter.getInstance(), originalEventListenerFactory: Factory) : this( - scopes, - originalEventListenerCreator = { originalEventListenerFactory.create(it) } - ) - - private val delegate = io.sentry.okhttp.SentryOkHttpEventListener(scopes, originalEventListenerCreator) - - override fun cacheConditionalHit(call: Call, cachedResponse: Response) { - delegate.cacheConditionalHit(call, cachedResponse) - } - - override fun cacheHit(call: Call, response: Response) { - delegate.cacheHit(call, response) - } - - override fun cacheMiss(call: Call) { - delegate.cacheMiss(call) - } - - override fun callEnd(call: Call) { - delegate.callEnd(call) - } - - override fun callFailed(call: Call, ioe: IOException) { - delegate.callFailed(call, ioe) - } - - override fun callStart(call: Call) { - delegate.callStart(call) - } - - override fun canceled(call: Call) { - delegate.canceled(call) - } - - override fun connectEnd( - call: Call, - inetSocketAddress: InetSocketAddress, - proxy: Proxy, - protocol: Protocol? - ) { - delegate.connectEnd(call, inetSocketAddress, proxy, protocol) - } - - override fun connectFailed( - call: Call, - inetSocketAddress: InetSocketAddress, - proxy: Proxy, - protocol: Protocol?, - ioe: IOException - ) { - delegate.connectFailed(call, inetSocketAddress, proxy, protocol, ioe) - } - - override fun connectStart(call: Call, inetSocketAddress: InetSocketAddress, proxy: Proxy) { - delegate.connectStart(call, inetSocketAddress, proxy) - } - - override fun connectionAcquired(call: Call, connection: Connection) { - delegate.connectionAcquired(call, connection) - } - - override fun connectionReleased(call: Call, connection: Connection) { - delegate.connectionReleased(call, connection) - } - - override fun dnsEnd(call: Call, domainName: String, inetAddressList: List) { - delegate.dnsEnd(call, domainName, inetAddressList) - } - - override fun dnsStart(call: Call, domainName: String) { - delegate.dnsStart(call, domainName) - } - - override fun proxySelectEnd(call: Call, url: HttpUrl, proxies: List) { - delegate.proxySelectEnd(call, url, proxies) - } - - override fun proxySelectStart(call: Call, url: HttpUrl) { - delegate.proxySelectStart(call, url) - } - - override fun requestBodyEnd(call: Call, byteCount: Long) { - delegate.requestBodyEnd(call, byteCount) - } - - override fun requestBodyStart(call: Call) { - delegate.requestBodyStart(call) - } - - override fun requestFailed(call: Call, ioe: IOException) { - delegate.requestFailed(call, ioe) - } - - override fun requestHeadersEnd(call: Call, request: Request) { - delegate.requestHeadersEnd(call, request) - } - - override fun requestHeadersStart(call: Call) { - delegate.requestHeadersStart(call) - } - - override fun responseBodyEnd(call: Call, byteCount: Long) { - delegate.responseBodyEnd(call, byteCount) - } - - override fun responseBodyStart(call: Call) { - delegate.responseBodyStart(call) - } - - override fun responseFailed(call: Call, ioe: IOException) { - delegate.responseFailed(call, ioe) - } - - override fun responseHeadersEnd(call: Call, response: Response) { - delegate.responseHeadersEnd(call, response) - } - - override fun responseHeadersStart(call: Call) { - delegate.responseHeadersStart(call) - } - - override fun satisfactionFailure(call: Call, response: Response) { - delegate.satisfactionFailure(call, response) - } - - override fun secureConnectEnd(call: Call, handshake: Handshake?) { - delegate.secureConnectEnd(call, handshake) - } - - override fun secureConnectStart(call: Call) { - delegate.secureConnectStart(call) - } -} diff --git a/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpInterceptor.kt b/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpInterceptor.kt deleted file mode 100644 index 3925a83199..0000000000 --- a/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpInterceptor.kt +++ /dev/null @@ -1,79 +0,0 @@ -package io.sentry.android.okhttp - -import io.sentry.HttpStatusCodeRange -import io.sentry.IScopes -import io.sentry.ISpan -import io.sentry.ScopesAdapter -import io.sentry.SentryIntegrationPackageStorage -import io.sentry.SentryOptions.DEFAULT_PROPAGATION_TARGETS -import io.sentry.android.okhttp.SentryOkHttpInterceptor.BeforeSpanCallback -import io.sentry.util.IntegrationUtils.addIntegrationToSdkVersion -import okhttp3.Interceptor -import okhttp3.Request -import okhttp3.Response - -/** - * The Sentry's [SentryOkHttpInterceptor], it will automatically add a breadcrumb and start a span - * out of the active span bound to the scope for each HTTP Request. - * If [captureFailedRequests] is enabled, the SDK will capture HTTP Client errors as well. - * - * @param scopes The [IScopes], internal and only used for testing. - * @param beforeSpan The [ISpan] can be customized or dropped with the [BeforeSpanCallback]. - * @param captureFailedRequests The SDK will only capture HTTP Client errors if it is enabled, - * Defaults to true. - * @param failedRequestStatusCodes The SDK will only capture HTTP Client errors if the HTTP Response - * status code is within the defined ranges. - * @param failedRequestTargets The SDK will only capture HTTP Client errors if the HTTP Request URL - * is a match for any of the defined targets. - */ -@Deprecated( - "Use SentryOkHttpInterceptor from sentry-okhttp instead", - ReplaceWith("SentryOkHttpInterceptor", "io.sentry.okhttp.SentryOkHttpInterceptor") -) -class SentryOkHttpInterceptor( - private val scopes: IScopes = ScopesAdapter.getInstance(), - private val beforeSpan: BeforeSpanCallback? = null, - private val captureFailedRequests: Boolean = true, - private val failedRequestStatusCodes: List = listOf( - HttpStatusCodeRange(HttpStatusCodeRange.DEFAULT_MIN, HttpStatusCodeRange.DEFAULT_MAX) - ), - private val failedRequestTargets: List = listOf(DEFAULT_PROPAGATION_TARGETS) -) : Interceptor by io.sentry.okhttp.SentryOkHttpInterceptor( - scopes, - { span, request, response -> - beforeSpan ?: return@SentryOkHttpInterceptor span - beforeSpan.execute(span, request, response) - }, - captureFailedRequests, - failedRequestStatusCodes, - failedRequestTargets -) { - - constructor() : this(ScopesAdapter.getInstance()) - constructor(scopes: IScopes) : this(scopes, null) - constructor(beforeSpan: BeforeSpanCallback) : this(ScopesAdapter.getInstance(), beforeSpan) - - init { - addIntegrationToSdkVersion(javaClass) - SentryIntegrationPackageStorage.getInstance() - .addPackage("maven:io.sentry:sentry-android-okhttp", BuildConfig.VERSION_NAME) - } - - /** - * The BeforeSpan callback - */ - @Deprecated( - "Use BeforeSpanCallback from sentry-okhttp instead", - ReplaceWith("BeforeSpanCallback", "io.sentry.okhttp.SentryOkHttpInterceptor.BeforeSpanCallback") - ) - fun interface BeforeSpanCallback { - /** - * Mutates or drops span before being added - * - * @param span the span to mutate or drop - * @param request the HTTP request executed by okHttp - * @param response the HTTP response received by okHttp - */ - fun execute(span: ISpan, request: Request, response: Response?): ISpan? - } -} diff --git a/sentry-android-okhttp/src/main/res/values/public.xml b/sentry-android-okhttp/src/main/res/values/public.xml deleted file mode 100644 index 379be515be..0000000000 --- a/sentry-android-okhttp/src/main/res/values/public.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/sentry-samples/sentry-samples-android/build.gradle.kts b/sentry-samples/sentry-samples-android/build.gradle.kts index 871f46ce09..e86dab253d 100644 --- a/sentry-samples/sentry-samples-android/build.gradle.kts +++ b/sentry-samples/sentry-samples-android/build.gradle.kts @@ -100,11 +100,11 @@ dependencies { implementation(kotlin(Config.kotlinStdLib, org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION)) implementation(projects.sentryAndroid) - implementation(projects.sentryAndroidOkhttp) implementation(projects.sentryAndroidFragment) implementation(projects.sentryAndroidTimber) implementation(projects.sentryCompose) implementation(projects.sentryComposeHelper) + implementation(projects.sentryOkhttp) implementation(Config.Libs.fragment) implementation(Config.Libs.timber) diff --git a/settings.gradle.kts b/settings.gradle.kts index 1f7d5c2226..822c8e9db0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,7 +17,6 @@ include( "sentry-android-ndk", "sentry-android", "sentry-android-timber", - "sentry-android-okhttp", "sentry-android-fragment", "sentry-android-navigation", "sentry-android-sqlite", From cee271cc13178307efbd26f30bac0c7273fc800b Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Mon, 1 Jul 2024 17:12:18 +0000 Subject: [PATCH 87/91] Format code --- .../io/sentry/android/core/InternalSentrySdk.java | 2 +- .../java/io/sentry/android/core/SentryAndroid.java | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java index f0e07cec3e..b93a81fb32 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java @@ -157,7 +157,7 @@ public static Map serializeScope( */ @Nullable public static SentryId captureEnvelope( - final @NotNull byte[] envelopeData, final boolean maybeStartNewSession) { + final @NotNull byte[] envelopeData, final boolean maybeStartNewSession) { final @NotNull IScopes scopes = ScopesAdapter.getInstance(); final @NotNull SentryOptions options = scopes.getOptions(); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java index 47cd6d9cfb..01b15c0286 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java @@ -155,12 +155,12 @@ public static synchronized void init( // This e.g. happens on React Native, or e.g. on deferred SDK init final AtomicBoolean sessionStarted = new AtomicBoolean(false); hub.configureScope( - scope -> { - final @Nullable Session currentSession = scope.getSession(); - if (currentSession != null && currentSession.getStarted() != null) { - sessionStarted.set(true); - } - }); + scope -> { + final @Nullable Session currentSession = scope.getSession(); + if (currentSession != null && currentSession.getStarted() != null) { + sessionStarted.set(true); + } + }); if (!sessionStarted.get()) { scopes.addBreadcrumb(BreadcrumbFactory.forSession("session.start")); scopes.startSession(); From 437936e44da83698636fdfa1b36b0ec72f7884c1 Mon Sep 17 00:00:00 2001 From: Stefano Date: Tue, 2 Jul 2024 10:01:27 +0200 Subject: [PATCH 88/91] Fix main merge (#3537) * fixed merge conflicts --- CHANGELOG.md | 50 ++++++------- .../android/core/InternalSentrySdk.java | 2 +- .../io/sentry/android/core/SentryAndroid.java | 6 +- .../sentry/android/core/SentryAndroidTest.kt | 1 + .../okhttp/SentryOkHttpEventListenerTest.kt | 70 ------------------- .../io/sentry/okhttp/SentryOkHttpEvent.kt | 2 +- sentry/api/sentry.api | 8 +++ .../java/io/sentry/CombinedScopeView.java | 5 ++ sentry/src/main/java/io/sentry/Scopes.java | 4 +- ...UncaughtExceptionHandlerIntegrationTest.kt | 8 +-- .../sentry/clientreport/ClientReportTest.kt | 6 +- 11 files changed, 53 insertions(+), 109 deletions(-) delete mode 100644 sentry-android-okhttp/src/test/java/io/sentry/android/okhttp/SentryOkHttpEventListenerTest.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index be8a1c26ac..a179e82555 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,31 +11,31 @@ ### Behavioural Changes - (Android) The JNI layer for sentry-native has now been moved from sentry-java to sentry-native ([#3189](https://github.com/getsentry/sentry-java/pull/3189)) - - This now includes prefab support for sentry-native, allowing you to link and access the sentry-native API within your native app code - - Checkout the `sentry-samples/sentry-samples-android` example on how to configure CMake and consume `sentry.h` + - This now includes prefab support for sentry-native, allowing you to link and access the sentry-native API within your native app code + - Checkout the `sentry-samples/sentry-samples-android` example on how to configure CMake and consume `sentry.h` ### Features - Our `sentry-opentelemetry-agent` has been completely reworked and now plays nicely with the rest of the Java SDK - - You may also want to give this new agent a try even if you haven't used OpenTelemetry (with Sentry) before. It offers support for [many more libraries and frameworks](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md), improving on our trace propagation, `Scopes` (used to be `Hub`) propagation as well as performance instrumentation (i.e. more spans). - - If you are using a framework we did not support before and currently resort to manual instrumentation, please give the agent a try. See [here for a list of supported libraries, frameworks and application servers](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md). - - NOTE: Not all features have been implemented yet for the OpenTelemetry agent. Features of note that are not working yet: - - Metrics - - Measurements - - `forceFinish` on transaction - - `scheduleFinish` on transaction - - see [#3436](https://github.com/getsentry/sentry-java/issues/3436) for a more up-to-date list of features we have (not) implemented - - Please see "Installing `sentry-opentelemetry-agent`" for more details on how to set up the agent. - - What's new about the Agent - - When the OpenTelemetry Agent is used, Sentry API creates OpenTelemetry spans under the hood, handing back a wrapper object which bridges the gap between traditional Sentry API and OpenTelemetry. We might be replacing some of the Sentry performance API in the future. - - This is achieved by configuring the SDK to use `OtelSpanFactory` instead of `DefaultSpanFactory` which is done automatically by the auto init of the Java Agent. - - OpenTelemetry spans are now only turned into Sentry spans when they are finished so they can be sent to the Sentry server. - - Now registers an OpenTelemetry `Sampler` which uses Sentry sampling configuration - - Other Performance integrations automatically stop creating spans to avoid duplicate spans - - The Sentry SDK now makes use of OpenTelemetry `Context` for storing Sentry `Scopes` (which is similar to what used to be called `Hub`) and thus relies on OpenTelemetry for `Context` propagation. - - Classes used for the previous version of our OpenTelemetry support have been deprecated but can still be used manually. We're not planning to keep the old agent around in favor of less complexity in the SDK. + - You may also want to give this new agent a try even if you haven't used OpenTelemetry (with Sentry) before. It offers support for [many more libraries and frameworks](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md), improving on our trace propagation, `Scopes` (used to be `Hub`) propagation as well as performance instrumentation (i.e. more spans). + - If you are using a framework we did not support before and currently resort to manual instrumentation, please give the agent a try. See [here for a list of supported libraries, frameworks and application servers](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md). + - NOTE: Not all features have been implemented yet for the OpenTelemetry agent. Features of note that are not working yet: + - Metrics + - Measurements + - `forceFinish` on transaction + - `scheduleFinish` on transaction + - see [#3436](https://github.com/getsentry/sentry-java/issues/3436) for a more up-to-date list of features we have (not) implemented + - Please see "Installing `sentry-opentelemetry-agent`" for more details on how to set up the agent. + - What's new about the Agent + - When the OpenTelemetry Agent is used, Sentry API creates OpenTelemetry spans under the hood, handing back a wrapper object which bridges the gap between traditional Sentry API and OpenTelemetry. We might be replacing some of the Sentry performance API in the future. + - This is achieved by configuring the SDK to use `OtelSpanFactory` instead of `DefaultSpanFactory` which is done automatically by the auto init of the Java Agent. + - OpenTelemetry spans are now only turned into Sentry spans when they are finished so they can be sent to the Sentry server. + - Now registers an OpenTelemetry `Sampler` which uses Sentry sampling configuration + - Other Performance integrations automatically stop creating spans to avoid duplicate spans + - The Sentry SDK now makes use of OpenTelemetry `Context` for storing Sentry `Scopes` (which is similar to what used to be called `Hub`) and thus relies on OpenTelemetry for `Context` propagation. + - Classes used for the previous version of our OpenTelemetry support have been deprecated but can still be used manually. We're not planning to keep the old agent around in favor of less complexity in the SDK. - Add `ignoredSpanOrigins` option for ignoring spans coming from certain integrations - - We pre-configure this to ignore Performance instrumentation for Spring and other integrations when using our OpenTelemetry Agent to avoid duplicate spans + - We pre-configure this to ignore Performance instrumentation for Spring and other integrations when using our OpenTelemetry Agent to avoid duplicate spans - Add data fetching environment hint to breadcrumb for GraphQL (#3413) ([#3431](https://github.com/getsentry/sentry-java/pull/3431)) ### Fixes @@ -60,9 +60,9 @@ If you've been using the previous version of `sentry-opentelemetry-agent`, simpl #### New to the agent If you've not been using OpenTelemetry before, you can add `sentry-opentelemetry-agent` to your setup by downloading the latest release and using it when starting up your application -- `SENTRY_PROPERTIES_FILE=sentry.properties java -javaagent:sentry-opentelemetry-agent-x.x.x.jar -jar your-application.jar` -- Please use `sentry.properties` or environment variables to configure the SDK as the agent is now in charge of initializing the SDK and options coming from things like logging integrations or our Spring Boot integration will not take effect. -- You may find the [docs page](https://docs.sentry.io/platforms/java/tracing/instrumentation/opentelemetry/#using-sentry-opentelemetry-agent-with-auto-initialization) useful. While we haven't updated it yet to reflect the changes described here, the section about using the agent with auto init should still be valid. + - `SENTRY_PROPERTIES_FILE=sentry.properties java -javaagent:sentry-opentelemetry-agent-x.x.x.jar -jar your-application.jar` + - Please use `sentry.properties` or environment variables to configure the SDK as the agent is now in charge of initializing the SDK and options coming from things like logging integrations or our Spring Boot integration will not take effect. + - You may find the [docs page](https://docs.sentry.io/platforms/java/tracing/instrumentation/opentelemetry/#using-sentry-opentelemetry-agent-with-auto-initialization) useful. While we haven't updated it yet to reflect the changes described here, the section about using the agent with auto init should still be valid. If you want to skip auto initialization of the SDK performed by the agent, please follow the steps above and set the environment variable `SENTRY_AUTO_INIT` to `false` then add the following to your `Sentry.init`: @@ -87,8 +87,8 @@ Sentry.OptionsConfiguration optionsConfiguration() { ### Dependencies - Bump Native SDK from v0.7.0 to v0.7.5 ([#3441](https://github.com/getsentry/sentry-java/pull/3189)) - - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#075) - - [diff](https://github.com/getsentry/sentry-native/compare/0.7.0...0.7.5) + - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#075) + - [diff](https://github.com/getsentry/sentry-native/compare/0.7.0...0.7.5) ## 8.0.0-alpha.1 diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java index b93a81fb32..40cbdb62b7 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java @@ -196,7 +196,7 @@ public static SentryId captureEnvelope( deleteCurrentSessionFile( options, // should be sync if going to crash or already not a main thread - !maybeStartNewSession || !hub.getOptions().getMainThreadChecker().isMainThread()); + !maybeStartNewSession || !scopes.getOptions().getMainThreadChecker().isMainThread()); if (maybeStartNewSession) { scopes.startSession(); } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java index 01b15c0286..49ad3ffeaa 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java @@ -90,7 +90,7 @@ public static synchronized void init( Sentry.init( OptionsContainer.create(SentryAndroidOptions.class), options -> { - final LoadClass classLoader = new LoadClass(); + final io.sentry.util.LoadClass classLoader = new io.sentry.util.LoadClass(); final boolean isTimberUpstreamAvailable = classLoader.isClassAvailable(TIMBER_CLASS_NAME, options); final boolean isFragmentUpstreamAvailable = @@ -104,7 +104,7 @@ public static synchronized void init( && classLoader.isClassAvailable(SENTRY_TIMBER_INTEGRATION_CLASS_NAME, options)); final BuildInfoProvider buildInfoProvider = new BuildInfoProvider(logger); - final LoadClass loadClass = new LoadClass(); + final io.sentry.util.LoadClass loadClass = new io.sentry.util.LoadClass(); final ActivityFramesTracker activityFramesTracker = new ActivityFramesTracker(loadClass, options); @@ -154,7 +154,7 @@ public static synchronized void init( // so only start a session if it's not already started // This e.g. happens on React Native, or e.g. on deferred SDK init final AtomicBoolean sessionStarted = new AtomicBoolean(false); - hub.configureScope( + scopes.configureScope( scope -> { final @Nullable Session currentSession = scope.getSession(); if (currentSession != null && currentSession.getStarted() != null) { diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt index 3d4d0a9837..be0f5cd71a 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt @@ -319,6 +319,7 @@ class SentryAndroidTest { @Test fun `init does not start a session if one is already running`() { val client = mock() + whenever(client.isEnabled).thenReturn(true) initSentryWithForegroundImportance(true, { options -> options.addIntegration { hub, _ -> diff --git a/sentry-android-okhttp/src/test/java/io/sentry/android/okhttp/SentryOkHttpEventListenerTest.kt b/sentry-android-okhttp/src/test/java/io/sentry/android/okhttp/SentryOkHttpEventListenerTest.kt deleted file mode 100644 index 9ed110ef7e..0000000000 --- a/sentry-android-okhttp/src/test/java/io/sentry/android/okhttp/SentryOkHttpEventListenerTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -package io.sentry.android.okhttp - -import io.sentry.IHub -import io.sentry.SentryOptions -import io.sentry.SentryTracer -import io.sentry.TransactionContext -import okhttp3.EventListener -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.mockwebserver.MockResponse -import okhttp3.mockwebserver.MockWebServer -import okhttp3.mockwebserver.SocketPolicy -import org.mockito.kotlin.mock -import org.mockito.kotlin.whenever -import kotlin.test.Test -import kotlin.test.assertEquals - -@SuppressWarnings("Deprecated") -class SentryOkHttpEventListenerTest { - - class Fixture { - val hub = mock() - val server = MockWebServer() - lateinit var sentryTracer: SentryTracer - - @SuppressWarnings("LongParameterList") - fun getSut( - eventListener: EventListener? = null - ): OkHttpClient { - val options = SentryOptions().apply { - dsn = "https://key@sentry.io/proj" - } - whenever(hub.options).thenReturn(options) - - sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) - whenever(hub.span).thenReturn(sentryTracer) - server.enqueue( - MockResponse() - .setBody("responseBody") - .setSocketPolicy(SocketPolicy.KEEP_OPEN) - .setResponseCode(200) - ) - - val builder = OkHttpClient.Builder().addInterceptor(SentryOkHttpInterceptor(hub)) - val sentryOkHttpEventListener = when { - eventListener != null -> SentryOkHttpEventListener(hub, eventListener) - else -> SentryOkHttpEventListener(hub) - } - return builder.eventListener(sentryOkHttpEventListener).build() - } - } - - private val fixture = Fixture() - - private fun getRequest(url: String = "/hello"): Request { - return Request.Builder() - .addHeader("myHeader", "myValue") - .get() - .url(fixture.server.url(url)) - .build() - } - - @Test - fun `when there are multiple SentryOkHttpEventListeners, they don't duplicate spans`() { - val sut = fixture.getSut(eventListener = SentryOkHttpEventListener(fixture.hub)) - val call = sut.newCall(getRequest()) - call.execute().close() - assertEquals(8, fixture.sentryTracer.children.size) - } -} diff --git a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt index 2f3862bd67..28d7375450 100644 --- a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt +++ b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt @@ -63,7 +63,7 @@ internal class SentryOkHttpEvent(private val scopes: IScopes, private val reques callRootSpan?.setData("url", url) callRootSpan?.setData("host", host) callRootSpan?.setData("path", encodedPath) - callRootSpan?.setData(SpanDataConvention.HTTP_METHOD_KEY, method.toUpperCase(Locale.ROOT)) + callRootSpan?.setData(SpanDataConvention.HTTP_METHOD_KEY, method.uppercase(Locale.ROOT)) } /** diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index a106b61618..68ad64144f 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -250,6 +250,7 @@ public final class io/sentry/CombinedScopeView : io/sentry/IScope { public fun clear ()V public fun clearAttachments ()V public fun clearBreadcrumbs ()V + public fun clearSession ()V public fun clearTransaction ()V public fun clone ()Lio/sentry/IScope; public synthetic fun clone ()Ljava/lang/Object; @@ -326,6 +327,7 @@ public final class io/sentry/DataCategory : java/lang/Enum { public static final field Profile Lio/sentry/DataCategory; public static final field Security Lio/sentry/DataCategory; public static final field Session Lio/sentry/DataCategory; + public static final field Span Lio/sentry/DataCategory; public static final field Transaction Lio/sentry/DataCategory; public static final field Unknown Lio/sentry/DataCategory; public static final field UserReport Lio/sentry/DataCategory; @@ -738,6 +740,7 @@ public abstract interface class io/sentry/IScope { public abstract fun clear ()V public abstract fun clearAttachments ()V public abstract fun clearBreadcrumbs ()V + public abstract fun clearSession ()V public abstract fun clearTransaction ()V public abstract fun clone ()Lio/sentry/IScope; public abstract fun endSession ()Lio/sentry/Session; @@ -1412,6 +1415,7 @@ public final class io/sentry/NoOpScope : io/sentry/IScope { public fun clear ()V public fun clearAttachments ()V public fun clearBreadcrumbs ()V + public fun clearSession ()V public fun clearTransaction ()V public fun clone ()Lio/sentry/IScope; public synthetic fun clone ()Ljava/lang/Object; @@ -1884,6 +1888,7 @@ public final class io/sentry/Scope : io/sentry/IScope { public fun clear ()V public fun clearAttachments ()V public fun clearBreadcrumbs ()V + public fun clearSession ()V public fun clearTransaction ()V public fun clone ()Lio/sentry/IScope; public synthetic fun clone ()Ljava/lang/Object; @@ -3632,6 +3637,7 @@ public final class io/sentry/clientreport/ClientReportRecorder : io/sentry/clien public fun recordLostEnvelope (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelope;)V public fun recordLostEnvelopeItem (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelopeItem;)V public fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;)V + public fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;J)V } public final class io/sentry/clientreport/DiscardReason : java/lang/Enum { @@ -3677,6 +3683,7 @@ public abstract interface class io/sentry/clientreport/IClientReportRecorder { public abstract fun recordLostEnvelope (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelope;)V public abstract fun recordLostEnvelopeItem (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelopeItem;)V public abstract fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;)V + public abstract fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;J)V } public abstract interface class io/sentry/clientreport/IClientReportStorage { @@ -3690,6 +3697,7 @@ public final class io/sentry/clientreport/NoOpClientReportRecorder : io/sentry/c public fun recordLostEnvelope (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelope;)V public fun recordLostEnvelopeItem (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelopeItem;)V public fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;)V + public fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;J)V } public abstract interface class io/sentry/config/PropertiesProvider { diff --git a/sentry/src/main/java/io/sentry/CombinedScopeView.java b/sentry/src/main/java/io/sentry/CombinedScopeView.java index 86b90379ad..c22fd060bb 100644 --- a/sentry/src/main/java/io/sentry/CombinedScopeView.java +++ b/sentry/src/main/java/io/sentry/CombinedScopeView.java @@ -412,6 +412,11 @@ public void withTransaction(Scope.@NotNull IWithTransaction callback) { return globalScope.getSession(); } + @Override + public void clearSession() { + getDefaultWriteScope().clearSession(); + } + @Override public void setPropagationContext(@NotNull PropagationContext propagationContext) { getDefaultWriteScope().setPropagationContext(propagationContext); diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 314063b340..1e26eefeba 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -775,7 +775,7 @@ public void flush(long timeoutMillis) { getOptions() .getClientReportRecorder() .recordLostEvent(DiscardReason.BACKPRESSURE, DataCategory.Transaction); - options + getOptions() .getClientReportRecorder() .recordLostEvent( DiscardReason.BACKPRESSURE, @@ -785,7 +785,7 @@ public void flush(long timeoutMillis) { getOptions() .getClientReportRecorder() .recordLostEvent(DiscardReason.SAMPLE_RATE, DataCategory.Transaction); - options + getOptions() .getClientReportRecorder() .recordLostEvent( DiscardReason.SAMPLE_RATE, diff --git a/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt b/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt index e50ee4258e..409d3b971b 100644 --- a/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt @@ -308,10 +308,10 @@ class UncaughtExceptionHandlerIntegrationTest { } val integration1 = UncaughtExceptionHandlerIntegration(handler) - integration1.register(fixture.hub, fixture.options) + integration1.register(fixture.scopes, fixture.options) val integration2 = UncaughtExceptionHandlerIntegration(handler) - integration2.register(fixture.hub, fixture.options) + integration2.register(fixture.scopes, fixture.options) assertEquals(currentDefaultHandler, integration2) integration2.close() @@ -334,10 +334,10 @@ class UncaughtExceptionHandlerIntegrationTest { } val integration1 = UncaughtExceptionHandlerIntegration(handler) - integration1.register(fixture.hub, fixture.options) + integration1.register(fixture.scopes, fixture.options) val integration2 = UncaughtExceptionHandlerIntegration(handler) - integration2.register(fixture.hub, fixture.options) + integration2.register(fixture.scopes, fixture.options) assertEquals(currentDefaultHandler, integration2) integration2.close() diff --git a/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt b/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt index 96468808ab..0cd9be9094 100644 --- a/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt +++ b/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt @@ -95,9 +95,9 @@ class ClientReportTest { @Test fun `lost transaction records dropped spans`() { givenClientReportRecorder() - val hub = mock() - whenever(hub.options).thenReturn(opts) - val transaction = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), hub) + val scopes = mock() + whenever(scopes.options).thenReturn(opts) + val transaction = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), scopes) transaction.startChild("lost span", "span1").finish() transaction.startChild("lost span", "span2").finish() transaction.startChild("lost span", "span3").finish() From c7232fe7a1f89681e5d6631d6ea8edfcc4acc698 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 2 Jul 2024 11:50:37 +0200 Subject: [PATCH 89/91] Parse and use `send-default-pii` and `max-request-body-size` from `sentry.properties` (#3534) * Parse and use sendDefaultPii and maxRequestBodySize from external options * changelog --- CHANGELOG.md | 4 ++++ sentry/api/sentry.api | 2 ++ sentry/src/main/java/io/sentry/ExternalOptions.java | 10 ++++++++++ sentry/src/main/java/io/sentry/SentryOptions.java | 6 ++++++ sentry/src/test/java/io/sentry/ExternalOptionsTest.kt | 7 +++++++ sentry/src/test/java/io/sentry/SentryOptionsTest.kt | 5 +++++ 6 files changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a179e82555..dd75e4be4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - `sentry-android-okhttp` has been removed in favor of `sentry-okhttp`, removing android dependency from the module ([#3510](https://github.com/getsentry/sentry-java/pull/3510)) +### Fixes + +- Parse and use `send-default-pii` and `max-request-body-size` from `sentry.properties` ([#3534](https://github.com/getsentry/sentry-java/pull/3534)) + ## 8.0.0-alpha.2 ### Behavioural Changes diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 68ad64144f..52d6c2f329 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -461,6 +461,7 @@ public final class io/sentry/ExternalOptions { public fun isEnableBackpressureHandling ()Ljava/lang/Boolean; public fun isEnablePrettySerializationOutput ()Ljava/lang/Boolean; public fun isEnabled ()Ljava/lang/Boolean; + public fun isSendDefaultPii ()Ljava/lang/Boolean; public fun isSendModules ()Ljava/lang/Boolean; public fun setCron (Lio/sentry/SentryOptions$Cron;)V public fun setDebug (Ljava/lang/Boolean;)V @@ -482,6 +483,7 @@ public final class io/sentry/ExternalOptions { public fun setProxy (Lio/sentry/SentryOptions$Proxy;)V public fun setRelease (Ljava/lang/String;)V public fun setSendClientReports (Ljava/lang/Boolean;)V + public fun setSendDefaultPii (Ljava/lang/Boolean;)V public fun setSendModules (Ljava/lang/Boolean;)V public fun setServerName (Ljava/lang/String;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V diff --git a/sentry/src/main/java/io/sentry/ExternalOptions.java b/sentry/src/main/java/io/sentry/ExternalOptions.java index 99eaacdd24..aa5aa43937 100644 --- a/sentry/src/main/java/io/sentry/ExternalOptions.java +++ b/sentry/src/main/java/io/sentry/ExternalOptions.java @@ -49,6 +49,7 @@ public final class ExternalOptions { private @Nullable List ignoredCheckIns; private @Nullable Boolean sendModules; + private @Nullable Boolean sendDefaultPii; private @Nullable Boolean enableBackpressureHandling; private @Nullable SentryOptions.Cron cron; @@ -131,6 +132,7 @@ public final class ExternalOptions { propertiesProvider.getBooleanProperty("enable-pretty-serialization-output")); options.setSendModules(propertiesProvider.getBooleanProperty("send-modules")); + options.setSendDefaultPii(propertiesProvider.getBooleanProperty("send-default-pii")); options.setIgnoredCheckIns(propertiesProvider.getList("ignored-checkins")); @@ -421,6 +423,14 @@ public void setSendModules(final @Nullable Boolean sendModules) { this.sendModules = sendModules; } + public @Nullable Boolean isSendDefaultPii() { + return sendDefaultPii; + } + + public void setSendDefaultPii(final @Nullable Boolean sendDefaultPii) { + this.sendDefaultPii = sendDefaultPii; + } + @ApiStatus.Experimental public void setIgnoredCheckIns(final @Nullable List ignoredCheckIns) { this.ignoredCheckIns = ignoredCheckIns; diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index b8e54952c8..7e24e81dce 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -2688,6 +2688,12 @@ public void merge(final @NotNull ExternalOptions options) { if (options.isEnableBackpressureHandling() != null) { setEnableBackpressureHandling(options.isEnableBackpressureHandling()); } + if (options.getMaxRequestBodySize() != null) { + setMaxRequestBodySize(options.getMaxRequestBodySize()); + } + if (options.isSendDefaultPii() != null) { + setSendDefaultPii(options.isSendDefaultPii()); + } if (options.getCron() != null) { if (getCron() == null) { diff --git a/sentry/src/test/java/io/sentry/ExternalOptionsTest.kt b/sentry/src/test/java/io/sentry/ExternalOptionsTest.kt index 04c181b194..fd5b363219 100644 --- a/sentry/src/test/java/io/sentry/ExternalOptionsTest.kt +++ b/sentry/src/test/java/io/sentry/ExternalOptionsTest.kt @@ -286,6 +286,13 @@ class ExternalOptionsTest { } } + @Test + fun `creates options with sendDefaultPii set to true`() { + withPropertiesFile("send-default-pii=true") { options -> + assertTrue(options.isSendDefaultPii == true) + } + } + private fun withPropertiesFile(textLines: List = emptyList(), logger: ILogger = mock(), fn: (ExternalOptions) -> Unit) { // create a sentry.properties file in temporary folder val temporaryFolder = TemporaryFolder() diff --git a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt index b474d4e4e0..c11eafdc5a 100644 --- a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt +++ b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt @@ -1,5 +1,6 @@ package io.sentry +import io.sentry.SentryOptions.RequestSize import io.sentry.util.StringUtils import org.mockito.kotlin.mock import java.io.File @@ -371,6 +372,8 @@ class SentryOptionsTest { externalOptions.isSendModules = false externalOptions.ignoredCheckIns = listOf("slug1", "slug-B") externalOptions.isEnableBackpressureHandling = false + externalOptions.maxRequestBodySize = SentryOptions.RequestSize.MEDIUM + externalOptions.isSendDefaultPii = true externalOptions.cron = SentryOptions.Cron().apply { defaultCheckinMargin = 10L defaultMaxRuntime = 30L @@ -415,6 +418,8 @@ class SentryOptionsTest { assertEquals(40L, options.cron?.defaultFailureIssueThreshold) assertEquals(50L, options.cron?.defaultRecoveryThreshold) assertEquals("America/New_York", options.cron?.defaultTimezone) + assertTrue(options.isSendDefaultPii) + assertEquals(RequestSize.MEDIUM, options.maxRequestBodySize) } @Test From a62056e79d1fd2a95dc7f96bc04e471219156344 Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 2 Jul 2024 14:00:39 +0200 Subject: [PATCH 90/91] Support spans that are split into multiple batches (#3539) * Support spans across batches * changelog --- CHANGELOG.md | 2 ++ .../main/java/io/sentry/opentelemetry/SentrySpanExporter.java | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd75e4be4d..1636d34d17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ ### Fixes +- Support spans that are split into multiple batches ([#3539](https://github.com/getsentry/sentry-java/pull/3539)) + - When spans belonging to a single transaction were split into multiple batches for SpanExporter, we did not add all spans because the isSpanTooOld check wasn't inverted. - Parse and use `send-default-pii` and `max-request-body-size` from `sentry.properties` ([#3534](https://github.com/getsentry/sentry-java/pull/3534)) ## 8.0.0-alpha.2 diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java index 5e7c610cc0..0e86f281bc 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java @@ -105,7 +105,8 @@ public CompletableResultCode export(Collection spans) { final @NotNull SentryInstantDate now = new SentryInstantDate(); final @NotNull List nonExpired = - remaining.stream().filter((span) -> isSpanTooOld(span, now)).collect(Collectors.toList()); + remaining.stream().filter((span) -> !isSpanTooOld(span, now)).collect(Collectors.toList()); + this.finishedSpans.addAll(nonExpired); // TODO From a9d69ca951066e3fb1befaf4d4b7dba7c46f30bb Mon Sep 17 00:00:00 2001 From: Alexander Dinauer Date: Tue, 2 Jul 2024 14:18:05 +0200 Subject: [PATCH 91/91] Revert "Merge branch '8.x.x' into chore/bump-spring-boot-jakarta-from-3.2.0-to-3.3.0" This reverts commit 92bc9e488856be29719bc301368445bf1a4ee999, reversing changes made to 394677f1508512a6d06681fad0fed8d3ea603010. --- .craft.yml | 1 + .github/ISSUE_TEMPLATE/bug_report_android.yml | 4 +- .github/workflows/agp-matrix.yml | 4 +- .github/workflows/build.yml | 4 +- .github/workflows/codeql-analysis.yml | 6 +- .../workflows/enforce-license-compliance.yml | 2 +- .github/workflows/generate-javadocs.yml | 2 +- .../workflows/gradle-wrapper-validation.yml | 2 +- .../integration-tests-benchmarks.yml | 8 +- .github/workflows/integration-tests-ui.yml | 4 +- .github/workflows/release-build.yml | 2 +- .github/workflows/system-tests-backend.yml | 6 +- .github/workflows/update-deps.yml | 2 +- .gitmodules | 3 + CHANGELOG.md | 169 +-- README.md | 1 + build.gradle.kts | 9 +- buildSrc/src/main/java/Config.kt | 13 +- gradle.properties | 2 +- scripts/update-sentry-native-ndk.sh | 35 - .../api/sentry-android-core.api | 41 +- .../core/ActivityBreadcrumbsIntegration.java | 17 +- .../android/core/ActivityFramesTracker.java | 7 +- .../core/ActivityLifecycleIntegration.java | 33 +- .../core/AndroidOptionsInitializer.java | 9 +- .../core/AndroidTransactionProfiler.java | 12 +- .../sentry/android/core/AnrIntegration.java | 19 +- .../android/core/AnrV2EventProcessor.java | 5 - .../sentry/android/core/AnrV2Integration.java | 14 +- .../AppComponentsBreadcrumbsIntegration.java | 16 +- .../android/core/AppLifecycleIntegration.java | 14 +- .../core/CurrentActivityIntegration.java | 4 +- .../core/DefaultAndroidEventProcessor.java | 5 - .../core/EnvelopeFileObserverIntegration.java | 14 +- .../android/core/InternalSentrySdk.java | 95 +- .../sentry/android/core/LifecycleWatcher.java | 22 +- .../io/sentry/android/core/LoadClass.java | 37 +- .../android/core/ManifestMetadataReader.java | 6 +- .../sentry/android/core/NdkIntegration.java | 9 +- .../core/NetworkBreadcrumbsIntegration.java | 21 +- .../PerformanceAndroidEventProcessor.java | 5 - .../PhoneStateBreadcrumbsIntegration.java | 20 +- .../core/ScreenshotEventProcessor.java | 5 - .../core/SendCachedEnvelopeIntegration.java | 20 +- .../io/sentry/android/core/SentryAndroid.java | 31 +- .../SystemEventsBreadcrumbsIntegration.java | 20 +- .../TempSensorBreadcrumbsIntegration.java | 12 +- .../core/UserInteractionIntegration.java | 14 +- .../core/ViewHierarchyEventProcessor.java | 5 - .../gestures/SentryGestureListener.java | 21 +- .../ActivityBreadcrumbsIntegrationTest.kt | 42 +- .../core/ActivityLifecycleIntegrationTest.kt | 212 ++-- .../core/AndroidTransactionProfilerTest.kt | 12 +- .../sentry/android/core/AnrIntegrationTest.kt | 28 +- .../android/core/AnrV2EventProcessorTest.kt | 2 +- .../android/core/AnrV2IntegrationTest.kt | 90 +- ...AppComponentsBreadcrumbsIntegrationTest.kt | 48 +- .../core/AppLifecycleIntegrationTest.kt | 16 +- .../core/CurrentActivityIntegrationTest.kt | 6 +- .../core/DefaultAndroidEventProcessorTest.kt | 8 +- .../EnvelopeFileObserverIntegrationTest.kt | 24 +- .../android/core/InternalSentrySdkTest.kt | 129 +- .../android/core/LifecycleWatcherTest.kt | 48 +- .../sentry/android/core/NdkIntegrationTest.kt | 22 +- .../core/NetworkBreadcrumbsIntegrationTest.kt | 108 +- .../PerformanceAndroidEventProcessorTest.kt | 30 +- .../PhoneStateBreadcrumbsIntegrationTest.kt | 38 +- .../core/SendCachedEnvelopeIntegrationTest.kt | 30 +- .../sentry/android/core/SentryAndroidTest.kt | 26 +- .../SystemEventsBreadcrumbsIntegrationTest.kt | 24 +- .../TempSensorBreadcrumbsIntegrationTest.kt | 32 +- .../core/UserInteractionIntegrationTest.kt | 18 +- .../SentryGestureListenerClickTest.kt | 22 +- .../SentryGestureListenerScrollTest.kt | 26 +- .../SentryGestureListenerTracingTest.kt | 75 +- .../api/sentry-android-fragment.api | 8 +- .../fragment/FragmentLifecycleIntegration.kt | 10 +- .../SentryFragmentLifecycleCallbacks.kt | 18 +- .../FragmentLifecycleIntegrationTest.kt | 16 +- .../SentryFragmentLifecycleCallbacksTest.kt | 26 +- .../io/sentry/uitest/android/SdkInitTests.kt | 25 +- .../android/mockservers/RelayAsserter.kt | 2 +- .../api/sentry-android-navigation.api | 10 +- .../navigation/SentryNavigationListener.kt | 24 +- .../SentryNavigationListenerTest.kt | 46 +- sentry-android-ndk/CMakeLists.txt | 17 + sentry-android-ndk/api/sentry-android-ndk.api | 2 +- sentry-android-ndk/build.gradle.kts | 32 +- sentry-android-ndk/sentry-native | 1 + .../sentry/android/ndk/DebugImagesLoader.java | 18 +- .../io/sentry/android/ndk/INativeScope.java | 18 + .../android/ndk/NativeModuleListLoader.java | 19 + .../io/sentry/android/ndk/NativeScope.java | 55 + .../sentry/android/ndk/NdkScopeObserver.java | 2 - .../java/io/sentry/android/ndk/SentryNdk.java | 32 +- sentry-android-ndk/src/main/jni/sentry.c | 494 ++++++++ .../android/ndk/DebugImagesLoaderTest.kt | 5 +- .../android/ndk/NdkScopeObserverTest.kt | 1 - .../api/sentry-android-okhttp.api | 62 + sentry-android-okhttp/build.gradle.kts | 83 ++ sentry-android-okhttp/proguard-rules.pro | 13 + .../okhttp/SentryOkHttpEventListener.kt | 201 +++ .../android/okhttp/SentryOkHttpInterceptor.kt | 79 ++ .../src/main/res/values/public.xml | 4 + .../android/sqlite/SQLiteSpanManager.kt | 12 +- .../android/sqlite/SQLiteSpanManagerTest.kt | 12 +- .../sqlite/SentrySupportSQLiteDatabaseTest.kt | 12 +- .../SentrySupportSQLiteStatementTest.kt | 12 +- .../api/sentry-android-timber.api | 4 +- .../android/timber/SentryTimberIntegration.kt | 6 +- .../sentry/android/timber/SentryTimberTree.kt | 8 +- .../timber/SentryTimberIntegrationTest.kt | 18 +- .../android/timber/SentryTimberTreeTest.kt | 52 +- sentry-apollo-3/api/sentry-apollo-3.api | 20 +- .../apollo3/SentryApollo3HttpInterceptor.kt | 34 +- .../apollo3/SentryApolloBuilderExtensions.kt | 10 +- .../SentryApollo3InterceptorClientErrors.kt | 40 +- .../apollo3/SentryApollo3InterceptorTest.kt | 40 +- ...ntryApollo3InterceptorWithVariablesTest.kt | 22 +- sentry-apollo/api/sentry-apollo.api | 6 +- .../sentry/apollo/SentryApolloInterceptor.kt | 20 +- .../apollo/SentryApolloInterceptorTest.kt | 38 +- sentry-graphql/api/sentry-graphql.api | 20 +- .../io/sentry/graphql/ExceptionReporter.java | 46 +- .../graphql/NoOpSubscriptionHandler.java | 4 +- .../SentryDataFetcherExceptionHandler.java | 14 +- ...tryGenericDataFetcherExceptionHandler.java | 4 +- .../sentry/graphql/SentryInstrumentation.java | 57 +- .../graphql/SentrySubscriptionHandler.java | 4 +- .../sentry/graphql/ExceptionReporterTest.kt | 32 +- .../SentryDataFetcherExceptionHandlerTest.kt | 8 +- ...yGenericDataFetcherExceptionHandlerTest.kt | 6 +- .../SentryInstrumentationAnotherTest.kt | 44 +- .../graphql/SentryInstrumentationTest.kt | 34 +- sentry-jdbc/api/sentry-jdbc.api | 2 +- .../sentry/jdbc/SentryJdbcEventListener.java | 20 +- .../jdbc/SentryJdbcEventListenerTest.kt | 16 +- .../java/io/sentry/jul/SentryHandler.java | 6 +- .../api/sentry-kotlin-extensions.api | 8 +- .../java/io/sentry/kotlin/SentryContext.kt | 24 +- .../io/sentry/kotlin/SentryContextTest.kt | 140 +-- sentry-log4j2/api/sentry-log4j2.api | 2 +- .../java/io/sentry/log4j2/SentryAppender.java | 20 +- .../io/sentry/log4j2/SentryAppenderTest.kt | 7 +- .../io/sentry/logback/SentryAppender.java | 6 +- .../io/sentry/logback/SentryAppenderTest.kt | 26 +- sentry-okhttp/api/sentry-okhttp.api | 18 +- .../io/sentry/okhttp/SentryOkHttpEvent.kt | 18 +- .../okhttp/SentryOkHttpEventListener.kt | 29 +- .../sentry/okhttp/SentryOkHttpInterceptor.kt | 22 +- .../io/sentry/okhttp/SentryOkHttpUtils.kt | 18 +- .../okhttp/SentryOkHttpEventListenerTest.kt | 28 +- .../io/sentry/okhttp/SentryOkHttpEventTest.kt | 53 +- .../okhttp/SentryOkHttpInterceptorTest.kt | 50 +- .../io/sentry/okhttp/SentryOkHttpUtilsTest.kt | 22 +- sentry-openfeign/api/sentry-openfeign.api | 4 +- .../io/sentry/openfeign/SentryCapability.java | 17 +- .../sentry/openfeign/SentryFeignClient.java | 20 +- .../sentry/openfeign/SentryFeignClientTest.kt | 22 +- .../build.gradle.kts | 1 - .../build.gradle.kts | 2 - ...ryAutoConfigurationCustomizerProvider.java | 30 +- .../SentryPropagatorProvider.java | 2 +- .../api/sentry-opentelemetry-bootstrap.api | 178 --- .../build.gradle.kts | 77 -- .../InternalSemanticAttributes.java | 22 - .../opentelemetry/OpenTelemetryUtil.java | 18 - .../OtelContextScopesStorage.java | 32 - .../sentry/opentelemetry/OtelSpanContext.java | 115 -- .../sentry/opentelemetry/OtelSpanFactory.java | 177 --- .../sentry/opentelemetry/OtelSpanWrapper.java | 498 -------- .../opentelemetry/OtelStorageToken.java | 21 - .../OtelTransactionSpanForwarder.java | 325 ----- .../opentelemetry/SentryContextStorage.java | 46 - .../opentelemetry/SentryContextWrapper.java | 92 -- .../SentryOtelThreadLocalStorage.java | 85 -- .../opentelemetry/SentryWeakSpanStorage.java | 43 - .../api/sentry-opentelemetry-core.api | 73 +- .../build.gradle.kts | 3 - .../OpenTelemetryLinkErrorEventProcessor.java | 41 +- .../OtelInternalSpanDetectionUtil.java | 66 - .../opentelemetry/OtelSamplingUtil.java | 38 - .../opentelemetry/OtelSentryPropagator.java | 145 --- .../OtelSentrySpanProcessor.java | 167 --- .../io/sentry/opentelemetry/OtelSpanInfo.java | 31 +- .../sentry/opentelemetry/SentryOtelKeys.java | 3 - .../opentelemetry/SentryPropagator.java | 35 +- .../sentry/opentelemetry/SentrySampler.java | 117 -- .../opentelemetry/SentrySamplingResult.java | 40 - .../opentelemetry/SentrySpanExporter.java | 511 -------- .../opentelemetry/SentrySpanProcessor.java | 79 +- .../SpanDescriptionExtractor.java | 97 +- .../io/sentry/opentelemetry/SpanNode.java | 56 - .../io/sentry/opentelemetry/TraceData.java | 1 - .../test/kotlin/SentrySpanProcessorTest.kt | 52 +- sentry-quartz/api/sentry-quartz.api | 3 +- .../io/sentry/quartz/SentryJobListener.java | 33 +- .../sentry-samples-android/CMakeLists.txt | 9 +- .../sentry-samples-android/build.gradle.kts | 15 +- .../samples/android/ProfilingActivity.kt | 6 +- .../samples/spring/jakarta/AppConfig.java | 6 +- .../samples/spring/jakarta/WebConfig.java | 8 +- .../io/sentry/samples/spring/AppConfig.java | 6 +- .../io/sentry/samples/spring/WebConfig.java | 8 +- .../api/sentry-servlet-jakarta.api | 3 +- ...tryRequestHttpServletRequestProcessor.java | 5 - .../jakarta/SentryServletRequestListener.java | 27 +- .../SentryServletRequestListenerTest.kt | 24 +- sentry-servlet/api/sentry-servlet.api | 3 +- ...tryRequestHttpServletRequestProcessor.java | 5 - .../servlet/SentryServletRequestListener.java | 27 +- .../SentryServletRequestListenerTest.kt | 23 +- .../api/sentry-spring-boot-jakarta.api | 2 +- .../boot/jakarta/SentryAutoConfiguration.java | 52 +- .../spring/boot/jakarta/SentryProperties.java | 3 +- .../SentrySpanRestClientCustomizer.java | 6 +- .../SentrySpanRestTemplateCustomizer.java | 6 +- .../SentrySpanWebClientCustomizer.java | 6 +- .../SentryWebfluxAutoConfiguration.java | 21 +- .../jakarta/SentryAutoConfigurationTest.kt | 12 +- .../SentrySpanRestClientCustomizerTest.kt | 22 +- .../SentrySpanRestTemplateCustomizerTest.kt | 22 +- .../SentrySpanWebClientCustomizerTest.kt | 22 +- .../jakarta/it/SentrySpringIntegrationTest.kt | 6 +- sentry-spring-boot/api/sentry-spring-boot.api | 4 +- .../spring/boot/SentryAutoConfiguration.java | 48 +- .../SentrySpanRestTemplateCustomizer.java | 6 +- .../boot/SentrySpanWebClientCustomizer.java | 6 +- .../boot/SentryWebfluxAutoConfiguration.java | 15 +- .../boot/SentryAutoConfigurationTest.kt | 12 +- .../SentrySpanRestTemplateCustomizerTest.kt | 22 +- .../boot/SentrySpanWebClientCustomizerTest.kt | 22 +- .../boot/it/SentrySpringIntegrationTest.kt | 6 +- .../api/sentry-spring-jakarta.api | 67 +- .../jakarta/ContextTagsEventProcessor.java | 5 - .../sentry/spring/jakarta/EnableSentry.java | 2 +- .../jakarta/SentryExceptionResolver.java | 10 +- .../spring/jakarta/SentryHubRegistrar.java | 4 +- .../jakarta/SentryInitBeanPostProcessor.java | 16 +- ...tryRequestHttpServletRequestProcessor.java | 6 - .../spring/jakarta/SentryRequestResolver.java | 16 +- .../spring/jakarta/SentrySpringFilter.java | 58 +- .../spring/jakarta/SentryTaskDecorator.java | 15 +- .../spring/jakarta/SentryUserFilter.java | 12 +- .../jakarta/checkin/SentryCheckInAdvice.java | 56 +- ...SentryCaptureExceptionParameterAdvice.java | 14 +- .../graphql/SentryBatchLoaderRegistry.java | 16 +- .../graphql/SentryDgsSubscriptionHandler.java | 6 +- .../SentrySpringSubscriptionHandler.java | 6 +- .../jakarta/tracing/SentrySpanAdvice.java | 20 +- ...entrySpanClientHttpRequestInterceptor.java | 25 +- .../SentrySpanClientWebRequestFilter.java | 21 +- .../jakarta/tracing/SentryTracingFilter.java | 45 +- .../tracing/SentryTransactionAdvice.java | 59 +- .../webflux/AbstractSentryWebFilter.java | 63 +- .../spring/jakarta/webflux/ReactorUtils.java | 68 +- .../SentryReactorThreadLocalAccessor.java | 18 +- .../webflux/SentryRequestResolver.java | 13 +- .../jakarta/webflux/SentryScheduleHook.java | 13 +- .../webflux/SentryWebExceptionHandler.java | 20 +- .../jakarta/webflux/SentryWebFilter.java | 20 +- ...entryWebFilterWithThreadLocalAccessor.java | 18 +- .../sentry/spring/jakarta/EnableSentryTest.kt | 4 +- .../spring/jakarta/SentryCheckInAdviceTest.kt | 107 +- .../jakarta/SentryExceptionResolverTest.kt | 28 +- .../SentryInitBeanPostProcessorTest.kt | 10 +- ...yRequestHttpServletRequestProcessorTest.kt | 6 +- .../spring/jakarta/SentrySpringFilterTest.kt | 28 +- .../spring/jakarta/SentryTaskDecoratorTest.kt | 18 +- .../spring/jakarta/SentryUserFilterTest.kt | 20 +- ...ntryCaptureExceptionParameterAdviceTest.kt | 16 +- .../SentrySpringSubscriptionHandlerTest.kt | 14 +- .../mvc/SentrySpringIntegrationTest.kt | 24 +- .../jakarta/tracing/SentrySpanAdviceTest.kt | 40 +- .../tracing/SentryTracingFilterTest.kt | 54 +- .../tracing/SentryTransactionAdviceTest.kt | 53 +- .../jakarta/webflux/ReactorUtilsTest.kt | 75 +- .../jakarta/webflux/SentryScheduleHookTest.kt | 18 +- .../webflux/SentryWebFluxTracingFilterTest.kt | 91 +- .../webflux/SentryWebfluxIntegrationTest.kt | 12 +- sentry-spring/api/sentry-spring.api | 39 +- .../spring/ContextTagsEventProcessor.java | 5 - .../java/io/sentry/spring/EnableSentry.java | 2 +- .../spring/SentryExceptionResolver.java | 10 +- .../io/sentry/spring/SentryHubRegistrar.java | 4 +- .../spring/SentryInitBeanPostProcessor.java | 16 +- ...tryRequestHttpServletRequestProcessor.java | 6 - .../sentry/spring/SentryRequestResolver.java | 16 +- .../io/sentry/spring/SentrySpringFilter.java | 58 +- .../io/sentry/spring/SentryTaskDecorator.java | 16 +- .../io/sentry/spring/SentryUserFilter.java | 12 +- .../spring/checkin/SentryCheckInAdvice.java | 56 +- ...SentryCaptureExceptionParameterAdvice.java | 14 +- .../graphql/SentryBatchLoaderRegistry.java | 16 +- .../graphql/SentryDgsSubscriptionHandler.java | 6 +- .../SentrySpringSubscriptionHandler.java | 6 +- .../spring/tracing/SentrySpanAdvice.java | 20 +- ...entrySpanClientHttpRequestInterceptor.java | 21 +- .../SentrySpanClientWebRequestFilter.java | 21 +- .../spring/tracing/SentryTracingFilter.java | 45 +- .../tracing/SentryTransactionAdvice.java | 59 +- .../spring/webflux/SentryRequestResolver.java | 13 +- .../spring/webflux/SentryScheduleHook.java | 13 +- .../webflux/SentryWebExceptionHandler.java | 10 +- .../spring/webflux/SentryWebFilter.java | 56 +- .../io/sentry/spring/EnableSentryTest.kt | 6 +- .../sentry/spring/SentryCheckInAdviceTest.kt | 107 +- .../spring/SentryExceptionResolverTest.kt | 28 +- .../spring/SentryInitBeanPostProcessorTest.kt | 10 +- ...yRequestHttpServletRequestProcessorTest.kt | 6 +- .../sentry/spring/SentrySpringFilterTest.kt | 28 +- .../sentry/spring/SentryTaskDecoratorTest.kt | 18 +- .../io/sentry/spring/SentryUserFilterTest.kt | 20 +- ...ntryCaptureExceptionParameterAdviceTest.kt | 16 +- .../SentrySpringSubscriptionHandlerTest.kt | 14 +- .../spring/mvc/SentrySpringIntegrationTest.kt | 24 +- .../spring/tracing/SentrySpanAdviceTest.kt | 40 +- .../spring/tracing/SentryTracingFilterTest.kt | 54 +- .../tracing/SentryTransactionAdviceTest.kt | 53 +- .../spring/webflux/SentryScheduleHookTest.kt | 18 +- .../webflux/SentryWebFluxTracingFilterTest.kt | 91 +- .../webflux/SentryWebfluxIntegrationTest.kt | 12 +- .../api/sentry-test-support.api | 8 - .../src/main/kotlin/io/sentry/test/Mocks.kt | 27 - .../main/kotlin/io/sentry/test/Reflection.kt | 11 +- sentry/api/sentry.api | 785 ++---------- sentry/src/main/java/io/sentry/Baggage.java | 32 +- .../src/main/java/io/sentry/Breadcrumb.java | 12 +- .../java/io/sentry/CombinedContextsView.java | 276 ---- .../java/io/sentry/CombinedScopeView.java | 487 ------- .../src/main/java/io/sentry/DataCategory.java | 1 - ...eduplicateMultithreadedEventProcessor.java | 5 - .../java/io/sentry/DefaultScopesStorage.java | 42 - .../java/io/sentry/DefaultSpanFactory.java | 40 - .../java/io/sentry/DirectoryProcessor.java | 8 +- ...DuplicateEventDetectionEventProcessor.java | 5 - .../main/java/io/sentry/EnvelopeSender.java | 10 +- .../main/java/io/sentry/EventProcessor.java | 11 - .../main/java/io/sentry/ExternalOptions.java | 10 - .../java/io/sentry/{Scopes.java => Hub.java} | 678 +++++----- .../src/main/java/io/sentry/HubAdapter.java | 98 +- .../main/java/io/sentry/HubScopesWrapper.java | 347 ----- sentry/src/main/java/io/sentry/IHub.java | 595 ++++++++- sentry/src/main/java/io/sentry/IScope.java | 32 - sentry/src/main/java/io/sentry/IScopes.java | 710 ----------- .../main/java/io/sentry/IScopesStorage.java | 17 - .../java/io/sentry/ISentryLifecycleToken.java | 8 - sentry/src/main/java/io/sentry/ISpan.java | 20 - .../src/main/java/io/sentry/ISpanFactory.java | 28 - .../src/main/java/io/sentry/ITransaction.java | 28 +- .../src/main/java/io/sentry/Integration.java | 4 +- .../java/io/sentry/MainEventProcessor.java | 5 - .../main/java/io/sentry/MonitorConfig.java | 2 +- sentry/src/main/java/io/sentry/NoOpHub.java | 84 +- sentry/src/main/java/io/sentry/NoOpScope.java | 38 - .../src/main/java/io/sentry/NoOpScopes.java | 312 ----- .../io/sentry/NoOpScopesLifecycleToken.java | 15 - .../java/io/sentry/NoOpScopesStorage.java | 26 - sentry/src/main/java/io/sentry/NoOpSpan.java | 30 - .../main/java/io/sentry/NoOpSpanFactory.java | 45 - .../main/java/io/sentry/NoOpTransaction.java | 13 +- .../src/main/java/io/sentry/OutboxSender.java | 14 +- .../io/sentry/PreviousSessionFinalizer.java | 8 +- sentry/src/main/java/io/sentry/Scope.java | 126 +- sentry/src/main/java/io/sentry/ScopeType.java | 8 - .../main/java/io/sentry/ScopesAdapter.java | 354 ------ .../java/io/sentry/ScopesStorageFactory.java | 40 - ...achedEnvelopeFireAndForgetIntegration.java | 20 +- .../SendFireAndForgetEnvelopeSender.java | 6 +- .../sentry/SendFireAndForgetOutboxSender.java | 6 +- sentry/src/main/java/io/sentry/Sentry.java | 280 ++--- .../src/main/java/io/sentry/SentryClient.java | 40 - .../io/sentry/SentryExceptionFactory.java | 40 +- .../java/io/sentry/SentryNanotimeDate.java | 2 +- .../main/java/io/sentry/SentryOptions.java | 79 +- .../sentry/SentryRuntimeEventProcessor.java | 5 - .../io/sentry/SentrySpanFactoryHolder.java | 30 - .../java/io/sentry/SentrySpanStorage.java | 3 - .../java/io/sentry/SentryThreadFactory.java | 4 +- .../src/main/java/io/sentry/SentryTracer.java | 195 +-- .../main/java/io/sentry/SentryWrapper.java | 30 +- .../io/sentry/ShutdownHookIntegration.java | 8 +- sentry/src/main/java/io/sentry/Span.java | 75 +- .../src/main/java/io/sentry/SpanContext.java | 48 +- .../java/io/sentry/SpanFinishedCallback.java | 4 +- .../src/main/java/io/sentry/SpanOptions.java | 34 - .../src/main/java/io/sentry/SpanStatus.java | 17 +- .../java/io/sentry/SpotlightIntegration.java | 2 +- .../src/main/java/io/sentry/TraceContext.java | 26 +- .../main/java/io/sentry/TracesSampler.java | 6 +- .../java/io/sentry/TransactionContext.java | 18 +- .../java/io/sentry/TransactionOptions.java | 35 +- .../main/java/io/sentry/TypeCheckHint.java | 3 - .../UncaughtExceptionHandlerIntegration.java | 21 +- .../backpressure/BackpressureMonitor.java | 11 +- .../clientreport/ClientReportRecorder.java | 25 +- .../clientreport/IClientReportRecorder.java | 2 - .../NoOpClientReportRecorder.java | 6 - .../file/FileIOSpanManager.java | 6 +- .../file/SentryFileInputStream.java | 42 +- .../file/SentryFileOutputStream.java | 44 +- .../file/SentryFileReader.java | 7 +- .../file/SentryFileWriter.java | 6 +- .../EventProcessorAndOrder.java | 34 - .../java/io/sentry/metrics/MetricsHelper.java | 4 +- .../java/io/sentry/protocol/Contexts.java | 74 +- .../java/io/sentry/protocol/Mechanism.java | 57 - .../main/java/io/sentry/protocol/User.java | 10 +- .../java/io/sentry/util/CheckInUtils.java | 46 +- .../io/sentry/util/EventProcessorUtils.java | 24 - .../java/io/sentry/util/LifecycleHelper.java | 15 - .../main/java/io/sentry/util/LoadClass.java | 48 - .../main/java/io/sentry/util/SpanUtils.java | 60 - .../java/io/sentry/util/TracingUtils.java | 18 +- .../io/sentry/CombinedContextsViewTest.kt | 568 --------- .../java/io/sentry/CombinedScopeViewTest.kt | 1116 ----------------- ...aultTransactionPerformanceCollectorTest.kt | 8 +- .../java/io/sentry/DirectoryProcessorTest.kt | 16 +- .../test/java/io/sentry/EnvelopeSenderTest.kt | 20 +- .../java/io/sentry/ExternalOptionsTest.kt | 7 - .../src/test/java/io/sentry/HubAdapterTest.kt | 96 +- .../io/sentry/{ScopesTest.kt => HubTest.kt} | 827 +++++------- .../test/java/io/sentry/JsonSerializerTest.kt | 8 +- .../java/io/sentry/MainEventProcessorTest.kt | 6 +- sentry/src/test/java/io/sentry/NoOpHubTest.kt | 4 +- .../test/java/io/sentry/OutboxSenderTest.kt | 28 +- .../io/sentry/PreviousSessionFinalizerTest.kt | 22 +- sentry/src/test/java/io/sentry/ScopeTest.kt | 24 +- .../test/java/io/sentry/ScopesAdapterTest.kt | 267 ---- ...hedEnvelopeFireAndForgetIntegrationTest.kt | 34 +- .../test/java/io/sentry/SentryClientTest.kt | 104 +- .../io/sentry/SentryExceptionFactoryTest.kt | 187 --- .../test/java/io/sentry/SentryOptionsTest.kt | 5 - sentry/src/test/java/io/sentry/SentryTest.kt | 189 ++- .../test/java/io/sentry/SentryTracerTest.kt | 110 +- .../test/java/io/sentry/SentryWrapperTest.kt | 40 +- .../io/sentry/ShutdownHookIntegrationTest.kt | 24 +- sentry/src/test/java/io/sentry/SpanTest.kt | 79 +- sentry/src/test/java/io/sentry/StackTest.kt | 3 +- .../sentry/TraceContextSerializationTest.kt | 11 +- ...UncaughtExceptionHandlerIntegrationTest.kt | 115 +- .../backpressure/BackpressureMonitorTest.kt | 12 +- .../sentry/clientreport/ClientReportTest.kt | 39 +- .../file/FileIOSpanManagerTest.kt | 14 +- .../file/SentryFileInputStreamTest.kt | 22 +- .../file/SentryFileOutputStreamTest.kt | 12 +- .../file/SentryFileReaderTest.kt | 12 +- .../file/SentryFileWriterTest.kt | 12 +- .../internal/SpotlightIntegrationTest.kt | 10 +- .../sentry/metrics/MetricsIntegrationTest.kt | 7 - .../CombinedContextsViewSerializationTest.kt | 89 -- .../java/io/sentry/protocol/SentrySpanTest.kt | 7 +- .../io/sentry/transport/RateLimiterTest.kt | 38 +- .../java/io/sentry/util/CheckInUtilsTest.kt | 113 +- .../java/io/sentry/util/TracingUtilsTest.kt | 31 +- settings.gradle.kts | 11 +- 456 files changed, 6292 insertions(+), 15860 deletions(-) delete mode 100755 scripts/update-sentry-native-ndk.sh create mode 100644 sentry-android-ndk/CMakeLists.txt create mode 160000 sentry-android-ndk/sentry-native create mode 100644 sentry-android-ndk/src/main/java/io/sentry/android/ndk/INativeScope.java create mode 100644 sentry-android-ndk/src/main/java/io/sentry/android/ndk/NativeModuleListLoader.java create mode 100644 sentry-android-ndk/src/main/java/io/sentry/android/ndk/NativeScope.java create mode 100644 sentry-android-ndk/src/main/jni/sentry.c create mode 100644 sentry-android-okhttp/api/sentry-android-okhttp.api create mode 100644 sentry-android-okhttp/build.gradle.kts create mode 100644 sentry-android-okhttp/proguard-rules.pro create mode 100644 sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpEventListener.kt create mode 100644 sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpInterceptor.kt create mode 100644 sentry-android-okhttp/src/main/res/values/public.xml delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/build.gradle.kts delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OpenTelemetryUtil.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelStorageToken.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextStorage.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextWrapper.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelThreadLocalStorage.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryWeakSpanStorage.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelInternalSpanDetectionUtil.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSamplingUtil.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentryPropagator.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentrySpanProcessor.java rename sentry-opentelemetry/{sentry-opentelemetry-bootstrap => sentry-opentelemetry-core}/src/main/java/io/sentry/opentelemetry/SentryOtelKeys.java (79%) delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySamplingResult.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java delete mode 100644 sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanNode.java delete mode 100644 sentry/src/main/java/io/sentry/CombinedContextsView.java delete mode 100644 sentry/src/main/java/io/sentry/CombinedScopeView.java delete mode 100644 sentry/src/main/java/io/sentry/DefaultScopesStorage.java delete mode 100644 sentry/src/main/java/io/sentry/DefaultSpanFactory.java rename sentry/src/main/java/io/sentry/{Scopes.java => Hub.java} (57%) delete mode 100644 sentry/src/main/java/io/sentry/HubScopesWrapper.java delete mode 100644 sentry/src/main/java/io/sentry/IScopes.java delete mode 100644 sentry/src/main/java/io/sentry/IScopesStorage.java delete mode 100644 sentry/src/main/java/io/sentry/ISentryLifecycleToken.java delete mode 100644 sentry/src/main/java/io/sentry/ISpanFactory.java delete mode 100644 sentry/src/main/java/io/sentry/NoOpScopes.java delete mode 100644 sentry/src/main/java/io/sentry/NoOpScopesLifecycleToken.java delete mode 100644 sentry/src/main/java/io/sentry/NoOpScopesStorage.java delete mode 100644 sentry/src/main/java/io/sentry/NoOpSpanFactory.java delete mode 100644 sentry/src/main/java/io/sentry/ScopeType.java delete mode 100644 sentry/src/main/java/io/sentry/ScopesAdapter.java delete mode 100644 sentry/src/main/java/io/sentry/ScopesStorageFactory.java delete mode 100644 sentry/src/main/java/io/sentry/SentrySpanFactoryHolder.java delete mode 100644 sentry/src/main/java/io/sentry/internal/eventprocessor/EventProcessorAndOrder.java delete mode 100644 sentry/src/main/java/io/sentry/util/EventProcessorUtils.java delete mode 100644 sentry/src/main/java/io/sentry/util/LifecycleHelper.java delete mode 100644 sentry/src/main/java/io/sentry/util/LoadClass.java delete mode 100644 sentry/src/main/java/io/sentry/util/SpanUtils.java delete mode 100644 sentry/src/test/java/io/sentry/CombinedContextsViewTest.kt delete mode 100644 sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt rename sentry/src/test/java/io/sentry/{ScopesTest.kt => HubTest.kt} (72%) delete mode 100644 sentry/src/test/java/io/sentry/ScopesAdapterTest.kt delete mode 100644 sentry/src/test/java/io/sentry/protocol/CombinedContextsViewSerializationTest.kt diff --git a/.craft.yml b/.craft.yml index c08a321343..d50705f433 100644 --- a/.craft.yml +++ b/.craft.yml @@ -37,6 +37,7 @@ targets: maven:io.sentry:sentry-android-core: maven:io.sentry:sentry-android-ndk: maven:io.sentry:sentry-android-timber: + maven:io.sentry:sentry-android-okhttp: maven:io.sentry:sentry-kotlin-extensions: maven:io.sentry:sentry-android-fragment: maven:io.sentry:sentry-bom: diff --git a/.github/ISSUE_TEMPLATE/bug_report_android.yml b/.github/ISSUE_TEMPLATE/bug_report_android.yml index 20db87e363..9b6bfc9ff6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report_android.yml +++ b/.github/ISSUE_TEMPLATE/bug_report_android.yml @@ -10,13 +10,13 @@ body: options: - sentry-android - sentry-android-ndk + - sentry-android-okhttp - sentry-android-timber - sentry-android-fragment - sentry-android-sqlite - sentry-apollo - - sentry-apollo-3 - sentry-compose - - sentry-okhttp + - sentry-apollo-3 - other validations: required: true diff --git a/.github/workflows/agp-matrix.yml b/.github/workflows/agp-matrix.yml index a9c4293292..1f0a46ae97 100644 --- a/.github/workflows/agp-matrix.yml +++ b/.github/workflows/agp-matrix.yml @@ -38,7 +38,7 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@2cd2a6e951bd0b53f55a08e4e4c6f2586f3a36b9 # pin@v3 + uses: gradle/actions/setup-gradle@579fbbe7221704325eb4c4d4bf20c2b0859fba76 # pin@v3 with: gradle-home-cache-cleanup: true @@ -82,7 +82,7 @@ jobs: **/build/outputs/mapping/release/* - name: Test Report - uses: phoenix-actions/test-reporting@f957cd93fc2d848d556fa0d03c57bc79127b6b5e # pin@v15 + uses: phoenix-actions/test-reporting@41efe7ebebe7ef156ef46f6b0acf50ec0f10315b # pin@v12 if: always() with: name: JUnit AGP ${{ matrix.agp }} - Integrations ${{ matrix.integrations }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 969ad6135e..4220bca310 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@2cd2a6e951bd0b53f55a08e4e4c6f2586f3a36b9 # pin@v3 + uses: gradle/actions/setup-gradle@579fbbe7221704325eb4c4d4bf20c2b0859fba76 # pin@v3 with: gradle-home-cache-cleanup: true @@ -50,7 +50,7 @@ jobs: **/build/reports/* - name: Test Report - uses: phoenix-actions/test-reporting@f957cd93fc2d848d556fa0d03c57bc79127b6b5e # pin@v15 + uses: phoenix-actions/test-reporting@41efe7ebebe7ef156ef46f6b0acf50ec0f10315b # pin@v12 if: always() with: name: JUnit Build diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 6636dc019d..5d09ac72cd 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -36,12 +36,12 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@2cd2a6e951bd0b53f55a08e4e4c6f2586f3a36b9 # pin@v3 + uses: gradle/actions/setup-gradle@579fbbe7221704325eb4c4d4bf20c2b0859fba76 # pin@v3 with: gradle-home-cache-cleanup: true - name: Initialize CodeQL - uses: github/codeql-action/init@23acc5c183826b7a8a97bce3cecc52db901f8251 # pin@v2 + uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a # pin@v2 with: languages: ${{ matrix.language }} @@ -55,4 +55,4 @@ jobs: ./gradlew buildForCodeQL - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@23acc5c183826b7a8a97bce3cecc52db901f8251 # pin@v2 + uses: github/codeql-action/analyze@cdcdbb579706841c47f7063dda365e292e5cad7a # pin@v2 diff --git a/.github/workflows/enforce-license-compliance.yml b/.github/workflows/enforce-license-compliance.yml index 2c93ed9e4b..c31fec539e 100644 --- a/.github/workflows/enforce-license-compliance.yml +++ b/.github/workflows/enforce-license-compliance.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Setup Gradle - uses: gradle/actions/setup-gradle@2cd2a6e951bd0b53f55a08e4e4c6f2586f3a36b9 # pin@v3 + uses: gradle/actions/setup-gradle@579fbbe7221704325eb4c4d4bf20c2b0859fba76 # pin@v3 with: gradle-home-cache-cleanup: true diff --git a/.github/workflows/generate-javadocs.yml b/.github/workflows/generate-javadocs.yml index 635a87609b..858cc3f82d 100644 --- a/.github/workflows/generate-javadocs.yml +++ b/.github/workflows/generate-javadocs.yml @@ -20,7 +20,7 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@2cd2a6e951bd0b53f55a08e4e4c6f2586f3a36b9 # pin@v3 + uses: gradle/actions/setup-gradle@579fbbe7221704325eb4c4d4bf20c2b0859fba76 # pin@v3 with: gradle-home-cache-cleanup: true diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index d4981c5583..e99aa31f9a 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -13,4 +13,4 @@ jobs: - uses: actions/checkout@v4 with: submodules: 'recursive' - - uses: gradle/wrapper-validation-action@88425854a36845f9c881450d9660b5fd46bee142 # pin@v1 + - uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 # pin@v1 diff --git a/.github/workflows/integration-tests-benchmarks.yml b/.github/workflows/integration-tests-benchmarks.yml index 2e885359ad..2b33b28528 100644 --- a/.github/workflows/integration-tests-benchmarks.yml +++ b/.github/workflows/integration-tests-benchmarks.yml @@ -37,7 +37,7 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@2cd2a6e951bd0b53f55a08e4e4c6f2586f3a36b9 # pin@v3 + uses: gradle/actions/setup-gradle@579fbbe7221704325eb4c4d4bf20c2b0859fba76 # pin@v3 with: gradle-home-cache-cleanup: true @@ -47,7 +47,7 @@ jobs: run: make assembleBenchmarks - name: Run All Tests in SauceLab - uses: saucelabs/saucectl-run-action@39e4f0666ca8ecb4b60847213c6e0fbd6a0c2bd8 # pin@v3 + uses: saucelabs/saucectl-run-action@7fe025ef1fdc6f211add3751a6c7d8bba27ba9b1 # pin@v3 if: github.event_name != 'pull_request' && env.SAUCE_USERNAME != null env: GITHUB_TOKEN: ${{ github.token }} @@ -57,7 +57,7 @@ jobs: config-file: .sauce/sentry-uitest-android-benchmark.yml - name: Run one test in SauceLab - uses: saucelabs/saucectl-run-action@39e4f0666ca8ecb4b60847213c6e0fbd6a0c2bd8 # pin@v3 + uses: saucelabs/saucectl-run-action@7fe025ef1fdc6f211add3751a6c7d8bba27ba9b1 # pin@v3 if: github.event_name == 'pull_request' && env.SAUCE_USERNAME != null env: GITHUB_TOKEN: ${{ github.token }} @@ -86,7 +86,7 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@2cd2a6e951bd0b53f55a08e4e4c6f2586f3a36b9 # pin@v3 + uses: gradle/actions/setup-gradle@579fbbe7221704325eb4c4d4bf20c2b0859fba76 # pin@v3 with: gradle-home-cache-cleanup: true diff --git a/.github/workflows/integration-tests-ui.yml b/.github/workflows/integration-tests-ui.yml index cd5134d38f..1d7038dd0a 100644 --- a/.github/workflows/integration-tests-ui.yml +++ b/.github/workflows/integration-tests-ui.yml @@ -32,7 +32,7 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@2cd2a6e951bd0b53f55a08e4e4c6f2586f3a36b9 # pin@v3 + uses: gradle/actions/setup-gradle@579fbbe7221704325eb4c4d4bf20c2b0859fba76 # pin@v3 with: gradle-home-cache-cleanup: true @@ -42,7 +42,7 @@ jobs: run: make assembleUiTests - name: Install SauceLabs CLI - uses: saucelabs/saucectl-run-action@39e4f0666ca8ecb4b60847213c6e0fbd6a0c2bd8 # pin@v3 + uses: saucelabs/saucectl-run-action@7fe025ef1fdc6f211add3751a6c7d8bba27ba9b1 # pin@v3 env: GITHUB_TOKEN: ${{ github.token }} with: diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index b021a6d8ec..a5449ed95b 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -26,7 +26,7 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@2cd2a6e951bd0b53f55a08e4e4c6f2586f3a36b9 # pin@v3 + uses: gradle/actions/setup-gradle@579fbbe7221704325eb4c4d4bf20c2b0859fba76 # pin@v3 with: gradle-home-cache-cleanup: true diff --git a/.github/workflows/system-tests-backend.yml b/.github/workflows/system-tests-backend.yml index 3ea1b601c9..a916d8f9ac 100644 --- a/.github/workflows/system-tests-backend.yml +++ b/.github/workflows/system-tests-backend.yml @@ -40,13 +40,13 @@ jobs: java-version: '17' - name: Setup Gradle - uses: gradle/actions/setup-gradle@2cd2a6e951bd0b53f55a08e4e4c6f2586f3a36b9 # pin@v3 + uses: gradle/actions/setup-gradle@579fbbe7221704325eb4c4d4bf20c2b0859fba76 # pin@v3 with: gradle-home-cache-cleanup: true - name: Exclude android modules from build run: | - sed -i -e '/.*"sentry-android-ndk",/d' -e '/.*"sentry-android",/d' -e '/.*"sentry-compose",/d' -e '/.*"sentry-android-core",/d' -e '/.*"sentry-android-fragment",/d' -e '/.*"sentry-android-navigation",/d' -e '/.*"sentry-android-sqlite",/d' -e '/.*"sentry-android-timber",/d' -e '/.*"sentry-android-integration-tests:sentry-uitest-android-benchmark",/d' -e '/.*"sentry-android-integration-tests:sentry-uitest-android",/d' -e '/.*"sentry-android-integration-tests:test-app-sentry",/d' -e '/.*"sentry-samples:sentry-samples-android",/d' settings.gradle.kts + sed -i -e '/.*"sentry-android-ndk",/d' -e '/.*"sentry-android",/d' -e '/.*"sentry-compose",/d' -e '/.*"sentry-android-core",/d' -e '/.*"sentry-android-fragment",/d' -e '/.*"sentry-android-navigation",/d' -e '/.*"sentry-android-okhttp",/d' -e '/.*"sentry-android-sqlite",/d' -e '/.*"sentry-android-timber",/d' -e '/.*"sentry-android-integration-tests:sentry-uitest-android-benchmark",/d' -e '/.*"sentry-android-integration-tests:sentry-uitest-android",/d' -e '/.*"sentry-android-integration-tests:test-app-sentry",/d' -e '/.*"sentry-samples:sentry-samples-android",/d' settings.gradle.kts - name: Exclude android modules from ignore list run: | @@ -71,7 +71,7 @@ jobs: spring-server.txt - name: Test Report - uses: phoenix-actions/test-reporting@f957cd93fc2d848d556fa0d03c57bc79127b6b5e # pin@v15 + uses: phoenix-actions/test-reporting@41efe7ebebe7ef156ef46f6b0acf50ec0f10315b # pin@v12 if: always() with: name: JUnit System Tests ${{ matrix.sample }} diff --git a/.github/workflows/update-deps.yml b/.github/workflows/update-deps.yml index 83d90bb919..24fce64050 100644 --- a/.github/workflows/update-deps.yml +++ b/.github/workflows/update-deps.yml @@ -13,7 +13,7 @@ jobs: native: uses: getsentry/github-workflows/.github/workflows/updater.yml@v2 with: - path: scripts/update-sentry-native-ndk.sh + path: sentry-android-ndk/sentry-native name: Native SDK secrets: # If a custom token is used instead, a CI would be triggered on a created PR. diff --git a/.gitmodules b/.gitmodules index e69de29bb2..fe6c3b7cc0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "sentry-android-ndk/sentry-native"] + path = sentry-android-ndk/sentry-native + url = https://github.com/getsentry/sentry-native diff --git a/CHANGELOG.md b/CHANGELOG.md index 08906d1df4..fd1866bf5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,184 +2,17 @@ ## Unreleased -### Breaking Changes - -- `sentry-android-okhttp` has been removed in favor of `sentry-okhttp`, removing android dependency from the module ([#3510](https://github.com/getsentry/sentry-java/pull/3510)) - -### Fixes - -- Support spans that are split into multiple batches ([#3539](https://github.com/getsentry/sentry-java/pull/3539)) - - When spans belonging to a single transaction were split into multiple batches for SpanExporter, we did not add all spans because the isSpanTooOld check wasn't inverted. -- Parse and use `send-default-pii` and `max-request-body-size` from `sentry.properties` ([#3534](https://github.com/getsentry/sentry-java/pull/3534)) - -### Dependencies - -- Bump Spring Boot Jakarta from 3.2.0 to 3.3.0 - -## 8.0.0-alpha.2 - -### Behavioural Changes - -- (Android) The JNI layer for sentry-native has now been moved from sentry-java to sentry-native ([#3189](https://github.com/getsentry/sentry-java/pull/3189)) - - This now includes prefab support for sentry-native, allowing you to link and access the sentry-native API within your native app code - - Checkout the `sentry-samples/sentry-samples-android` example on how to configure CMake and consume `sentry.h` - -### Features - -- Our `sentry-opentelemetry-agent` has been completely reworked and now plays nicely with the rest of the Java SDK - - You may also want to give this new agent a try even if you haven't used OpenTelemetry (with Sentry) before. It offers support for [many more libraries and frameworks](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md), improving on our trace propagation, `Scopes` (used to be `Hub`) propagation as well as performance instrumentation (i.e. more spans). - - If you are using a framework we did not support before and currently resort to manual instrumentation, please give the agent a try. See [here for a list of supported libraries, frameworks and application servers](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/supported-libraries.md). - - NOTE: Not all features have been implemented yet for the OpenTelemetry agent. Features of note that are not working yet: - - Metrics - - Measurements - - `forceFinish` on transaction - - `scheduleFinish` on transaction - - see [#3436](https://github.com/getsentry/sentry-java/issues/3436) for a more up-to-date list of features we have (not) implemented - - Please see "Installing `sentry-opentelemetry-agent`" for more details on how to set up the agent. - - What's new about the Agent - - When the OpenTelemetry Agent is used, Sentry API creates OpenTelemetry spans under the hood, handing back a wrapper object which bridges the gap between traditional Sentry API and OpenTelemetry. We might be replacing some of the Sentry performance API in the future. - - This is achieved by configuring the SDK to use `OtelSpanFactory` instead of `DefaultSpanFactory` which is done automatically by the auto init of the Java Agent. - - OpenTelemetry spans are now only turned into Sentry spans when they are finished so they can be sent to the Sentry server. - - Now registers an OpenTelemetry `Sampler` which uses Sentry sampling configuration - - Other Performance integrations automatically stop creating spans to avoid duplicate spans - - The Sentry SDK now makes use of OpenTelemetry `Context` for storing Sentry `Scopes` (which is similar to what used to be called `Hub`) and thus relies on OpenTelemetry for `Context` propagation. - - Classes used for the previous version of our OpenTelemetry support have been deprecated but can still be used manually. We're not planning to keep the old agent around in favor of less complexity in the SDK. -- Add `ignoredSpanOrigins` option for ignoring spans coming from certain integrations - - We pre-configure this to ignore Performance instrumentation for Spring and other integrations when using our OpenTelemetry Agent to avoid duplicate spans -- Add data fetching environment hint to breadcrumb for GraphQL (#3413) ([#3431](https://github.com/getsentry/sentry-java/pull/3431)) - -### Fixes - -- `TracesSampler` is now only created once in `SentryOptions` instead of creating a new one for every `Hub` (which is now `Scopes`). This means we're now creating fewer `SecureRandom` instances. -- Move onFinishCallback before span or transaction is finished ([#3459](https://github.com/getsentry/sentry-java/pull/3459)) -- Add timestamp when a profile starts ([#3442](https://github.com/getsentry/sentry-java/pull/3442)) -- Move fragment auto span finish to onFragmentStarted ([#3424](https://github.com/getsentry/sentry-java/pull/3424)) -- Remove profiling timeout logic and disable profiling on API 21 ([#3478](https://github.com/getsentry/sentry-java/pull/3478)) -- Properly reset metric flush flag on metric emission ([#3493](https://github.com/getsentry/sentry-java/pull/3493)) - -### Migration Guide / Deprecations - -- Classes used for the previous version of the Sentry OpenTelemetry Java Agent have been deprecated (`SentrySpanProcessor`, `SentryPropagator`, `OpenTelemetryLinkErrorEventProcessor`) -- Sentry OpenTelemetry Java Agent has been reworked and now allows you to manually create spans using Sentry API as well. -- Please see "Installing `sentry-opentelemetry-agent`" for more details on how to set up the agent. - -### Installing `sentry-opentelemetry-agent` - -#### Upgrading from a previous agent -If you've been using the previous version of `sentry-opentelemetry-agent`, simply replace the agent JAR with the [latest release](https://central.sonatype.com/artifact/io.sentry/sentry-opentelemetry-agent?smo=true) and start your application. That should be it. - -#### New to the agent -If you've not been using OpenTelemetry before, you can add `sentry-opentelemetry-agent` to your setup by downloading the latest release and using it when starting up your application - - `SENTRY_PROPERTIES_FILE=sentry.properties java -javaagent:sentry-opentelemetry-agent-x.x.x.jar -jar your-application.jar` - - Please use `sentry.properties` or environment variables to configure the SDK as the agent is now in charge of initializing the SDK and options coming from things like logging integrations or our Spring Boot integration will not take effect. - - You may find the [docs page](https://docs.sentry.io/platforms/java/tracing/instrumentation/opentelemetry/#using-sentry-opentelemetry-agent-with-auto-initialization) useful. While we haven't updated it yet to reflect the changes described here, the section about using the agent with auto init should still be valid. - -If you want to skip auto initialization of the SDK performed by the agent, please follow the steps above and set the environment variable `SENTRY_AUTO_INIT` to `false` then add the following to your `Sentry.init`: - -``` -Sentry.init(options -> { - options.setDsn("https://3d2ac63d6e1a4c6e9214443678f119a3@o87286.ingest.us.sentry.io/1801383"); - OpenTelemetryUtil.applyOpenTelemetryOptions(options); - ... -}); -``` - -If you're using our Spring (Boot) integration with auto init, use the following: -``` -@Bean -Sentry.OptionsConfiguration optionsConfiguration() { - return (options) -> { - OpenTelemetryUtil.applyOpenTelemetryOptions(options); - }; -} -``` - -### Dependencies - -- Bump Native SDK from v0.7.0 to v0.7.5 ([#3441](https://github.com/getsentry/sentry-java/pull/3189)) - - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#075) - - [diff](https://github.com/getsentry/sentry-native/compare/0.7.0...0.7.5) - -## 8.0.0-alpha.1 - -Version 8 of the Sentry Android/Java SDK brings a variety of features and fixes. The most notable changes are: - -- New `Scope` types have been introduced, see "Behavioural Changes" for more details. -- Lifecycle tokens have been introduced to manage `Scope` lifecycle, see "Behavioural Changes" for more details. -- `Hub` has been replaced by `Scopes` - -### Behavioural Changes - -- We're introducing some new `Scope` types in the SDK, allowing for better control over what data is attached where. Previously there was a stack of scopes that was pushed and popped. Instead we now fork scopes for a given lifecycle and then restore the previous scopes. Since `Hub` is gone, it is also never cloned anymore. Separation of data now happens through the different scope types while making it easier to manipulate exactly what you need without having to attach data at the right time to have it apply where wanted. - - Global scope is attached to all events created by the SDK. It can also be modified before `Sentry.init` has been called. It can be manipulated using `Sentry.configureScope(ScopeType.GLOBAL, (scope) -> { ... })`. - - Isolation scope can be used e.g. to attach data to all events that come up while handling an incoming request. It can also be used for other isolation purposes. It can be manipulated using `Sentry.configureScope(ScopeType.ISOLATION, (scope) -> { ... })`. The SDK automatically forks isolation scope in certain cases like incoming requests, CRON jobs, Spring `@Async` and more. - - Current scope is forked often and data added to it is only added to events that are created while this scope is active. Data is also passed on to newly forked child scopes but not to parents. -- `Sentry.popScope` has been deprecated, please call `.close()` on the token returned by `Sentry.pushScope` instead or use it in a way described in more detail in "Migration Guide". -- We have chosen a default scope that is used for `Sentry.configureScope()` as well as API like `Sentry.setTag()` - - For Android the type defaults to `CURRENT` scope - - For Backend and other JVM applicatons it defaults to `ISOLATION` scope -- Event processors on `Scope` can now be ordered by overriding the `getOrder` method on implementations of `EventProcessor`. NOTE: This order only applies to event processors on `Scope` but not `SentryOptions` at the moment. Feel free to request this if you need it. -- `Hub` is deprecated in favor of `Scopes`, alongside some `Hub` relevant APIs. More details can be found in the "Migration Guide" section. - -### Breaking Changes - -- `Contexts` no longer extends `ConcurrentHashMap`, instead we offer a selected set of methods. - -### Migration Guide / Deprecations - -- `Hub` has been deprecated, we're replacing the following: - - `IHub` has been replaced by `IScopes`, however you should be able to simply pass `IHub` instances to code expecting `IScopes`, allowing for an easier migration. - - `HubAdapter.getInstance()` has been replaced by `ScopesAdapter.getInstance()` - - The `.clone()` method on `IHub`/`IScopes` has been deprecated, please use `.pushScope()` or `.pushIsolationScope()` instead - - Some internal methods like `.getCurrentHub()` and `.setCurrentHub()` have also been replaced. -- `Sentry.popScope` has been replaced by calling `.close()` on the token returned by `Sentry.pushScope()` and `Sentry.pushIsolationScope()`. The token can also be used in a `try` block like this: - -``` -try (final @NotNull ISentryLifecycleToken ignored = Sentry.pushScope()) { - // this block has its separate current scope -} -``` - -as well as: - - -``` -try (final @NotNull ISentryLifecycleToken ignored = Sentry.pushIsolationScope()) { - // this block has its separate isolation scope -} -``` - -You may also use `LifecycleHelper.close(token)`, e.g. in case you need to pass the token around for closing later. - -### Features - -- Report exceptions returned by Throwable.getSuppressed() to Sentry as exception groups ([#3396] https://github.com/getsentry/sentry-java/pull/3396) - -## 7.11.0 - -### Features - -- Report dropped spans ([#3528](https://github.com/getsentry/sentry-java/pull/3528)) - ### Fixes -- Fix duplicate session start for React Native ([#3504](https://github.com/getsentry/sentry-java/pull/3504)) - Move onFinishCallback before span or transaction is finished ([#3459](https://github.com/getsentry/sentry-java/pull/3459)) - Add timestamp when a profile starts ([#3442](https://github.com/getsentry/sentry-java/pull/3442)) - Move fragment auto span finish to onFragmentStarted ([#3424](https://github.com/getsentry/sentry-java/pull/3424)) - Remove profiling timeout logic and disable profiling on API 21 ([#3478](https://github.com/getsentry/sentry-java/pull/3478)) - Properly reset metric flush flag on metric emission ([#3493](https://github.com/getsentry/sentry-java/pull/3493)) -- Use SecureRandom in favor of Random for Metrics ([#3495](https://github.com/getsentry/sentry-java/pull/3495)) -- Fix UncaughtExceptionHandlerIntegration Memory Leak ([#3398](https://github.com/getsentry/sentry-java/pull/3398)) -- Deprecated `User.segment`. Use a custom tag or context instead. ([#3511](https://github.com/getsentry/sentry-java/pull/3511)) -- Fix duplicated http spans ([#3526](https://github.com/getsentry/sentry-java/pull/3526)) -- When capturing unhandled hybrid exception session should be ended and new start if need ([#3480](https://github.com/getsentry/sentry-java/pull/3480)) ### Dependencies -- Bump Native SDK from v0.7.0 to v0.7.2 ([#3314](https://github.com/getsentry/sentry-java/pull/3314)) - - [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#072) - - [diff](https://github.com/getsentry/sentry-native/compare/0.7.0...0.7.2) +- Bump Spring Boot Jakarta from 3.2.0 to 3.3.0 ## 7.10.0 diff --git a/README.md b/README.md index d6107cd267..338a59eba5 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Sentry SDK for Java and Android | sentry-android | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android) | 19 | | sentry-android-core | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-core) | 19 | | sentry-android-ndk | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-ndk/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-ndk) | 19 | +| sentry-android-okhttp | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-okhttp/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-okhttp) | 21 | | sentry-android-timber | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-timber/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-timber) | 19 | | sentry-android-fragment | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-fragment/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-fragment) | 19 | | sentry-android-navigation | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-navigation/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.sentry/sentry-android-navigation) | 19 | diff --git a/build.gradle.kts b/build.gradle.kts index 4b84c17aba..998c547efb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,6 +32,10 @@ buildscript { classpath(Config.QualityPlugins.errorpronePlugin) classpath(Config.QualityPlugins.gradleVersionsPlugin) + // add classpath of androidNativeBundle + // com.ydq.android.gradle.build.tool:nativeBundle:{version}} + classpath(Config.NativePlugins.nativeBundlePlugin) + // add classpath of sentry android gradle plugin // classpath("io.sentry:sentry-android-gradle-plugin:{version}") @@ -74,7 +78,6 @@ allprojects { repositories { google() mavenCentral() - mavenLocal() } group = Config.Sentry.group version = properties[Config.Sentry.versionNameProp].toString() @@ -96,7 +99,7 @@ allprojects { dependsOn("cleanTest") } withType { - options.compilerArgs.addAll(arrayOf("-Xlint:all", "-Werror", "-Xlint:-classfile", "-Xlint:-processing", "-Xlint:-try")) + options.compilerArgs.addAll(arrayOf("-Xlint:all", "-Werror", "-Xlint:-classfile", "-Xlint:-processing")) } } } @@ -107,6 +110,7 @@ subprojects { "sentry-android-fragment", "sentry-android-navigation", "sentry-android-ndk", + "sentry-android-okhttp", "sentry-android-sqlite", "sentry-android-timber" ) @@ -290,6 +294,7 @@ private val androidLibs = setOf( "sentry-android-ndk", "sentry-android-fragment", "sentry-android-navigation", + "sentry-android-okhttp", "sentry-android-timber", "sentry-compose-android" ) diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index d6fab2dbdc..697f509ebb 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -151,9 +151,9 @@ object Config { val apolloKotlin = "com.apollographql.apollo3:apollo-runtime:3.8.2" object OpenTelemetry { - val otelVersion = "1.39.0" + val otelVersion = "1.33.0" val otelAlphaVersion = "$otelVersion-alpha" - val otelJavaagentVersion = "2.5.0" + val otelJavaagentVersion = "1.32.0" val otelJavaagentAlphaVersion = "$otelJavaagentVersion-alpha" val otelSemanticConvetionsVersion = "1.23.1-alpha" @@ -199,9 +199,7 @@ object Config { object QualityPlugins { object Jacoco { val version = "0.8.7" - - // TODO [POTEL] add tests and restore - val minimumCoverage = BigDecimal.valueOf(0.1) + val minimumCoverage = BigDecimal.valueOf(0.6) } val spotless = "com.diffplug.spotless" val spotlessVersion = "6.11.0" @@ -256,4 +254,9 @@ object Config { val errorprone = "com.google.errorprone:error_prone_core:2.11.0" val errorProneNullAway = "com.uber.nullaway:nullaway:0.9.5" } + + object NativePlugins { + val nativeBundlePlugin = "io.github.howardpang:androidNativeBundle:1.1.1" + val nativeBundleExport = "com.ydq.android.gradle.native-aar.export" + } } diff --git a/gradle.properties b/gradle.properties index 0a136e76c0..43afd204d7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,7 +10,7 @@ android.useAndroidX=true android.defaults.buildfeatures.buildconfig=true # Release information -versionName=8.0.0-alpha.2 +versionName=7.10.0 # Override the SDK name on native crashes on Android sentryAndroidSdkName=sentry.native.android diff --git a/scripts/update-sentry-native-ndk.sh b/scripts/update-sentry-native-ndk.sh deleted file mode 100755 index 544dc403ac..0000000000 --- a/scripts/update-sentry-native-ndk.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -cd $(dirname "$0")/../ -GRADLE_NDK_FILEPATH=sentry-android-ndk/build.gradle.kts -GRADLE_SAMPLE_FILEPATH=sentry-samples/sentry-samples-android/build.gradle.kts - -case $1 in -get-version) - version=$(perl -ne 'print "$1\n" if ( m/io\.sentry:sentry-native-ndk:([0-9.]+)+/ )' $GRADLE_NDK_FILEPATH) - - echo "v$version" - ;; -get-repo) - echo "https://github.com/getsentry/sentry-native.git" - ;; -set-version) - version=$2 - - # Remove leading "v" - if [[ "$version" == v* ]]; then - version="${version:1}" - fi - - echo "Setting sentry-native-ndk version to '$version'" - - PATTERN="io\.sentry:sentry-native-ndk:([0-9.]+)+" - perl -pi -e "s/$PATTERN/io.sentry:sentry-native-ndk:$version/g" $GRADLE_NDK_FILEPATH - perl -pi -e "s/$PATTERN/io.sentry:sentry-native-ndk:$version/g" $GRADLE_SAMPLE_FILEPATH - ;; -*) - echo "Unknown argument $1" - exit 1 - ;; -esac diff --git a/sentry-android-core/api/sentry-android-core.api b/sentry-android-core/api/sentry-android-core.api index c5b63f889b..97f7cddb7c 100644 --- a/sentry-android-core/api/sentry-android-core.api +++ b/sentry-android-core/api/sentry-android-core.api @@ -8,12 +8,12 @@ public final class io/sentry/android/core/ActivityBreadcrumbsIntegration : andro public fun onActivitySaveInstanceState (Landroid/app/Activity;Landroid/os/Bundle;)V public fun onActivityStarted (Landroid/app/Activity;)V public fun onActivityStopped (Landroid/app/Activity;)V - public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/ActivityFramesTracker { - public fun (Lio/sentry/util/LoadClass;Lio/sentry/android/core/SentryAndroidOptions;)V - public fun (Lio/sentry/util/LoadClass;Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/android/core/MainLooperHandler;)V + public fun (Lio/sentry/android/core/LoadClass;Lio/sentry/android/core/SentryAndroidOptions;)V + public fun (Lio/sentry/android/core/LoadClass;Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/android/core/MainLooperHandler;)V public fun addActivity (Landroid/app/Activity;)V public fun isFrameMetricsAggregatorAvailable ()Z public fun setMetrics (Landroid/app/Activity;Lio/sentry/protocol/SentryId;)V @@ -33,7 +33,7 @@ public final class io/sentry/android/core/ActivityLifecycleIntegration : android public fun onActivitySaveInstanceState (Landroid/app/Activity;Landroid/os/Bundle;)V public fun onActivityStarted (Landroid/app/Activity;)V public fun onActivityStopped (Landroid/app/Activity;)V - public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/AndroidCpuCollector : io/sentry/IPerformanceSnapshotCollector { @@ -88,7 +88,7 @@ public class io/sentry/android/core/AndroidProfiler$ProfileStartData { public final class io/sentry/android/core/AnrIntegration : io/sentry/Integration, java/io/Closeable { public fun (Landroid/content/Context;)V public fun close ()V - public final fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public final fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/AnrIntegrationFactory { @@ -98,7 +98,6 @@ public final class io/sentry/android/core/AnrIntegrationFactory { public final class io/sentry/android/core/AnrV2EventProcessor : io/sentry/BackfillingEventProcessor { public fun (Landroid/content/Context;Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/android/core/BuildInfoProvider;)V - public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; public fun process (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryTransaction; } @@ -106,7 +105,7 @@ public final class io/sentry/android/core/AnrV2EventProcessor : io/sentry/Backfi public class io/sentry/android/core/AnrV2Integration : io/sentry/Integration, java/io/Closeable { public fun (Landroid/content/Context;)V public fun close ()V - public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/AnrV2Integration$AnrV2Hint : io/sentry/hints/BlockingFlushHint, io/sentry/hints/AbnormalExit, io/sentry/hints/Backfillable { @@ -125,13 +124,13 @@ public final class io/sentry/android/core/AppComponentsBreadcrumbsIntegration : public fun onConfigurationChanged (Landroid/content/res/Configuration;)V public fun onLowMemory ()V public fun onTrimMemory (I)V - public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/AppLifecycleIntegration : io/sentry/Integration, java/io/Closeable { public fun ()V public fun close ()V - public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/AppState { @@ -179,7 +178,7 @@ public final class io/sentry/android/core/CurrentActivityIntegration : android/a public fun onActivitySaveInstanceState (Landroid/app/Activity;Landroid/os/Bundle;)V public fun onActivityStarted (Landroid/app/Activity;)V public fun onActivityStopped (Landroid/app/Activity;)V - public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/DeviceInfoUtil { @@ -195,7 +194,7 @@ public abstract class io/sentry/android/core/EnvelopeFileObserverIntegration : i public fun ()V public fun close ()V public static fun getOutboxFileObserver ()Lio/sentry/android/core/EnvelopeFileObserverIntegration; - public final fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public final fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public abstract interface class io/sentry/android/core/IDebugImagesLoader { @@ -205,13 +204,13 @@ public abstract interface class io/sentry/android/core/IDebugImagesLoader { public final class io/sentry/android/core/InternalSentrySdk { public fun ()V - public static fun captureEnvelope ([BZ)Lio/sentry/protocol/SentryId; + public static fun captureEnvelope ([B)Lio/sentry/protocol/SentryId; public static fun getAppStartMeasurement ()Ljava/util/Map; public static fun getCurrentScope ()Lio/sentry/IScope; public static fun serializeScope (Landroid/content/Context;Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/IScope;)Ljava/util/Map; } -public final class io/sentry/android/core/LoadClass : io/sentry/util/LoadClass { +public final class io/sentry/android/core/LoadClass { public fun ()V public fun isClassAvailable (Ljava/lang/String;Lio/sentry/ILogger;)Z public fun isClassAvailable (Ljava/lang/String;Lio/sentry/SentryOptions;)Z @@ -222,24 +221,23 @@ public final class io/sentry/android/core/NdkIntegration : io/sentry/Integration public static final field SENTRY_NDK_CLASS_NAME Ljava/lang/String; public fun (Ljava/lang/Class;)V public fun close ()V - public final fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public final fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/NetworkBreadcrumbsIntegration : io/sentry/Integration, java/io/Closeable { public fun (Landroid/content/Context;Lio/sentry/android/core/BuildInfoProvider;Lio/sentry/ILogger;)V public fun close ()V - public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/PhoneStateBreadcrumbsIntegration : io/sentry/Integration, java/io/Closeable { public fun (Landroid/content/Context;)V public fun close ()V - public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/ScreenshotEventProcessor : io/sentry/EventProcessor { public fun (Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/android/core/BuildInfoProvider;)V - public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; public fun process (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryTransaction; } @@ -364,7 +362,7 @@ public final class io/sentry/android/core/SystemEventsBreadcrumbsIntegration : i public fun (Landroid/content/Context;)V public fun (Landroid/content/Context;Ljava/util/List;)V public fun close ()V - public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/TempSensorBreadcrumbsIntegration : android/hardware/SensorEventListener, io/sentry/Integration, java/io/Closeable { @@ -372,11 +370,11 @@ public final class io/sentry/android/core/TempSensorBreadcrumbsIntegration : and public fun close ()V public fun onAccuracyChanged (Landroid/hardware/Sensor;I)V public fun onSensorChanged (Landroid/hardware/SensorEvent;)V - public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/UserInteractionIntegration : android/app/Application$ActivityLifecycleCallbacks, io/sentry/Integration, java/io/Closeable { - public fun (Landroid/app/Application;Lio/sentry/util/LoadClass;)V + public fun (Landroid/app/Application;Lio/sentry/android/core/LoadClass;)V public fun close ()V public fun onActivityCreated (Landroid/app/Activity;Landroid/os/Bundle;)V public fun onActivityDestroyed (Landroid/app/Activity;)V @@ -385,12 +383,11 @@ public final class io/sentry/android/core/UserInteractionIntegration : android/a public fun onActivitySaveInstanceState (Landroid/app/Activity;Landroid/os/Bundle;)V public fun onActivityStarted (Landroid/app/Activity;)V public fun onActivityStopped (Landroid/app/Activity;)V - public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/core/ViewHierarchyEventProcessor : io/sentry/EventProcessor { public fun (Lio/sentry/android/core/SentryAndroidOptions;)V - public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; public fun process (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryTransaction; public static fun snapshotViewHierarchy (Landroid/app/Activity;Lio/sentry/ILogger;)Lio/sentry/protocol/ViewHierarchy; diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityBreadcrumbsIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityBreadcrumbsIntegration.java index 4a5ca63717..dc03abe808 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityBreadcrumbsIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityBreadcrumbsIntegration.java @@ -8,7 +8,7 @@ import android.os.Bundle; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.Integration; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -23,7 +23,7 @@ public final class ActivityBreadcrumbsIntegration implements Integration, Closeable, Application.ActivityLifecycleCallbacks { private final @NotNull Application application; - private @Nullable IScopes scopes; + private @Nullable IHub hub; private boolean enabled; public ActivityBreadcrumbsIntegration(final @NotNull Application application) { @@ -31,13 +31,13 @@ public ActivityBreadcrumbsIntegration(final @NotNull Application application) { } @Override - public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { final SentryAndroidOptions androidOptions = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, "SentryAndroidOptions is required"); - this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); + this.hub = Objects.requireNonNull(hub, "Hub is required"); this.enabled = androidOptions.isEnableActivityLifecycleBreadcrumbs(); options .getLogger() @@ -54,9 +54,8 @@ public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions public void close() throws IOException { if (enabled) { application.unregisterActivityLifecycleCallbacks(this); - if (scopes != null) { - scopes - .getOptions() + if (hub != null) { + hub.getOptions() .getLogger() .log(SentryLevel.DEBUG, "ActivityBreadcrumbsIntegration removed."); } @@ -101,7 +100,7 @@ public synchronized void onActivityDestroyed(final @NotNull Activity activity) { } private void addBreadcrumb(final @NotNull Activity activity, final @NotNull String state) { - if (scopes == null) { + if (hub == null) { return; } @@ -115,7 +114,7 @@ private void addBreadcrumb(final @NotNull Activity activity, final @NotNull Stri final Hint hint = new Hint(); hint.set(ANDROID_ACTIVITY, activity); - scopes.addBreadcrumb(breadcrumb, hint); + hub.addBreadcrumb(breadcrumb, hint); } private @NotNull String getActivityName(final @NotNull Activity activity) { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java index 99d230b305..93f50b3ec1 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java @@ -39,7 +39,7 @@ public final class ActivityFramesTracker { private final @NotNull MainLooperHandler handler; public ActivityFramesTracker( - final @NotNull io.sentry.util.LoadClass loadClass, + final @NotNull LoadClass loadClass, final @NotNull SentryAndroidOptions options, final @NotNull MainLooperHandler handler) { @@ -54,14 +54,13 @@ public ActivityFramesTracker( } public ActivityFramesTracker( - final @NotNull io.sentry.util.LoadClass loadClass, - final @NotNull SentryAndroidOptions options) { + final @NotNull LoadClass loadClass, final @NotNull SentryAndroidOptions options) { this(loadClass, options, new MainLooperHandler()); } @TestOnly ActivityFramesTracker( - final @NotNull io.sentry.util.LoadClass loadClass, + final @NotNull LoadClass loadClass, final @NotNull SentryAndroidOptions options, final @NotNull MainLooperHandler handler, final @Nullable FrameMetricsAggregator frameMetricsAggregator) { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java index 749b9d8f19..1121a6bfe7 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java @@ -12,8 +12,8 @@ import android.view.View; import androidx.annotation.NonNull; import io.sentry.FullyDisplayedReporter; +import io.sentry.IHub; import io.sentry.IScope; -import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.ITransaction; import io.sentry.Instrumenter; @@ -60,7 +60,7 @@ public final class ActivityLifecycleIntegration private final @NotNull Application application; private final @NotNull BuildInfoProvider buildInfoProvider; - private @Nullable IScopes scopes; + private @Nullable IHub hub; private @Nullable SentryAndroidOptions options; private boolean performanceEnabled = false; @@ -102,13 +102,13 @@ public ActivityLifecycleIntegration( } @Override - public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, "SentryAndroidOptions is required"); - this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); + this.hub = Objects.requireNonNull(hub, "Hub is required"); performanceEnabled = isPerformanceEnabled(this.options); fullyDisplayedReporter = this.options.getFullyDisplayedReporter(); @@ -150,10 +150,10 @@ private void stopPreviousTransactions() { private void startTracing(final @NotNull Activity activity) { WeakReference weakActivity = new WeakReference<>(activity); - if (scopes != null && !isRunningTransactionOrTrace(activity)) { + if (hub != null && !isRunningTransactionOrTrace(activity)) { if (!performanceEnabled) { activitiesWithOngoingTransactions.put(activity, NoOpTransaction.getInstance()); - TracingUtils.startNewTrace(scopes); + TracingUtils.startNewTrace(hub); } else { // as we allow a single transaction running on the bound Scope, we finish the previous ones stopPreviousTransactions(); @@ -225,7 +225,7 @@ private void startTracing(final @NotNull Activity activity) { // we can only bind to the scope if there's no running transaction ITransaction transaction = - scopes.startTransaction( + hub.startTransaction( new TransactionContext( activityName, TransactionNameSource.COMPONENT, @@ -278,7 +278,7 @@ private void startTracing(final @NotNull Activity activity) { } // lets bind to the scope so other integrations can pick it up - scopes.configureScope( + hub.configureScope( scope -> { applyScope(scope, transaction); }); @@ -290,7 +290,6 @@ private void startTracing(final @NotNull Activity activity) { private void setSpanOrigin(ISpan span) { if (span != null) { - // TODO [POTEL] replace with transactionOptions.setOrigin span.getSpanContext().setOrigin(TRACE_ORIGIN); } } @@ -357,10 +356,10 @@ private void finishTransaction( status = SpanStatus.OK; } transaction.finish(status); - if (scopes != null) { + if (hub != null) { // make sure to remove the transaction from scope, as it may contain running children, // therefore `finish` method will not remove it from scope - scopes.configureScope( + hub.configureScope( scope -> { clearScope(scope, transaction); }); @@ -372,9 +371,9 @@ private void finishTransaction( public synchronized void onActivityCreated( final @NotNull Activity activity, final @Nullable Bundle savedInstanceState) { setColdStart(savedInstanceState); - if (scopes != null) { + if (hub != null) { final @Nullable String activityClassName = ClassUtil.getClassName(activity); - scopes.configureScope(scope -> scope.setScreen(activityClassName)); + hub.configureScope(scope -> scope.setScreen(activityClassName)); } startTracing(activity); final @Nullable ISpan ttfdSpan = ttfdSpanMap.get(activity); @@ -430,10 +429,10 @@ public void onActivityPrePaused(@NonNull Activity activity) { // well // this ensures any newly launched activity will not use the app start timestamp as txn start firstActivityCreated = true; - if (scopes == null) { + if (hub == null) { lastPausedTime = AndroidDateUtils.getCurrentSentryDateTime(); } else { - lastPausedTime = scopes.getOptions().getDateProvider().now(); + lastPausedTime = hub.getOptions().getDateProvider().now(); } } } @@ -446,10 +445,10 @@ public synchronized void onActivityPaused(final @NotNull Activity activity) { // well // this ensures any newly launched activity will not use the app start timestamp as txn start firstActivityCreated = true; - if (scopes == null) { + if (hub == null) { lastPausedTime = AndroidDateUtils.getCurrentSentryDateTime(); } else { - lastPausedTime = scopes.getOptions().getDateProvider().now(); + lastPausedTime = hub.getOptions().getDateProvider().now(); } } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java index dd5c3c5254..372448b8e7 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java @@ -10,7 +10,6 @@ import io.sentry.ILogger; import io.sentry.ITransactionProfiler; import io.sentry.NoOpConnectionStatusProvider; -import io.sentry.ScopeType; import io.sentry.SendFireAndForgetEnvelopeSender; import io.sentry.SendFireAndForgetOutboxSender; import io.sentry.SentryLevel; @@ -99,8 +98,6 @@ static void loadDefaultAndMetadataOptions( // Firstly set the logger, if `debug=true` configured, logging can start asap. options.setLogger(logger); - options.setDefaultScopeType(ScopeType.CURRENT); - options.setDateProvider(new SentryAndroidDateProvider()); // set a lower flush timeout on Android to avoid ANRs @@ -119,7 +116,7 @@ static void loadDefaultAndMetadataOptions( static void initializeIntegrationsAndProcessors( final @NotNull SentryAndroidOptions options, final @NotNull Context context, - final @NotNull io.sentry.util.LoadClass loadClass, + final @NotNull LoadClass loadClass, final @NotNull ActivityFramesTracker activityFramesTracker) { initializeIntegrationsAndProcessors( options, @@ -133,7 +130,7 @@ static void initializeIntegrationsAndProcessors( final @NotNull SentryAndroidOptions options, final @NotNull Context context, final @NotNull BuildInfoProvider buildInfoProvider, - final @NotNull io.sentry.util.LoadClass loadClass, + final @NotNull LoadClass loadClass, final @NotNull ActivityFramesTracker activityFramesTracker) { if (options.getCacheDirPath() != null @@ -237,7 +234,7 @@ static void installDefaultIntegrations( final @NotNull Context context, final @NotNull SentryAndroidOptions options, final @NotNull BuildInfoProvider buildInfoProvider, - final @NotNull io.sentry.util.LoadClass loadClass, + final @NotNull LoadClass loadClass, final @NotNull ActivityFramesTracker activityFramesTracker, final boolean isFragmentAvailable, final boolean isTimberAvailable) { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransactionProfiler.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransactionProfiler.java index eca5d744f6..d9ece7fb46 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransactionProfiler.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransactionProfiler.java @@ -10,15 +10,15 @@ import android.os.Process; import android.os.SystemClock; import io.sentry.DateUtils; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ILogger; -import io.sentry.IScopes; import io.sentry.ISentryExecutorService; import io.sentry.ITransaction; import io.sentry.ITransactionProfiler; import io.sentry.PerformanceCollectionData; import io.sentry.ProfilingTraceData; import io.sentry.ProfilingTransactionData; -import io.sentry.ScopesAdapter; import io.sentry.SentryLevel; import io.sentry.SentryOptions; import io.sentry.android.core.internal.util.CpuInfoUtils; @@ -49,8 +49,8 @@ final class AndroidTransactionProfiler implements ITransactionProfiler { private @NotNull Date profileStartTimestamp; /** - * @deprecated please use a constructor that doesn't takes a {@link IScopes} instead, as it would - * be ignored anyway. + * @deprecated please use a constructor that doesn't takes a {@link IHub} instead, as it would be + * ignored anyway. */ @Deprecated public AndroidTransactionProfiler( @@ -58,7 +58,7 @@ public AndroidTransactionProfiler( final @NotNull SentryAndroidOptions sentryAndroidOptions, final @NotNull BuildInfoProvider buildInfoProvider, final @NotNull SentryFrameMetricsCollector frameMetricsCollector, - final @NotNull IScopes scopes) { + final @NotNull IHub hub) { this(context, sentryAndroidOptions, buildInfoProvider, frameMetricsCollector); } @@ -318,7 +318,7 @@ public void close() { currentProfilingTransactionData.getTraceId(), true, null, - ScopesAdapter.getInstance().getOptions()); + HubAdapter.getInstance().getOptions()); } else if (transactionsCounter != 0) { // in case the app start profiling is running, and it's not bound to a transaction, we still // stop profiling, but we also have to manually update the counter. diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AnrIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/AnrIntegration.java index 90d53a3c9b..0ad2c242da 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AnrIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AnrIntegration.java @@ -5,7 +5,7 @@ import android.annotation.SuppressLint; import android.content.Context; import io.sentry.Hint; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.Integration; import io.sentry.SentryEvent; import io.sentry.SentryLevel; @@ -48,13 +48,12 @@ public AnrIntegration(final @NotNull Context context) { private static final @NotNull Object watchDogLock = new Object(); @Override - public final void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + public final void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { this.options = Objects.requireNonNull(options, "SentryOptions is required"); - register(scopes, (SentryAndroidOptions) options); + register(hub, (SentryAndroidOptions) options); } - private void register( - final @NotNull IScopes scopes, final @NotNull SentryAndroidOptions options) { + private void register(final @NotNull IHub hub, final @NotNull SentryAndroidOptions options) { options .getLogger() .log(SentryLevel.DEBUG, "AnrIntegration enabled: %s", options.isAnrEnabled()); @@ -68,7 +67,7 @@ private void register( () -> { synchronized (startLock) { if (!isClosed) { - startAnrWatchdog(scopes, options); + startAnrWatchdog(hub, options); } } }); @@ -81,7 +80,7 @@ private void register( } private void startAnrWatchdog( - final @NotNull IScopes scopes, final @NotNull SentryAndroidOptions options) { + final @NotNull IHub hub, final @NotNull SentryAndroidOptions options) { synchronized (watchDogLock) { if (anrWatchDog == null) { options @@ -95,7 +94,7 @@ private void startAnrWatchdog( new ANRWatchDog( options.getAnrTimeoutIntervalMillis(), options.isAnrReportInDebug(), - error -> reportANR(scopes, options, error), + error -> reportANR(hub, options, error), options.getLogger(), context); anrWatchDog.start(); @@ -107,7 +106,7 @@ private void startAnrWatchdog( @TestOnly void reportANR( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull SentryAndroidOptions options, final @NotNull ApplicationNotResponding error) { options.getLogger().log(SentryLevel.INFO, "ANR triggered with message: %s", error.getMessage()); @@ -123,7 +122,7 @@ void reportANR( final AnrHint anrHint = new AnrHint(isAppInBackground); final Hint hint = HintUtils.createWithTypeCheckHint(anrHint); - scopes.captureEvent(event, hint); + hub.captureEvent(event, hint); } private @NotNull Throwable buildAnrThrowable( diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2EventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2EventProcessor.java index 9ff1294338..45f997542b 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2EventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2EventProcessor.java @@ -429,11 +429,6 @@ private void setOptionsTags(final @NotNull SentryBaseEvent event) { } // endregion - @Override - public @Nullable Long getOrder() { - return 12000L; - } - // region static values private void setStaticValues(final @NotNull SentryEvent event) { mergeUser(event); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java b/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java index 152ceed322..669233bb09 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java @@ -9,8 +9,8 @@ import io.sentry.Attachment; import io.sentry.DateUtils; import io.sentry.Hint; +import io.sentry.IHub; import io.sentry.ILogger; -import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.SentryEvent; import io.sentry.SentryLevel; @@ -69,7 +69,7 @@ public AnrV2Integration(final @NotNull Context context) { @SuppressLint("NewApi") // we do the check in the AnrIntegrationFactory @Override - public void register(@NotNull IScopes scopes, @NotNull SentryOptions options) { + public void register(@NotNull IHub hub, @NotNull SentryOptions options) { this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -90,7 +90,7 @@ public void register(@NotNull IScopes scopes, @NotNull SentryOptions options) { try { options .getExecutorService() - .submit(new AnrProcessor(context, scopes, this.options, dateProvider)); + .submit(new AnrProcessor(context, hub, this.options, dateProvider)); } catch (Throwable e) { options.getLogger().log(SentryLevel.DEBUG, "Failed to start AnrProcessor.", e); } @@ -109,17 +109,17 @@ public void close() throws IOException { static class AnrProcessor implements Runnable { private final @NotNull Context context; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private final @NotNull SentryAndroidOptions options; private final long threshold; AnrProcessor( final @NotNull Context context, - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull SentryAndroidOptions options, final @NotNull ICurrentDateProvider dateProvider) { this.context = context; - this.scopes = scopes; + this.hub = hub; this.options = options; this.threshold = dateProvider.getCurrentTimeMillis() - NINETY_DAYS_THRESHOLD; } @@ -277,7 +277,7 @@ private void reportAsSentryEvent( } } - final @NotNull SentryId sentryId = scopes.captureEvent(event, hint); + final @NotNull SentryId sentryId = hub.captureEvent(event, hint); final boolean isEventDropped = sentryId.equals(SentryId.EMPTY_ID); if (!isEventDropped) { // Block until the event is flushed to disk and the last_reported_anr marker is updated diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegration.java index 0d20dc7a90..eef4707683 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegration.java @@ -8,7 +8,7 @@ import android.content.res.Configuration; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.Integration; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -25,7 +25,7 @@ public final class AppComponentsBreadcrumbsIntegration implements Integration, Closeable, ComponentCallbacks2 { private final @NotNull Context context; - private @Nullable IScopes scopes; + private @Nullable IHub hub; private @Nullable SentryAndroidOptions options; public AppComponentsBreadcrumbsIntegration(final @NotNull Context context) { @@ -33,8 +33,8 @@ public AppComponentsBreadcrumbsIntegration(final @NotNull Context context) { } @Override - public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { - this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); + public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { + this.hub = Objects.requireNonNull(hub, "Hub is required"); this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -84,7 +84,7 @@ public void close() throws IOException { @SuppressWarnings("deprecation") @Override public void onConfigurationChanged(@NotNull Configuration newConfig) { - if (scopes != null) { + if (hub != null) { final Device.DeviceOrientation deviceOrientation = DeviceOrientations.getOrientation(context.getResources().getConfiguration().orientation); @@ -104,7 +104,7 @@ public void onConfigurationChanged(@NotNull Configuration newConfig) { final Hint hint = new Hint(); hint.set(ANDROID_CONFIGURATION, newConfig); - scopes.addBreadcrumb(breadcrumb, hint); + hub.addBreadcrumb(breadcrumb, hint); } } @@ -119,7 +119,7 @@ public void onTrimMemory(final int level) { } private void createLowMemoryBreadcrumb(final @Nullable Integer level) { - if (scopes != null) { + if (hub != null) { final Breadcrumb breadcrumb = new Breadcrumb(); if (level != null) { // only add breadcrumb if TRIM_MEMORY_BACKGROUND, TRIM_MEMORY_MODERATE or @@ -143,7 +143,7 @@ private void createLowMemoryBreadcrumb(final @Nullable Integer level) { breadcrumb.setMessage("Low memory"); breadcrumb.setData("action", "LOW_MEMORY"); breadcrumb.setLevel(SentryLevel.WARNING); - scopes.addBreadcrumb(breadcrumb); + hub.addBreadcrumb(breadcrumb); } } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AppLifecycleIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/AppLifecycleIntegration.java index 8614a60061..3e8fe6383f 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AppLifecycleIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AppLifecycleIntegration.java @@ -3,7 +3,7 @@ import static io.sentry.util.IntegrationUtils.addIntegrationToSdkVersion; import androidx.lifecycle.ProcessLifecycleOwner; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.Integration; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -32,8 +32,8 @@ public AppLifecycleIntegration() { } @Override - public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { - Objects.requireNonNull(scopes, "Scopes are required"); + public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { + Objects.requireNonNull(hub, "Hub is required"); this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -59,11 +59,11 @@ public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions Class.forName("androidx.lifecycle.DefaultLifecycleObserver"); Class.forName("androidx.lifecycle.ProcessLifecycleOwner"); if (AndroidMainThreadChecker.getInstance().isMainThread()) { - addObserver(scopes); + addObserver(hub); } else { // some versions of the androidx lifecycle-process require this to be executed on the main // thread. - handler.post(() -> addObserver(scopes)); + handler.post(() -> addObserver(hub)); } } catch (ClassNotFoundException e) { options @@ -80,7 +80,7 @@ public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions } } - private void addObserver(final @NotNull IScopes scopes) { + private void addObserver(final @NotNull IHub hub) { // this should never happen, check added to avoid warnings from NullAway if (this.options == null) { return; @@ -88,7 +88,7 @@ private void addObserver(final @NotNull IScopes scopes) { watcher = new LifecycleWatcher( - scopes, + hub, this.options.getSessionTrackingIntervalMillis(), this.options.isEnableAutoSessionTracking(), this.options.isEnableAppLifecycleBreadcrumbs()); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/CurrentActivityIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/CurrentActivityIntegration.java index 0b618636d3..b4c5f1ed02 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/CurrentActivityIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/CurrentActivityIntegration.java @@ -4,7 +4,7 @@ import android.app.Application; import android.os.Bundle; import androidx.annotation.NonNull; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.Integration; import io.sentry.SentryOptions; import io.sentry.util.Objects; @@ -25,7 +25,7 @@ public CurrentActivityIntegration(final @NotNull Application application) { } @Override - public void register(@NotNull IScopes scopes, @NotNull SentryOptions options) { + public void register(@NotNull IHub hub, @NotNull SentryOptions options) { application.registerActivityLifecycleCallbacks(this); } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java index 999f187fe5..45e4b78787 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java @@ -303,9 +303,4 @@ private void setSideLoadedInfo(final @NotNull SentryBaseEvent event) { return transaction; } - - @Override - public @Nullable Long getOrder() { - return 8000L; - } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/EnvelopeFileObserverIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/EnvelopeFileObserverIntegration.java index 6e821e5be7..f99294584b 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/EnvelopeFileObserverIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/EnvelopeFileObserverIntegration.java @@ -1,7 +1,7 @@ package io.sentry.android.core; +import io.sentry.IHub; import io.sentry.ILogger; -import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.OutboxSender; import io.sentry.SentryLevel; @@ -24,8 +24,8 @@ public abstract class EnvelopeFileObserverIntegration implements Integration, Cl } @Override - public final void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { - Objects.requireNonNull(scopes, "Scopes are required"); + public final void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { + Objects.requireNonNull(hub, "Hub is required"); Objects.requireNonNull(options, "SentryOptions is required"); logger = options.getLogger(); @@ -46,7 +46,7 @@ public final void register(final @NotNull IScopes scopes, final @NotNull SentryO () -> { synchronized (startLock) { if (!isClosed) { - startOutboxSender(scopes, options, path); + startOutboxSender(hub, options, path); } } }); @@ -60,12 +60,10 @@ public final void register(final @NotNull IScopes scopes, final @NotNull SentryO } private void startOutboxSender( - final @NotNull IScopes scopes, - final @NotNull SentryOptions options, - final @NotNull String path) { + final @NotNull IHub hub, final @NotNull SentryOptions options, final @NotNull String path) { final OutboxSender outboxSender = new OutboxSender( - scopes, + hub, options.getEnvelopeReader(), options.getSerializer(), options.getLogger(), diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java index 40cbdb62b7..a4d1db09df 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java @@ -1,20 +1,15 @@ package io.sentry.android.core; -import static io.sentry.SentryLevel.DEBUG; -import static io.sentry.SentryLevel.INFO; -import static io.sentry.SentryLevel.WARNING; - import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import io.sentry.DateUtils; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ILogger; import io.sentry.IScope; -import io.sentry.IScopes; import io.sentry.ISerializer; import io.sentry.ObjectWriter; -import io.sentry.ScopeType; -import io.sentry.ScopesAdapter; import io.sentry.SentryEnvelope; import io.sentry.SentryEnvelopeItem; import io.sentry.SentryEvent; @@ -24,14 +19,12 @@ import io.sentry.android.core.performance.ActivityLifecycleTimeSpan; import io.sentry.android.core.performance.AppStartMetrics; import io.sentry.android.core.performance.TimeSpan; -import io.sentry.cache.EnvelopeCache; import io.sentry.protocol.App; import io.sentry.protocol.Device; import io.sentry.protocol.SentryId; import io.sentry.protocol.User; import io.sentry.util.MapObjectWriter; import java.io.ByteArrayInputStream; -import java.io.File; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; @@ -48,14 +41,13 @@ public final class InternalSentrySdk { /** - * @return a copy of the current scopes's topmost scope, or null in case the scopes is disabled + * @return a copy of the current hub's topmost scope, or null in case the hub is disabled */ @Nullable public static IScope getCurrentScope() { final @NotNull AtomicReference scopeRef = new AtomicReference<>(); - ScopesAdapter.getInstance() + HubAdapter.getInstance() .configureScope( - ScopeType.COMBINED, scope -> { scopeRef.set(scope.clone()); }); @@ -144,8 +136,8 @@ public static Map serializeScope( } /** - * Captures the provided envelope. Compared to {@link IScopes#captureEvent(SentryEvent)} this - * method
    + * Captures the provided envelope. Compared to {@link IHub#captureEvent(SentryEvent)} this method + *
    * - will not enrich events with additional data (e.g. scope)
    * - will not execute beforeSend: it's up to the caller to take care of this
    * - will not perform any sampling: it's up to the caller to take care of this
    @@ -156,10 +148,9 @@ public static Map serializeScope( * captured */ @Nullable - public static SentryId captureEnvelope( - final @NotNull byte[] envelopeData, final boolean maybeStartNewSession) { - final @NotNull IScopes scopes = ScopesAdapter.getInstance(); - final @NotNull SentryOptions options = scopes.getOptions(); + public static SentryId captureEnvelope(final @NotNull byte[] envelopeData) { + final @NotNull IHub hub = HubAdapter.getInstance(); + final @NotNull SentryOptions options = hub.getOptions(); try (final InputStream envelopeInputStream = new ByteArrayInputStream(envelopeData)) { final @NotNull ISerializer serializer = options.getSerializer(); @@ -189,22 +180,15 @@ public static SentryId captureEnvelope( } // update session and add it to envelope if necessary - final @Nullable Session session = updateSession(scopes, options, status, crashedOrErrored); + final @Nullable Session session = updateSession(hub, options, status, crashedOrErrored); if (session != null) { final SentryEnvelopeItem sessionItem = SentryEnvelopeItem.fromSession(serializer, session); envelopeItems.add(sessionItem); - deleteCurrentSessionFile( - options, - // should be sync if going to crash or already not a main thread - !maybeStartNewSession || !scopes.getOptions().getMainThreadChecker().isMainThread()); - if (maybeStartNewSession) { - scopes.startSession(); - } } final SentryEnvelope repackagedEnvelope = new SentryEnvelope(envelope.getHeader(), envelopeItems); - return scopes.captureEnvelope(repackagedEnvelope); + return hub.captureEnvelope(repackagedEnvelope); } catch (Throwable t) { options.getLogger().log(SentryLevel.ERROR, "Failed to capture envelope", t); } @@ -246,18 +230,18 @@ public static Map getAppStartMeasurement() { private static void addTimeSpanToSerializedSpans(TimeSpan span, List> spans) { if (span.hasNotStarted()) { - ScopesAdapter.getInstance() + HubAdapter.getInstance() .getOptions() .getLogger() - .log(WARNING, "Can not convert not-started TimeSpan to Map for Hybrid SDKs."); + .log(SentryLevel.WARNING, "Can not convert not-started TimeSpan to Map for Hybrid SDKs."); return; } if (span.hasNotStopped()) { - ScopesAdapter.getInstance() + HubAdapter.getInstance() .getOptions() .getLogger() - .log(WARNING, "Can not convert not-stopped TimeSpan to Map for Hybrid SDKs."); + .log(SentryLevel.WARNING, "Can not convert not-stopped TimeSpan to Map for Hybrid SDKs."); return; } @@ -268,54 +252,14 @@ private static void addTimeSpanToSerializedSpans(TimeSpan span, List { - deleteCurrentSessionFile(options); - }); - } catch (Throwable e) { - options - .getLogger() - .log(WARNING, "Submission of deletion of the current session file rejected.", e); - } - } else { - deleteCurrentSessionFile(options); - } - } - - private static void deleteCurrentSessionFile(final @NotNull SentryOptions options) { - final String cacheDirPath = options.getCacheDirPath(); - if (cacheDirPath == null) { - options.getLogger().log(INFO, "Cache dir is not set, not deleting the current session."); - return; - } - - if (!options.isEnableAutoSessionTracking()) { - options - .getLogger() - .log(DEBUG, "Session tracking is disabled, bailing from deleting current session file."); - return; - } - - final File sessionFile = EnvelopeCache.getCurrentSessionFile(cacheDirPath); - if (!sessionFile.delete()) { - options.getLogger().log(WARNING, "Failed to delete the current session file."); - } - } - @Nullable private static Session updateSession( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull SentryOptions options, final @Nullable Session.State status, final boolean crashedOrErrored) { final @NotNull AtomicReference sessionRef = new AtomicReference<>(); - scopes.configureScope( + hub.configureScope( scope -> { final @Nullable Session session = scope.getSession(); if (session != null) { @@ -324,14 +268,11 @@ private static Session updateSession( if (updated) { if (session.getStatus() == Session.State.Crashed) { session.end(); - // Session needs to be removed from the scope, otherwise it will be send twice - // standalone and with the crash event - scope.clearSession(); } sessionRef.set(session); } } else { - options.getLogger().log(INFO, "Session is null on updateSession"); + options.getLogger().log(SentryLevel.INFO, "Session is null on updateSession"); } }); return sessionRef.get(); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java b/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java index a32fa51d3f..7b38bcd9c2 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/LifecycleWatcher.java @@ -3,7 +3,7 @@ import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.LifecycleOwner; import io.sentry.Breadcrumb; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.SentryLevel; import io.sentry.Session; import io.sentry.android.core.internal.util.BreadcrumbFactory; @@ -25,19 +25,19 @@ final class LifecycleWatcher implements DefaultLifecycleObserver { private @Nullable TimerTask timerTask; private final @Nullable Timer timer; private final @NotNull Object timerLock = new Object(); - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private final boolean enableSessionTracking; private final boolean enableAppLifecycleBreadcrumbs; private final @NotNull ICurrentDateProvider currentDateProvider; LifecycleWatcher( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final long sessionIntervalMillis, final boolean enableSessionTracking, final boolean enableAppLifecycleBreadcrumbs) { this( - scopes, + hub, sessionIntervalMillis, enableSessionTracking, enableAppLifecycleBreadcrumbs, @@ -45,7 +45,7 @@ final class LifecycleWatcher implements DefaultLifecycleObserver { } LifecycleWatcher( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final long sessionIntervalMillis, final boolean enableSessionTracking, final boolean enableAppLifecycleBreadcrumbs, @@ -53,7 +53,7 @@ final class LifecycleWatcher implements DefaultLifecycleObserver { this.sessionIntervalMillis = sessionIntervalMillis; this.enableSessionTracking = enableSessionTracking; this.enableAppLifecycleBreadcrumbs = enableAppLifecycleBreadcrumbs; - this.scopes = scopes; + this.hub = hub; this.currentDateProvider = currentDateProvider; if (enableSessionTracking) { timer = new Timer(true); @@ -79,7 +79,7 @@ private void startSession() { final long currentTimeMillis = currentDateProvider.getCurrentTimeMillis(); - scopes.configureScope( + hub.configureScope( scope -> { if (lastUpdatedSession.get() == 0L) { final @Nullable Session currentSession = scope.getSession(); @@ -93,7 +93,7 @@ private void startSession() { if (lastUpdatedSession == 0L || (lastUpdatedSession + sessionIntervalMillis) <= currentTimeMillis) { addSessionBreadcrumb("start"); - scopes.startSession(); + hub.startSession(); } this.lastUpdatedSession.set(currentTimeMillis); } @@ -123,7 +123,7 @@ private void scheduleEndSession() { @Override public void run() { addSessionBreadcrumb("end"); - scopes.endSession(); + hub.endSession(); } }; @@ -148,13 +148,13 @@ private void addAppBreadcrumb(final @NotNull String state) { breadcrumb.setData("state", state); breadcrumb.setCategory("app.lifecycle"); breadcrumb.setLevel(SentryLevel.INFO); - scopes.addBreadcrumb(breadcrumb); + hub.addBreadcrumb(breadcrumb); } } private void addSessionBreadcrumb(final @NotNull String state) { final Breadcrumb breadcrumb = BreadcrumbFactory.forSession(state); - scopes.addBreadcrumb(breadcrumb); + hub.addBreadcrumb(breadcrumb); } @TestOnly diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/LoadClass.java b/sentry-android-core/src/main/java/io/sentry/android/core/LoadClass.java index 34b8d1d5f1..6401945cab 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/LoadClass.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/LoadClass.java @@ -1,23 +1,13 @@ package io.sentry.android.core; import io.sentry.ILogger; +import io.sentry.SentryLevel; import io.sentry.SentryOptions; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** - * An Adapter for making Class.forName testable - * - * @deprecated please use {@link io.sentry.util.LoadClass} instead. - */ -@Deprecated -public final class LoadClass extends io.sentry.util.LoadClass { - - private final io.sentry.util.LoadClass delegate; - - public LoadClass() { - delegate = new io.sentry.util.LoadClass(); - } +/** An Adapter for making Class.forName testable */ +public final class LoadClass { /** * Try to load a class via reflection @@ -27,15 +17,30 @@ public LoadClass() { * @return a Class if it's available, or null */ public @Nullable Class loadClass(final @NotNull String clazz, final @Nullable ILogger logger) { - return delegate.loadClass(clazz, logger); + try { + return Class.forName(clazz); + } catch (ClassNotFoundException e) { + if (logger != null) { + logger.log(SentryLevel.DEBUG, "Class not available:" + clazz, e); + } + } catch (UnsatisfiedLinkError e) { + if (logger != null) { + logger.log(SentryLevel.ERROR, "Failed to load (UnsatisfiedLinkError) " + clazz, e); + } + } catch (Throwable e) { + if (logger != null) { + logger.log(SentryLevel.ERROR, "Failed to initialize " + clazz, e); + } + } + return null; } public boolean isClassAvailable(final @NotNull String clazz, final @Nullable ILogger logger) { - return delegate.isClassAvailable(clazz, logger); + return loadClass(clazz, logger) != null; } public boolean isClassAvailable( final @NotNull String clazz, final @Nullable SentryOptions options) { - return delegate.isClassAvailable(clazz, options); + return isClassAvailable(clazz, options != null ? options.getLogger() : null); } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java b/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java index b51c4b22a8..31e026dd00 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java @@ -37,7 +37,7 @@ final class ManifestMetadataReader { static final String SDK_NAME = "io.sentry.sdk.name"; static final String SDK_VERSION = "io.sentry.sdk.version"; - // TODO [MAJOR]: remove on 6.x in favor of SESSION_AUTO_TRACKING_ENABLE + // TODO: remove on 6.x in favor of SESSION_AUTO_TRACKING_ENABLE static final String SESSION_TRACKING_ENABLE = "io.sentry.session-tracking.enable"; static final String AUTO_SESSION_TRACKING_ENABLE = "io.sentry.auto-session-tracking.enable"; @@ -70,7 +70,7 @@ final class ManifestMetadataReader { @ApiStatus.Experimental static final String TRACE_SAMPLING = "io.sentry.traces.trace-sampling"; - // TODO [MAJOR]: remove in favor of TRACE_PROPAGATION_TARGETS + // TODO: remove in favor of TRACE_PROPAGATION_TARGETS @Deprecated static final String TRACING_ORIGINS = "io.sentry.traces.tracing-origins"; static final String TRACE_PROPAGATION_TARGETS = "io.sentry.traces.trace-propagation-targets"; @@ -323,7 +323,7 @@ static void applyMetadata( List tracePropagationTargets = readList(metadata, logger, TRACE_PROPAGATION_TARGETS); - // TODO [MAJOR] remove once TRACING_ORIGINS have been removed + // TODO remove once TRACING_ORIGINS have been removed if (!metadata.containsKey(TRACE_PROPAGATION_TARGETS) && (tracePropagationTargets == null || tracePropagationTargets.isEmpty())) { tracePropagationTargets = readList(metadata, logger, TRACING_ORIGINS); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/NdkIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/NdkIntegration.java index dc464303c6..3a4a91498e 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/NdkIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/NdkIntegration.java @@ -2,7 +2,7 @@ import static io.sentry.util.IntegrationUtils.addIntegrationToSdkVersion; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.Integration; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -28,8 +28,8 @@ public NdkIntegration(final @Nullable Class sentryNdkClass) { } @Override - public final void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { - Objects.requireNonNull(scopes, "Scopes are required"); + public final void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { + Objects.requireNonNull(hub, "Hub is required"); this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -38,8 +38,7 @@ public final void register(final @NotNull IScopes scopes, final @NotNull SentryO final boolean enabled = this.options.isEnableNdk(); this.options.getLogger().log(SentryLevel.DEBUG, "NdkIntegration enabled: %s", enabled); - // Note: `scopes` isn't used here because the NDK integration writes files to disk which are - // picked + // Note: `hub` isn't used here because the NDK integration writes files to disk which are picked // up by another integration (EnvelopeFileObserverIntegration). if (enabled && sentryNdkClass != null) { final String cachedDir = this.options.getCacheDirPath(); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/NetworkBreadcrumbsIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/NetworkBreadcrumbsIntegration.java index 9f1dd3ecf6..1cd42e9dab 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/NetworkBreadcrumbsIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/NetworkBreadcrumbsIntegration.java @@ -13,8 +13,8 @@ import io.sentry.Breadcrumb; import io.sentry.DateUtils; import io.sentry.Hint; +import io.sentry.IHub; import io.sentry.ILogger; -import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.SentryDateProvider; import io.sentry.SentryLevel; @@ -50,8 +50,8 @@ public NetworkBreadcrumbsIntegration( @SuppressLint("NewApi") @Override - public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { - Objects.requireNonNull(scopes, "Scopes are required"); + public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { + Objects.requireNonNull(hub, "Hub is required"); SentryAndroidOptions androidOptions = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -72,8 +72,7 @@ public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions } networkCallback = - new NetworkBreadcrumbsNetworkCallback( - scopes, buildInfoProvider, options.getDateProvider()); + new NetworkBreadcrumbsNetworkCallback(hub, buildInfoProvider, options.getDateProvider()); final boolean registered = AndroidConnectionStatusProvider.registerNetworkCallback( context, logger, buildInfoProvider, networkCallback); @@ -102,7 +101,7 @@ public void close() throws IOException { @SuppressLint("ObsoleteSdkInt") @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) static final class NetworkBreadcrumbsNetworkCallback extends ConnectivityManager.NetworkCallback { - final @NotNull IScopes scopes; + final @NotNull IHub hub; final @NotNull BuildInfoProvider buildInfoProvider; @Nullable Network currentNetwork = null; @@ -112,10 +111,10 @@ static final class NetworkBreadcrumbsNetworkCallback extends ConnectivityManager final @NotNull SentryDateProvider dateProvider; NetworkBreadcrumbsNetworkCallback( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull BuildInfoProvider buildInfoProvider, final @NotNull SentryDateProvider dateProvider) { - this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); + this.hub = Objects.requireNonNull(hub, "Hub is required"); this.buildInfoProvider = Objects.requireNonNull(buildInfoProvider, "BuildInfoProvider is required"); this.dateProvider = Objects.requireNonNull(dateProvider, "SentryDateProvider is required"); @@ -127,7 +126,7 @@ public void onAvailable(final @NonNull Network network) { return; } final Breadcrumb breadcrumb = createBreadcrumb("NETWORK_AVAILABLE"); - scopes.addBreadcrumb(breadcrumb); + hub.addBreadcrumb(breadcrumb); currentNetwork = network; lastCapabilities = null; } @@ -157,7 +156,7 @@ public void onCapabilitiesChanged( } Hint hint = new Hint(); hint.set(TypeCheckHint.ANDROID_NETWORK_CAPABILITIES, connectionDetail); - scopes.addBreadcrumb(breadcrumb, hint); + hub.addBreadcrumb(breadcrumb, hint); } @Override @@ -166,7 +165,7 @@ public void onLost(final @NonNull Network network) { return; } final Breadcrumb breadcrumb = createBreadcrumb("NETWORK_LOST"); - scopes.addBreadcrumb(breadcrumb); + hub.addBreadcrumb(breadcrumb); currentNetwork = null; lastCapabilities = null; } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java index afefed676f..00ba9122e7 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java @@ -338,9 +338,4 @@ private static SentrySpan timeSpanToSentrySpan( null, defaultSpanData); } - - @Override - public @Nullable Long getOrder() { - return 9000L; - } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/PhoneStateBreadcrumbsIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/PhoneStateBreadcrumbsIntegration.java index cae1492d3e..c10d25b057 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/PhoneStateBreadcrumbsIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/PhoneStateBreadcrumbsIntegration.java @@ -6,7 +6,7 @@ import android.content.Context; import android.telephony.TelephonyManager; import io.sentry.Breadcrumb; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.Integration; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -32,8 +32,8 @@ public PhoneStateBreadcrumbsIntegration(final @NotNull Context context) { } @Override - public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { - Objects.requireNonNull(scopes, "Scopes are required"); + public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { + Objects.requireNonNull(hub, "Hub is required"); this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -55,7 +55,7 @@ public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions () -> { synchronized (startLock) { if (!isClosed) { - startTelephonyListener(scopes, options); + startTelephonyListener(hub, options); } } }); @@ -72,11 +72,11 @@ public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions @SuppressWarnings("deprecation") private void startTelephonyListener( - final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + final @NotNull IHub hub, final @NotNull SentryOptions options) { telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); if (telephonyManager != null) { try { - listener = new PhoneStateChangeListener(scopes); + listener = new PhoneStateChangeListener(hub); telephonyManager.listen(listener, android.telephony.PhoneStateListener.LISTEN_CALL_STATE); options.getLogger().log(SentryLevel.DEBUG, "PhoneStateBreadcrumbsIntegration installed."); @@ -110,10 +110,10 @@ public void close() throws IOException { @SuppressWarnings("deprecation") static final class PhoneStateChangeListener extends android.telephony.PhoneStateListener { - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; - PhoneStateChangeListener(final @NotNull IScopes scopes) { - this.scopes = scopes; + PhoneStateChangeListener(final @NotNull IHub hub) { + this.hub = hub; } @SuppressWarnings("deprecation") @@ -128,7 +128,7 @@ public void onCallStateChanged(int state, String incomingNumber) { breadcrumb.setData("action", "CALL_STATE_RINGING"); breadcrumb.setMessage("Device ringing"); breadcrumb.setLevel(SentryLevel.INFO); - scopes.addBreadcrumb(breadcrumb); + hub.addBreadcrumb(breadcrumb); } } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ScreenshotEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/ScreenshotEventProcessor.java index 87a2caf05f..5e07a44078 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ScreenshotEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ScreenshotEventProcessor.java @@ -98,9 +98,4 @@ public ScreenshotEventProcessor( hint.set(ANDROID_ACTIVITY, activity); return event; } - - @Override - public @Nullable Long getOrder() { - return 10000L; - } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SendCachedEnvelopeIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/SendCachedEnvelopeIntegration.java index 64f1cab362..66e534bb7d 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SendCachedEnvelopeIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SendCachedEnvelopeIntegration.java @@ -2,7 +2,7 @@ import io.sentry.DataCategory; import io.sentry.IConnectionStatusProvider; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.Integration; import io.sentry.SendCachedEnvelopeFireAndForgetIntegration; import io.sentry.SentryLevel; @@ -28,7 +28,7 @@ final class SendCachedEnvelopeIntegration private final @NotNull LazyEvaluator startupCrashMarkerEvaluator; private final AtomicBoolean startupCrashHandled = new AtomicBoolean(false); private @Nullable IConnectionStatusProvider connectionStatusProvider; - private @Nullable IScopes scopes; + private @Nullable IHub hub; private @Nullable SentryAndroidOptions options; private @Nullable SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget sender; private final AtomicBoolean isInitialized = new AtomicBoolean(false); @@ -42,8 +42,8 @@ public SendCachedEnvelopeIntegration( } @Override - public void register(@NotNull IScopes scopes, @NotNull SentryOptions options) { - this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); + public void register(@NotNull IHub hub, @NotNull SentryOptions options) { + this.hub = Objects.requireNonNull(hub, "Hub is required"); this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -55,7 +55,7 @@ public void register(@NotNull IScopes scopes, @NotNull SentryOptions options) { return; } - sendCachedEnvelopes(scopes, this.options); + sendCachedEnvelopes(hub, this.options); } @Override @@ -69,14 +69,14 @@ public void close() throws IOException { @Override public void onConnectionStatusChanged( final @NotNull IConnectionStatusProvider.ConnectionStatus status) { - if (scopes != null && options != null) { - sendCachedEnvelopes(scopes, options); + if (hub != null && options != null) { + sendCachedEnvelopes(hub, options); } } @SuppressWarnings({"NullAway"}) private synchronized void sendCachedEnvelopes( - final @NotNull IScopes scopes, final @NotNull SentryAndroidOptions options) { + final @NotNull IHub hub, final @NotNull SentryAndroidOptions options) { try { final Future future = options @@ -97,7 +97,7 @@ private synchronized void sendCachedEnvelopes( connectionStatusProvider = options.getConnectionStatusProvider(); connectionStatusProvider.addConnectionStatusObserver(this); - sender = factory.create(scopes, options); + sender = factory.create(hub, options); } if (connectionStatusProvider != null @@ -110,7 +110,7 @@ private synchronized void sendCachedEnvelopes( } // in case there's rate limiting active, skip processing - final @Nullable RateLimiter rateLimiter = scopes.getRateLimiter(); + final @Nullable RateLimiter rateLimiter = hub.getRateLimiter(); if (rateLimiter != null && rateLimiter.isActiveForCategory(DataCategory.All)) { options diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java index 49ad3ffeaa..af68a026fb 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java @@ -4,14 +4,13 @@ import android.content.Context; import android.os.Process; import android.os.SystemClock; +import io.sentry.IHub; import io.sentry.ILogger; -import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.OptionsContainer; import io.sentry.Sentry; import io.sentry.SentryLevel; import io.sentry.SentryOptions; -import io.sentry.Session; import io.sentry.android.core.internal.util.BreadcrumbFactory; import io.sentry.android.core.performance.AppStartMetrics; import io.sentry.android.core.performance.TimeSpan; @@ -20,9 +19,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** Sentry initialization class */ public final class SentryAndroid { @@ -90,7 +87,7 @@ public static synchronized void init( Sentry.init( OptionsContainer.create(SentryAndroidOptions.class), options -> { - final io.sentry.util.LoadClass classLoader = new io.sentry.util.LoadClass(); + final LoadClass classLoader = new LoadClass(); final boolean isTimberUpstreamAvailable = classLoader.isClassAvailable(TIMBER_CLASS_NAME, options); final boolean isFragmentUpstreamAvailable = @@ -104,7 +101,7 @@ public static synchronized void init( && classLoader.isClassAvailable(SENTRY_TIMBER_INTEGRATION_CLASS_NAME, options)); final BuildInfoProvider buildInfoProvider = new BuildInfoProvider(logger); - final io.sentry.util.LoadClass loadClass = new io.sentry.util.LoadClass(); + final LoadClass loadClass = new LoadClass(); final ActivityFramesTracker activityFramesTracker = new ActivityFramesTracker(loadClass, options); @@ -147,24 +144,10 @@ public static synchronized void init( }, true); - final @NotNull IScopes scopes = Sentry.getCurrentScopes(); - if (scopes.getOptions().isEnableAutoSessionTracking() - && ContextUtils.isForegroundImportance()) { - // The LifecycleWatcher of AppLifecycleIntegration may already started a session - // so only start a session if it's not already started - // This e.g. happens on React Native, or e.g. on deferred SDK init - final AtomicBoolean sessionStarted = new AtomicBoolean(false); - scopes.configureScope( - scope -> { - final @Nullable Session currentSession = scope.getSession(); - if (currentSession != null && currentSession.getStarted() != null) { - sessionStarted.set(true); - } - }); - if (!sessionStarted.get()) { - scopes.addBreadcrumb(BreadcrumbFactory.forSession("session.start")); - scopes.startSession(); - } + final @NotNull IHub hub = Sentry.getCurrentHub(); + if (hub.getOptions().isEnableAutoSessionTracking() && ContextUtils.isForegroundImportance()) { + hub.addBreadcrumb(BreadcrumbFactory.forSession("session.start")); + hub.startSession(); } } catch (IllegalAccessException e) { logger.log(SentryLevel.FATAL, "Fatal error during SentryAndroid.init(...)", e); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java index 333ece2148..1c22a7dcc8 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java @@ -40,8 +40,8 @@ import android.os.Bundle; import io.sentry.Breadcrumb; import io.sentry.Hint; +import io.sentry.IHub; import io.sentry.ILogger; -import io.sentry.IScopes; import io.sentry.Integration; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -80,8 +80,8 @@ public SystemEventsBreadcrumbsIntegration( } @Override - public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { - Objects.requireNonNull(scopes, "Scopes are required"); + public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { + Objects.requireNonNull(hub, "Hub is required"); this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -103,7 +103,7 @@ public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions () -> { synchronized (startLock) { if (!isClosed) { - startSystemEventsReceiver(scopes, (SentryAndroidOptions) options); + startSystemEventsReceiver(hub, (SentryAndroidOptions) options); } } }); @@ -119,8 +119,8 @@ public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions } private void startSystemEventsReceiver( - final @NotNull IScopes scopes, final @NotNull SentryAndroidOptions options) { - receiver = new SystemEventsBroadcastReceiver(scopes, options.getLogger()); + final @NotNull IHub hub, final @NotNull SentryAndroidOptions options) { + receiver = new SystemEventsBroadcastReceiver(hub, options.getLogger()); final IntentFilter filter = new IntentFilter(); for (String item : actions) { filter.addAction(item); @@ -204,11 +204,11 @@ public void close() throws IOException { static final class SystemEventsBroadcastReceiver extends BroadcastReceiver { - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private final @NotNull ILogger logger; - SystemEventsBroadcastReceiver(final @NotNull IScopes scopes, final @NotNull ILogger logger) { - this.scopes = scopes; + SystemEventsBroadcastReceiver(final @NotNull IHub hub, final @NotNull ILogger logger) { + this.hub = hub; this.logger = logger; } @@ -249,7 +249,7 @@ public void onReceive(Context context, Intent intent) { final Hint hint = new Hint(); hint.set(ANDROID_INTENT, intent); - scopes.addBreadcrumb(breadcrumb, hint); + hub.addBreadcrumb(breadcrumb, hint); } } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/TempSensorBreadcrumbsIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/TempSensorBreadcrumbsIntegration.java index 4d0e9c7e60..eaf5c64991 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/TempSensorBreadcrumbsIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/TempSensorBreadcrumbsIntegration.java @@ -11,7 +11,7 @@ import android.hardware.SensorManager; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.Integration; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -26,7 +26,7 @@ public final class TempSensorBreadcrumbsIntegration implements Integration, Closeable, SensorEventListener { private final @NotNull Context context; - private @Nullable IScopes scopes; + private @Nullable IHub hub; private @Nullable SentryAndroidOptions options; @TestOnly @Nullable SensorManager sensorManager; @@ -38,8 +38,8 @@ public TempSensorBreadcrumbsIntegration(final @NotNull Context context) { } @Override - public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { - this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); + public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { + this.hub = Objects.requireNonNull(hub, "Hub is required"); this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, @@ -121,7 +121,7 @@ public void onSensorChanged(final @NotNull SensorEvent event) { return; } - if (scopes != null) { + if (hub != null) { final Breadcrumb breadcrumb = new Breadcrumb(); breadcrumb.setType("system"); breadcrumb.setCategory("device.event"); @@ -134,7 +134,7 @@ public void onSensorChanged(final @NotNull SensorEvent event) { final Hint hint = new Hint(); hint.set(ANDROID_SENSOR_EVENT, event); - scopes.addBreadcrumb(breadcrumb, hint); + hub.addBreadcrumb(breadcrumb, hint); } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/UserInteractionIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/UserInteractionIntegration.java index 02a707173a..c361529671 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/UserInteractionIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/UserInteractionIntegration.java @@ -6,7 +6,7 @@ import android.app.Application; import android.os.Bundle; import android.view.Window; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.Integration; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -23,13 +23,13 @@ public final class UserInteractionIntegration implements Integration, Closeable, Application.ActivityLifecycleCallbacks { private final @NotNull Application application; - private @Nullable IScopes scopes; + private @Nullable IHub hub; private @Nullable SentryAndroidOptions options; private final boolean isAndroidXAvailable; public UserInteractionIntegration( - final @NotNull Application application, final @NotNull io.sentry.util.LoadClass classLoader) { + final @NotNull Application application, final @NotNull LoadClass classLoader) { this.application = Objects.requireNonNull(application, "Application is required"); isAndroidXAvailable = classLoader.isClassAvailable("androidx.core.view.GestureDetectorCompat", options); @@ -44,14 +44,14 @@ private void startTracking(final @NotNull Activity activity) { return; } - if (scopes != null && options != null) { + if (hub != null && options != null) { Window.Callback delegate = window.getCallback(); if (delegate == null) { delegate = new NoOpWindowCallback(); } final SentryGestureListener gestureListener = - new SentryGestureListener(activity, scopes, options); + new SentryGestureListener(activity, hub, options); window.setCallback(new SentryWindowCallback(delegate, activity, gestureListener, options)); } } @@ -102,13 +102,13 @@ public void onActivitySaveInstanceState(@NotNull Activity activity, @NotNull Bun public void onActivityDestroyed(@NotNull Activity activity) {} @Override - public void register(@NotNull IScopes scopes, @NotNull SentryOptions options) { + public void register(@NotNull IHub hub, @NotNull SentryOptions options) { this.options = Objects.requireNonNull( (options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null, "SentryAndroidOptions is required"); - this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); + this.hub = Objects.requireNonNull(hub, "Hub is required"); final boolean integrationEnabled = this.options.isEnableUserInteractionBreadcrumbs() diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ViewHierarchyEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/ViewHierarchyEventProcessor.java index f8f42ac145..30e9f8de11 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ViewHierarchyEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ViewHierarchyEventProcessor.java @@ -284,9 +284,4 @@ private static ViewHierarchyNode viewToNode(@NotNull final View view) { return node; } - - @Override - public @Nullable Long getOrder() { - return 11000L; - } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java index cd80f5ced7..0ec0d83258 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureListener.java @@ -10,8 +10,8 @@ import android.view.Window; import io.sentry.Breadcrumb; import io.sentry.Hint; +import io.sentry.IHub; import io.sentry.IScope; -import io.sentry.IScopes; import io.sentry.ITransaction; import io.sentry.SentryLevel; import io.sentry.SpanStatus; @@ -43,7 +43,7 @@ private enum GestureType { private static final String TRACE_ORIGIN = "auto.ui.gesture_listener"; private final @NotNull WeakReference activityRef; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private final @NotNull SentryAndroidOptions options; private @Nullable UiElement activeUiElement = null; @@ -54,10 +54,10 @@ private enum GestureType { public SentryGestureListener( final @NotNull Activity currentActivity, - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull SentryAndroidOptions options) { this.activityRef = new WeakReference<>(currentActivity); - this.scopes = scopes; + this.hub = hub; this.options = options; } @@ -185,7 +185,7 @@ private void addBreadcrumb( hint.set(ANDROID_MOTION_EVENT, motionEvent); hint.set(ANDROID_VIEW, target.getView()); - scopes.addBreadcrumb( + hub.addBreadcrumb( Breadcrumb.userInteraction( type, target.getResourceName(), target.getClassName(), target.getTag(), additionalData), hint); @@ -202,7 +202,7 @@ private void startTracing(final @NotNull UiElement target, final @NotNull Gestur if (!(options.isTracingEnabled() && options.isEnableUserInteractionTracing())) { if (isNewInteraction) { - TracingUtils.startNewTrace(scopes); + TracingUtils.startNewTrace(hub); activeUiElement = target; activeEventType = eventType; } @@ -251,13 +251,14 @@ private void startTracing(final @NotNull UiElement target, final @NotNull Gestur TransactionOptions.DEFAULT_DEADLINE_TIMEOUT_AUTO_TRANSACTION); transactionOptions.setIdleTimeout(options.getIdleTimeout()); transactionOptions.setTrimEnd(true); - transactionOptions.setOrigin(TRACE_ORIGIN + "." + target.getOrigin()); final ITransaction transaction = - scopes.startTransaction( + hub.startTransaction( new TransactionContext(name, TransactionNameSource.COMPONENT, op), transactionOptions); - scopes.configureScope( + transaction.getSpanContext().setOrigin(TRACE_ORIGIN + "." + target.getOrigin()); + + hub.configureScope( scope -> { applyScope(scope, transaction); }); @@ -277,7 +278,7 @@ void stopTracing(final @NotNull SpanStatus status) { activeTransaction.finish(); } } - scopes.configureScope( + hub.configureScope( scope -> { // avoid method refs on Android due to some issues with older AGP setups // noinspection Convert2MethodRef diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityBreadcrumbsIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ActivityBreadcrumbsIntegrationTest.kt index 56dabd2fbc..10dc60e74b 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityBreadcrumbsIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ActivityBreadcrumbsIntegrationTest.kt @@ -4,7 +4,7 @@ import android.app.Activity import android.app.Application import android.os.Bundle import io.sentry.Breadcrumb -import io.sentry.Scopes +import io.sentry.Hub import io.sentry.SentryLevel import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull @@ -20,7 +20,7 @@ class ActivityBreadcrumbsIntegrationTest { private class Fixture { val application = mock() - val scopes = mock() + val hub = mock() val options = SentryAndroidOptions().apply { dsn = "https://key@sentry.io/proj" } @@ -28,7 +28,7 @@ class ActivityBreadcrumbsIntegrationTest { fun getSut(enabled: Boolean = true): ActivityBreadcrumbsIntegration { options.isEnableActivityLifecycleBreadcrumbs = enabled - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) return ActivityBreadcrumbsIntegration( application ) @@ -40,7 +40,7 @@ class ActivityBreadcrumbsIntegrationTest { @Test fun `When ActivityBreadcrumbsIntegration is disabled, it should not register the activity callback`() { val sut = fixture.getSut(false) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.application, never()).registerActivityLifecycleCallbacks(any()) } @@ -48,7 +48,7 @@ class ActivityBreadcrumbsIntegrationTest { @Test fun `When ActivityBreadcrumbsIntegration is enabled, it should register the activity callback`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.application).registerActivityLifecycleCallbacks(any()) @@ -59,12 +59,12 @@ class ActivityBreadcrumbsIntegrationTest { @Test fun `When breadcrumb is added, type and category should be set`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("ui.lifecycle", it.category) assertEquals("navigation", it.type) @@ -78,77 +78,77 @@ class ActivityBreadcrumbsIntegrationTest { @Test fun `When activity is created, it should add a breadcrumb`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.scopes).addBreadcrumb(any(), anyOrNull()) + verify(fixture.hub).addBreadcrumb(any(), anyOrNull()) } @Test fun `When activity is started, it should add a breadcrumb`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityStarted(activity) - verify(fixture.scopes).addBreadcrumb(any(), anyOrNull()) + verify(fixture.hub).addBreadcrumb(any(), anyOrNull()) } @Test fun `When activity is resumed, it should add a breadcrumb`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityResumed(activity) - verify(fixture.scopes).addBreadcrumb(any(), anyOrNull()) + verify(fixture.hub).addBreadcrumb(any(), anyOrNull()) } @Test fun `When activity is paused, it should add a breadcrumb`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityPaused(activity) - verify(fixture.scopes).addBreadcrumb(any(), anyOrNull()) + verify(fixture.hub).addBreadcrumb(any(), anyOrNull()) } @Test fun `When activity is stopped, it should add a breadcrumb`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityStopped(activity) - verify(fixture.scopes).addBreadcrumb(any(), anyOrNull()) + verify(fixture.hub).addBreadcrumb(any(), anyOrNull()) } @Test fun `When activity is save instance, it should add a breadcrumb`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivitySaveInstanceState(activity, fixture.bundle) - verify(fixture.scopes).addBreadcrumb(any(), anyOrNull()) + verify(fixture.hub).addBreadcrumb(any(), anyOrNull()) } @Test fun `When activity is destroyed, it should add a breadcrumb`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityDestroyed(activity) - verify(fixture.scopes).addBreadcrumb(any(), anyOrNull()) + verify(fixture.hub).addBreadcrumb(any(), anyOrNull()) } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt index 7f55c24eff..f936b6251c 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt @@ -14,10 +14,10 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.DateUtils import io.sentry.FullyDisplayedReporter +import io.sentry.Hub import io.sentry.IScope import io.sentry.Scope import io.sentry.ScopeCallback -import io.sentry.Scopes import io.sentry.Sentry import io.sentry.SentryDate import io.sentry.SentryDateProvider @@ -71,7 +71,7 @@ class ActivityLifecycleIntegrationTest { private class Fixture { val application = mock() - val scopes = mock() + val hub = mock() val options = SentryAndroidOptions().apply { dsn = "https://key@sentry.io/proj" } @@ -92,13 +92,13 @@ class ActivityLifecycleIntegrationTest { ): ActivityLifecycleIntegration { initializer?.configure(options) - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) // We let the ActivityLifecycleIntegration create the proper transaction here val optionCaptor = argumentCaptor() val contextCaptor = argumentCaptor() - whenever(scopes.startTransaction(contextCaptor.capture(), optionCaptor.capture())).thenAnswer { - val t = SentryTracer(contextCaptor.lastValue, scopes, optionCaptor.lastValue) + whenever(hub.startTransaction(contextCaptor.capture(), optionCaptor.capture())).thenAnswer { + val t = SentryTracer(contextCaptor.lastValue, hub, optionCaptor.lastValue) transaction = t return@thenAnswer t } @@ -145,7 +145,7 @@ class ActivityLifecycleIntegrationTest { @Test fun `When ActivityLifecycleIntegration is registered, it registers activity callback`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.application).registerActivityLifecycleCallbacks(any()) } @@ -153,7 +153,7 @@ class ActivityLifecycleIntegrationTest { @Test fun `When ActivityLifecycleIntegration is closed, it should unregister the callback`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.close() @@ -163,7 +163,7 @@ class ActivityLifecycleIntegrationTest { @Test fun `When ActivityLifecycleIntegration is closed, it should close the ActivityFramesTracker`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.close() @@ -173,39 +173,39 @@ class ActivityLifecycleIntegrationTest { @Test fun `When tracing is disabled, do not start tracing`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.scopes, never()).startTransaction(any(), any()) + verify(fixture.hub, never()).startTransaction(any(), any()) } @Test fun `When tracing is enabled but activity is running, do not start tracing again`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.scopes).startTransaction(any(), any()) + verify(fixture.hub).startTransaction(any(), any()) } @Test fun `Transaction op is ui_load and idle+deadline timeouts are set`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) setAppStartTime() val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("ui.load", it.operation) assertEquals(TransactionNameSource.COMPONENT, it.transactionNameSource) @@ -221,7 +221,7 @@ class ActivityLifecycleIntegrationTest { fun `Activity gets added to ActivityFramesTracker during transaction creation`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityStarted(activity) @@ -233,14 +233,14 @@ class ActivityLifecycleIntegrationTest { fun `Transaction name is the Activity's name`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) setAppStartTime() val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("Activity", it.name) assertEquals(TransactionNameSource.COMPONENT, it.transactionNameSource) @@ -254,9 +254,9 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) - whenever(fixture.scopes.configureScope(any())).thenAnswer { + whenever(fixture.hub.configureScope(any())).thenAnswer { val scope = Scope(fixture.options) sut.applyScope(scope, fixture.transaction) @@ -273,11 +273,11 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) - whenever(fixture.scopes.configureScope(any())).thenAnswer { + whenever(fixture.hub.configureScope(any())).thenAnswer { val scope = Scope(fixture.options) - val previousTransaction = SentryTracer(TransactionContext("name", "op"), fixture.scopes) + val previousTransaction = SentryTracer(TransactionContext("name", "op"), fixture.hub) scope.transaction = previousTransaction sut.applyScope(scope, fixture.transaction) @@ -297,14 +297,14 @@ class ActivityLifecycleIntegrationTest { it.isEnableTimeToFullDisplayTracing = true it.idleTimeout = 200 }) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.onActivityCreated(activity, fixture.bundle) sut.ttidSpanMap.values.first().finish() sut.ttfdSpanMap.values.first().finish() // then transaction should not be immediately finished - verify(fixture.scopes, never()) + verify(fixture.hub, never()) .captureTransaction( anyOrNull(), anyOrNull(), @@ -316,7 +316,7 @@ class ActivityLifecycleIntegrationTest { Thread.sleep(400) // then the transaction should be finished - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(SpanStatus.OK, it.status) }, @@ -330,13 +330,13 @@ class ActivityLifecycleIntegrationTest { fun `When tracing auto finish is enabled, it doesn't stop the transaction on onActivityPostResumed`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) sut.onActivityPostResumed(activity) - verify(fixture.scopes, never()).captureTransaction( + verify(fixture.hub, never()).captureTransaction( check { assertEquals(SpanStatus.OK, it.status) }, @@ -350,7 +350,7 @@ class ActivityLifecycleIntegrationTest { fun `When tracing has status, do not overwrite it`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) @@ -360,7 +360,7 @@ class ActivityLifecycleIntegrationTest { sut.onActivityPostResumed(activity) sut.onActivityDestroyed(activity) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(SpanStatus.UNKNOWN_ERROR, it.status) }, @@ -376,43 +376,43 @@ class ActivityLifecycleIntegrationTest { it.tracesSampleRate = 1.0 it.isEnableActivityLifecycleTracingAutoFinish = false }) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) sut.onActivityPostResumed(activity) - verify(fixture.scopes, never()).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) + verify(fixture.hub, never()).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) } @Test fun `When tracing is disabled, do not finish transaction`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityPostResumed(activity) - verify(fixture.scopes, never()).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) + verify(fixture.hub, never()).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) } @Test fun `When Activity is destroyed but transaction is running, finish it`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) sut.onActivityDestroyed(activity) - verify(fixture.scopes).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) + verify(fixture.hub).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) } @Test fun `When transaction is started, adds to WeakWef`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) @@ -424,7 +424,7 @@ class ActivityLifecycleIntegrationTest { fun `When Activity is destroyed removes WeakRef`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) @@ -437,7 +437,7 @@ class ActivityLifecycleIntegrationTest { fun `When Activity is destroyed, sets appStartSpan status to cancelled and finish it`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) setAppStartTime() @@ -454,7 +454,7 @@ class ActivityLifecycleIntegrationTest { fun `When Activity is destroyed, sets appStartSpan to null`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) setAppStartTime() @@ -469,7 +469,7 @@ class ActivityLifecycleIntegrationTest { fun `When Activity is destroyed, sets ttidSpan status to deadline_exceeded and finish it`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) setAppStartTime() @@ -486,7 +486,7 @@ class ActivityLifecycleIntegrationTest { fun `When Activity is destroyed, sets ttidSpan to null`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) setAppStartTime() @@ -503,7 +503,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) setAppStartTime() @@ -521,7 +521,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) setAppStartTime() @@ -537,25 +537,25 @@ class ActivityLifecycleIntegrationTest { fun `When new Activity and transaction is created, finish previous ones`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.onActivityCreated(mock(), mock()) sut.onActivityCreated(mock(), fixture.bundle) - verify(fixture.scopes).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) + verify(fixture.hub).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) } @Test fun `do not stop transaction on resumed if API 29`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, mock()) sut.onActivityResumed(activity) - verify(fixture.scopes, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) + verify(fixture.hub, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) } @Test @@ -563,7 +563,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut(Build.VERSION_CODES.P) fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, mock()) @@ -571,21 +571,21 @@ class ActivityLifecycleIntegrationTest { sut.ttfdSpanMap.values.first().finish() sut.onActivityResumed(activity) - verify(fixture.scopes, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) + verify(fixture.hub, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) } @Test fun `start transaction on created if API less than 29`() { val sut = fixture.getSut(Build.VERSION_CODES.P) fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) setAppStartTime() val activity = mock() sut.onActivityCreated(activity, mock()) - verify(fixture.scopes).startTransaction(any(), any()) + verify(fixture.hub).startTransaction(any(), any()) } @Test @@ -593,7 +593,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, mock()) @@ -611,7 +611,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, mock()) @@ -626,7 +626,7 @@ class ActivityLifecycleIntegrationTest { fun `App start is Cold when savedInstanceState is null`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, null) @@ -638,7 +638,7 @@ class ActivityLifecycleIntegrationTest { fun `App start is Warm when savedInstanceState is not null`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() val bundle = Bundle() @@ -651,7 +651,7 @@ class ActivityLifecycleIntegrationTest { fun `Do not overwrite App start type after set`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() val bundle = Bundle() @@ -665,7 +665,7 @@ class ActivityLifecycleIntegrationTest { fun `When firstActivityCreated is true, start transaction with given appStartTime`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val date = SentryNanotimeDate(Date(1), 0) setAppStartTime(date) @@ -674,7 +674,7 @@ class ActivityLifecycleIntegrationTest { sut.onActivityCreated(activity, fixture.bundle) // call only once - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( any(), check { assertEquals(date.nanoTimestamp(), it.startTimestamp!!.nanoTimestamp()) @@ -686,7 +686,7 @@ class ActivityLifecycleIntegrationTest { fun `When firstActivityCreated is true and app start sampling decision is set, start transaction with isAppStart true`() { AppStartMetrics.getInstance().appStartSamplingDecision = mock() val sut = fixture.getSut { it.tracesSampleRate = 1.0 } - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val date = SentryNanotimeDate(Date(1), 0) setAppStartTime(date) @@ -694,7 +694,7 @@ class ActivityLifecycleIntegrationTest { val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( any(), check { assertEquals(date.nanoTimestamp(), it.startTimestamp!!.nanoTimestamp()) @@ -706,7 +706,7 @@ class ActivityLifecycleIntegrationTest { @Test fun `When firstActivityCreated is true and app start sampling decision is not set, start transaction with isAppStart false`() { val sut = fixture.getSut { it.tracesSampleRate = 1.0 } - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val date = SentryNanotimeDate(Date(1), 0) setAppStartTime(date) @@ -714,7 +714,7 @@ class ActivityLifecycleIntegrationTest { val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( any(), check { assertEquals(date.nanoTimestamp(), it.startTimestamp!!.nanoTimestamp()) @@ -727,19 +727,19 @@ class ActivityLifecycleIntegrationTest { fun `When firstActivityCreated is false and app start sampling decision is set, start transaction with isAppStart false`() { AppStartMetrics.getInstance().appStartSamplingDecision = mock() val sut = fixture.getSut { it.tracesSampleRate = 1.0 } - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - verify(fixture.scopes).startTransaction(any(), check { assertFalse(it.isAppStartTransaction) }) + verify(fixture.hub).startTransaction(any(), check { assertFalse(it.isAppStartTransaction) }) } @Test fun `When firstActivityCreated is true, do not create app start span if not foregroundImportance`() { val sut = fixture.getSut(importance = RunningAppProcessInfo.IMPORTANCE_BACKGROUND) fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) // usually set by SentryPerformanceProvider val date = SentryNanotimeDate(Date(1), 0) @@ -750,7 +750,7 @@ class ActivityLifecycleIntegrationTest { sut.onActivityCreated(activity, fixture.bundle) // call only once - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( any(), check { assertNotEquals(date, it.startTimestamp) } ) @@ -760,7 +760,7 @@ class ActivityLifecycleIntegrationTest { fun `Create and finish app start span immediately in case SDK init is deferred`() { val sut = fixture.getSut(importance = RunningAppProcessInfo.IMPORTANCE_FOREGROUND) fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) // usually set by SentryPerformanceProvider val startDate = SentryNanotimeDate(Date(1), 0) @@ -786,7 +786,7 @@ class ActivityLifecycleIntegrationTest { fun `When SentryPerformanceProvider is disabled, app start time span is still created`() { val sut = fixture.getSut(importance = RunningAppProcessInfo.IMPORTANCE_FOREGROUND) fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) // usually done by SentryPerformanceProvider, if disabled it's done by // SentryAndroid.init @@ -814,7 +814,7 @@ class ActivityLifecycleIntegrationTest { fun `When app-start end time is already set, it should not be overwritten`() { val sut = fixture.getSut(importance = RunningAppProcessInfo.IMPORTANCE_FOREGROUND) fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) // usually done by SentryPerformanceProvider val startDate = SentryNanotimeDate(Date(1), 0) @@ -838,7 +838,7 @@ class ActivityLifecycleIntegrationTest { fun `When activity lifecycle happens multiple times, app-start end time should not be overwritten`() { val sut = fixture.getSut(importance = RunningAppProcessInfo.IMPORTANCE_FOREGROUND) fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) // usually done by SentryPerformanceProvider val startDate = SentryNanotimeDate(Date(1), 0) @@ -876,7 +876,7 @@ class ActivityLifecycleIntegrationTest { fun `When firstActivityCreated is true, start app start warm span with given appStartTime`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val date = SentryNanotimeDate(Date(1), 0) setAppStartTime(date) @@ -893,7 +893,7 @@ class ActivityLifecycleIntegrationTest { fun `When firstActivityCreated is true, start app start cold span with given appStartTime`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val date = SentryNanotimeDate(Date(1), 0) setAppStartTime(date) @@ -910,7 +910,7 @@ class ActivityLifecycleIntegrationTest { fun `When firstActivityCreated is true, start app start span with Warm description`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val date = SentryNanotimeDate(Date(1), 0) setAppStartTime(date) @@ -927,7 +927,7 @@ class ActivityLifecycleIntegrationTest { fun `When firstActivityCreated is true, start app start span with Cold description`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val date = SentryNanotimeDate(Date(1), 0) setAppStartTime(date) @@ -944,7 +944,7 @@ class ActivityLifecycleIntegrationTest { fun `When firstActivityCreated is false, start transaction but not with given appStartTime`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val date = SentryNanotimeDate(Date(1), 0) setAppStartTime() @@ -965,12 +965,12 @@ class ActivityLifecycleIntegrationTest { fun `When transaction is finished, it gets removed from scope`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) - whenever(fixture.scopes.configureScope(any())).thenAnswer { + whenever(fixture.hub.configureScope(any())).thenAnswer { val scope = Scope(fixture.options) scope.transaction = fixture.transaction @@ -988,7 +988,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = false - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) @@ -1001,7 +1001,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) @@ -1016,7 +1016,7 @@ class ActivityLifecycleIntegrationTest { fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true fixture.options.executorService = deferredExecutorService - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) val ttfdSpan = sut.ttfdSpanMap[activity] @@ -1032,7 +1032,7 @@ class ActivityLifecycleIntegrationTest { assertEquals(SpanStatus.DEADLINE_EXCEEDED, ttfdSpan.status) sut.onActivityDestroyed(activity) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { // ttfd timed out, so its measurement should not be set val ttfdMeasurement = it.measurements[MeasurementValue.KEY_TIME_TO_FULL_DISPLAY] @@ -1049,7 +1049,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) val ttfdSpan = sut.ttfdSpanMap[activity] @@ -1072,7 +1072,7 @@ class ActivityLifecycleIntegrationTest { assertNull(autoCloseFuture) sut.onActivityDestroyed(activity) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { // ttfd was finished successfully, so its measurement should be set val ttfdMeasurement = it.measurements[MeasurementValue.KEY_TIME_TO_FULL_DISPLAY] @@ -1090,7 +1090,7 @@ class ActivityLifecycleIntegrationTest { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() val activity2 = mock() sut.onActivityCreated(activity, fixture.bundle) @@ -1128,7 +1128,7 @@ class ActivityLifecycleIntegrationTest { whenever(activity.findViewById(any())).thenReturn(view) // Make the integration create the spans and register to the FirstDrawDoneListener - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.onActivityCreated(activity, fixture.bundle) sut.onActivityResumed(activity) @@ -1143,7 +1143,7 @@ class ActivityLifecycleIntegrationTest { assertTrue(ttidSpan.isFinished) sut.onActivityDestroyed(activity) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { // ttid measurement should be set val ttidMeasurement = it.measurements[MeasurementValue.KEY_TIME_TO_INITIAL_DISPLAY] @@ -1166,7 +1166,7 @@ class ActivityLifecycleIntegrationTest { whenever(activity.findViewById(any())).thenReturn(view) // Make the integration create the spans and register to the FirstDrawDoneListener - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.onActivityCreated(activity, fixture.bundle) sut.onActivityResumed(activity) @@ -1189,7 +1189,7 @@ class ActivityLifecycleIntegrationTest { assertEquals(newEndDate, ttidSpan.finishDate) sut.onActivityDestroyed(activity) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { // ttid and ttfd measurements should be the same val ttidMeasurement = it.measurements[MeasurementValue.KEY_TIME_TO_INITIAL_DISPLAY] @@ -1211,7 +1211,7 @@ class ActivityLifecycleIntegrationTest { fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.onActivityCreated(activity, fixture.bundle) // The ttid span should be running @@ -1233,7 +1233,7 @@ class ActivityLifecycleIntegrationTest { fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true fixture.options.executorService = deferredExecutorService - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.onActivityCreated(activity, fixture.bundle) sut.onActivityResumed(activity) @@ -1268,7 +1268,7 @@ class ActivityLifecycleIntegrationTest { fixture.options.tracesSampleRate = 1.0 fixture.options.isEnableTimeToFullDisplayTracing = true - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.onActivityCreated(activity, fixture.bundle) val ttfdSpan = sut.ttfdSpanMap[activity] assertNotNull(ttfdSpan) @@ -1294,15 +1294,15 @@ class ActivityLifecycleIntegrationTest { val argumentCaptor: ArgumentCaptor = ArgumentCaptor.forClass(ScopeCallback::class.java) val scope = Scope(fixture.options) val propagationContextAtStart = scope.propagationContext - whenever(fixture.scopes.configureScope(argumentCaptor.capture())).thenAnswer { + whenever(fixture.hub.configureScope(argumentCaptor.capture())).thenAnswer { argumentCaptor.value.run(scope) } - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.onActivityCreated(activity, fixture.bundle) // once for the screen, and once for the tracing propagation context - verify(fixture.scopes, times(2)).configureScope(any()) + verify(fixture.hub, times(2)).configureScope(any()) assertNotSame(propagationContextAtStart, scope.propagationContext) } @@ -1314,15 +1314,15 @@ class ActivityLifecycleIntegrationTest { val argumentCaptor: ArgumentCaptor = ArgumentCaptor.forClass(ScopeCallback::class.java) val scope = mock() - whenever(fixture.scopes.configureScope(argumentCaptor.capture())).thenAnswer { + whenever(fixture.hub.configureScope(argumentCaptor.capture())).thenAnswer { argumentCaptor.value.run(scope) } - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.onActivityCreated(activity, fixture.bundle) // once for the screen, and once for the tracing propagation context - verify(fixture.scopes, times(2)).configureScope(any()) + verify(fixture.hub, times(2)).configureScope(any()) verify(scope).setScreen(any()) } @@ -1335,32 +1335,32 @@ class ActivityLifecycleIntegrationTest { val argumentCaptor: ArgumentCaptor = ArgumentCaptor.forClass(ScopeCallback::class.java) val scope = Scope(fixture.options) val propagationContextAtStart = scope.propagationContext - whenever(fixture.scopes.configureScope(argumentCaptor.capture())).thenAnswer { + whenever(fixture.hub.configureScope(argumentCaptor.capture())).thenAnswer { argumentCaptor.value.run(scope) } - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.onActivityCreated(activity, fixture.bundle) // once for the screen, and once for the tracing propagation context - verify(fixture.scopes, times(2)).configureScope(any()) + verify(fixture.hub, times(2)).configureScope(any()) val propagationContextAfterNewTrace = scope.propagationContext assertNotSame(propagationContextAtStart, propagationContextAfterNewTrace) - clearInvocations(fixture.scopes) + clearInvocations(fixture.hub) sut.onActivityCreated(activity, fixture.bundle) // once for the screen, but not for the tracing propagation context - verify(fixture.scopes).configureScope(any()) + verify(fixture.hub).configureScope(any()) assertSame(propagationContextAfterNewTrace, scope.propagationContext) sut.onActivityDestroyed(activity) - clearInvocations(fixture.scopes) + clearInvocations(fixture.hub) sut.onActivityCreated(activity, fixture.bundle) // once for the screen, and once for the tracing propagation context - verify(fixture.scopes, times(2)).configureScope(any()) + verify(fixture.hub, times(2)).configureScope(any()) assertNotSame(propagationContextAfterNewTrace, scope.propagationContext) } @@ -1368,7 +1368,7 @@ class ActivityLifecycleIntegrationTest { fun `when transaction is finished, sets frame metrics`() { val sut = fixture.getSut() fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val activity = mock() sut.onActivityCreated(activity, fixture.bundle) @@ -1384,7 +1384,7 @@ class ActivityLifecycleIntegrationTest { fixture.options.tracesSampleRate = 1.0 fixture.options.dateProvider = SentryDateProvider { now } - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) // usually done by SentryPerformanceProvider val startDate = SentryNanotimeDate(Date(5678), 910) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidTransactionProfilerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidTransactionProfilerTest.kt index 7f83d4016d..fd03d34631 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidTransactionProfilerTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidTransactionProfilerTest.kt @@ -5,8 +5,8 @@ import android.os.Build import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.CpuCollectionData +import io.sentry.IHub import io.sentry.ILogger -import io.sentry.IScopes import io.sentry.ISentryExecutorService import io.sentry.MemoryCollectionData import io.sentry.PerformanceCollectionData @@ -89,7 +89,7 @@ class AndroidTransactionProfilerTest { executorService = mockExecutorService } - val scopes: IScopes = mock() + val hub: IHub = mock() val frameMetricsCollector: SentryFrameMetricsCollector = mock() lateinit var transaction1: SentryTracer @@ -97,10 +97,10 @@ class AndroidTransactionProfilerTest { lateinit var transaction3: SentryTracer fun getSut(context: Context, buildInfoProvider: BuildInfoProvider = buildInfo): AndroidTransactionProfiler { - whenever(scopes.options).thenReturn(options) - transaction1 = SentryTracer(TransactionContext("", ""), scopes) - transaction2 = SentryTracer(TransactionContext("", ""), scopes) - transaction3 = SentryTracer(TransactionContext("", ""), scopes) + whenever(hub.options).thenReturn(options) + transaction1 = SentryTracer(TransactionContext("", ""), hub) + transaction2 = SentryTracer(TransactionContext("", ""), hub) + transaction3 = SentryTracer(TransactionContext("", ""), hub) return AndroidTransactionProfiler(context, options, buildInfoProvider, frameMetricsCollector) } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AnrIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AnrIntegrationTest.kt index 1a74a47ae1..cceabc9774 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AnrIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AnrIntegrationTest.kt @@ -2,7 +2,7 @@ package io.sentry.android.core import android.content.Context import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryLevel import io.sentry.android.core.AnrIntegration.AnrHint import io.sentry.exception.ExceptionMechanismException @@ -24,7 +24,7 @@ class AnrIntegrationTest { private class Fixture { val context = mock() - val scopes = mock() + val hub = mock() var options: SentryAndroidOptions = SentryAndroidOptions().apply { setLogger(mock()) } @@ -49,7 +49,7 @@ class AnrIntegrationTest { fixture.options.executorService = ImmediateExecutorService() val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertNotNull(sut.anrWatchDog) assertTrue((sut.anrWatchDog as ANRWatchDog).isAlive) @@ -60,7 +60,7 @@ class AnrIntegrationTest { fixture.options.executorService = mock() val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertNull(sut.anrWatchDog) } @@ -70,7 +70,7 @@ class AnrIntegrationTest { val sut = fixture.getSut() fixture.options.isAnrEnabled = false - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertNull(sut.anrWatchDog) } @@ -79,9 +79,9 @@ class AnrIntegrationTest { fun `When ANR watch dog is triggered, it should capture an error event with AnrHint`() { val sut = fixture.getSut() - sut.reportANR(fixture.scopes, fixture.options, getApplicationNotResponding()) + sut.reportANR(fixture.hub, fixture.options, getApplicationNotResponding()) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { assertEquals(SentryLevel.ERROR, it.level) }, @@ -97,7 +97,7 @@ class AnrIntegrationTest { val sut = fixture.getSut() fixture.options.executorService = ImmediateExecutorService() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertNotNull(sut.anrWatchDog) @@ -107,11 +107,11 @@ class AnrIntegrationTest { } @Test - fun `when scopes is closed right after start, integration is not registered`() { + fun `when hub is closed right after start, integration is not registered`() { val deferredExecutorService = DeferredExecutorService() val sut = fixture.getSut() fixture.options.executorService = deferredExecutorService - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertNull(sut.anrWatchDog) sut.close() deferredExecutorService.runAll() @@ -122,9 +122,9 @@ class AnrIntegrationTest { fun `When ANR watch dog is triggered, constructs exception with proper mechanism and snapshot flag`() { val sut = fixture.getSut() - sut.reportANR(fixture.scopes, fixture.options, getApplicationNotResponding()) + sut.reportANR(fixture.hub, fixture.options, getApplicationNotResponding()) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { val ex = it.throwableMechanism as ExceptionMechanismException assertTrue(ex.isSnapshot) @@ -139,9 +139,9 @@ class AnrIntegrationTest { val sut = fixture.getSut() AppState.getInstance().setInBackground(true) - sut.reportANR(fixture.scopes, fixture.options, getApplicationNotResponding()) + sut.reportANR(fixture.hub, fixture.options, getApplicationNotResponding()) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { val message = it.throwable?.message assertTrue(message?.startsWith("Background") == true) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2EventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2EventProcessorTest.kt index 2f34b4d2e0..b581856fe0 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2EventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2EventProcessorTest.kt @@ -169,7 +169,7 @@ class AnrV2EventProcessorTest { assertNull(processed.platform) assertNull(processed.exceptions) - assertTrue(processed.contexts.isEmpty) + assertEquals(emptyMap(), processed.contexts) } @Test diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2IntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2IntegrationTest.kt index 1abcd43719..885ad22c8f 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2IntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2IntegrationTest.kt @@ -6,8 +6,8 @@ import android.content.Context import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.Hint +import io.sentry.IHub import io.sentry.ILogger -import io.sentry.IScopes import io.sentry.SentryEnvelope import io.sentry.SentryLevel import io.sentry.android.core.AnrV2Integration.AnrV2Hint @@ -59,7 +59,7 @@ class AnrV2IntegrationTest { lateinit var lastReportedAnrFile: File val options = SentryAndroidOptions() - val scopes = mock() + val hub = mock() val logger = mock() fun getSut( @@ -93,7 +93,7 @@ class AnrV2IntegrationTest { lastReportedAnrFile = File(cacheDir, AndroidEnvelopeCache.LAST_ANR_REPORT) lastReportedAnrFile.writeText(lastReportedAnrTimestamp.toString()) } - whenever(scopes.captureEvent(any(), anyOrNull())).thenReturn(lastEventId) + whenever(hub.captureEvent(any(), anyOrNull())).thenReturn(lastEventId) return AnrV2Integration(context) } @@ -170,7 +170,7 @@ class AnrV2IntegrationTest { fun `when cacheDir is not set, does not process historical exits`() { val integration = fixture.getSut(null, useImmediateExecutorService = false) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) verify(fixture.options.executorService, never()).submit(any()) } @@ -180,7 +180,7 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir, isAnrEnabled = false, useImmediateExecutorService = false) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) verify(fixture.options.executorService, never()).submit(any()) } @@ -189,9 +189,9 @@ class AnrV2IntegrationTest { fun `when historical exit list is empty, does not process historical exits`() { val integration = fixture.getSut(tmpDir) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) - verify(fixture.scopes, never()).captureEvent(any(), anyOrNull()) + verify(fixture.hub, never()).captureEvent(any(), anyOrNull()) } @Test @@ -199,9 +199,9 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir) fixture.addAppExitInfo(reason = null) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) - verify(fixture.scopes, never()).captureEvent(any(), anyOrNull()) + verify(fixture.hub, never()).captureEvent(any(), anyOrNull()) } @Test @@ -212,9 +212,9 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir) fixture.addAppExitInfo(timestamp = oldTimestamp) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) - verify(fixture.scopes, never()).captureEvent(any(), anyOrNull()) + verify(fixture.hub, never()).captureEvent(any(), anyOrNull()) } @Test @@ -222,9 +222,9 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir, lastReportedAnrTimestamp = oldTimestamp) fixture.addAppExitInfo(timestamp = oldTimestamp) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) - verify(fixture.scopes, never()).captureEvent(any(), anyOrNull()) + verify(fixture.hub, never()).captureEvent(any(), anyOrNull()) } @Test @@ -232,9 +232,9 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir, lastReportedAnrTimestamp = null) fixture.addAppExitInfo(timestamp = oldTimestamp) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) - verify(fixture.scopes).captureEvent(any(), anyOrNull()) + verify(fixture.hub).captureEvent(any(), anyOrNull()) } @Test @@ -242,9 +242,9 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir, lastReportedAnrTimestamp = oldTimestamp) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { assertEquals(newTimestamp, it.timestamp.time) assertEquals(SentryLevel.FATAL, it.level) @@ -291,9 +291,9 @@ class AnrV2IntegrationTest { importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND ) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( any(), argThat { val hint = HintUtils.getSentrySdkHint(this) @@ -311,7 +311,7 @@ class AnrV2IntegrationTest { ) fixture.addAppExitInfo(timestamp = newTimestamp) - whenever(fixture.scopes.captureEvent(any(), any())).thenAnswer { invocation -> + whenever(fixture.hub.captureEvent(any(), any())).thenAnswer { invocation -> val hint = HintUtils.getSentrySdkHint(invocation.getArgument(1)) as DiskFlushNotification thread { @@ -321,9 +321,9 @@ class AnrV2IntegrationTest { SentryId() } - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) - verify(fixture.scopes).captureEvent(any(), anyOrNull()) + verify(fixture.hub).captureEvent(any(), anyOrNull()) // shouldn't fall into timed out state, because we marked event as flushed on another thread verify(fixture.logger, never()).log( any(), @@ -341,9 +341,9 @@ class AnrV2IntegrationTest { ) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) - verify(fixture.scopes).captureEvent(any(), anyOrNull()) + verify(fixture.hub).captureEvent(any(), anyOrNull()) // we do not call markFlushed, hence it should time out waiting for flush, but because // we drop the event, it should not even come to this if-check verify(fixture.logger, never()).log( @@ -360,9 +360,9 @@ class AnrV2IntegrationTest { fixture.addAppExitInfo(timestamp = newTimestamp - 1 * 60 * 1000) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) - verify(fixture.scopes, times(2)).captureEvent( + verify(fixture.hub, times(2)).captureEvent( any(), argThat { val hint = HintUtils.getSentrySdkHint(this) @@ -382,10 +382,10 @@ class AnrV2IntegrationTest { fixture.addAppExitInfo(timestamp = newTimestamp - 1 * 60 * 1000) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) // only the latest anr is reported which should be enrichable - verify(fixture.scopes, atMost(1)).captureEvent( + verify(fixture.hub, atMost(1)).captureEvent( any(), argThat { val hint = HintUtils.getSentrySdkHint(this) @@ -402,20 +402,20 @@ class AnrV2IntegrationTest { fixture.addAppExitInfo(timestamp = newTimestamp - TimeUnit.DAYS.toMillis(1)) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) // the order is reverse here, so the oldest ANR will be reported first to keep track of // last reported ANR in a marker file - inOrder(fixture.scopes) { - verify(fixture.scopes).captureEvent( + inOrder(fixture.hub) { + verify(fixture.hub).captureEvent( argThat { timestamp.time == newTimestamp - TimeUnit.DAYS.toMillis(2) }, anyOrNull() ) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( argThat { timestamp.time == newTimestamp - TimeUnit.DAYS.toMillis(1) }, anyOrNull() ) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( argThat { timestamp.time == newTimestamp }, anyOrNull() ) @@ -427,9 +427,9 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir, lastReportedAnrTimestamp = oldTimestamp) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( any(), argThat { val hint = HintUtils.getSentrySdkHint(this) @@ -443,9 +443,9 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir, lastReportedAnrTimestamp = oldTimestamp) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( any(), argThat { val hint = HintUtils.getSentrySdkHint(this) @@ -472,7 +472,7 @@ class AnrV2IntegrationTest { ) } - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) // we store envelope with StartSessionHint on different thread after some delay, which // triggers the previous session flush, so no timeout @@ -493,14 +493,14 @@ class AnrV2IntegrationTest { ) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) verify(fixture.logger, never()).log( any(), argThat { startsWith("Timed out waiting to flush previous session to its own file.") }, any() ) - verify(fixture.scopes).captureEvent(any(), any()) + verify(fixture.hub).captureEvent(any(), any()) } @Test @@ -512,7 +512,7 @@ class AnrV2IntegrationTest { ) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) verify(fixture.logger).log( any(), @@ -532,9 +532,9 @@ class AnrV2IntegrationTest { ) fixture.addAppExitInfo(timestamp = newTimestamp) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( any(), check { assertNotNull(it.threadDump) @@ -547,8 +547,8 @@ class AnrV2IntegrationTest { val integration = fixture.getSut(tmpDir, lastReportedAnrTimestamp = oldTimestamp) fixture.addAppExitInfo(timestamp = newTimestamp, addTrace = false) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) - verify(fixture.scopes, never()).captureEvent(any(), anyOrNull()) + verify(fixture.hub, never()).captureEvent(any(), anyOrNull()) } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegrationTest.kt index 5f8792c850..15a6d690e5 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AppComponentsBreadcrumbsIntegrationTest.kt @@ -5,7 +5,7 @@ import android.content.Context import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.Breadcrumb -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryLevel import org.junit.runner.RunWith import org.mockito.kotlin.any @@ -37,8 +37,8 @@ class AppComponentsBreadcrumbsIntegrationTest { fun `When app components breadcrumb is enabled, it registers callback`() { val sut = fixture.getSut() val options = SentryAndroidOptions() - val scopes = mock() - sut.register(scopes, options) + val hub = mock() + sut.register(hub, options) verify(fixture.context).registerComponentCallbacks(any()) } @@ -46,10 +46,10 @@ class AppComponentsBreadcrumbsIntegrationTest { fun `When app components breadcrumb is enabled, but ComponentCallbacks is not ready, do not throw`() { val sut = fixture.getSut() val options = SentryAndroidOptions() - val scopes = mock() - sut.register(scopes, options) + val hub = mock() + sut.register(hub, options) whenever(fixture.context.registerComponentCallbacks(any())).thenThrow(NullPointerException()) - sut.register(scopes, options) + sut.register(hub, options) assertFalse(options.isEnableAppComponentBreadcrumbs) } @@ -59,8 +59,8 @@ class AppComponentsBreadcrumbsIntegrationTest { val options = SentryAndroidOptions().apply { isEnableAppComponentBreadcrumbs = false } - val scopes = mock() - sut.register(scopes, options) + val hub = mock() + sut.register(hub, options) verify(fixture.context, never()).registerComponentCallbacks(any()) } @@ -68,8 +68,8 @@ class AppComponentsBreadcrumbsIntegrationTest { fun `When AppComponentsBreadcrumbsIntegrationTest is closed, it should unregister the callback`() { val sut = fixture.getSut() val options = SentryAndroidOptions() - val scopes = mock() - sut.register(scopes, options) + val hub = mock() + sut.register(hub, options) sut.close() verify(fixture.context).unregisterComponentCallbacks(any()) } @@ -78,10 +78,10 @@ class AppComponentsBreadcrumbsIntegrationTest { fun `When app components breadcrumb is closed, but ComponentCallbacks is not ready, do not throw`() { val sut = fixture.getSut() val options = SentryAndroidOptions() - val scopes = mock() + val hub = mock() whenever(fixture.context.registerComponentCallbacks(any())).thenThrow(NullPointerException()) whenever(fixture.context.unregisterComponentCallbacks(any())).thenThrow(NullPointerException()) - sut.register(scopes, options) + sut.register(hub, options) sut.close() } @@ -89,10 +89,10 @@ class AppComponentsBreadcrumbsIntegrationTest { fun `When low memory event, a breadcrumb with type, category and level should be set`() { val sut = fixture.getSut() val options = SentryAndroidOptions() - val scopes = mock() - sut.register(scopes, options) + val hub = mock() + sut.register(hub, options) sut.onLowMemory() - verify(scopes).addBreadcrumb( + verify(hub).addBreadcrumb( check { assertEquals("device.event", it.category) assertEquals("system", it.type) @@ -105,10 +105,10 @@ class AppComponentsBreadcrumbsIntegrationTest { fun `When trim memory event with level, a breadcrumb with type, category and level should be set`() { val sut = fixture.getSut() val options = SentryAndroidOptions() - val scopes = mock() - sut.register(scopes, options) + val hub = mock() + sut.register(hub, options) sut.onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) - verify(scopes).addBreadcrumb( + verify(hub).addBreadcrumb( check { assertEquals("device.event", it.category) assertEquals("system", it.type) @@ -121,20 +121,20 @@ class AppComponentsBreadcrumbsIntegrationTest { fun `When trim memory event with level not so high, do not add a breadcrumb`() { val sut = fixture.getSut() val options = SentryAndroidOptions() - val scopes = mock() - sut.register(scopes, options) + val hub = mock() + sut.register(hub, options) sut.onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) - verify(scopes, never()).addBreadcrumb(any()) + verify(hub, never()).addBreadcrumb(any()) } @Test fun `When device orientation event, a breadcrumb with type, category and level should be set`() { val sut = AppComponentsBreadcrumbsIntegration(ApplicationProvider.getApplicationContext()) val options = SentryAndroidOptions() - val scopes = mock() - sut.register(scopes, options) + val hub = mock() + sut.register(hub, options) sut.onConfigurationChanged(mock()) - verify(scopes).addBreadcrumb( + verify(hub).addBreadcrumb( check { assertEquals("device.orientation", it.category) assertEquals("navigation", it.type) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AppLifecycleIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AppLifecycleIntegrationTest.kt index 733aefa8d6..ed8d53227c 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AppLifecycleIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AppLifecycleIntegrationTest.kt @@ -2,7 +2,7 @@ package io.sentry.android.core import android.os.Looper import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.sentry.IScopes +import io.sentry.IHub import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.mock @@ -17,7 +17,7 @@ import kotlin.test.assertNull class AppLifecycleIntegrationTest { private class Fixture { - val scopes = mock() + val hub = mock() lateinit var handler: MainLooperHandler val options = SentryAndroidOptions() @@ -33,7 +33,7 @@ class AppLifecycleIntegrationTest { fun `When AppLifecycleIntegration is added, lifecycle watcher should be started`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertNotNull(sut.watcher) } @@ -46,7 +46,7 @@ class AppLifecycleIntegrationTest { isEnableAutoSessionTracking = false } - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertNull(sut.watcher) } @@ -55,7 +55,7 @@ class AppLifecycleIntegrationTest { fun `When AppLifecycleIntegration is closed, lifecycle watcher should be closed`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertNotNull(sut.watcher) @@ -70,7 +70,7 @@ class AppLifecycleIntegrationTest { val latch = CountDownLatch(1) Thread { - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) latch.countDown() }.start() @@ -84,7 +84,7 @@ class AppLifecycleIntegrationTest { val sut = fixture.getSut() val latch = CountDownLatch(1) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertNotNull(sut.watcher) @@ -103,7 +103,7 @@ class AppLifecycleIntegrationTest { val sut = fixture.getSut(mockHandler = false) val latch = CountDownLatch(1) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertNotNull(sut.watcher) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/CurrentActivityIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/CurrentActivityIntegrationTest.kt index ecdbff5104..6330623121 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/CurrentActivityIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/CurrentActivityIntegrationTest.kt @@ -3,7 +3,7 @@ package io.sentry.android.core import android.app.Activity import android.app.Application import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.sentry.IScopes +import io.sentry.IHub import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.mock @@ -19,7 +19,7 @@ class CurrentActivityIntegrationTest { private class Fixture { val application = mock() val activity = mock() - val scopes = mock() + val hub = mock() val options = SentryAndroidOptions().apply { dsn = "https://key@sentry.io/proj" @@ -27,7 +27,7 @@ class CurrentActivityIntegrationTest { fun getSut(): CurrentActivityIntegration { val integration = CurrentActivityIntegration(application) - integration.register(scopes, options) + integration.register(hub, options) return integration } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt index c4fef01cc2..80954f67a5 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt @@ -7,7 +7,7 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.DiagnosticLogger import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryEvent import io.sentry.SentryLevel import io.sentry.SentryTracer @@ -62,13 +62,13 @@ class DefaultAndroidEventProcessorTest { sdkVersion = SdkVersion("test", "1.2.3") } - val scopes: IScopes = mock() + val hub: IHub = mock() lateinit var sentryTracer: SentryTracer fun getSut(context: Context): DefaultAndroidEventProcessor { - whenever(scopes.options).thenReturn(options) - sentryTracer = SentryTracer(TransactionContext("", ""), scopes) + whenever(hub.options).thenReturn(options) + sentryTracer = SentryTracer(TransactionContext("", ""), hub) return DefaultAndroidEventProcessor(context, buildInfo, options) } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/EnvelopeFileObserverIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/EnvelopeFileObserverIntegrationTest.kt index 69b2eee2ec..699fa2d2f2 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/EnvelopeFileObserverIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/EnvelopeFileObserverIntegrationTest.kt @@ -1,13 +1,13 @@ package io.sentry.android.core import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.sentry.Hub +import io.sentry.IHub import io.sentry.ILogger -import io.sentry.IScopes import io.sentry.SentryLevel import io.sentry.SentryOptions import io.sentry.test.DeferredExecutorService import io.sentry.test.ImmediateExecutorService -import io.sentry.test.createTestScopes import org.junit.runner.RunWith import org.mockito.kotlin.eq import org.mockito.kotlin.mock @@ -24,7 +24,7 @@ import kotlin.test.assertEquals @RunWith(AndroidJUnit4::class) class EnvelopeFileObserverIntegrationTest { inner class Fixture { - val scopes: IScopes = mock() + val hub: IHub = mock() private lateinit var options: SentryAndroidOptions val logger = mock() @@ -33,7 +33,7 @@ class EnvelopeFileObserverIntegrationTest { options.setLogger(logger) options.isDebug = true optionConfiguration(options) - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) return object : EnvelopeFileObserverIntegration() { override fun getPath(options: SentryOptions): String? = file.absolutePath @@ -65,25 +65,27 @@ class EnvelopeFileObserverIntegrationTest { } @Test - fun `when scopes is closed, integrations should be closed`() { + fun `when hub is closed, integrations should be closed`() { val integrationMock = mock() val options = SentryOptions() options.dsn = "https://key@sentry.io/proj" options.cacheDirPath = file.absolutePath options.addIntegration(integrationMock) options.setSerializer(mock()) - val scopes = createTestScopes(options) - scopes.close() +// val expected = HubAdapter.getInstance() + val hub = Hub(options) +// verify(integrationMock).register(expected, options) + hub.close() verify(integrationMock).close() } @Test - fun `when scopes is closed right after start, integration is not registered`() { + fun `when hub is closed right after start, integration is not registered`() { val deferredExecutorService = DeferredExecutorService() val integration = fixture.getSut { it.executorService = deferredExecutorService } - integration.register(fixture.scopes, fixture.scopes.options) + integration.register(fixture.hub, fixture.hub.options) integration.close() deferredExecutorService.runAll() verify(fixture.logger, never()).log(eq(SentryLevel.DEBUG), eq("EnvelopeFileObserverIntegration installed.")) @@ -94,7 +96,7 @@ class EnvelopeFileObserverIntegrationTest { val integration = fixture.getSut { it.executorService = mock() } - integration.register(fixture.scopes, fixture.scopes.options) + integration.register(fixture.hub, fixture.hub.options) verify(fixture.logger).log( eq(SentryLevel.DEBUG), eq("Registering EnvelopeFileObserverIntegration for path: %s"), @@ -108,7 +110,7 @@ class EnvelopeFileObserverIntegrationTest { val integration = fixture.getSut { it.executorService = ImmediateExecutorService() } - integration.register(fixture.scopes, fixture.scopes.options) + integration.register(fixture.hub, fixture.hub.options) verify(fixture.logger).log( eq(SentryLevel.DEBUG), eq("Registering EnvelopeFileObserverIntegration for path: %s"), diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt index 5f63f39cae..65ecdb3b8c 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt @@ -7,9 +7,9 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.Breadcrumb import io.sentry.Hint +import io.sentry.Hub import io.sentry.IScope import io.sentry.Scope -import io.sentry.ScopeType import io.sentry.Sentry import io.sentry.SentryEnvelope import io.sentry.SentryEnvelopeHeader @@ -27,7 +27,6 @@ import io.sentry.protocol.Contexts import io.sentry.protocol.Mechanism import io.sentry.protocol.SentryId import io.sentry.protocol.User -import io.sentry.test.createTestScopes import io.sentry.transport.ITransport import io.sentry.transport.RateLimiter import org.junit.runner.RunWith @@ -40,7 +39,6 @@ import java.util.concurrent.atomic.AtomicReference import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals -import kotlin.test.assertNotEquals import kotlin.test.assertNotNull import kotlin.test.assertNull import kotlin.test.assertTrue @@ -86,9 +84,9 @@ class InternalSentrySdkTest { capturedEnvelopes.clear() } - fun captureEnvelopeWithEvent(event: SentryEvent = SentryEvent(), maybeStartNewSession: Boolean = false) { + fun captureEnvelopeWithEvent(event: SentryEvent = SentryEvent()) { // create an envelope with session data - val options = Sentry.getCurrentScopes().options + val options = Sentry.getCurrentHub().options val eventId = SentryId() val header = SentryEnvelopeHeader(eventId) val eventItem = SentryEnvelopeItem.fromEvent(options.serializer, event) @@ -105,24 +103,7 @@ class InternalSentrySdkTest { options.serializer.serialize(envelope, outputStream) val data = outputStream.toByteArray() - InternalSentrySdk.captureEnvelope(data, maybeStartNewSession) - } - - fun createSentryEventWithUnhandledException(): SentryEvent { - return SentryEvent(RuntimeException()).apply { - val mechanism = Mechanism() - mechanism.isHandled = false - - val factory = SentryExceptionFactory(mock()) - val sentryExceptions = factory.getSentryExceptions( - ExceptionMechanismException( - mechanism, - Throwable(), - Thread() - ) - ) - exceptions = sentryExceptions - } + InternalSentrySdk.captureEnvelope(data) } fun mockFinishedAppStart() { @@ -203,34 +184,40 @@ class InternalSentrySdkTest { @BeforeTest fun `set up`() { - Sentry.close() context = ApplicationProvider.getApplicationContext() DeviceInfoUtil.resetInstance() } @Test - fun `current scope returns null when scopes is no-op`() { - Sentry.setCurrentScopes(createTestScopes(enabled = false)) + fun `current scope returns null when hub is no-op`() { + Sentry.getCurrentHub().close() val scope = InternalSentrySdk.getCurrentScope() assertNull(scope) } @Test - fun `current scope returns obj when scopes is active`() { - val fixture = Fixture() - fixture.init(context) + fun `current scope returns obj when hub is active`() { + Sentry.setCurrentHub( + Hub( + SentryOptions().apply { + dsn = "https://key@uri/1234567" + } + ) + ) val scope = InternalSentrySdk.getCurrentScope() assertNotNull(scope) } @Test fun `current scope returns a copy of the scope`() { - val fixture = Fixture() - fixture.init(context) + Sentry.setCurrentHub( + Hub( + SentryOptions().apply { + dsn = "https://key@uri/1234567" + } + ) + ) Sentry.addBreadcrumb("test") - Sentry.configureScope(ScopeType.CURRENT) { scope -> scope.addBreadcrumb(Breadcrumb("currentBreadcrumb")) } - Sentry.configureScope(ScopeType.ISOLATION) { scope -> scope.addBreadcrumb(Breadcrumb("isolationBreadcrumb")) } - Sentry.configureScope(ScopeType.GLOBAL) { scope -> scope.addBreadcrumb(Breadcrumb("globalBreadcrumb")) } // when the clone is modified val clonedScope = InternalSentrySdk.getCurrentScope()!! @@ -238,7 +225,7 @@ class InternalSentrySdkTest { // then modifications should not be reflected Sentry.configureScope { scope -> - assertEquals(3, scope.breadcrumbs.size) + assertEquals(1, scope.breadcrumbs.size) } } @@ -326,7 +313,7 @@ class InternalSentrySdkTest { @Test fun `captureEnvelope fails if payload is invalid`() { - assertNull(InternalSentrySdk.captureEnvelope(ByteArray(8), false)) + assertNull(InternalSentrySdk.captureEnvelope(ByteArray(8))) } @Test @@ -350,19 +337,27 @@ class InternalSentrySdkTest { } @Test - fun `captureEnvelope correctly enriches the envelope with session data and does not start new session`() { + fun `captureEnvelope correctly enriches the envelope with session data`() { val fixture = Fixture() fixture.init(context) - // keep reference for current session for later assertions - // we need to get the reference now as it will be removed from the scope - val sessionRef = AtomicReference() - Sentry.configureScope { scope -> - sessionRef.set(scope.session) - } - // when capture envelope is called with an crashed event - fixture.captureEnvelopeWithEvent(fixture.createSentryEventWithUnhandledException()) + fixture.captureEnvelopeWithEvent( + SentryEvent(RuntimeException()).apply { + val mechanism = Mechanism() + mechanism.isHandled = false + + val factory = SentryExceptionFactory(mock()) + val sentryExceptions = factory.getSentryExceptions( + ExceptionMechanismException( + mechanism, + Throwable(), + Thread() + ) + ) + exceptions = sentryExceptions + } + ) val capturedEnvelope = fixture.capturedEnvelopes.first() val capturedEnvelopeItems = capturedEnvelope.items.toList() @@ -379,52 +374,12 @@ class InternalSentrySdkTest { )!! assertEquals(Session.State.Crashed, capturedSession.status) - assertEquals(Session.State.Crashed, sessionRef.get().status) - assertEquals(capturedSession.sessionId, sessionRef.get().sessionId) - } - - @Test - fun `captureEnvelope starts new session when enabled`() { - val fixture = Fixture() - fixture.init(context) - - // when capture envelope is called with an crashed event - fixture.captureEnvelopeWithEvent(fixture.createSentryEventWithUnhandledException(), true) - + // and the local session should be marked as crashed too val scopeRef = AtomicReference() Sentry.configureScope { scope -> scopeRef.set(scope) } - - // first envelope is the new session start - val capturedStartSessionEnvelope = fixture.capturedEnvelopes.first() - val capturedNewSessionStart = fixture.options.serializer.deserialize( - InputStreamReader(ByteArrayInputStream(capturedStartSessionEnvelope.items.toList()[0].data)), - Session::class.java - )!! - assertEquals(capturedNewSessionStart.sessionId, scopeRef.get().session!!.sessionId) - assertEquals(Session.State.Ok, capturedNewSessionStart.status) - - val capturedEnvelope = fixture.capturedEnvelopes.last() - val capturedEnvelopeItems = capturedEnvelope.items.toList() - - // there should be two envelopes session start and captured crash - assertEquals(2, fixture.capturedEnvelopes.size) - - // then it should contain the original event + session - assertEquals(2, capturedEnvelopeItems.size) - assertEquals(SentryItemType.Event, capturedEnvelopeItems[0].header.type) - assertEquals(SentryItemType.Session, capturedEnvelopeItems[1].header.type) - - // and then the sent session should be marked as crashed - val capturedSession = fixture.options.serializer.deserialize( - InputStreamReader(ByteArrayInputStream(capturedEnvelopeItems[1].data)), - Session::class.java - )!! - assertEquals(Session.State.Crashed, capturedSession.status) - - // and the local session should be a new session - assertNotEquals(capturedSession.sessionId, scopeRef.get().session!!.sessionId) + assertEquals(Session.State.Crashed, scopeRef.get().session!!.status) } @Test diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/LifecycleWatcherTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/LifecycleWatcherTest.kt index 73571a5ad4..be30993142 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/LifecycleWatcherTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/LifecycleWatcherTest.kt @@ -3,8 +3,8 @@ package io.sentry.android.core import androidx.lifecycle.LifecycleOwner import io.sentry.Breadcrumb import io.sentry.DateUtils +import io.sentry.IHub import io.sentry.IScope -import io.sentry.IScopes import io.sentry.ScopeCallback import io.sentry.SentryLevel import io.sentry.Session @@ -32,7 +32,7 @@ class LifecycleWatcherTest { private class Fixture { val ownerMock = mock() - val scopes = mock() + val hub = mock() val dateProvider = mock() fun getSUT( @@ -44,12 +44,12 @@ class LifecycleWatcherTest { val argumentCaptor: ArgumentCaptor = ArgumentCaptor.forClass(ScopeCallback::class.java) val scope = mock() whenever(scope.session).thenReturn(session) - whenever(scopes.configureScope(argumentCaptor.capture())).thenAnswer { + whenever(hub.configureScope(argumentCaptor.capture())).thenAnswer { argumentCaptor.value.run(scope) } return LifecycleWatcher( - scopes, + hub, sessionIntervalMillis, enableAutoSessionTracking, enableAppLifecycleBreadcrumbs, @@ -69,7 +69,7 @@ class LifecycleWatcherTest { fun `if last started session is 0, start new session`() { val watcher = fixture.getSUT(enableAppLifecycleBreadcrumbs = false) watcher.onStart(fixture.ownerMock) - verify(fixture.scopes).startSession() + verify(fixture.hub).startSession() } @Test @@ -78,7 +78,7 @@ class LifecycleWatcherTest { whenever(fixture.dateProvider.currentTimeMillis).thenReturn(1L, 2L) watcher.onStart(fixture.ownerMock) watcher.onStart(fixture.ownerMock) - verify(fixture.scopes, times(2)).startSession() + verify(fixture.hub, times(2)).startSession() } @Test @@ -87,7 +87,7 @@ class LifecycleWatcherTest { whenever(fixture.dateProvider.currentTimeMillis).thenReturn(2L, 1L) watcher.onStart(fixture.ownerMock) watcher.onStart(fixture.ownerMock) - verify(fixture.scopes).startSession() + verify(fixture.hub).startSession() } @Test @@ -95,7 +95,7 @@ class LifecycleWatcherTest { val watcher = fixture.getSUT(enableAppLifecycleBreadcrumbs = false) watcher.onStart(fixture.ownerMock) watcher.onStop(fixture.ownerMock) - verify(fixture.scopes, timeout(10000)).endSession() + verify(fixture.hub, timeout(10000)).endSession() } @Test @@ -109,14 +109,14 @@ class LifecycleWatcherTest { watcher.onStart(fixture.ownerMock) assertNull(watcher.timerTask) - verify(fixture.scopes, never()).endSession() + verify(fixture.hub, never()).endSession() } @Test fun `When session tracking is disabled, do not start session`() { val watcher = fixture.getSUT(enableAutoSessionTracking = false, enableAppLifecycleBreadcrumbs = false) watcher.onStart(fixture.ownerMock) - verify(fixture.scopes, never()).startSession() + verify(fixture.hub, never()).startSession() } @Test @@ -124,14 +124,14 @@ class LifecycleWatcherTest { val watcher = fixture.getSUT(enableAutoSessionTracking = false, enableAppLifecycleBreadcrumbs = false) watcher.onStop(fixture.ownerMock) assertNull(watcher.timerTask) - verify(fixture.scopes, never()).endSession() + verify(fixture.hub, never()).endSession() } @Test fun `When session tracking is enabled, add breadcrumb on start`() { val watcher = fixture.getSUT(enableAppLifecycleBreadcrumbs = false) watcher.onStart(fixture.ownerMock) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("app.lifecycle", it.category) assertEquals("session", it.type) @@ -145,8 +145,8 @@ class LifecycleWatcherTest { fun `When session tracking is enabled, add breadcrumb on stop`() { val watcher = fixture.getSUT(enableAppLifecycleBreadcrumbs = false) watcher.onStop(fixture.ownerMock) - verify(fixture.scopes, timeout(10000)).endSession() - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub, timeout(10000)).endSession() + verify(fixture.hub).addBreadcrumb( check { assertEquals("app.lifecycle", it.category) assertEquals("session", it.type) @@ -160,7 +160,7 @@ class LifecycleWatcherTest { fun `When session tracking is disabled, do not add breadcrumb on start`() { val watcher = fixture.getSUT(enableAutoSessionTracking = false, enableAppLifecycleBreadcrumbs = false) watcher.onStart(fixture.ownerMock) - verify(fixture.scopes, never()).addBreadcrumb(any()) + verify(fixture.hub, never()).addBreadcrumb(any()) } @Test @@ -168,14 +168,14 @@ class LifecycleWatcherTest { val watcher = fixture.getSUT(enableAutoSessionTracking = false, enableAppLifecycleBreadcrumbs = false) watcher.onStop(fixture.ownerMock) assertNull(watcher.timerTask) - verify(fixture.scopes, never()).addBreadcrumb(any()) + verify(fixture.hub, never()).addBreadcrumb(any()) } @Test fun `When app lifecycle breadcrumbs is enabled, add breadcrumb on start`() { val watcher = fixture.getSUT(enableAutoSessionTracking = false) watcher.onStart(fixture.ownerMock) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("app.lifecycle", it.category) assertEquals("navigation", it.type) @@ -189,14 +189,14 @@ class LifecycleWatcherTest { fun `When app lifecycle breadcrumbs is disabled, do not add breadcrumb on start`() { val watcher = fixture.getSUT(enableAutoSessionTracking = false, enableAppLifecycleBreadcrumbs = false) watcher.onStart(fixture.ownerMock) - verify(fixture.scopes, never()).addBreadcrumb(any()) + verify(fixture.hub, never()).addBreadcrumb(any()) } @Test fun `When app lifecycle breadcrumbs is enabled, add breadcrumb on stop`() { val watcher = fixture.getSUT(enableAutoSessionTracking = false) watcher.onStop(fixture.ownerMock) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("app.lifecycle", it.category) assertEquals("navigation", it.type) @@ -210,7 +210,7 @@ class LifecycleWatcherTest { fun `When app lifecycle breadcrumbs is disabled, do not add breadcrumb on stop`() { val watcher = fixture.getSUT(enableAutoSessionTracking = false, enableAppLifecycleBreadcrumbs = false) watcher.onStop(fixture.ownerMock) - verify(fixture.scopes, never()).addBreadcrumb(any()) + verify(fixture.hub, never()).addBreadcrumb(any()) } @Test @@ -226,7 +226,7 @@ class LifecycleWatcherTest { } @Test - fun `if the scopes has already a fresh session running, don't start new one`() { + fun `if the hub has already a fresh session running, don't start new one`() { val watcher = fixture.getSUT( enableAppLifecycleBreadcrumbs = false, session = Session( @@ -248,11 +248,11 @@ class LifecycleWatcherTest { ) watcher.onStart(fixture.ownerMock) - verify(fixture.scopes, never()).startSession() + verify(fixture.hub, never()).startSession() } @Test - fun `if the scopes has a long running session, start new one`() { + fun `if the hub has a long running session, start new one`() { val watcher = fixture.getSUT( enableAppLifecycleBreadcrumbs = false, session = Session( @@ -274,7 +274,7 @@ class LifecycleWatcherTest { ) watcher.onStart(fixture.ownerMock) - verify(fixture.scopes).startSession() + verify(fixture.hub).startSession() } @Test diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/NdkIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/NdkIntegrationTest.kt index e282ad7141..e86de06814 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/NdkIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/NdkIntegrationTest.kt @@ -1,7 +1,7 @@ package io.sentry.android.core +import io.sentry.IHub import io.sentry.ILogger -import io.sentry.IScopes import io.sentry.SentryLevel import org.mockito.kotlin.any import org.mockito.kotlin.eq @@ -15,7 +15,7 @@ import kotlin.test.assertTrue class NdkIntegrationTest { private class Fixture { - val scopes = mock() + val hub = mock() val logger = mock() fun getSut(clazz: Class<*>? = SentryNdk::class.java): NdkIntegration { @@ -31,7 +31,7 @@ class NdkIntegrationTest { val options = getOptions() - integration.register(fixture.scopes, options) + integration.register(fixture.hub, options) verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any()) assertTrue(options.isEnableNdk) @@ -44,7 +44,7 @@ class NdkIntegrationTest { val options = getOptions() - integration.register(fixture.scopes, options) + integration.register(fixture.hub, options) assertTrue(options.isEnableNdk) assertTrue(options.isEnableScopeSync) @@ -62,7 +62,7 @@ class NdkIntegrationTest { val options = getOptions(enableNdk = false) - integration.register(fixture.scopes, options) + integration.register(fixture.hub, options) verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any()) @@ -76,7 +76,7 @@ class NdkIntegrationTest { val options = getOptions() - integration.register(fixture.scopes, options) + integration.register(fixture.hub, options) verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any()) @@ -90,7 +90,7 @@ class NdkIntegrationTest { val options = getOptions() - integration.register(fixture.scopes, options) + integration.register(fixture.hub, options) verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), any()) @@ -104,7 +104,7 @@ class NdkIntegrationTest { val options = getOptions() - integration.register(fixture.scopes, options) + integration.register(fixture.hub, options) assertTrue(options.isEnableNdk) assertTrue(options.isEnableScopeSync) @@ -122,7 +122,7 @@ class NdkIntegrationTest { val options = getOptions() - integration.register(fixture.scopes, options) + integration.register(fixture.hub, options) verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), any()) @@ -136,7 +136,7 @@ class NdkIntegrationTest { val options = getOptions(cacheDir = null) - integration.register(fixture.scopes, options) + integration.register(fixture.hub, options) verify(fixture.logger).log(eq(SentryLevel.ERROR), any()) @@ -150,7 +150,7 @@ class NdkIntegrationTest { val options = getOptions(cacheDir = "") - integration.register(fixture.scopes, options) + integration.register(fixture.hub, options) verify(fixture.logger).log(eq(SentryLevel.ERROR), any()) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/NetworkBreadcrumbsIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/NetworkBreadcrumbsIntegrationTest.kt index c28cf64085..146f229fdf 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/NetworkBreadcrumbsIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/NetworkBreadcrumbsIntegrationTest.kt @@ -8,7 +8,7 @@ import android.net.NetworkCapabilities import android.os.Build import io.sentry.Breadcrumb import io.sentry.DateUtils -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryDateProvider import io.sentry.SentryLevel import io.sentry.SentryNanotimeDate @@ -39,7 +39,7 @@ class NetworkBreadcrumbsIntegrationTest { private class Fixture { val context = mock() var options = SentryAndroidOptions() - val scopes = mock() + val hub = mock() val mockBuildInfoProvider = mock() val connectivityManager = mock() var nowMs: Long = 0 @@ -68,7 +68,7 @@ class NetworkBreadcrumbsIntegrationTest { fun `When network events breadcrumb is enabled, it registers callback`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.connectivityManager).registerDefaultNetworkCallback(any()) assertNotNull(sut.networkCallback) @@ -78,7 +78,7 @@ class NetworkBreadcrumbsIntegrationTest { fun `When system events breadcrumb is disabled, it doesn't register callback`() { val sut = fixture.getSut(enableNetworkEventBreadcrumbs = false) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.connectivityManager, never()).registerDefaultNetworkCallback(any()) assertNull(sut.networkCallback) @@ -90,7 +90,7 @@ class NetworkBreadcrumbsIntegrationTest { whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.M) val sut = fixture.getSut(buildInfo = buildInfo) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.connectivityManager, never()).registerDefaultNetworkCallback(any()) assertNull(sut.networkCallback) @@ -100,7 +100,7 @@ class NetworkBreadcrumbsIntegrationTest { fun `When NetworkBreadcrumbsIntegration is closed, it should unregister the callback`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.close() verify(fixture.connectivityManager).unregisterNetworkCallback(any()) @@ -114,7 +114,7 @@ class NetworkBreadcrumbsIntegrationTest { val sut = fixture.getSut(buildInfo = buildInfo) assertNull(sut.networkCallback) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.close() verify(fixture.connectivityManager, never()).unregisterNetworkCallback(any()) @@ -124,12 +124,12 @@ class NetworkBreadcrumbsIntegrationTest { @Test fun `When connected to a new network, a breadcrumb is captured`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(mock()) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("system", it.type) assertEquals("network.event", it.category) @@ -142,27 +142,27 @@ class NetworkBreadcrumbsIntegrationTest { @Test fun `When connected to the same network without disconnecting from the previous one, only one breadcrumb is captured`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) callback.onAvailable(fixture.network) - verify(fixture.scopes, times(1)).addBreadcrumb(any()) + verify(fixture.hub, times(1)).addBreadcrumb(any()) } @Test fun `When disconnected from a network, a breadcrumb is captured`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) - verify(fixture.scopes).addBreadcrumb(any()) + verify(fixture.hub).addBreadcrumb(any()) callback.onLost(fixture.network) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("system", it.type) assertEquals("network.event", it.category) @@ -175,12 +175,12 @@ class NetworkBreadcrumbsIntegrationTest { @Test fun `When disconnected from a network, a breadcrumb is captured only if previously connected to that network`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) // callback.onAvailable(network) was not called, so no breadcrumb should be captured callback.onLost(mock()) - verify(fixture.scopes, never()).addBreadcrumb(any()) + verify(fixture.hub, never()).addBreadcrumb(any()) } @Test @@ -188,7 +188,7 @@ class NetworkBreadcrumbsIntegrationTest { val buildInfo = mock() whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.Q) val sut = fixture.getSut(buildInfo = buildInfo) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -204,7 +204,7 @@ class NetworkBreadcrumbsIntegrationTest { isCellular = false ) ) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("system", it.type) assertEquals("network.event", it.category) @@ -223,18 +223,18 @@ class NetworkBreadcrumbsIntegrationTest { @Test fun `When a network connection detail changes, a breadcrumb is captured only if previously connected to that network`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) // callback.onAvailable(network) was not called, so no breadcrumb should be captured onCapabilitiesChanged(callback, mock()) - verify(fixture.scopes, never()).addBreadcrumb(any(), anyOrNull()) + verify(fixture.hub, never()).addBreadcrumb(any(), anyOrNull()) } @Test fun `When a network connection detail changes, a new breadcrumb is captured if vpn flag changes`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -245,17 +245,17 @@ class NetworkBreadcrumbsIntegrationTest { onCapabilitiesChanged(callback, details1) onCapabilitiesChanged(callback, details2) onCapabilitiesChanged(callback, details3) - inOrder(fixture.scopes) { + inOrder(fixture.hub) { verifyBreadcrumbInOrder { assertFalse(it.isVpn) } verifyBreadcrumbInOrder { assertTrue(it.isVpn) } - verify(fixture.scopes, never()).addBreadcrumb(any(), any()) + verify(fixture.hub, never()).addBreadcrumb(any(), any()) } } @Test fun `When a network connection detail changes, a new breadcrumb is captured if type changes`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -266,10 +266,10 @@ class NetworkBreadcrumbsIntegrationTest { onCapabilitiesChanged(callback, details1) onCapabilitiesChanged(callback, details2) onCapabilitiesChanged(callback, details3) - inOrder(fixture.scopes) { + inOrder(fixture.hub) { verifyBreadcrumbInOrder { assertEquals("wifi", it.type) } verifyBreadcrumbInOrder { assertEquals("cellular", it.type) } - verify(fixture.scopes, never()).addBreadcrumb(any(), any()) + verify(fixture.hub, never()).addBreadcrumb(any(), any()) } } @@ -278,7 +278,7 @@ class NetworkBreadcrumbsIntegrationTest { val buildInfo = mock() whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.Q) val sut = fixture.getSut(buildInfo = buildInfo) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -289,10 +289,10 @@ class NetworkBreadcrumbsIntegrationTest { // A change of signal strength of 5 doesn't trigger a new breadcrumb onCapabilitiesChanged(callback, details2) onCapabilitiesChanged(callback, details3) - inOrder(fixture.scopes) { + inOrder(fixture.hub) { verifyBreadcrumbInOrder { assertEquals(50, it.signalStrength) } verifyBreadcrumbInOrder { assertEquals(56, it.signalStrength) } - verify(fixture.scopes, never()).addBreadcrumb(any(), any()) + verify(fixture.hub, never()).addBreadcrumb(any(), any()) } } @@ -301,7 +301,7 @@ class NetworkBreadcrumbsIntegrationTest { val buildInfo = mock() whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.Q) val sut = fixture.getSut(buildInfo = buildInfo) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -317,11 +317,11 @@ class NetworkBreadcrumbsIntegrationTest { // A change of download bandwidth of 10% (more than 1000) doesn't trigger a new breadcrumb onCapabilitiesChanged(callback, details4) onCapabilitiesChanged(callback, details5) - inOrder(fixture.scopes) { + inOrder(fixture.hub) { verifyBreadcrumbInOrder { assertEquals(1000, it.downBandwidth) } verifyBreadcrumbInOrder { assertEquals(20000, it.downBandwidth) } verifyBreadcrumbInOrder { assertEquals(22001, it.downBandwidth) } - verify(fixture.scopes, never()).addBreadcrumb(any(), any()) + verify(fixture.hub, never()).addBreadcrumb(any(), any()) } } @@ -330,7 +330,7 @@ class NetworkBreadcrumbsIntegrationTest { val buildInfo = mock() whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.Q) val sut = fixture.getSut(buildInfo = buildInfo) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -346,18 +346,18 @@ class NetworkBreadcrumbsIntegrationTest { // A change of upload bandwidth of 10% (more than 1000) doesn't trigger a new breadcrumb onCapabilitiesChanged(callback, details4) onCapabilitiesChanged(callback, details5) - inOrder(fixture.scopes) { + inOrder(fixture.hub) { verifyBreadcrumbInOrder { assertEquals(1000, it.upBandwidth) } verifyBreadcrumbInOrder { assertEquals(20000, it.upBandwidth) } verifyBreadcrumbInOrder { assertEquals(22001, it.upBandwidth) } - verify(fixture.scopes, never()).addBreadcrumb(any(), any()) + verify(fixture.hub, never()).addBreadcrumb(any(), any()) } } @Test fun `signal strength is 0 if not on Android Q+`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -371,7 +371,7 @@ class NetworkBreadcrumbsIntegrationTest { val buildInfo = mock() whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.Q) val sut = fixture.getSut(buildInfo = buildInfo) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -384,7 +384,7 @@ class NetworkBreadcrumbsIntegrationTest { @Test fun `A breadcrumb is captured when vpn status changes, regardless of the timestamp`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -392,17 +392,17 @@ class NetworkBreadcrumbsIntegrationTest { val details2 = createConnectionDetail(isVpn = true) onCapabilitiesChanged(callback, details1, 0) onCapabilitiesChanged(callback, details2, 0) - inOrder(fixture.scopes) { + inOrder(fixture.hub) { verifyBreadcrumbInOrder { assertFalse(it.isVpn) } verifyBreadcrumbInOrder { assertTrue(it.isVpn) } - verify(fixture.scopes, never()).addBreadcrumb(any(), any()) + verify(fixture.hub, never()).addBreadcrumb(any(), any()) } } @Test fun `A breadcrumb is captured when connection type changes, regardless of the timestamp`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -412,11 +412,11 @@ class NetworkBreadcrumbsIntegrationTest { onCapabilitiesChanged(callback, details1, 0) onCapabilitiesChanged(callback, details2, 0) onCapabilitiesChanged(callback, details3, 0) - inOrder(fixture.scopes) { + inOrder(fixture.hub) { verifyBreadcrumbInOrder { assertEquals("wifi", it.type) } verifyBreadcrumbInOrder { assertEquals("cellular", it.type) } verifyBreadcrumbInOrder { assertEquals("ethernet", it.type) } - verify(fixture.scopes, never()).addBreadcrumb(any(), any()) + verify(fixture.hub, never()).addBreadcrumb(any(), any()) } } @@ -425,7 +425,7 @@ class NetworkBreadcrumbsIntegrationTest { val buildInfo = mock() whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.Q) val sut = fixture.getSut(buildInfo = buildInfo) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -435,17 +435,17 @@ class NetworkBreadcrumbsIntegrationTest { onCapabilitiesChanged(callback, details1, 0) onCapabilitiesChanged(callback, details2, 0) onCapabilitiesChanged(callback, details3, 5000) - inOrder(fixture.scopes) { + inOrder(fixture.hub) { verifyBreadcrumbInOrder { assertEquals(1, it.signalStrength) } verifyBreadcrumbInOrder { assertEquals(51, it.signalStrength) } - verify(fixture.scopes, never()).addBreadcrumb(any(), any()) + verify(fixture.hub, never()).addBreadcrumb(any(), any()) } } @Test fun `A breadcrumb is captured when downBandwidth changes at most once every 5 seconds`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -455,17 +455,17 @@ class NetworkBreadcrumbsIntegrationTest { onCapabilitiesChanged(callback, details1, 0) onCapabilitiesChanged(callback, details2, 0) onCapabilitiesChanged(callback, details3, 5000) - inOrder(fixture.scopes) { + inOrder(fixture.hub) { verifyBreadcrumbInOrder { assertEquals(1, it.downBandwidth) } verifyBreadcrumbInOrder { assertEquals(2001, it.downBandwidth) } - verify(fixture.scopes, never()).addBreadcrumb(any(), any()) + verify(fixture.hub, never()).addBreadcrumb(any(), any()) } } @Test fun `A breadcrumb is captured when upBandwidth changes at most once every 5 seconds`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val callback = sut.networkCallback assertNotNull(callback) callback.onAvailable(fixture.network) @@ -475,15 +475,15 @@ class NetworkBreadcrumbsIntegrationTest { onCapabilitiesChanged(callback, details1, 0) onCapabilitiesChanged(callback, details2, 0) onCapabilitiesChanged(callback, details3, 5000) - inOrder(fixture.scopes) { + inOrder(fixture.hub) { verifyBreadcrumbInOrder { assertEquals(1, it.upBandwidth) } verifyBreadcrumbInOrder { assertEquals(2001, it.upBandwidth) } - verify(fixture.scopes, never()).addBreadcrumb(any(), any()) + verify(fixture.hub, never()).addBreadcrumb(any(), any()) } } private fun KInOrder.verifyBreadcrumbInOrder(check: (detail: NetworkBreadcrumbConnectionDetail) -> Unit) { - verify(fixture.scopes, times(1)).addBreadcrumb( + verify(fixture.hub, times(1)).addBreadcrumb( any(), check { val connectionDetail = it[TypeCheckHint.ANDROID_NETWORK_CAPABILITIES] as NetworkBreadcrumbConnectionDetail @@ -493,7 +493,7 @@ class NetworkBreadcrumbsIntegrationTest { } private fun verifyBreadcrumb(check: (detail: NetworkBreadcrumbConnectionDetail) -> Unit) { - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( any(), check { val connectionDetail = it[TypeCheckHint.ANDROID_NETWORK_CAPABILITIES] as NetworkBreadcrumbConnectionDetail diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt index 7577f1cd1e..4283326677 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt @@ -3,7 +3,7 @@ package io.sentry.android.core import android.content.ContentProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.MeasurementUnit import io.sentry.SentryTracer import io.sentry.SpanContext @@ -37,7 +37,7 @@ class PerformanceAndroidEventProcessorTest { private class Fixture { val options = SentryAndroidOptions() - val scopes = mock() + val hub = mock() val context = TransactionContext("name", "op", TracesSamplingDecision(true)) lateinit var tracer: SentryTracer val activityFramesTracker = mock() @@ -48,8 +48,8 @@ class PerformanceAndroidEventProcessorTest { ): PerformanceAndroidEventProcessor { options.tracesSampleRate = tracesSampleRate options.isEnablePerformanceV2 = enablePerformanceV2 - whenever(scopes.options).thenReturn(options) - tracer = SentryTracer(context, scopes) + whenever(hub.options).thenReturn(options) + tracer = SentryTracer(context, hub) return PerformanceAndroidEventProcessor(options, activityFramesTracker) } } @@ -183,7 +183,7 @@ class PerformanceAndroidEventProcessorTest { fun `add slow and frozen frames if auto transaction`() { val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.scopes) + val tracer = SentryTracer(context, fixture.hub) var tr = SentryTransaction(tracer) val metrics = mapOf( @@ -229,7 +229,7 @@ class PerformanceAndroidEventProcessorTest { // when an activity transaction is created val sut = fixture.getSut(enablePerformanceV2 = true) val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.scopes) + val tracer = SentryTracer(context, fixture.hub) var tr = SentryTransaction(tracer) // and it contains an app.start.cold span @@ -299,7 +299,7 @@ class PerformanceAndroidEventProcessorTest { // when an activity transaction is created val sut = fixture.getSut(enablePerformanceV2 = true) val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.scopes) + val tracer = SentryTracer(context, fixture.hub) var tr = SentryTransaction(tracer) // then the app start metrics should not be attached @@ -328,7 +328,7 @@ class PerformanceAndroidEventProcessorTest { // when the first activity transaction is created val sut = fixture.getSut(enablePerformanceV2 = true) val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.scopes) + val tracer = SentryTracer(context, fixture.hub) var tr = SentryTransaction(tracer) val appStartSpan = SentrySpan( 0.0, @@ -379,7 +379,7 @@ class PerformanceAndroidEventProcessorTest { val sut = fixture.getSut(enablePerformanceV2 = true) val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.scopes) + val tracer = SentryTracer(context, fixture.hub) var tr = SentryTransaction(tracer) val appStartSpan = SentrySpan( 0.0, @@ -426,7 +426,7 @@ class PerformanceAndroidEventProcessorTest { val sut = fixture.getSut(enablePerformanceV2 = true) val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.scopes) + val tracer = SentryTracer(context, fixture.hub) var tr = SentryTransaction(tracer) val appStartSpan = SentrySpan( 0.0, @@ -473,7 +473,7 @@ class PerformanceAndroidEventProcessorTest { val sut = fixture.getSut(enablePerformanceV2 = true) val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.scopes) + val tracer = SentryTracer(context, fixture.hub) var tr = SentryTransaction(tracer) // when it contains no app start span and is processed @@ -490,7 +490,7 @@ class PerformanceAndroidEventProcessorTest { val sut = fixture.getSut(enablePerformanceV2 = true) val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.scopes) + val tracer = SentryTracer(context, fixture.hub) var tr = SentryTransaction(tracer) val appStartSpan = SentrySpan( @@ -525,7 +525,7 @@ class PerformanceAndroidEventProcessorTest { val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.scopes) + val tracer = SentryTracer(context, fixture.hub) val tr = SentryTransaction(tracer) // given a ttid from 0.0 -> 1.0 @@ -649,7 +649,7 @@ class PerformanceAndroidEventProcessorTest { val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.scopes) + val tracer = SentryTracer(context, fixture.hub) val tr = SentryTransaction(tracer) val span = SentrySpan( @@ -683,7 +683,7 @@ class PerformanceAndroidEventProcessorTest { val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.scopes) + val tracer = SentryTracer(context, fixture.hub) val tr = SentryTransaction(tracer) // given a ttid from 0.0 -> 1.0 diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/PhoneStateBreadcrumbsIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/PhoneStateBreadcrumbsIntegrationTest.kt index c764d11c2d..2b6ca801da 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/PhoneStateBreadcrumbsIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/PhoneStateBreadcrumbsIntegrationTest.kt @@ -4,7 +4,7 @@ import android.content.Context import android.telephony.PhoneStateListener import android.telephony.TelephonyManager import io.sentry.Breadcrumb -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ISentryExecutorService import io.sentry.SentryLevel import io.sentry.test.DeferredExecutorService @@ -41,8 +41,8 @@ class PhoneStateBreadcrumbsIntegrationTest { @Test fun `When system events breadcrumb is enabled, it registers callback`() { val sut = fixture.getSut() - val scopes = mock() - sut.register(scopes, fixture.options) + val hub = mock() + sut.register(hub, fixture.options) verify(fixture.manager).listen(any(), eq(PhoneStateListener.LISTEN_CALL_STATE)) assertNotNull(sut.listener) } @@ -50,8 +50,8 @@ class PhoneStateBreadcrumbsIntegrationTest { @Test fun `Phone state callback is registered in the executorService`() { val sut = fixture.getSut(mock()) - val scopes = mock() - sut.register(scopes, fixture.options) + val hub = mock() + sut.register(hub, fixture.options) assertNull(sut.listener) } @@ -59,9 +59,9 @@ class PhoneStateBreadcrumbsIntegrationTest { @Test fun `When system events breadcrumb is disabled, it doesn't register callback`() { val sut = fixture.getSut() - val scopes = mock() + val hub = mock() sut.register( - scopes, + hub, fixture.options.apply { isEnableSystemEventBreadcrumbs = false } @@ -73,15 +73,15 @@ class PhoneStateBreadcrumbsIntegrationTest { @Test fun `When ActivityBreadcrumbsIntegration is closed, it should unregister the callback`() { val sut = fixture.getSut() - val scopes = mock() - sut.register(scopes, fixture.options) + val hub = mock() + sut.register(hub, fixture.options) sut.close() verify(fixture.manager).listen(any(), eq(PhoneStateListener.LISTEN_NONE)) assertNull(sut.listener) } @Test - fun `when scopes is closed right after start, integration is not registered`() { + fun `when hub is closed right after start, integration is not registered`() { val deferredExecutorService = DeferredExecutorService() val sut = fixture.getSut(executorService = deferredExecutorService) sut.register(mock(), fixture.options) @@ -94,11 +94,11 @@ class PhoneStateBreadcrumbsIntegrationTest { @Test fun `When on call state received, added breadcrumb with type and category`() { val sut = fixture.getSut() - val scopes = mock() - sut.register(scopes, fixture.options) + val hub = mock() + sut.register(hub, fixture.options) sut.listener!!.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING, null) - verify(scopes).addBreadcrumb( + verify(hub).addBreadcrumb( check { assertEquals("device.event", it.category) assertEquals("system", it.type) @@ -111,18 +111,18 @@ class PhoneStateBreadcrumbsIntegrationTest { @Test fun `When on idle state received, added breadcrumb with type and category`() { val sut = fixture.getSut() - val scopes = mock() - sut.register(scopes, fixture.options) + val hub = mock() + sut.register(hub, fixture.options) sut.listener!!.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE, null) - verify(scopes, never()).addBreadcrumb(any()) + verify(hub, never()).addBreadcrumb(any()) } @Test fun `When on offhook state received, added breadcrumb with type and category`() { val sut = fixture.getSut() - val scopes = mock() - sut.register(scopes, fixture.options) + val hub = mock() + sut.register(hub, fixture.options) sut.listener!!.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK, null) - verify(scopes, never()).addBreadcrumb(any()) + verify(hub, never()).addBreadcrumb(any()) } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SendCachedEnvelopeIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SendCachedEnvelopeIntegrationTest.kt index f1e345eefc..403f40ee70 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SendCachedEnvelopeIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SendCachedEnvelopeIntegrationTest.kt @@ -2,8 +2,8 @@ package io.sentry.android.core import io.sentry.IConnectionStatusProvider import io.sentry.IConnectionStatusProvider.ConnectionStatus +import io.sentry.IHub import io.sentry.ILogger -import io.sentry.IScopes import io.sentry.ISentryExecutorService import io.sentry.SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget import io.sentry.SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForgetFactory @@ -28,7 +28,7 @@ import kotlin.test.Test class SendCachedEnvelopeIntegrationTest { private class Fixture { - val scopes: IScopes = mock() + val hub: IHub = mock() val options = SentryAndroidOptions() val logger = mock() val factory = mock() @@ -74,7 +74,7 @@ class SendCachedEnvelopeIntegrationTest { fun `when cacheDirPath is not set, does nothing`() { val sut = fixture.getSut(cacheDirPath = null) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.factory, never()).create(any(), any()) } @@ -83,7 +83,7 @@ class SendCachedEnvelopeIntegrationTest { fun `when factory returns null, does nothing`() { val sut = fixture.getSut(hasSender = false, mockExecutorService = ImmediateExecutorService()) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.factory).create(any(), any()) verify(fixture.sender, never()).send() @@ -93,7 +93,7 @@ class SendCachedEnvelopeIntegrationTest { fun `when has factory and cacheDirPath set, submits task into queue`() { val sut = fixture.getSut(mockExecutorService = ImmediateExecutorService()) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) await.untilFalse(fixture.flag) verify(fixture.sender).send() @@ -102,7 +102,7 @@ class SendCachedEnvelopeIntegrationTest { @Test fun `when executorService is fake, does nothing`() { val sut = fixture.getSut(mockExecutorService = mock()) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.factory, never()).create(any(), any()) verify(fixture.sender, never()).send() @@ -112,7 +112,7 @@ class SendCachedEnvelopeIntegrationTest { fun `when has startup crash marker, awaits the task on the calling thread`() { val sut = fixture.getSut(hasStartupCrashMarker = true) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) // we do not need to await here, because it's executed synchronously verify(fixture.sender).send() @@ -123,7 +123,7 @@ class SendCachedEnvelopeIntegrationTest { val sut = fixture.getSut(hasStartupCrashMarker = true, delaySend = 1000) fixture.options.startupCrashFlushTimeoutMillis = 100 - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) // first wait until synchronous send times out and check that the logger was hit in the catch block await.atLeast(500, MILLISECONDS) @@ -144,7 +144,7 @@ class SendCachedEnvelopeIntegrationTest { val connectionStatusProvider = mock() fixture.options.connectionStatusProvider = connectionStatusProvider - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(connectionStatusProvider).addConnectionStatusObserver(any()) } @@ -159,7 +159,7 @@ class SendCachedEnvelopeIntegrationTest { ConnectionStatus.DISCONNECTED ) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.sender, never()).send() } @@ -174,7 +174,7 @@ class SendCachedEnvelopeIntegrationTest { ConnectionStatus.UNKNOWN ) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.factory).create(any(), any()) } @@ -187,7 +187,7 @@ class SendCachedEnvelopeIntegrationTest { whenever(connectionStatusProvider.connectionStatus).thenReturn( ConnectionStatus.DISCONNECTED ) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) // when there's no connection no factory create call should be done verify(fixture.sender, never()).send() @@ -215,9 +215,9 @@ class SendCachedEnvelopeIntegrationTest { val rateLimiter = mock { whenever(mock.isActiveForCategory(any())).thenReturn(true) } - whenever(fixture.scopes.rateLimiter).thenReturn(rateLimiter) + whenever(fixture.hub.rateLimiter).thenReturn(rateLimiter) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) // no factory call should be done if there's rate limiting active verify(fixture.sender, never()).send() @@ -228,7 +228,7 @@ class SendCachedEnvelopeIntegrationTest { val deferredExecutorService = DeferredExecutorService() val sut = fixture.getSut(mockExecutorService = deferredExecutorService) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.sender, never()).send() sut.close() diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt index be0f5cd71a..cd0f8ed8c0 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt @@ -12,7 +12,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.Breadcrumb import io.sentry.Hint import io.sentry.ILogger -import io.sentry.ISentryClient import io.sentry.Sentry import io.sentry.SentryEnvelope import io.sentry.SentryLevel @@ -51,7 +50,6 @@ import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.spy -import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import org.robolectric.annotation.Config @@ -316,25 +314,8 @@ class SentryAndroidTest { } } - @Test - fun `init does not start a session if one is already running`() { - val client = mock() - whenever(client.isEnabled).thenReturn(true) - - initSentryWithForegroundImportance(true, { options -> - options.addIntegration { hub, _ -> - hub.bindClient(client) - // usually done by LifecycleWatcher - hub.startSession() - } - }) {} - - verify(client, times(1)).captureSession(any(), any()) - } - private fun initSentryWithForegroundImportance( inForeground: Boolean, - optionsConfig: (SentryAndroidOptions) -> Unit = {}, callback: (session: Session?) -> Unit ) { val context = ContextUtilsTestHelper.createMockContext() @@ -346,11 +327,10 @@ class SentryAndroidTest { options.release = "prod" options.dsn = "https://key@sentry.io/123" options.isEnableAutoSessionTracking = true - optionsConfig(options) } var session: Session? = null - Sentry.getCurrentScopes().configureScope { scope -> + Sentry.getCurrentHub().configureScope { scope -> session = scope.session } callback(session) @@ -362,7 +342,7 @@ class SentryAndroidTest { fixture.initSut { options -> options.isEnableAutoSessionTracking = false } - Sentry.getCurrentScopes().withScope { scope -> + Sentry.getCurrentHub().withScope { scope -> assertNull(scope.session) } } @@ -398,7 +378,7 @@ class SentryAndroidTest { it.release = "io.sentry.sample@1.1.0+220" it.environment = "debug" // this is necessary to delay the AnrV2Integration processing to execute the configure - // scope block below (otherwise it won't be possible as scopes is no-op before .init) + // scope block below (otherwise it won't be possible as hub is no-op before .init) it.executorService.submit { Sentry.configureScope { scope -> // make sure the scope values changed to test that we're still using previously diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegrationTest.kt index 146abb617e..f8293f9b87 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegrationTest.kt @@ -3,7 +3,7 @@ package io.sentry.android.core import android.content.Context import android.content.Intent import io.sentry.Breadcrumb -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ISentryExecutorService import io.sentry.SentryLevel import io.sentry.test.DeferredExecutorService @@ -26,7 +26,7 @@ class SystemEventsBreadcrumbsIntegrationTest { private class Fixture { val context = mock() var options = SentryAndroidOptions() - val scopes = mock() + val hub = mock() fun getSut(enableSystemEventBreadcrumbs: Boolean = true, executorService: ISentryExecutorService = ImmediateExecutorService()): SystemEventsBreadcrumbsIntegration { options = SentryAndroidOptions().apply { @@ -43,7 +43,7 @@ class SystemEventsBreadcrumbsIntegrationTest { fun `When system events breadcrumb is enabled, it registers callback`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.context).registerReceiver(any(), any()) assertNotNull(sut.receiver) @@ -52,8 +52,8 @@ class SystemEventsBreadcrumbsIntegrationTest { @Test fun `system events callback is registered in the executorService`() { val sut = fixture.getSut(executorService = mock()) - val scopes = mock() - sut.register(scopes, fixture.options) + val hub = mock() + sut.register(hub, fixture.options) assertNull(sut.receiver) } @@ -62,7 +62,7 @@ class SystemEventsBreadcrumbsIntegrationTest { fun `When system events breadcrumb is disabled, it doesn't register callback`() { val sut = fixture.getSut(enableSystemEventBreadcrumbs = false) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.context, never()).registerReceiver(any(), any()) assertNull(sut.receiver) @@ -72,7 +72,7 @@ class SystemEventsBreadcrumbsIntegrationTest { fun `When ActivityBreadcrumbsIntegration is closed, it should unregister the callback`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.close() verify(fixture.context).unregisterReceiver(any()) @@ -80,10 +80,10 @@ class SystemEventsBreadcrumbsIntegrationTest { } @Test - fun `when scopes is closed right after start, integration is not registered`() { + fun `when hub is closed right after start, integration is not registered`() { val deferredExecutorService = DeferredExecutorService() val sut = fixture.getSut(executorService = deferredExecutorService) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertNull(sut.receiver) sut.close() deferredExecutorService.runAll() @@ -94,13 +94,13 @@ class SystemEventsBreadcrumbsIntegrationTest { fun `When broadcast received, added breadcrumb with type and category`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) val intent = Intent().apply { action = Intent.ACTION_TIME_CHANGED } sut.receiver!!.onReceive(fixture.context, intent) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("device.event", it.category) assertEquals("system", it.type) @@ -116,7 +116,7 @@ class SystemEventsBreadcrumbsIntegrationTest { val sut = fixture.getSut() whenever(fixture.context.registerReceiver(any(), any())).thenThrow(SecurityException()) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertFalse(fixture.options.isEnableSystemEventBreadcrumbs) } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/TempSensorBreadcrumbsIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/TempSensorBreadcrumbsIntegrationTest.kt index 5d049e3dad..d443b1e345 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/TempSensorBreadcrumbsIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/TempSensorBreadcrumbsIntegrationTest.kt @@ -7,7 +7,7 @@ import android.hardware.SensorEventListener import android.hardware.SensorManager import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ISentryExecutorService import io.sentry.SentryLevel import io.sentry.TypeCheckHint @@ -47,8 +47,8 @@ class TempSensorBreadcrumbsIntegrationTest { @Test fun `When system events breadcrumb is enabled, it registers callback`() { val sut = fixture.getSut() - val scopes = mock() - sut.register(scopes, fixture.options) + val hub = mock() + sut.register(hub, fixture.options) verify(fixture.manager).registerListener(any(), any(), eq(SensorManager.SENSOR_DELAY_NORMAL)) assertNotNull(sut.sensorManager) } @@ -56,8 +56,8 @@ class TempSensorBreadcrumbsIntegrationTest { @Test fun `temp sensor listener is registered in the executorService`() { val sut = fixture.getSut(executorService = mock()) - val scopes = mock() - sut.register(scopes, fixture.options) + val hub = mock() + sut.register(hub, fixture.options) assertNull(sut.sensorManager) } @@ -65,9 +65,9 @@ class TempSensorBreadcrumbsIntegrationTest { @Test fun `When system events breadcrumb is disabled, it should not register a callback`() { val sut = fixture.getSut() - val scopes = mock() + val hub = mock() sut.register( - scopes, + hub, fixture.options.apply { isEnableSystemEventBreadcrumbs = false } @@ -79,15 +79,15 @@ class TempSensorBreadcrumbsIntegrationTest { @Test fun `When TempSensorBreadcrumbsIntegration is closed, it should unregister the callback`() { val sut = fixture.getSut() - val scopes = mock() - sut.register(scopes, fixture.options) + val hub = mock() + sut.register(hub, fixture.options) sut.close() verify(fixture.manager).unregisterListener(any()) assertNull(sut.sensorManager) } @Test - fun `when scopes is closed right after start, integration is not registered`() { + fun `when hub is closed right after start, integration is not registered`() { val deferredExecutorService = DeferredExecutorService() val sut = fixture.getSut(executorService = deferredExecutorService) sut.register(mock(), fixture.options) @@ -100,14 +100,14 @@ class TempSensorBreadcrumbsIntegrationTest { @Test fun `When onSensorChanged received, add a breadcrumb with type and category`() { val sut = fixture.getSut() - val scopes = mock() - sut.register(scopes, fixture.options) + val hub = mock() + sut.register(hub, fixture.options) val sensorCtor = "android.hardware.SensorEvent".getDeclaredCtor(emptyArray()) val sensorEvent: SensorEvent = sensorCtor.newInstance() as SensorEvent sensorEvent.injectForField("values", FloatArray(2) { 1F }) sut.onSensorChanged(sensorEvent) - verify(scopes).addBreadcrumb( + verify(hub).addBreadcrumb( check { assertEquals("device.event", it.category) assertEquals("system", it.type) @@ -122,12 +122,12 @@ class TempSensorBreadcrumbsIntegrationTest { @Test fun `When onSensorChanged received and null values, do not add a breadcrumb`() { val sut = fixture.getSut() - val scopes = mock() - sut.register(scopes, fixture.options) + val hub = mock() + sut.register(hub, fixture.options) val event = mock() assertNull(event.values) sut.onSensorChanged(event) - verify(scopes, never()).addBreadcrumb(any()) + verify(hub, never()).addBreadcrumb(any()) } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/UserInteractionIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/UserInteractionIntegrationTest.kt index 379e3db353..d43dfe1419 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/UserInteractionIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/UserInteractionIntegrationTest.kt @@ -7,7 +7,7 @@ import android.content.res.Resources import android.util.DisplayMetrics import android.view.Window import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.sentry.Scopes +import io.sentry.Hub import io.sentry.android.core.internal.gestures.NoOpWindowCallback import io.sentry.android.core.internal.gestures.SentryWindowCallback import org.junit.runner.RunWith @@ -26,7 +26,7 @@ class UserInteractionIntegrationTest { private class Fixture { val application = mock() - val scopes = mock() + val hub = mock() val options = SentryAndroidOptions().apply { dsn = "https://key@sentry.io/proj" } @@ -39,7 +39,7 @@ class UserInteractionIntegrationTest { isAndroidXAvailable: Boolean = true ): UserInteractionIntegration { whenever(loadClass.isClassAvailable(any(), anyOrNull())).thenReturn(isAndroidXAvailable) - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) whenever(window.callback).thenReturn(callback) whenever(activity.window).thenReturn(window) @@ -65,7 +65,7 @@ class UserInteractionIntegrationTest { @Test fun `when user interaction breadcrumb is enabled registers a callback`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.application).registerActivityLifecycleCallbacks(any()) } @@ -75,7 +75,7 @@ class UserInteractionIntegrationTest { val sut = fixture.getSut() fixture.options.isEnableUserInteractionBreadcrumbs = false - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.application, never()).registerActivityLifecycleCallbacks(any()) } @@ -83,7 +83,7 @@ class UserInteractionIntegrationTest { @Test fun `when UserInteractionIntegration is closed unregisters the callback`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.close() @@ -94,7 +94,7 @@ class UserInteractionIntegrationTest { fun `when androidx is unavailable doesn't register a callback`() { val sut = fixture.getSut(isAndroidXAvailable = false) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.application, never()).registerActivityLifecycleCallbacks(any()) } @@ -102,7 +102,7 @@ class UserInteractionIntegrationTest { @Test fun `registers window callback on activity resumed`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.onActivityResumed(fixture.activity) @@ -114,7 +114,7 @@ class UserInteractionIntegrationTest { @Test fun `when no original callback delegates to NoOpWindowCallback`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.onActivityResumed(fixture.activity) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerClickTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerClickTest.kt index 74edfb4302..1e6652276a 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerClickTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerClickTest.kt @@ -10,8 +10,8 @@ import android.view.Window import android.widget.CheckBox import android.widget.RadioButton import io.sentry.Breadcrumb +import io.sentry.IHub import io.sentry.IScope -import io.sentry.IScopes import io.sentry.PropagationContext import io.sentry.Scope.IWithPropagationContext import io.sentry.ScopeCallback @@ -40,7 +40,7 @@ class SentryGestureListenerClickTest { gestureTargetLocators = listOf(AndroidViewGestureTargetLocator(true)) dsn = "https://key@sentry.io/proj" } - val scopes = mock() + val hub = mock() val scope = mock() val propagationContext = PropagationContext() lateinit var target: View @@ -86,11 +86,11 @@ class SentryGestureListenerClickTest { whenever(context.resources).thenReturn(resources) whenever(this.target.context).thenReturn(context) whenever(activity.window).thenReturn(window) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) doAnswer { (it.arguments[0] as IWithPropagationContext).accept(propagationContext); propagationContext; }.whenever(scope).withPropagationContext(any()) return SentryGestureListener( activity, - scopes, + hub, options ) } @@ -123,7 +123,7 @@ class SentryGestureListenerClickTest { sut.onSingleTapUp(event) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("ui.click", it.category) assertEquals("user", it.type) @@ -146,7 +146,7 @@ class SentryGestureListenerClickTest { sut.onSingleTapUp(event) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("radio_button", it.data["view.id"]) assertEquals("android.widget.RadioButton", it.data["view.class"]) @@ -166,7 +166,7 @@ class SentryGestureListenerClickTest { sut.onSingleTapUp(event) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("check_box", it.data["view.id"]) assertEquals("android.widget.CheckBox", it.data["view.class"]) @@ -185,7 +185,7 @@ class SentryGestureListenerClickTest { sut.onSingleTapUp(event) - verify(fixture.scopes, never()).addBreadcrumb(any()) + verify(fixture.hub, never()).addBreadcrumb(any()) } @Test @@ -198,7 +198,7 @@ class SentryGestureListenerClickTest { val sut = fixture.getSut(event, "decor_view", targetOverride = decorView) sut.onSingleTapUp(event) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals(decorView.javaClass.canonicalName, it.data["view.class"]) assertEquals("decor_view", it.data["view.id"]) @@ -214,7 +214,7 @@ class SentryGestureListenerClickTest { sut.onSingleTapUp(event) - verify(fixture.scopes, never()).addBreadcrumb(any()) + verify(fixture.hub, never()).addBreadcrumb(any()) } @Test @@ -230,7 +230,7 @@ class SentryGestureListenerClickTest { sut.onSingleTapUp(event) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals(fixture.target.javaClass.simpleName, it.data["view.class"]) }, diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerScrollTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerScrollTest.kt index e5a9623c4d..5d39b64753 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerScrollTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerScrollTest.kt @@ -11,8 +11,8 @@ import android.widget.AbsListView import android.widget.ListAdapter import androidx.core.view.ScrollingView import io.sentry.Breadcrumb +import io.sentry.IHub import io.sentry.IScope -import io.sentry.IScopes import io.sentry.PropagationContext import io.sentry.Scope import io.sentry.ScopeCallback @@ -44,7 +44,7 @@ class SentryGestureListenerScrollTest { isEnableUserInteractionTracing = true gestureTargetLocators = listOf(AndroidViewGestureTargetLocator(true)) } - val scopes = mock() + val hub = mock() val scope = mock() val propagationContext = PropagationContext() @@ -77,11 +77,11 @@ class SentryGestureListenerScrollTest { endEvent.mockDirection(firstEvent, direction) } whenever(activity.window).thenReturn(window) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) doAnswer { (it.arguments[0] as Scope.IWithPropagationContext).accept(propagationContext); propagationContext }.whenever(scope).withPropagationContext(any()) return SentryGestureListener( activity, - scopes, + hub, options ) } @@ -99,7 +99,7 @@ class SentryGestureListenerScrollTest { } sut.onUp(fixture.endEvent) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("ui.scroll", it.category) assertEquals("user", it.type) @@ -122,7 +122,7 @@ class SentryGestureListenerScrollTest { } sut.onUp(fixture.endEvent) - verify(fixture.scopes, never()).addBreadcrumb(any()) + verify(fixture.hub, never()).addBreadcrumb(any()) } @Test @@ -143,8 +143,8 @@ class SentryGestureListenerScrollTest { sut.onFling(fixture.firstEvent, fixture.endEvent, 1.0f, 1.0f) sut.onUp(fixture.endEvent) - inOrder(fixture.scopes) { - verify(fixture.scopes).addBreadcrumb( + inOrder(fixture.hub) { + verify(fixture.hub).addBreadcrumb( check { assertEquals("ui.swipe", it.category) assertEquals("user", it.type) @@ -155,8 +155,8 @@ class SentryGestureListenerScrollTest { }, anyOrNull() ) - verify(fixture.scopes).configureScope(anyOrNull()) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).configureScope(anyOrNull()) + verify(fixture.hub).addBreadcrumb( check { assertEquals("ui.swipe", it.category) assertEquals("user", it.type) @@ -168,7 +168,7 @@ class SentryGestureListenerScrollTest { anyOrNull() ) } - verifyNoMoreInteractions(fixture.scopes) + verifyNoMoreInteractions(fixture.hub) } @Test @@ -177,7 +177,7 @@ class SentryGestureListenerScrollTest { sut.onUp(fixture.firstEvent) sut.onDown(fixture.endEvent) - verify(fixture.scopes, never()).addBreadcrumb(any()) + verify(fixture.hub, never()).addBreadcrumb(any()) } @Test @@ -190,7 +190,7 @@ class SentryGestureListenerScrollTest { } sut.onUp(fixture.endEvent) - verify(fixture.scopes, never()).addBreadcrumb(any()) + verify(fixture.hub, never()).addBreadcrumb(any()) } @Test diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerTracingTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerTracingTest.kt index 8f3e824a2b..c7ada69c88 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerTracingTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerTracingTest.kt @@ -9,8 +9,8 @@ import android.view.ViewGroup import android.view.Window import android.widget.AbsListView import android.widget.ListAdapter +import io.sentry.IHub import io.sentry.IScope -import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryTracer @@ -46,7 +46,7 @@ class SentryGestureListenerTracingTest { val options = SentryAndroidOptions().apply { dsn = "https://key@sentry.io/proj" } - val scopes = mock() + val hub = mock() val event = mock() val scope = mock() lateinit var target: View @@ -64,9 +64,9 @@ class SentryGestureListenerTracingTest { options.isEnableUserInteractionBreadcrumbs = true options.gestureTargetLocators = listOf(AndroidViewGestureTargetLocator(true)) - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) - this.transaction = transaction ?: SentryTracer(TransactionContext("name", "op"), scopes) + this.transaction = transaction ?: SentryTracer(TransactionContext("name", "op"), hub) target = mockView(event = event, clickable = true, context = context) window.mockDecorView(event = event, context = context) { @@ -86,13 +86,13 @@ class SentryGestureListenerTracingTest { whenever(activity.window).thenReturn(window) - whenever(scopes.startTransaction(any(), any())) + whenever(hub.startTransaction(any(), any())) .thenReturn(this.transaction) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) return SentryGestureListener( activity, - scopes, + hub, options ) } @@ -106,7 +106,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) - verify(fixture.scopes, never()).startTransaction( + verify(fixture.hub, never()).startTransaction( any(), any() ) @@ -118,7 +118,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) - verify(fixture.scopes, never()).startTransaction( + verify(fixture.hub, never()).startTransaction( any(), any() ) @@ -130,7 +130,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) - verify(fixture.scopes, never()).startTransaction( + verify(fixture.hub, never()).startTransaction( any(), any() ) @@ -140,7 +140,7 @@ class SentryGestureListenerTracingTest { fun `when transaction is created, set transaction to the bound Scope`() { val sut = fixture.getSut() - whenever(fixture.scopes.configureScope(any())).thenAnswer { + whenever(fixture.hub.configureScope(any())).thenAnswer { val scope = Scope(fixture.options) sut.applyScope(scope, fixture.transaction) @@ -155,9 +155,9 @@ class SentryGestureListenerTracingTest { fun `when transaction is created, do not overwrite transaction already bound to the Scope`() { val sut = fixture.getSut() - whenever(fixture.scopes.configureScope(any())).thenAnswer { + whenever(fixture.hub.configureScope(any())).thenAnswer { val scope = Scope(fixture.options) - val previousTransaction = SentryTracer(TransactionContext("name", "op"), fixture.scopes) + val previousTransaction = SentryTracer(TransactionContext("name", "op"), fixture.hub) scope.transaction = previousTransaction sut.applyScope(scope, fixture.transaction) @@ -173,14 +173,14 @@ class SentryGestureListenerTracingTest { val sut = fixture.getSut() val expectedStatus = SpanStatus.CANCELLED - whenever(fixture.scopes.configureScope(any())).thenAnswer { + whenever(fixture.hub.configureScope(any())).thenAnswer { val scope = Scope(fixture.options) sut.applyScope(scope, fixture.transaction) } sut.onSingleTapUp(fixture.event) - whenever(fixture.scopes.configureScope(any())).thenAnswer { + whenever(fixture.hub.configureScope(any())).thenAnswer { val scope = Scope(fixture.options) scope.transaction = fixture.transaction @@ -199,7 +199,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("Activity.test_button", it.name) assertEquals(TransactionNameSource.COMPONENT, it.transactionNameSource) @@ -214,7 +214,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( any(), check { transactionOptions -> assertEquals(fixture.options.idleTimeout, transactionOptions.idleTimeout) @@ -232,7 +232,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("ui.action.click", it.operation) assertEquals(TransactionNameSource.COMPONENT, it.transactionNameSource) @@ -248,7 +248,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("Activity.test_button", it.name) assertEquals(TransactionNameSource.COMPONENT, it.transactionNameSource) @@ -256,7 +256,7 @@ class SentryGestureListenerTracingTest { any() ) - clearInvocations(fixture.scopes) + clearInvocations(fixture.hub) // second view interaction with another view val newTarget = mockView(event = fixture.event, clickable = true, context = fixture.context) val newContext = mock() @@ -269,16 +269,16 @@ class SentryGestureListenerTracingTest { whenever(it.getChildAt(0)).thenReturn(newTarget) } - whenever(fixture.scopes.startTransaction(any(), any())) + whenever(fixture.hub.startTransaction(any(), any())) .thenAnswer { // verify that the active transaction gets finished when a new one appears assertEquals(true, fixture.transaction.isFinished) - SentryTracer(TransactionContext("name", "op"), fixture.scopes) + SentryTracer(TransactionContext("name", "op"), fixture.hub) } sut.onSingleTapUp(fixture.event) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("Activity.test_checkbox", it.name) assertEquals(TransactionNameSource.COMPONENT, it.transactionNameSource) @@ -293,7 +293,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("Activity.test_scroll_view", it.name) assertEquals("ui.action.click", it.operation) @@ -302,20 +302,20 @@ class SentryGestureListenerTracingTest { any() ) - clearInvocations(fixture.scopes) + clearInvocations(fixture.hub) // second view interaction with a different interaction type (scroll) - whenever(fixture.scopes.startTransaction(any(), any())) + whenever(fixture.hub.startTransaction(any(), any())) .thenAnswer { // verify that the active transaction gets finished when a new one appears assertEquals(true, fixture.transaction.isFinished) - SentryTracer(TransactionContext("name", "op"), fixture.scopes) + SentryTracer(TransactionContext("name", "op"), fixture.hub) } sut.onScroll(fixture.event, mock(), 10.0f, 0f) sut.onUp(mock()) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("Activity.test_scroll_view", it.name) assertEquals("ui.action.scroll", it.operation) @@ -340,7 +340,7 @@ class SentryGestureListenerTracingTest { sut.onSingleTapUp(fixture.event) // then two transaction should be captured - verify(fixture.scopes, times(2)).startTransaction( + verify(fixture.hub, times(2)).startTransaction( check { assertEquals("Activity.test_button", it.name) assertEquals(TransactionNameSource.COMPONENT, it.transactionNameSource) @@ -349,15 +349,14 @@ class SentryGestureListenerTracingTest { ) } - // TODO [POTEL] rewrite -// @Test -// fun `captures transaction and sets trace origin`() { -// val sut = fixture.getSut() -// -// sut.onSingleTapUp(fixture.event) -// -// assertEquals("auto.ui.gesture_listener.old_view_system", fixture.transaction.spanContext.origin) -// } + @Test + fun `captures transaction and sets trace origin`() { + val sut = fixture.getSut() + + sut.onSingleTapUp(fixture.event) + + assertEquals("auto.ui.gesture_listener.old_view_system", fixture.transaction.spanContext.origin) + } @Test fun `preserves existing transaction status`() { diff --git a/sentry-android-fragment/api/sentry-android-fragment.api b/sentry-android-fragment/api/sentry-android-fragment.api index f2f3334280..4b3487c36e 100644 --- a/sentry-android-fragment/api/sentry-android-fragment.api +++ b/sentry-android-fragment/api/sentry-android-fragment.api @@ -18,7 +18,7 @@ public final class io/sentry/android/fragment/FragmentLifecycleIntegration : and public fun onActivitySaveInstanceState (Landroid/app/Activity;Landroid/os/Bundle;)V public fun onActivityStarted (Landroid/app/Activity;)V public fun onActivityStopped (Landroid/app/Activity;)V - public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/fragment/FragmentLifecycleState : java/lang/Enum { @@ -40,9 +40,9 @@ public final class io/sentry/android/fragment/FragmentLifecycleState : java/lang public final class io/sentry/android/fragment/SentryFragmentLifecycleCallbacks : androidx/fragment/app/FragmentManager$FragmentLifecycleCallbacks { public static final field Companion Lio/sentry/android/fragment/SentryFragmentLifecycleCallbacks$Companion; public static final field FRAGMENT_LOAD_OP Ljava/lang/String; - public fun (Lio/sentry/IScopes;Ljava/util/Set;Z)V - public synthetic fun (Lio/sentry/IScopes;Ljava/util/Set;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Lio/sentry/IScopes;ZZ)V + public fun (Lio/sentry/IHub;Ljava/util/Set;Z)V + public synthetic fun (Lio/sentry/IHub;Ljava/util/Set;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IHub;ZZ)V public fun (ZZ)V public synthetic fun (ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getEnableAutoFragmentLifecycleTracing ()Z diff --git a/sentry-android-fragment/src/main/java/io/sentry/android/fragment/FragmentLifecycleIntegration.kt b/sentry-android-fragment/src/main/java/io/sentry/android/fragment/FragmentLifecycleIntegration.kt index d2fd393200..4129ea4356 100644 --- a/sentry-android-fragment/src/main/java/io/sentry/android/fragment/FragmentLifecycleIntegration.kt +++ b/sentry-android-fragment/src/main/java/io/sentry/android/fragment/FragmentLifecycleIntegration.kt @@ -5,7 +5,7 @@ import android.app.Application import android.app.Application.ActivityLifecycleCallbacks import android.os.Bundle import androidx.fragment.app.FragmentActivity -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.Integration import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryLevel.DEBUG @@ -40,11 +40,11 @@ class FragmentLifecycleIntegration( enableAutoFragmentLifecycleTracing = enableAutoFragmentLifecycleTracing ) - private lateinit var scopes: IScopes + private lateinit var hub: IHub private lateinit var options: SentryOptions - override fun register(scopes: IScopes, options: SentryOptions) { - this.scopes = scopes + override fun register(hub: IHub, options: SentryOptions) { + this.hub = hub this.options = options application.registerActivityLifecycleCallbacks(this) @@ -66,7 +66,7 @@ class FragmentLifecycleIntegration( ?.supportFragmentManager ?.registerFragmentLifecycleCallbacks( SentryFragmentLifecycleCallbacks( - scopes = scopes, + hub = hub, filterFragmentLifecycleBreadcrumbs = filterFragmentLifecycleBreadcrumbs, enableAutoFragmentLifecycleTracing = enableAutoFragmentLifecycleTracing ), diff --git a/sentry-android-fragment/src/main/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacks.kt b/sentry-android-fragment/src/main/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacks.kt index fc99f588ae..5e03c99e0d 100644 --- a/sentry-android-fragment/src/main/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacks.kt +++ b/sentry-android-fragment/src/main/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacks.kt @@ -8,9 +8,9 @@ import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.HubAdapter +import io.sentry.IHub import io.sentry.ISpan -import io.sentry.ScopesAdapter import io.sentry.SentryLevel.INFO import io.sentry.SpanStatus import io.sentry.TypeCheckHint.ANDROID_FRAGMENT @@ -20,17 +20,17 @@ private const val TRACE_ORIGIN = "auto.ui.fragment" @Suppress("TooManyFunctions") class SentryFragmentLifecycleCallbacks( - private val scopes: IScopes = ScopesAdapter.getInstance(), + private val hub: IHub = HubAdapter.getInstance(), val filterFragmentLifecycleBreadcrumbs: Set, val enableAutoFragmentLifecycleTracing: Boolean ) : FragmentLifecycleCallbacks() { constructor( - scopes: IScopes, + hub: IHub, enableFragmentLifecycleBreadcrumbs: Boolean, enableAutoFragmentLifecycleTracing: Boolean ) : this( - scopes = scopes, + hub = hub, filterFragmentLifecycleBreadcrumbs = FragmentLifecycleState.values().toSet() .takeIf { enableFragmentLifecycleBreadcrumbs } .orEmpty(), @@ -41,14 +41,14 @@ class SentryFragmentLifecycleCallbacks( enableFragmentLifecycleBreadcrumbs: Boolean = true, enableAutoFragmentLifecycleTracing: Boolean = false ) : this( - scopes = ScopesAdapter.getInstance(), + hub = HubAdapter.getInstance(), filterFragmentLifecycleBreadcrumbs = FragmentLifecycleState.values().toSet() .takeIf { enableFragmentLifecycleBreadcrumbs } .orEmpty(), enableAutoFragmentLifecycleTracing = enableAutoFragmentLifecycleTracing ) - private val isPerformanceEnabled get() = scopes.options.isTracingEnabled && enableAutoFragmentLifecycleTracing + private val isPerformanceEnabled get() = hub.options.isTracingEnabled && enableAutoFragmentLifecycleTracing private val fragmentsWithOngoingTransactions = WeakHashMap() @@ -142,7 +142,7 @@ class SentryFragmentLifecycleCallbacks( val hint = Hint() .also { it.set(ANDROID_FRAGMENT, fragment) } - scopes.addBreadcrumb(breadcrumb, hint) + hub.addBreadcrumb(breadcrumb, hint) } private fun getFragmentName(fragment: Fragment): String { @@ -158,7 +158,7 @@ class SentryFragmentLifecycleCallbacks( } var transaction: ISpan? = null - scopes.configureScope { + hub.configureScope { transaction = it.transaction } diff --git a/sentry-android-fragment/src/test/java/io/sentry/android/fragment/FragmentLifecycleIntegrationTest.kt b/sentry-android-fragment/src/test/java/io/sentry/android/fragment/FragmentLifecycleIntegrationTest.kt index 84286503b9..032aef58e1 100644 --- a/sentry-android-fragment/src/test/java/io/sentry/android/fragment/FragmentLifecycleIntegrationTest.kt +++ b/sentry-android-fragment/src/test/java/io/sentry/android/fragment/FragmentLifecycleIntegrationTest.kt @@ -4,7 +4,7 @@ import android.app.Activity import android.app.Application import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentManager -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryOptions import org.mockito.kotlin.check import org.mockito.kotlin.doReturn @@ -24,14 +24,14 @@ class FragmentLifecycleIntegrationTest { val fragmentActivity = mock { on { supportFragmentManager } doReturn fragmentManager } - val scopes = mock() + val hub = mock() val options = SentryOptions() fun getSut( enableFragmentLifecycleBreadcrumbs: Boolean = true, enableAutoFragmentLifecycleTracing: Boolean = false ): FragmentLifecycleIntegration { - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) return FragmentLifecycleIntegration( application = application, enableFragmentLifecycleBreadcrumbs = enableFragmentLifecycleBreadcrumbs, @@ -46,7 +46,7 @@ class FragmentLifecycleIntegrationTest { fun `When register, it should register activity lifecycle callbacks`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.application).registerActivityLifecycleCallbacks(sut) } @@ -55,7 +55,7 @@ class FragmentLifecycleIntegrationTest { fun `When close, it should unregister lifecycle callbacks`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.close() verify(fixture.application).unregisterActivityLifecycleCallbacks(sut) @@ -69,7 +69,7 @@ class FragmentLifecycleIntegrationTest { on { supportFragmentManager } doReturn fragmentManager } - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.onActivityCreated(fragmentActivity, savedInstanceState = null) verify(fragmentManager).registerFragmentLifecycleCallbacks( @@ -84,7 +84,7 @@ class FragmentLifecycleIntegrationTest { fun `When FragmentActivity is created, it should register fragment lifecycle callbacks with passed config`() { val sut = fixture.getSut(enableFragmentLifecycleBreadcrumbs = false, enableAutoFragmentLifecycleTracing = true) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.onActivityCreated(fixture.fragmentActivity, savedInstanceState = null) verify(fixture.fragmentManager).registerFragmentLifecycleCallbacks( @@ -102,7 +102,7 @@ class FragmentLifecycleIntegrationTest { val sut = fixture.getSut() val activity = mock() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.onActivityCreated(activity, savedInstanceState = null) } diff --git a/sentry-android-fragment/src/test/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacksTest.kt b/sentry-android-fragment/src/test/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacksTest.kt index 394d4e0aaf..812d78de30 100644 --- a/sentry-android-fragment/src/test/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacksTest.kt +++ b/sentry-android-fragment/src/test/java/io/sentry/android/fragment/SentryFragmentLifecycleCallbacksTest.kt @@ -5,8 +5,8 @@ import android.os.Bundle import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import io.sentry.Breadcrumb +import io.sentry.Hub import io.sentry.IScope -import io.sentry.IScopes import io.sentry.ISpan import io.sentry.ITransaction import io.sentry.ScopeCallback @@ -32,7 +32,7 @@ class SentryFragmentLifecycleCallbacksTest { private class Fixture { val fragmentManager = mock() - val scopes = mock() + val hub = mock() val fragment = mock() val context = mock() val scope = mock() @@ -45,7 +45,7 @@ class SentryFragmentLifecycleCallbacksTest { tracesSampleRate: Double? = 1.0, isAdded: Boolean = true ): SentryFragmentLifecycleCallbacks { - whenever(scopes.options).thenReturn( + whenever(hub.options).thenReturn( SentryOptions().apply { setTracesSampleRate(tracesSampleRate) } @@ -53,14 +53,14 @@ class SentryFragmentLifecycleCallbacksTest { whenever(span.spanContext).thenReturn( SpanContext(SentryId.EMPTY_ID, SpanId.EMPTY_ID, "op", null, null) ) - whenever(transaction.startChild(any(), any())).thenReturn(span) + whenever(transaction.startChild(any(), any())).thenReturn(span) whenever(scope.transaction).thenReturn(transaction) - whenever(scopes.configureScope(any())).thenAnswer { + whenever(hub.configureScope(any())).thenAnswer { (it.arguments[0] as ScopeCallback).run(scope) } whenever(fragment.isAdded).thenReturn(isAdded) return SentryFragmentLifecycleCallbacks( - scopes = scopes, + hub = hub, filterFragmentLifecycleBreadcrumbs = loggedFragmentLifecycleStates, enableAutoFragmentLifecycleTracing = enableAutoFragmentLifecycleTracing ) @@ -190,7 +190,7 @@ class SentryFragmentLifecycleCallbacksTest { sut.onFragmentCreated(fixture.fragmentManager, fixture.fragment, savedInstanceState = null) - verify(fixture.transaction, never()).startChild(any(), any()) + verify(fixture.transaction, never()).startChild(any(), any()) } @Test @@ -200,10 +200,10 @@ class SentryFragmentLifecycleCallbacksTest { sut.onFragmentCreated(fixture.fragmentManager, fixture.fragment, savedInstanceState = null) verify(fixture.transaction).startChild( - check { + check { assertEquals(SentryFragmentLifecycleCallbacks.FRAGMENT_LOAD_OP, it) }, - check { + check { assertEquals("androidx.fragment.app.Fragment", it) } ) @@ -215,7 +215,7 @@ class SentryFragmentLifecycleCallbacksTest { sut.onFragmentCreated(fixture.fragmentManager, fixture.fragment, savedInstanceState = null) - verify(fixture.transaction, never()).startChild(any(), any()) + verify(fixture.transaction, never()).startChild(any(), any()) } @Test @@ -225,7 +225,7 @@ class SentryFragmentLifecycleCallbacksTest { sut.onFragmentCreated(fixture.fragmentManager, fixture.fragment, savedInstanceState = null) sut.onFragmentCreated(fixture.fragmentManager, fixture.fragment, savedInstanceState = null) - verify(fixture.transaction).startChild(any(), any()) + verify(fixture.transaction).startChild(any(), any()) } @Test @@ -272,7 +272,7 @@ class SentryFragmentLifecycleCallbacksTest { } private fun verifyBreadcrumbAdded(expectedState: String) { - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { breadcrumb: Breadcrumb -> assertEquals("ui.fragment.lifecycle", breadcrumb.category) assertEquals("navigation", breadcrumb.type) @@ -285,6 +285,6 @@ class SentryFragmentLifecycleCallbacksTest { } private fun verifyBreadcrumbAddedCount(count: Int) { - verify(fixture.scopes, times(count)).addBreadcrumb(any(), anyOrNull()) + verify(fixture.hub, times(count)).addBreadcrumb(any(), anyOrNull()) } } diff --git a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt index a40bc301a1..c942783548 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt +++ b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/SdkInitTests.kt @@ -10,7 +10,6 @@ import io.sentry.android.core.SentryAndroidOptions import io.sentry.assertEnvelopeTransaction import io.sentry.protocol.SentryTransaction import org.junit.runner.RunWith -import kotlin.test.Ignore import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -36,7 +35,6 @@ class SdkInitTests : BaseUiTest() { transaction2.finish() } - @Ignore("TODO [POTEL] reinit should be discussed with mobile team") @Test fun doubleInitWithSameOptionsDoesNotThrow() { val options = SentryAndroidOptions() @@ -58,7 +56,6 @@ class SdkInitTests : BaseUiTest() { it.isDebug = true } relayIdlingResource.increment() - relayIdlingResource.increment() transaction.finish() sampleScenario.moveToState(Lifecycle.State.DESTROYED) val transaction2 = Sentry.startTransaction("e2etests2", "testInit") @@ -66,23 +63,7 @@ class SdkInitTests : BaseUiTest() { relay.assert { findEnvelope { - assertEnvelopeTransaction( - it.items.toList(), - AndroidLogger() - ).transaction == "e2etests" - }.assert { - val transactionItem: SentryTransaction = it.assertTransaction() - it.assertNoOtherItems() - assertEquals("e2etests", transactionItem.transaction) - } - } - - relay.assert { - findEnvelope { - assertEnvelopeTransaction( - it.items.toList(), - AndroidLogger() - ).transaction == "e2etests2" + assertEnvelopeTransaction(it.items.toList(), AndroidLogger()).transaction == "e2etests2" }.assert { val transactionItem: SentryTransaction = it.assertTransaction() // Profiling uses executorService, so if the executorService is shutdown it would fail @@ -95,7 +76,6 @@ class SdkInitTests : BaseUiTest() { } } - @Ignore("TODO [POTEL] reinit should be discussed with mobile team") @Test fun doubleInitDoesNotWait() { relayIdlingResource.increment() @@ -125,8 +105,7 @@ class SdkInitTests : BaseUiTest() { Sentry.startTransaction("afterRestart", "emptyTransaction").finish() // We assert for less than 1 second just to account for slow devices in saucelabs or headless emulator - // TODO: Revert back to 1000ms after making scope.close() faster again - assertTrue(restartMs < 2500, "Expected less than 2500 ms for SDK restart. Got $restartMs ms") + assertTrue(restartMs < 1000, "Expected less than 1000 ms for SDK restart. Got $restartMs ms") relay.assert { findEnvelope { diff --git a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/mockservers/RelayAsserter.kt b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/mockservers/RelayAsserter.kt index f685e84874..e956978086 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/mockservers/RelayAsserter.kt +++ b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/mockservers/RelayAsserter.kt @@ -93,7 +93,7 @@ class RelayAsserter( /** Request parsed as envelope. */ val envelope: SentryEnvelope? by lazy { try { - EnvelopeReader(Sentry.getCurrentScopes().options.serializer) + EnvelopeReader(Sentry.getCurrentHub().options.serializer) .read(GZIPInputStream(request.body.inputStream())) } catch (e: IOException) { null diff --git a/sentry-android-navigation/api/sentry-android-navigation.api b/sentry-android-navigation/api/sentry-android-navigation.api index 03a46d8b87..79151bb3fb 100644 --- a/sentry-android-navigation/api/sentry-android-navigation.api +++ b/sentry-android-navigation/api/sentry-android-navigation.api @@ -10,11 +10,11 @@ public final class io/sentry/android/navigation/SentryNavigationListener : andro public static final field Companion Lio/sentry/android/navigation/SentryNavigationListener$Companion; public static final field NAVIGATION_OP Ljava/lang/String; public fun ()V - public fun (Lio/sentry/IScopes;)V - public fun (Lio/sentry/IScopes;Z)V - public fun (Lio/sentry/IScopes;ZZ)V - public fun (Lio/sentry/IScopes;ZZLjava/lang/String;)V - public synthetic fun (Lio/sentry/IScopes;ZZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IHub;Z)V + public fun (Lio/sentry/IHub;ZZ)V + public fun (Lio/sentry/IHub;ZZLjava/lang/String;)V + public synthetic fun (Lio/sentry/IHub;ZZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun onDestinationChanged (Landroidx/navigation/NavController;Landroidx/navigation/NavDestination;Landroid/os/Bundle;)V } diff --git a/sentry-android-navigation/src/main/java/io/sentry/android/navigation/SentryNavigationListener.kt b/sentry-android-navigation/src/main/java/io/sentry/android/navigation/SentryNavigationListener.kt index bb06d66b3c..8fdf8b0df8 100644 --- a/sentry-android-navigation/src/main/java/io/sentry/android/navigation/SentryNavigationListener.kt +++ b/sentry-android-navigation/src/main/java/io/sentry/android/navigation/SentryNavigationListener.kt @@ -6,9 +6,9 @@ import androidx.navigation.NavController import androidx.navigation.NavDestination import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.HubAdapter +import io.sentry.IHub import io.sentry.ITransaction -import io.sentry.ScopesAdapter import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryLevel.DEBUG import io.sentry.SentryLevel.INFO @@ -34,7 +34,7 @@ private const val TRACE_ORIGIN = "auto.navigation" * with [SentryOptions.idleTimeout] for navigation events. */ class SentryNavigationListener @JvmOverloads constructor( - private val scopes: IScopes = ScopesAdapter.getInstance(), + private val hub: IHub = HubAdapter.getInstance(), private val enableNavigationBreadcrumbs: Boolean = true, private val enableNavigationTracing: Boolean = true, private val traceOriginAppendix: String? = null @@ -43,7 +43,7 @@ class SentryNavigationListener @JvmOverloads constructor( private var previousDestinationRef: WeakReference? = null private var previousArgs: Bundle? = null - private val isPerformanceEnabled get() = scopes.options.isTracingEnabled && enableNavigationTracing + private val isPerformanceEnabled get() = hub.options.isTracingEnabled && enableNavigationTracing private var activeTransaction: ITransaction? = null @@ -91,7 +91,7 @@ class SentryNavigationListener @JvmOverloads constructor( } val hint = Hint() hint.set(TypeCheckHint.ANDROID_NAV_DESTINATION, destination) - scopes.addBreadcrumb(breadcrumb, hint) + hub.addBreadcrumb(breadcrumb, hint) } private fun startTracing( @@ -100,7 +100,7 @@ class SentryNavigationListener @JvmOverloads constructor( arguments: Map ) { if (!isPerformanceEnabled) { - TracingUtils.startNewTrace(scopes) + TracingUtils.startNewTrace(hub) return } @@ -111,7 +111,7 @@ class SentryNavigationListener @JvmOverloads constructor( if (destination.navigatorName == "activity") { // we do not trace navigation between activities to avoid clashing with activity lifecycle tracing - scopes.options.logger.log( + hub.options.logger.log( DEBUG, "Navigating to activity destination, no transaction captured." ) @@ -122,7 +122,7 @@ class SentryNavigationListener @JvmOverloads constructor( var name = destination.route ?: try { controller.context.resources.getResourceEntryName(destination.id) } catch (e: NotFoundException) { - scopes.options.logger.log( + hub.options.logger.log( DEBUG, "Destination id cannot be retrieved from Resources, no transaction captured." ) @@ -134,12 +134,12 @@ class SentryNavigationListener @JvmOverloads constructor( val transactionOptions = TransactionOptions().also { it.isWaitForChildren = true - it.idleTimeout = scopes.options.idleTimeout + it.idleTimeout = hub.options.idleTimeout it.deadlineTimeout = TransactionOptions.DEFAULT_DEADLINE_TIMEOUT_AUTO_TRANSACTION it.isTrimEnd = true } - val transaction = scopes.startTransaction( + val transaction = hub.startTransaction( TransactionContext(name, TransactionNameSource.ROUTE, NAVIGATION_OP), transactionOptions ) @@ -151,7 +151,7 @@ class SentryNavigationListener @JvmOverloads constructor( if (arguments.isNotEmpty()) { transaction.setData("arguments", arguments) } - scopes.configureScope { scope -> + hub.configureScope { scope -> scope.withTransaction { tx -> if (tx == null) { scope.transaction = transaction @@ -166,7 +166,7 @@ class SentryNavigationListener @JvmOverloads constructor( activeTransaction?.finish(status) // clear transaction from scope so others can bind to it - scopes.configureScope { scope -> + hub.configureScope { scope -> scope.withTransaction { tx -> if (tx == activeTransaction) { scope.clearTransaction() diff --git a/sentry-android-navigation/src/test/java/io/sentry/android/navigation/SentryNavigationListenerTest.kt b/sentry-android-navigation/src/test/java/io/sentry/android/navigation/SentryNavigationListenerTest.kt index b37133410f..76c57159c3 100644 --- a/sentry-android-navigation/src/test/java/io/sentry/android/navigation/SentryNavigationListenerTest.kt +++ b/sentry-android-navigation/src/test/java/io/sentry/android/navigation/SentryNavigationListenerTest.kt @@ -7,8 +7,8 @@ import androidx.navigation.NavController import androidx.navigation.NavDestination import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.Breadcrumb +import io.sentry.IHub import io.sentry.IScope -import io.sentry.IScopes import io.sentry.Scope import io.sentry.Scope.IWithTransaction import io.sentry.ScopeCallback @@ -39,7 +39,7 @@ import kotlin.test.assertNull class SentryNavigationListenerTest { class Fixture { - val scopes = mock() + val hub = mock() val destination = mock() val navController = mock() @@ -67,20 +67,20 @@ class SentryNavigationListenerTest { tracesSampleRate ) } - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) this.transaction = transaction ?: SentryTracer( TransactionContext( "/$toRoute", SentryNavigationListener.NAVIGATION_OP ), - scopes + hub ) - whenever(scopes.startTransaction(any(), any())) + whenever(hub.startTransaction(any(), any())) .thenReturn(this.transaction) - whenever(scopes.configureScope(any())).thenAnswer { + whenever(hub.configureScope(any())).thenAnswer { (it.arguments[0] as ScopeCallback).run(scope) } @@ -96,7 +96,7 @@ class SentryNavigationListenerTest { whenever(navController.context).thenReturn(context) whenever(destination.route).thenReturn(toRoute) return SentryNavigationListener( - scopes, + hub, enableBreadcrumbs, enableTracing, traceOriginAppendix @@ -112,7 +112,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("navigation", it.type) assertEquals("navigation", it.category) @@ -133,7 +133,7 @@ class SentryNavigationListenerTest { bundleOf("arg1" to "foo", "arg2" to "bar") ) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("/route", it.data["to"]) assertEquals(mapOf("arg1" to "foo", "arg2" to "bar"), it.data["to_arguments"]) @@ -152,7 +152,7 @@ class SentryNavigationListenerTest { bundleOf() ) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("/route", it.data["to"]) assertNull(it.data["to_arguments"]) @@ -180,7 +180,7 @@ class SentryNavigationListenerTest { bundleOf("to_arg1" to "to_foo") ) val captor = argumentCaptor() - verify(fixture.scopes, times(2)).addBreadcrumb(captor.capture(), any()) + verify(fixture.hub, times(2)).addBreadcrumb(captor.capture(), any()) captor.secondValue.let { assertEquals("/route_from", it.data["from"]) assertEquals(mapOf("from_arg1" to "from_foo"), it.data["from_arguments"]) @@ -196,7 +196,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.scopes, never()).addBreadcrumb(any()) + verify(fixture.hub, never()).addBreadcrumb(any()) } @Test @@ -205,7 +205,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.scopes, never()).startTransaction( + verify(fixture.hub, never()).startTransaction( any(), any() ) @@ -217,7 +217,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.scopes, never()).startTransaction( + verify(fixture.hub, never()).startTransaction( any(), any() ) @@ -230,7 +230,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.scopes, never()).startTransaction( + verify(fixture.hub, never()).startTransaction( any(), any() ) @@ -242,7 +242,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.scopes, never()).startTransaction( + verify(fixture.hub, never()).startTransaction( any(), any() ) @@ -254,7 +254,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("/route", it.name) assertEquals(SentryNavigationListener.NAVIGATION_OP, it.operation) @@ -270,7 +270,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("/github", it.name) assertEquals(TransactionNameSource.ROUTE, it.transactionNameSource) @@ -285,7 +285,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("/destination-id-1", it.name) assertEquals(TransactionNameSource.ROUTE, it.transactionNameSource) @@ -304,7 +304,7 @@ class SentryNavigationListenerTest { bundleOf("user_id" to 123, "per_page" to 10) ) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("/github", it.name) assertEquals(TransactionNameSource.ROUTE, it.transactionNameSource) @@ -365,13 +365,13 @@ class SentryNavigationListenerTest { ArgumentCaptor.forClass(ScopeCallback::class.java) val scope = Scope(fixture.options) val propagationContextAtStart = scope.propagationContext - whenever(fixture.scopes.configureScope(argumentCaptor.capture())).thenAnswer { + whenever(fixture.hub.configureScope(argumentCaptor.capture())).thenAnswer { argumentCaptor.value.run(scope) } sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.scopes).configureScope(any()) + verify(fixture.hub).configureScope(any()) assertNotSame(propagationContextAtStart, scope.propagationContext) } @@ -399,7 +399,7 @@ class SentryNavigationListenerTest { sut.onDestinationChanged(fixture.navController, fixture.destination, null) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( any(), check { options -> assertEquals(TransactionOptions.DEFAULT_DEADLINE_TIMEOUT_AUTO_TRANSACTION, options.deadlineTimeout) diff --git a/sentry-android-ndk/CMakeLists.txt b/sentry-android-ndk/CMakeLists.txt new file mode 100644 index 0000000000..c9a0181935 --- /dev/null +++ b/sentry-android-ndk/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.10) +project(Sentry-Android LANGUAGES C CXX) + +# Add sentry-android shared library +add_library(sentry-android SHARED src/main/jni/sentry.c) + +# make sure that we build it as a shared lib instead of a static lib +set(BUILD_SHARED_LIBS ON) +set(SENTRY_BUILD_SHARED_LIBS ON) + +# Adding sentry-native submodule subdirectory +add_subdirectory(${SENTRY_NATIVE_SRC} sentry_build) + +# Link to sentry-native +target_link_libraries(sentry-android PRIVATE + $ +) diff --git a/sentry-android-ndk/api/sentry-android-ndk.api b/sentry-android-ndk/api/sentry-android-ndk.api index 155a368b11..e8f838ce8b 100644 --- a/sentry-android-ndk/api/sentry-android-ndk.api +++ b/sentry-android-ndk/api/sentry-android-ndk.api @@ -7,7 +7,7 @@ public final class io/sentry/android/ndk/BuildConfig { } public final class io/sentry/android/ndk/DebugImagesLoader : io/sentry/android/core/IDebugImagesLoader { - public fun (Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/ndk/NativeModuleListLoader;)V + public fun (Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/android/ndk/NativeModuleListLoader;)V public fun clearDebugImages ()V public fun loadDebugImages ()Ljava/util/List; } diff --git a/sentry-android-ndk/build.gradle.kts b/sentry-android-ndk/build.gradle.kts index fe67063139..f6564cd97f 100644 --- a/sentry-android-ndk/build.gradle.kts +++ b/sentry-android-ndk/build.gradle.kts @@ -5,21 +5,38 @@ plugins { kotlin("android") jacoco id(Config.QualityPlugins.jacocoAndroid) + id(Config.NativePlugins.nativeBundleExport) id(Config.QualityPlugins.gradleVersions) } +var sentryNativeSrc: String = "sentry-native" val sentryAndroidSdkName: String by project android { compileSdk = Config.Android.compileSdkVersion namespace = "io.sentry.android.ndk" + sentryNativeSrc = if (File("${project.projectDir}/sentry-native-local").exists()) { + "sentry-native-local" + } else { + "sentry-native" + } + println("sentry-android-ndk: $sentryNativeSrc") + defaultConfig { targetSdk = Config.Android.targetSdkVersion minSdk = Config.Android.minSdkVersionNdk // NDK requires a higher API level than core. testInstrumentationRunner = Config.TestLibs.androidJUnitRunner + externalNativeBuild { + cmake { + arguments.add(0, "-DANDROID_STL=c++_static") + arguments.add(0, "-DSENTRY_NATIVE_SRC=$sentryNativeSrc") + arguments.add(0, "-DSENTRY_SDK_NAME=$sentryAndroidSdkName") + } + } + ndk { abiFilters.addAll(Config.Android.abiFilters) } @@ -28,6 +45,15 @@ android { buildConfigField("String", "VERSION_NAME", "\"${project.version}\"") } + // we use the default NDK and CMake versions based on the AGP's version + // https://developer.android.com/studio/projects/install-ndk#apply-specific-version + + externalNativeBuild { + cmake { + path("CMakeLists.txt") + } + } + buildTypes { getByName("debug") getByName("release") { @@ -55,6 +81,10 @@ android { checkReleaseBuilds = false } + nativeBundleExport { + headerDir = "${project.projectDir}/$sentryNativeSrc/include" + } + // needed because of Kotlin 1.4.x configurations.all { resolutionStrategy.force(Config.CompileOnly.jetbrainsAnnotations) @@ -71,8 +101,6 @@ dependencies { api(projects.sentry) api(projects.sentryAndroidCore) - implementation("io.sentry:sentry-native-ndk:0.7.5") - compileOnly(Config.CompileOnly.jetbrainsAnnotations) testImplementation(kotlin(Config.kotlinStdLib, KotlinCompilerVersion.VERSION)) diff --git a/sentry-android-ndk/sentry-native b/sentry-android-ndk/sentry-native new file mode 160000 index 0000000000..4ec95c0725 --- /dev/null +++ b/sentry-android-ndk/sentry-native @@ -0,0 +1 @@ +Subproject commit 4ec95c0725df5f34440db8fa8d37b4c519fce74e diff --git a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/DebugImagesLoader.java b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/DebugImagesLoader.java index 2e069dcc74..cb38db498a 100644 --- a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/DebugImagesLoader.java +++ b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/DebugImagesLoader.java @@ -4,10 +4,9 @@ import io.sentry.SentryOptions; import io.sentry.android.core.IDebugImagesLoader; import io.sentry.android.core.SentryAndroidOptions; -import io.sentry.ndk.NativeModuleListLoader; import io.sentry.protocol.DebugImage; import io.sentry.util.Objects; -import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -46,20 +45,9 @@ public DebugImagesLoader( synchronized (debugImagesLock) { if (debugImages == null) { try { - final io.sentry.ndk.DebugImage[] debugImagesArr = moduleListLoader.loadModuleList(); + final DebugImage[] debugImagesArr = moduleListLoader.loadModuleList(); if (debugImagesArr != null) { - debugImages = new ArrayList<>(debugImagesArr.length); - for (io.sentry.ndk.DebugImage d : debugImagesArr) { - final DebugImage debugImage = new DebugImage(); - debugImage.setUuid(d.getUuid()); - debugImage.setType(d.getType()); - debugImage.setDebugId(d.getDebugId()); - debugImage.setCodeId(d.getCodeId()); - debugImage.setImageAddr(d.getImageAddr()); - debugImage.setImageSize(d.getImageSize()); - debugImage.setArch(d.getArch()); - debugImages.add(debugImage); - } + debugImages = Arrays.asList(debugImagesArr); options .getLogger() .log(SentryLevel.DEBUG, "Debug images loaded: %d", debugImages.size()); diff --git a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/INativeScope.java b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/INativeScope.java new file mode 100644 index 0000000000..a8d50e40fe --- /dev/null +++ b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/INativeScope.java @@ -0,0 +1,18 @@ +package io.sentry.android.ndk; + +interface INativeScope { + void setTag(String key, String value); + + void removeTag(String key); + + void setExtra(String key, String value); + + void removeExtra(String key); + + void setUser(String id, String email, String ipAddress, String username); + + void removeUser(); + + void addBreadcrumb( + String level, String message, String category, String type, String timestamp, String data); +} diff --git a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NativeModuleListLoader.java b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NativeModuleListLoader.java new file mode 100644 index 0000000000..464fcd3992 --- /dev/null +++ b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NativeModuleListLoader.java @@ -0,0 +1,19 @@ +package io.sentry.android.ndk; + +import io.sentry.protocol.DebugImage; +import org.jetbrains.annotations.Nullable; + +final class NativeModuleListLoader { + + public @Nullable DebugImage[] loadModuleList() { + return nativeLoadModuleList(); + } + + public void clearModuleList() { + nativeClearModuleList(); + } + + public static native DebugImage[] nativeLoadModuleList(); + + public static native void nativeClearModuleList(); +} diff --git a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NativeScope.java b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NativeScope.java new file mode 100644 index 0000000000..9d82f9d5c8 --- /dev/null +++ b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NativeScope.java @@ -0,0 +1,55 @@ +package io.sentry.android.ndk; + +final class NativeScope implements INativeScope { + @Override + public void setTag(String key, String value) { + nativeSetTag(key, value); + } + + @Override + public void removeTag(String key) { + nativeRemoveTag(key); + } + + @Override + public void setExtra(String key, String value) { + nativeSetExtra(key, value); + } + + @Override + public void removeExtra(String key) { + nativeRemoveExtra(key); + } + + @Override + public void setUser(String id, String email, String ipAddress, String username) { + nativeSetUser(id, email, ipAddress, username); + } + + @Override + public void removeUser() { + nativeRemoveUser(); + } + + @Override + public void addBreadcrumb( + String level, String message, String category, String type, String timestamp, String data) { + nativeAddBreadcrumb(level, message, category, type, timestamp, data); + } + + public static native void nativeSetTag(String key, String value); + + public static native void nativeRemoveTag(String key); + + public static native void nativeSetExtra(String key, String value); + + public static native void nativeRemoveExtra(String key); + + public static native void nativeSetUser( + String id, String email, String ipAddress, String username); + + public static native void nativeRemoveUser(); + + public static native void nativeAddBreadcrumb( + String level, String message, String category, String type, String timestamp, String data); +} diff --git a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NdkScopeObserver.java b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NdkScopeObserver.java index 4a4237ba08..009bba9b81 100644 --- a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NdkScopeObserver.java +++ b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/NdkScopeObserver.java @@ -5,8 +5,6 @@ import io.sentry.ScopeObserverAdapter; import io.sentry.SentryLevel; import io.sentry.SentryOptions; -import io.sentry.ndk.INativeScope; -import io.sentry.ndk.NativeScope; import io.sentry.protocol.User; import io.sentry.util.Objects; import java.util.Locale; diff --git a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/SentryNdk.java b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/SentryNdk.java index ebce1a12fd..1ddc04c524 100644 --- a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/SentryNdk.java +++ b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/SentryNdk.java @@ -1,8 +1,6 @@ package io.sentry.android.ndk; import io.sentry.android.core.SentryAndroidOptions; -import io.sentry.ndk.NativeModuleListLoader; -import io.sentry.ndk.NdkOptions; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -11,6 +9,21 @@ public final class SentryNdk { private SentryNdk() {} + static { + // On older Android versions, it was necessary to manually call "`System.loadLibrary` on all + // transitive dependencies before loading [the] main library." + // The dependencies of `libsentry.so` are currently `lib{c,m,dl,log}.so`. + // See + // https://android.googlesource.com/platform/bionic/+/master/android-changes-for-ndk-developers.md#changes-to-library-dependency-resolution + System.loadLibrary("log"); + System.loadLibrary("sentry"); + System.loadLibrary("sentry-android"); + } + + private static native void initSentryNative(@NotNull final SentryAndroidOptions options); + + private static native void shutdown(); + /** * Init the NDK integration * @@ -18,18 +31,7 @@ private SentryNdk() {} */ public static void init(@NotNull final SentryAndroidOptions options) { SentryNdkUtil.addPackage(options.getSdkVersion()); - - final @NotNull NdkOptions ndkOptions = - new NdkOptions( - options.getDsn(), - options.isDebug(), - options.getOutboxPath(), - options.getRelease(), - options.getEnvironment(), - options.getDist(), - options.getMaxBreadcrumbs(), - options.getNativeSdkName()); - io.sentry.ndk.SentryNdk.init(ndkOptions); + initSentryNative(options); // only add scope sync observer if the scope sync is enabled. if (options.isEnableScopeSync()) { @@ -41,6 +43,6 @@ public static void init(@NotNull final SentryAndroidOptions options) { /** Closes the NDK integration */ public static void close() { - io.sentry.ndk.SentryNdk.close(); + shutdown(); } } diff --git a/sentry-android-ndk/src/main/jni/sentry.c b/sentry-android-ndk/src/main/jni/sentry.c new file mode 100644 index 0000000000..d62ef56123 --- /dev/null +++ b/sentry-android-ndk/src/main/jni/sentry.c @@ -0,0 +1,494 @@ +#include +#include +#include +#include +#include + +#define ENSURE(Expr) \ + if (!(Expr)) \ + return + +#define ENSURE_OR_FAIL(Expr) \ + if (!(Expr)) \ + goto fail + +static bool get_string_into(JNIEnv *env, jstring jstr, char* buf, size_t buf_len) +{ + jsize utf_len = (*env)->GetStringUTFLength(env, jstr); + if ((size_t)utf_len >= buf_len) { + return false; + } + + jsize j_len = (*env)->GetStringLength(env, jstr); + + (*env)->GetStringUTFRegion(env, jstr, 0, j_len, buf); + if ((*env)->ExceptionCheck(env) == JNI_TRUE) { + return false; + } + + buf[utf_len] = '\0'; + return true; +} + +static char* get_string(JNIEnv *env, jstring jstr) { + char *buf = NULL; + + jsize utf_len = (*env)->GetStringUTFLength(env, jstr); + size_t buf_len = (size_t)utf_len + 1; + buf = sentry_malloc(buf_len); + ENSURE_OR_FAIL(buf); + + ENSURE_OR_FAIL(get_string_into(env, jstr, buf, buf_len)); + + return buf; + +fail: + sentry_free(buf); + + return NULL; +} + +static char *call_get_string(JNIEnv *env, jobject obj, jmethodID mid) +{ + jstring j_str = (jstring)(*env)->CallObjectMethod(env, obj, mid); + ENSURE_OR_FAIL(j_str); + char* str = get_string(env, j_str); + (*env)->DeleteLocalRef(env, j_str); + + return str; + +fail: + return NULL; +} + +JNIEXPORT void JNICALL +Java_io_sentry_android_ndk_NativeScope_nativeSetTag( + JNIEnv *env, + jclass cls, + jstring key, + jstring value) { + const char *charKey = (*env)->GetStringUTFChars(env, key, 0); + const char *charValue = (*env)->GetStringUTFChars(env, value, 0); + + sentry_set_tag(charKey, charValue); + + (*env)->ReleaseStringUTFChars(env, key, charKey); + (*env)->ReleaseStringUTFChars(env, value, charValue); +} + +JNIEXPORT void JNICALL +Java_io_sentry_android_ndk_NativeScope_nativeRemoveTag(JNIEnv *env, jclass cls, jstring key) { + const char *charKey = (*env)->GetStringUTFChars(env, key, 0); + + sentry_remove_tag(charKey); + + (*env)->ReleaseStringUTFChars(env, key, charKey); +} + +JNIEXPORT void JNICALL +Java_io_sentry_android_ndk_NativeScope_nativeSetExtra( + JNIEnv *env, + jclass cls, + jstring key, + jstring value) { + const char *charKey = (*env)->GetStringUTFChars(env, key, 0); + const char *charValue = (*env)->GetStringUTFChars(env, value, 0); + + sentry_value_t sentryValue = sentry_value_new_string(charValue); + sentry_set_extra(charKey, sentryValue); + + (*env)->ReleaseStringUTFChars(env, key, charKey); + (*env)->ReleaseStringUTFChars(env, value, charValue); +} + +JNIEXPORT void JNICALL +Java_io_sentry_android_ndk_NativeScope_nativeRemoveExtra(JNIEnv *env, jclass cls, jstring key) { + const char *charKey = (*env)->GetStringUTFChars(env, key, 0); + + sentry_remove_extra(charKey); + + (*env)->ReleaseStringUTFChars(env, key, charKey); +} + +JNIEXPORT void JNICALL +Java_io_sentry_android_ndk_NativeScope_nativeSetUser( + JNIEnv *env, + jclass cls, + jstring id, + jstring email, + jstring ipAddress, + jstring username) { + sentry_value_t user = sentry_value_new_object(); + if (id) { + const char *charId = (*env)->GetStringUTFChars(env, id, 0); + sentry_value_set_by_key(user, "id", sentry_value_new_string(charId)); + (*env)->ReleaseStringUTFChars(env, id, charId); + } + if (email) { + const char *charEmail = (*env)->GetStringUTFChars(env, email, 0); + sentry_value_set_by_key( + user, "email", sentry_value_new_string(charEmail)); + (*env)->ReleaseStringUTFChars(env, email, charEmail); + } + if (ipAddress) { + const char *charIpAddress = (*env)->GetStringUTFChars(env, ipAddress, 0); + sentry_value_set_by_key( + user, "ip_address", sentry_value_new_string(charIpAddress)); + (*env)->ReleaseStringUTFChars(env, ipAddress, charIpAddress); + } + if (username) { + const char *charUsername = (*env)->GetStringUTFChars(env, username, 0); + sentry_value_set_by_key( + user, "username", sentry_value_new_string(charUsername)); + (*env)->ReleaseStringUTFChars(env, username, charUsername); + } + sentry_set_user(user); +} + +JNIEXPORT void JNICALL +Java_io_sentry_android_ndk_NativeScope_nativeRemoveUser(JNIEnv *env, jclass cls) { + sentry_remove_user(); +} + +JNIEXPORT void JNICALL +Java_io_sentry_android_ndk_NativeScope_nativeAddBreadcrumb( + JNIEnv *env, + jclass cls, + jstring level, + jstring message, + jstring category, + jstring type, + jstring timestamp, + jstring data) { + if (!level && !message && !category && !type) { + return; + } + const char *charMessage = NULL; + if (message) { + charMessage = (*env)->GetStringUTFChars(env, message, 0); + } + const char *charType = NULL; + if (type) { + charType = (*env)->GetStringUTFChars(env, type, 0); + } + sentry_value_t crumb = sentry_value_new_breadcrumb(charType, charMessage); + + if (charMessage) { + (*env)->ReleaseStringUTFChars(env, message, charMessage); + } + if (charType) { + (*env)->ReleaseStringUTFChars(env, type, charType); + } + + if (category) { + const char *charCategory = (*env)->GetStringUTFChars(env, category, 0); + sentry_value_set_by_key( + crumb, "category", sentry_value_new_string(charCategory)); + (*env)->ReleaseStringUTFChars(env, category, charCategory); + } + if (level) { + const char *charLevel = (*env)->GetStringUTFChars(env, level, 0); + sentry_value_set_by_key( + crumb, "level", sentry_value_new_string(charLevel)); + (*env)->ReleaseStringUTFChars(env, level, charLevel); + } + + if (timestamp) { + // overwrite timestamp that is already created on sentry_value_new_breadcrumb + const char *charTimestamp = (*env)->GetStringUTFChars(env, timestamp, 0); + sentry_value_set_by_key( + crumb, "timestamp", sentry_value_new_string(charTimestamp)); + (*env)->ReleaseStringUTFChars(env, timestamp, charTimestamp); + } + + if (data) { + const char *charData = (*env)->GetStringUTFChars(env, data, 0); + + // we create an object because the Java layer parses it as a Map + sentry_value_t dataObject = sentry_value_new_object(); + sentry_value_set_by_key(dataObject, "data", sentry_value_new_string(charData)); + + sentry_value_set_by_key(crumb, "data", dataObject); + + (*env)->ReleaseStringUTFChars(env, data, charData); + } + + sentry_add_breadcrumb(crumb); +} + +static void send_envelope(sentry_envelope_t *envelope, void *data) { + const char *outbox_path = (const char *) data; + char envelope_id_str[40]; + + sentry_uuid_t envelope_id = sentry_uuid_new_v4(); + sentry_uuid_as_string(&envelope_id, envelope_id_str); + + size_t outbox_len = strlen(outbox_path); + size_t final_len = outbox_len + 42; // "/" + envelope_id_str + "\0" = 42 + char* envelope_path = sentry_malloc(final_len); + ENSURE(envelope_path); + int written = snprintf(envelope_path, final_len, "%s/%s", outbox_path, envelope_id_str); + if (written > outbox_len && written < final_len) { + sentry_envelope_write_to_file(envelope, envelope_path); + } + + sentry_free(envelope_path); + sentry_envelope_free(envelope); +} + +JNIEXPORT void JNICALL +Java_io_sentry_android_ndk_SentryNdk_initSentryNative( + JNIEnv *env, + jclass cls, + jobject sentry_sdk_options) { + jclass options_cls = (*env)->GetObjectClass(env, sentry_sdk_options); + jmethodID outbox_path_mid = (*env)->GetMethodID(env, options_cls, "getOutboxPath", + "()Ljava/lang/String;"); + jmethodID dsn_mid = (*env)->GetMethodID(env, options_cls, "getDsn", "()Ljava/lang/String;"); + jmethodID is_debug_mid = (*env)->GetMethodID(env, options_cls, "isDebug", "()Z"); + jmethodID release_mid = (*env)->GetMethodID(env, options_cls, "getRelease", + "()Ljava/lang/String;"); + jmethodID environment_mid = (*env)->GetMethodID(env, options_cls, "getEnvironment", + "()Ljava/lang/String;"); + jmethodID dist_mid = (*env)->GetMethodID(env, options_cls, "getDist", "()Ljava/lang/String;"); + jmethodID max_crumbs_mid = (*env)->GetMethodID(env, options_cls, "getMaxBreadcrumbs", "()I"); + jmethodID native_sdk_name_mid = (*env)->GetMethodID(env, options_cls, "getNativeSdkName", + "()Ljava/lang/String;"); + + (*env)->DeleteLocalRef(env, options_cls); + + char *outbox_path = NULL; + sentry_transport_t *transport = NULL; + bool transport_owns_path = false; + sentry_options_t *options = NULL; + bool options_owns_transport = false; + char *dsn_str = NULL; + char *release_str = NULL; + char *environment_str = NULL; + char *dist_str = NULL; + char *native_sdk_name_str = NULL; + + options = sentry_options_new(); + ENSURE_OR_FAIL(options); + + // session tracking is enabled by default, but the Android SDK already handles it + sentry_options_set_auto_session_tracking(options, 0); + + jboolean debug = (jboolean)(*env)->CallBooleanMethod(env, sentry_sdk_options, is_debug_mid); + sentry_options_set_debug(options, debug); + + jint max_crumbs = (jint) (*env)->CallIntMethod(env, sentry_sdk_options, max_crumbs_mid); + sentry_options_set_max_breadcrumbs(options, max_crumbs); + + outbox_path = call_get_string(env, sentry_sdk_options, outbox_path_mid); + ENSURE_OR_FAIL(outbox_path); + + transport = sentry_transport_new(send_envelope); + ENSURE_OR_FAIL(transport); + sentry_transport_set_state(transport, outbox_path); + sentry_transport_set_free_func(transport, sentry_free); + transport_owns_path = true; + + sentry_options_set_transport(options, transport); + options_owns_transport = true; + + // give sentry-native its own database path it can work with, next to the outbox + size_t outbox_len = strlen(outbox_path); + size_t final_len = outbox_len + 15; // len(".sentry-native\0") = 15 + char* database_path = sentry_malloc(final_len); + ENSURE_OR_FAIL(database_path); + strncpy(database_path, outbox_path, final_len); + char *dir = strrchr(database_path, '/'); + if (dir) + { + strncpy(dir + 1, ".sentry-native", final_len - (dir + 1 - database_path)); + } + sentry_options_set_database_path(options, database_path); + sentry_free(database_path); + + dsn_str = call_get_string(env, sentry_sdk_options, dsn_mid); + ENSURE_OR_FAIL(dsn_str); + sentry_options_set_dsn(options, dsn_str); + sentry_free(dsn_str); + + release_str = call_get_string(env, sentry_sdk_options, release_mid); + if (release_str) { + sentry_options_set_release(options, release_str); + sentry_free(release_str); + } + + environment_str = call_get_string(env, sentry_sdk_options, environment_mid); + if (environment_str) + { + sentry_options_set_environment(options, environment_str); + sentry_free(environment_str); + } + + dist_str = call_get_string(env, sentry_sdk_options, dist_mid); + if (dist_str) + { + sentry_options_set_dist(options, dist_str); + sentry_free(dist_str); + } + + native_sdk_name_str = call_get_string(env, sentry_sdk_options, native_sdk_name_mid); + if (native_sdk_name_str) { + sentry_options_set_sdk_name(options, native_sdk_name_str); + sentry_free(native_sdk_name_str); + } + + sentry_init(options); + return; + +fail: + if (!transport_owns_path) { + sentry_free(outbox_path); + } + if (!options_owns_transport) { + sentry_transport_free(transport); + } + sentry_options_free(options); +} + +JNIEXPORT void JNICALL +Java_io_sentry_android_ndk_NativeModuleListLoader_nativeClearModuleList(JNIEnv *env, jclass cls) { + sentry_clear_modulecache(); +} + +JNIEXPORT jobjectArray JNICALL +Java_io_sentry_android_ndk_NativeModuleListLoader_nativeLoadModuleList(JNIEnv *env, jclass cls) { + sentry_value_t image_list_t = sentry_get_modules_list(); + jobjectArray image_list = NULL; + + if (sentry_value_get_type(image_list_t) == SENTRY_VALUE_TYPE_LIST) { + size_t len_t = sentry_value_get_length(image_list_t); + + jclass image_class = (*env)->FindClass(env, "io/sentry/protocol/DebugImage"); + image_list = (*env)->NewObjectArray(env, len_t, image_class, NULL); + + jmethodID image_addr_method = (*env)->GetMethodID(env, image_class, "setImageAddr", + "(Ljava/lang/String;)V"); + + jmethodID image_size_method = (*env)->GetMethodID(env, image_class, "setImageSize", + "(J)V"); + + jmethodID code_file_method = (*env)->GetMethodID(env, image_class, "setCodeFile", + "(Ljava/lang/String;)V"); + + jmethodID image_addr_ctor = (*env)->GetMethodID(env, image_class, "", + "()V"); + + jmethodID type_method = (*env)->GetMethodID(env, image_class, "setType", + "(Ljava/lang/String;)V"); + + jmethodID debug_id_method = (*env)->GetMethodID(env, image_class, "setDebugId", + "(Ljava/lang/String;)V"); + + jmethodID code_id_method = (*env)->GetMethodID(env, image_class, "setCodeId", + "(Ljava/lang/String;)V"); + + jmethodID debug_file_method = (*env)->GetMethodID(env, image_class, "setDebugFile", + "(Ljava/lang/String;)V"); + + for (size_t i = 0; i < len_t; i++) { + sentry_value_t image_t = sentry_value_get_by_index(image_list_t, i); + + if (!sentry_value_is_null(image_t)) { + jobject image = (*env)->NewObject(env, image_class, image_addr_ctor); + + sentry_value_t image_addr_t = sentry_value_get_by_key(image_t, "image_addr"); + if (!sentry_value_is_null(image_addr_t)) { + + const char *value_v = sentry_value_as_string(image_addr_t); + jstring value = (*env)->NewStringUTF(env, value_v); + + (*env)->CallVoidMethod(env, image, image_addr_method, value); + + // Local refs (eg NewStringUTF) are freed automatically when the native method + // returns, but if you're iterating a large array, it's recommended to release + // manually due to allocation limits (512) on Android < 8 or OOM. + // https://developer.android.com/training/articles/perf-jni.html#local-and-global-references + (*env)->DeleteLocalRef(env, value); + } + + sentry_value_t image_size_t = sentry_value_get_by_key(image_t, "image_size"); + if (!sentry_value_is_null(image_size_t)) { + + int32_t value_v = sentry_value_as_int32(image_size_t); + jlong value = (jlong) value_v; + + (*env)->CallVoidMethod(env, image, image_size_method, value); + } + + sentry_value_t code_file_t = sentry_value_get_by_key(image_t, "code_file"); + if (!sentry_value_is_null(code_file_t)) { + + const char *value_v = sentry_value_as_string(code_file_t); + jstring value = (*env)->NewStringUTF(env, value_v); + + (*env)->CallVoidMethod(env, image, code_file_method, value); + + (*env)->DeleteLocalRef(env, value); + } + + sentry_value_t code_type_t = sentry_value_get_by_key(image_t, "type"); + if (!sentry_value_is_null(code_type_t)) { + + const char *value_v = sentry_value_as_string(code_type_t); + jstring value = (*env)->NewStringUTF(env, value_v); + + (*env)->CallVoidMethod(env, image, type_method, value); + + (*env)->DeleteLocalRef(env, value); + } + + sentry_value_t debug_id_t = sentry_value_get_by_key(image_t, "debug_id"); + if (!sentry_value_is_null(code_type_t)) { + + const char *value_v = sentry_value_as_string(debug_id_t); + jstring value = (*env)->NewStringUTF(env, value_v); + + (*env)->CallVoidMethod(env, image, debug_id_method, value); + + (*env)->DeleteLocalRef(env, value); + } + + sentry_value_t code_id_t = sentry_value_get_by_key(image_t, "code_id"); + if (!sentry_value_is_null(code_id_t)) { + + const char *value_v = sentry_value_as_string(code_id_t); + jstring value = (*env)->NewStringUTF(env, value_v); + + (*env)->CallVoidMethod(env, image, code_id_method, value); + + (*env)->DeleteLocalRef(env, value); + } + + // not needed on Android, but keeping for forward compatibility + sentry_value_t debug_file_t = sentry_value_get_by_key(image_t, "debug_file"); + if (!sentry_value_is_null(debug_file_t)) { + + const char *value_v = sentry_value_as_string(debug_file_t); + jstring value = (*env)->NewStringUTF(env, value_v); + + (*env)->CallVoidMethod(env, image, debug_file_method, value); + + (*env)->DeleteLocalRef(env, value); + } + + (*env)->SetObjectArrayElement(env, image_list, i, image); + + (*env)->DeleteLocalRef(env, image); + } + } + + sentry_value_decref(image_list_t); + } + + return image_list; +} + +JNIEXPORT void JNICALL +Java_io_sentry_android_ndk_SentryNdk_shutdown(JNIEnv *env, jclass cls) { + sentry_close(); +} diff --git a/sentry-android-ndk/src/test/java/io/sentry/android/ndk/DebugImagesLoaderTest.kt b/sentry-android-ndk/src/test/java/io/sentry/android/ndk/DebugImagesLoaderTest.kt index 927ce98c3b..db584c814f 100644 --- a/sentry-android-ndk/src/test/java/io/sentry/android/ndk/DebugImagesLoaderTest.kt +++ b/sentry-android-ndk/src/test/java/io/sentry/android/ndk/DebugImagesLoaderTest.kt @@ -1,10 +1,11 @@ package io.sentry.android.ndk import io.sentry.android.core.SentryAndroidOptions -import io.sentry.ndk.NativeModuleListLoader +import io.sentry.protocol.DebugImage import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever +import java.lang.RuntimeException import kotlin.test.Test import kotlin.test.assertNotNull import kotlin.test.assertNull @@ -37,7 +38,7 @@ class DebugImagesLoaderTest { whenever(fixture.nativeLoader.loadModuleList()).thenReturn(arrayOf()) assertNotNull(sut.loadDebugImages()) - whenever(fixture.nativeLoader.loadModuleList()).thenReturn(arrayOf(io.sentry.ndk.DebugImage())) + whenever(fixture.nativeLoader.loadModuleList()).thenReturn(arrayOf(DebugImage())) assertTrue(sut.loadDebugImages()!!.isEmpty()) } diff --git a/sentry-android-ndk/src/test/java/io/sentry/android/ndk/NdkScopeObserverTest.kt b/sentry-android-ndk/src/test/java/io/sentry/android/ndk/NdkScopeObserverTest.kt index ad523a883e..335a7679e1 100644 --- a/sentry-android-ndk/src/test/java/io/sentry/android/ndk/NdkScopeObserverTest.kt +++ b/sentry-android-ndk/src/test/java/io/sentry/android/ndk/NdkScopeObserverTest.kt @@ -5,7 +5,6 @@ import io.sentry.DateUtils import io.sentry.JsonSerializer import io.sentry.SentryLevel import io.sentry.SentryOptions -import io.sentry.ndk.INativeScope import io.sentry.protocol.User import org.mockito.kotlin.eq import org.mockito.kotlin.mock diff --git a/sentry-android-okhttp/api/sentry-android-okhttp.api b/sentry-android-okhttp/api/sentry-android-okhttp.api new file mode 100644 index 0000000000..a1ad9114a2 --- /dev/null +++ b/sentry-android-okhttp/api/sentry-android-okhttp.api @@ -0,0 +1,62 @@ +public final class io/sentry/android/okhttp/BuildConfig { + public static final field BUILD_TYPE Ljava/lang/String; + public static final field DEBUG Z + public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String; + public static final field VERSION_NAME Ljava/lang/String; + public fun ()V +} + +public final class io/sentry/android/okhttp/SentryOkHttpEventListener : okhttp3/EventListener { + public fun ()V + public fun (Lio/sentry/IHub;Lkotlin/jvm/functions/Function1;)V + public synthetic fun (Lio/sentry/IHub;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IHub;Lokhttp3/EventListener$Factory;)V + public synthetic fun (Lio/sentry/IHub;Lokhttp3/EventListener$Factory;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IHub;Lokhttp3/EventListener;)V + public synthetic fun (Lio/sentry/IHub;Lokhttp3/EventListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lokhttp3/EventListener$Factory;)V + public fun (Lokhttp3/EventListener;)V + public fun cacheConditionalHit (Lokhttp3/Call;Lokhttp3/Response;)V + public fun cacheHit (Lokhttp3/Call;Lokhttp3/Response;)V + public fun cacheMiss (Lokhttp3/Call;)V + public fun callEnd (Lokhttp3/Call;)V + public fun callFailed (Lokhttp3/Call;Ljava/io/IOException;)V + public fun callStart (Lokhttp3/Call;)V + public fun canceled (Lokhttp3/Call;)V + public fun connectEnd (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;Lokhttp3/Protocol;)V + public fun connectFailed (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;Lokhttp3/Protocol;Ljava/io/IOException;)V + public fun connectStart (Lokhttp3/Call;Ljava/net/InetSocketAddress;Ljava/net/Proxy;)V + public fun connectionAcquired (Lokhttp3/Call;Lokhttp3/Connection;)V + public fun connectionReleased (Lokhttp3/Call;Lokhttp3/Connection;)V + public fun dnsEnd (Lokhttp3/Call;Ljava/lang/String;Ljava/util/List;)V + public fun dnsStart (Lokhttp3/Call;Ljava/lang/String;)V + public fun proxySelectEnd (Lokhttp3/Call;Lokhttp3/HttpUrl;Ljava/util/List;)V + public fun proxySelectStart (Lokhttp3/Call;Lokhttp3/HttpUrl;)V + public fun requestBodyEnd (Lokhttp3/Call;J)V + public fun requestBodyStart (Lokhttp3/Call;)V + public fun requestFailed (Lokhttp3/Call;Ljava/io/IOException;)V + public fun requestHeadersEnd (Lokhttp3/Call;Lokhttp3/Request;)V + public fun requestHeadersStart (Lokhttp3/Call;)V + public fun responseBodyEnd (Lokhttp3/Call;J)V + public fun responseBodyStart (Lokhttp3/Call;)V + public fun responseFailed (Lokhttp3/Call;Ljava/io/IOException;)V + public fun responseHeadersEnd (Lokhttp3/Call;Lokhttp3/Response;)V + public fun responseHeadersStart (Lokhttp3/Call;)V + public fun satisfactionFailure (Lokhttp3/Call;Lokhttp3/Response;)V + public fun secureConnectEnd (Lokhttp3/Call;Lokhttp3/Handshake;)V + public fun secureConnectStart (Lokhttp3/Call;)V +} + +public final class io/sentry/android/okhttp/SentryOkHttpInterceptor : okhttp3/Interceptor { + public fun ()V + public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IHub;Lio/sentry/android/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;ZLjava/util/List;Ljava/util/List;)V + public synthetic fun (Lio/sentry/IHub;Lio/sentry/android/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;ZLjava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/android/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;)V + public fun intercept (Lokhttp3/Interceptor$Chain;)Lokhttp3/Response; +} + +public abstract interface class io/sentry/android/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback { + public abstract fun execute (Lio/sentry/ISpan;Lokhttp3/Request;Lokhttp3/Response;)Lio/sentry/ISpan; +} + diff --git a/sentry-android-okhttp/build.gradle.kts b/sentry-android-okhttp/build.gradle.kts new file mode 100644 index 0000000000..f3eaa59303 --- /dev/null +++ b/sentry-android-okhttp/build.gradle.kts @@ -0,0 +1,83 @@ +import io.gitlab.arturbosch.detekt.Detekt +import org.jetbrains.kotlin.config.KotlinCompilerVersion + +plugins { + id("com.android.library") + kotlin("android") + jacoco + id(Config.QualityPlugins.jacocoAndroid) + id(Config.QualityPlugins.gradleVersions) + id(Config.QualityPlugins.detektPlugin) +} + +android { + compileSdk = Config.Android.compileSdkVersion + namespace = "io.sentry.android.okhttp" + + defaultConfig { + targetSdk = Config.Android.targetSdkVersion + minSdk = Config.Android.minSdkVersionOkHttp + + // for AGP 4.1 + buildConfigField("String", "VERSION_NAME", "\"${project.version}\"") + } + + buildTypes { + getByName("debug") + getByName("release") + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + kotlinOptions.languageVersion = Config.kotlinCompatibleLanguageVersion + } + + testOptions { + animationsDisabled = true + unitTests.apply { + isReturnDefaultValues = true + isIncludeAndroidResources = true + } + } + + lint { + warningsAsErrors = true + checkDependencies = true + + // We run a full lint analysis as build part in CI, so skip vital checks for assemble tasks. + checkReleaseBuilds = false + } + + variantFilter { + if (Config.Android.shouldSkipDebugVariant(buildType.name)) { + ignore = true + } + } +} + +kotlin { + explicitApi() +} + +dependencies { + api(projects.sentry) + api(projects.sentryOkhttp) + + compileOnly(Config.Libs.okhttp) + + implementation(kotlin(Config.kotlinStdLib, KotlinCompilerVersion.VERSION)) + + // tests + testImplementation(projects.sentryTestSupport) + testImplementation(Config.Libs.okhttp) + testImplementation(Config.TestLibs.kotlinTestJunit) + testImplementation(Config.TestLibs.androidxJunit) + testImplementation(Config.TestLibs.mockitoKotlin) + testImplementation(Config.TestLibs.mockitoInline) + testImplementation(Config.TestLibs.mockWebserver) +} + +tasks.withType { + // Target version of the generated JVM bytecode. It is used for type resolution. + jvmTarget = JavaVersion.VERSION_1_8.toString() +} diff --git a/sentry-android-okhttp/proguard-rules.pro b/sentry-android-okhttp/proguard-rules.pro new file mode 100644 index 0000000000..3f9ea4feb2 --- /dev/null +++ b/sentry-android-okhttp/proguard-rules.pro @@ -0,0 +1,13 @@ +##---------------Begin: proguard configuration for OkHttp ---------- + +# To ensure that stack traces is unambiguous +# https://developer.android.com/studio/build/shrink-code#decode-stack-trace +-keepattributes LineNumberTable,SourceFile + +# https://square.github.io/okhttp/features/r8_proguard/ +# If you use OkHttp as a dependency in an Android project which uses R8 as a default compiler you +# don’t have to do anything. The specific rules are already bundled into the JAR which can +# be interpreted by R8 automatically. +# https://raw.githubusercontent.com/square/okhttp/master/okhttp/src/jvmMain/resources/META-INF/proguard/okhttp3.pro + +##---------------End: proguard configuration for OkHttp ---------- diff --git a/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpEventListener.kt b/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpEventListener.kt new file mode 100644 index 0000000000..7ca5313d8f --- /dev/null +++ b/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpEventListener.kt @@ -0,0 +1,201 @@ +package io.sentry.android.okhttp + +import io.sentry.HubAdapter +import io.sentry.IHub +import okhttp3.Call +import okhttp3.Connection +import okhttp3.EventListener +import okhttp3.Handshake +import okhttp3.HttpUrl +import okhttp3.Protocol +import okhttp3.Request +import okhttp3.Response +import java.io.IOException +import java.net.InetAddress +import java.net.InetSocketAddress +import java.net.Proxy + +/** + * Logs network performance event metrics to Sentry + * + * Usage - add instance of [SentryOkHttpEventListener] in [okhttp3.OkHttpClient.Builder.eventListener] + * + * ``` + * val client = OkHttpClient.Builder() + * .eventListener(SentryOkHttpEventListener()) + * .addInterceptor(SentryOkHttpInterceptor()) + * .build() + * ``` + * + * If you already use a [okhttp3.EventListener], you can pass it in the constructor. + * + * ``` + * val client = OkHttpClient.Builder() + * .eventListener(SentryOkHttpEventListener(myEventListener)) + * .addInterceptor(SentryOkHttpInterceptor()) + * .build() + * ``` + */ +@Deprecated( + "Use SentryOkHttpEventListener from sentry-okhttp instead", + ReplaceWith("SentryOkHttpEventListener", "io.sentry.okhttp.SentryOkHttpEventListener") +) +@Suppress("TooManyFunctions") +class SentryOkHttpEventListener( + hub: IHub = HubAdapter.getInstance(), + originalEventListenerCreator: ((call: Call) -> EventListener)? = null +) : EventListener() { + constructor() : this( + HubAdapter.getInstance(), + originalEventListenerCreator = null + ) + + constructor(originalEventListener: EventListener) : this( + HubAdapter.getInstance(), + originalEventListenerCreator = { originalEventListener } + ) + + constructor(originalEventListenerFactory: Factory) : this( + HubAdapter.getInstance(), + originalEventListenerCreator = { originalEventListenerFactory.create(it) } + ) + + constructor(hub: IHub = HubAdapter.getInstance(), originalEventListener: EventListener) : this( + hub, + originalEventListenerCreator = { originalEventListener } + ) + + constructor(hub: IHub = HubAdapter.getInstance(), originalEventListenerFactory: Factory) : this( + hub, + originalEventListenerCreator = { originalEventListenerFactory.create(it) } + ) + + private val delegate = io.sentry.okhttp.SentryOkHttpEventListener(hub, originalEventListenerCreator) + + override fun cacheConditionalHit(call: Call, cachedResponse: Response) { + delegate.cacheConditionalHit(call, cachedResponse) + } + + override fun cacheHit(call: Call, response: Response) { + delegate.cacheHit(call, response) + } + + override fun cacheMiss(call: Call) { + delegate.cacheMiss(call) + } + + override fun callEnd(call: Call) { + delegate.callEnd(call) + } + + override fun callFailed(call: Call, ioe: IOException) { + delegate.callFailed(call, ioe) + } + + override fun callStart(call: Call) { + delegate.callStart(call) + } + + override fun canceled(call: Call) { + delegate.canceled(call) + } + + override fun connectEnd( + call: Call, + inetSocketAddress: InetSocketAddress, + proxy: Proxy, + protocol: Protocol? + ) { + delegate.connectEnd(call, inetSocketAddress, proxy, protocol) + } + + override fun connectFailed( + call: Call, + inetSocketAddress: InetSocketAddress, + proxy: Proxy, + protocol: Protocol?, + ioe: IOException + ) { + delegate.connectFailed(call, inetSocketAddress, proxy, protocol, ioe) + } + + override fun connectStart(call: Call, inetSocketAddress: InetSocketAddress, proxy: Proxy) { + delegate.connectStart(call, inetSocketAddress, proxy) + } + + override fun connectionAcquired(call: Call, connection: Connection) { + delegate.connectionAcquired(call, connection) + } + + override fun connectionReleased(call: Call, connection: Connection) { + delegate.connectionReleased(call, connection) + } + + override fun dnsEnd(call: Call, domainName: String, inetAddressList: List) { + delegate.dnsEnd(call, domainName, inetAddressList) + } + + override fun dnsStart(call: Call, domainName: String) { + delegate.dnsStart(call, domainName) + } + + override fun proxySelectEnd(call: Call, url: HttpUrl, proxies: List) { + delegate.proxySelectEnd(call, url, proxies) + } + + override fun proxySelectStart(call: Call, url: HttpUrl) { + delegate.proxySelectStart(call, url) + } + + override fun requestBodyEnd(call: Call, byteCount: Long) { + delegate.requestBodyEnd(call, byteCount) + } + + override fun requestBodyStart(call: Call) { + delegate.requestBodyStart(call) + } + + override fun requestFailed(call: Call, ioe: IOException) { + delegate.requestFailed(call, ioe) + } + + override fun requestHeadersEnd(call: Call, request: Request) { + delegate.requestHeadersEnd(call, request) + } + + override fun requestHeadersStart(call: Call) { + delegate.requestHeadersStart(call) + } + + override fun responseBodyEnd(call: Call, byteCount: Long) { + delegate.responseBodyEnd(call, byteCount) + } + + override fun responseBodyStart(call: Call) { + delegate.responseBodyStart(call) + } + + override fun responseFailed(call: Call, ioe: IOException) { + delegate.responseFailed(call, ioe) + } + + override fun responseHeadersEnd(call: Call, response: Response) { + delegate.responseHeadersEnd(call, response) + } + + override fun responseHeadersStart(call: Call) { + delegate.responseHeadersStart(call) + } + + override fun satisfactionFailure(call: Call, response: Response) { + delegate.satisfactionFailure(call, response) + } + + override fun secureConnectEnd(call: Call, handshake: Handshake?) { + delegate.secureConnectEnd(call, handshake) + } + + override fun secureConnectStart(call: Call) { + delegate.secureConnectStart(call) + } +} diff --git a/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpInterceptor.kt b/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpInterceptor.kt new file mode 100644 index 0000000000..28c242c82c --- /dev/null +++ b/sentry-android-okhttp/src/main/java/io/sentry/android/okhttp/SentryOkHttpInterceptor.kt @@ -0,0 +1,79 @@ +package io.sentry.android.okhttp + +import io.sentry.HttpStatusCodeRange +import io.sentry.HubAdapter +import io.sentry.IHub +import io.sentry.ISpan +import io.sentry.SentryIntegrationPackageStorage +import io.sentry.SentryOptions.DEFAULT_PROPAGATION_TARGETS +import io.sentry.android.okhttp.SentryOkHttpInterceptor.BeforeSpanCallback +import io.sentry.util.IntegrationUtils.addIntegrationToSdkVersion +import okhttp3.Interceptor +import okhttp3.Request +import okhttp3.Response + +/** + * The Sentry's [SentryOkHttpInterceptor], it will automatically add a breadcrumb and start a span + * out of the active span bound to the scope for each HTTP Request. + * If [captureFailedRequests] is enabled, the SDK will capture HTTP Client errors as well. + * + * @param hub The [IHub], internal and only used for testing. + * @param beforeSpan The [ISpan] can be customized or dropped with the [BeforeSpanCallback]. + * @param captureFailedRequests The SDK will only capture HTTP Client errors if it is enabled, + * Defaults to true. + * @param failedRequestStatusCodes The SDK will only capture HTTP Client errors if the HTTP Response + * status code is within the defined ranges. + * @param failedRequestTargets The SDK will only capture HTTP Client errors if the HTTP Request URL + * is a match for any of the defined targets. + */ +@Deprecated( + "Use SentryOkHttpInterceptor from sentry-okhttp instead", + ReplaceWith("SentryOkHttpInterceptor", "io.sentry.okhttp.SentryOkHttpInterceptor") +) +class SentryOkHttpInterceptor( + private val hub: IHub = HubAdapter.getInstance(), + private val beforeSpan: BeforeSpanCallback? = null, + private val captureFailedRequests: Boolean = true, + private val failedRequestStatusCodes: List = listOf( + HttpStatusCodeRange(HttpStatusCodeRange.DEFAULT_MIN, HttpStatusCodeRange.DEFAULT_MAX) + ), + private val failedRequestTargets: List = listOf(DEFAULT_PROPAGATION_TARGETS) +) : Interceptor by io.sentry.okhttp.SentryOkHttpInterceptor( + hub, + { span, request, response -> + beforeSpan ?: return@SentryOkHttpInterceptor span + beforeSpan.execute(span, request, response) + }, + captureFailedRequests, + failedRequestStatusCodes, + failedRequestTargets +) { + + constructor() : this(HubAdapter.getInstance()) + constructor(hub: IHub) : this(hub, null) + constructor(beforeSpan: BeforeSpanCallback) : this(HubAdapter.getInstance(), beforeSpan) + + init { + addIntegrationToSdkVersion(javaClass) + SentryIntegrationPackageStorage.getInstance() + .addPackage("maven:io.sentry:sentry-android-okhttp", BuildConfig.VERSION_NAME) + } + + /** + * The BeforeSpan callback + */ + @Deprecated( + "Use BeforeSpanCallback from sentry-okhttp instead", + ReplaceWith("BeforeSpanCallback", "io.sentry.okhttp.SentryOkHttpInterceptor.BeforeSpanCallback") + ) + fun interface BeforeSpanCallback { + /** + * Mutates or drops span before being added + * + * @param span the span to mutate or drop + * @param request the HTTP request executed by okHttp + * @param response the HTTP response received by okHttp + */ + fun execute(span: ISpan, request: Request, response: Response?): ISpan? + } +} diff --git a/sentry-android-okhttp/src/main/res/values/public.xml b/sentry-android-okhttp/src/main/res/values/public.xml new file mode 100644 index 0000000000..379be515be --- /dev/null +++ b/sentry-android-okhttp/src/main/res/values/public.xml @@ -0,0 +1,4 @@ + + + + diff --git a/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SQLiteSpanManager.kt b/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SQLiteSpanManager.kt index 2e39bf76ec..3bfa855d53 100644 --- a/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SQLiteSpanManager.kt +++ b/sentry-android-sqlite/src/main/java/io/sentry/android/sqlite/SQLiteSpanManager.kt @@ -1,8 +1,8 @@ package io.sentry.android.sqlite import android.database.SQLException -import io.sentry.IScopes -import io.sentry.ScopesAdapter +import io.sentry.HubAdapter +import io.sentry.IHub import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryStackTraceFactory import io.sentry.SpanDataConvention @@ -11,10 +11,10 @@ import io.sentry.SpanStatus private const val TRACE_ORIGIN = "auto.db.sqlite" internal class SQLiteSpanManager( - private val scopes: IScopes = ScopesAdapter.getInstance(), + private val hub: IHub = HubAdapter.getInstance(), private val databaseName: String? = null ) { - private val stackTraceFactory = SentryStackTraceFactory(scopes.options) + private val stackTraceFactory = SentryStackTraceFactory(hub.options) init { SentryIntegrationPackageStorage.getInstance().addIntegration("SQLite") @@ -30,7 +30,7 @@ internal class SQLiteSpanManager( @Suppress("TooGenericExceptionCaught") @Throws(SQLException::class) fun performSql(sql: String, operation: () -> T): T { - val span = scopes.span?.startChild("db.sql.query", sql) + val span = hub.span?.startChild("db.sql.query", sql) span?.spanContext?.origin = TRACE_ORIGIN return try { val result = operation() @@ -42,7 +42,7 @@ internal class SQLiteSpanManager( throw e } finally { span?.apply { - val isMainThread: Boolean = scopes.options.mainThreadChecker.isMainThread + val isMainThread: Boolean = hub.options.mainThreadChecker.isMainThread setData(SpanDataConvention.BLOCKED_MAIN_THREAD_KEY, isMainThread) if (isMainThread) { setData(SpanDataConvention.CALL_STACK_KEY, stackTraceFactory.inAppCallStack) diff --git a/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SQLiteSpanManagerTest.kt b/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SQLiteSpanManagerTest.kt index 9265cd260a..e2fa0c2e4d 100644 --- a/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SQLiteSpanManagerTest.kt +++ b/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SQLiteSpanManagerTest.kt @@ -1,7 +1,7 @@ package io.sentry.android.sqlite import android.database.SQLException -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -22,7 +22,7 @@ import kotlin.test.assertTrue class SQLiteSpanManagerTest { private class Fixture { - private val scopes = mock() + private val hub = mock() lateinit var sentryTracer: SentryTracer lateinit var options: SentryOptions @@ -30,13 +30,13 @@ class SQLiteSpanManagerTest { options = SentryOptions().apply { dsn = "https://key@sentry.io/proj" } - whenever(scopes.options).thenReturn(options) - sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) + whenever(hub.options).thenReturn(options) + sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) if (isSpanActive) { - whenever(scopes.span).thenReturn(sentryTracer) + whenever(hub.span).thenReturn(sentryTracer) } - return SQLiteSpanManager(scopes, databaseName) + return SQLiteSpanManager(hub, databaseName) } } diff --git a/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentrySupportSQLiteDatabaseTest.kt b/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentrySupportSQLiteDatabaseTest.kt index 99e1d5f4a0..cf22c3b0ec 100644 --- a/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentrySupportSQLiteDatabaseTest.kt +++ b/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentrySupportSQLiteDatabaseTest.kt @@ -3,7 +3,7 @@ package io.sentry.android.sqlite import android.database.Cursor import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteQuery -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ISpan import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -23,8 +23,8 @@ import kotlin.test.assertTrue class SentrySupportSQLiteDatabaseTest { private class Fixture { - private val scopes = mock() - private val spanManager = SQLiteSpanManager(scopes) + private val hub = mock() + private val spanManager = SQLiteSpanManager(hub) val mockDatabase = mock() lateinit var sentryTracer: SentryTracer lateinit var options: SentryOptions @@ -37,11 +37,11 @@ class SentrySupportSQLiteDatabaseTest { options = SentryOptions().apply { dsn = "https://key@sentry.io/proj" } - whenever(scopes.options).thenReturn(options) - sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) + whenever(hub.options).thenReturn(options) + sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) if (isSpanActive) { - whenever(scopes.span).thenReturn(sentryTracer) + whenever(hub.span).thenReturn(sentryTracer) } return SentrySupportSQLiteDatabase(mockDatabase, spanManager) diff --git a/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentrySupportSQLiteStatementTest.kt b/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentrySupportSQLiteStatementTest.kt index 4b6292bd27..9078ba8b08 100644 --- a/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentrySupportSQLiteStatementTest.kt +++ b/sentry-android-sqlite/src/test/java/io/sentry/android/sqlite/SentrySupportSQLiteStatementTest.kt @@ -1,7 +1,7 @@ package io.sentry.android.sqlite import androidx.sqlite.db.SupportSQLiteStatement -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ISpan import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -18,8 +18,8 @@ import kotlin.test.assertTrue class SentrySupportSQLiteStatementTest { private class Fixture { - private val scopes = mock() - private val spanManager = SQLiteSpanManager(scopes) + private val hub = mock() + private val spanManager = SQLiteSpanManager(hub) val mockStatement = mock() lateinit var sentryTracer: SentryTracer lateinit var options: SentryOptions @@ -28,11 +28,11 @@ class SentrySupportSQLiteStatementTest { options = SentryOptions().apply { dsn = "https://key@sentry.io/proj" } - whenever(scopes.options).thenReturn(options) - sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) + whenever(hub.options).thenReturn(options) + sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) if (isSpanActive) { - whenever(scopes.span).thenReturn(sentryTracer) + whenever(hub.span).thenReturn(sentryTracer) } return SentrySupportSQLiteStatement(mockStatement, spanManager, sql) } diff --git a/sentry-android-timber/api/sentry-android-timber.api b/sentry-android-timber/api/sentry-android-timber.api index 2d71f67570..808e91bf10 100644 --- a/sentry-android-timber/api/sentry-android-timber.api +++ b/sentry-android-timber/api/sentry-android-timber.api @@ -14,11 +14,11 @@ public final class io/sentry/android/timber/SentryTimberIntegration : io/sentry/ public fun close ()V public final fun getMinBreadcrumbLevel ()Lio/sentry/SentryLevel; public final fun getMinEventLevel ()Lio/sentry/SentryLevel; - public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/android/timber/SentryTimberTree : timber/log/Timber$Tree { - public fun (Lio/sentry/IScopes;Lio/sentry/SentryLevel;Lio/sentry/SentryLevel;)V + public fun (Lio/sentry/IHub;Lio/sentry/SentryLevel;Lio/sentry/SentryLevel;)V public fun d (Ljava/lang/String;[Ljava/lang/Object;)V public fun d (Ljava/lang/Throwable;)V public fun d (Ljava/lang/Throwable;Ljava/lang/String;[Ljava/lang/Object;)V diff --git a/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberIntegration.kt b/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberIntegration.kt index 334146a218..d043faa5f6 100644 --- a/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberIntegration.kt +++ b/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberIntegration.kt @@ -1,7 +1,7 @@ package io.sentry.android.timber +import io.sentry.IHub import io.sentry.ILogger -import io.sentry.IScopes import io.sentry.Integration import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryLevel @@ -21,10 +21,10 @@ class SentryTimberIntegration( private lateinit var tree: SentryTimberTree private lateinit var logger: ILogger - override fun register(scopes: IScopes, options: SentryOptions) { + override fun register(hub: IHub, options: SentryOptions) { logger = options.logger - tree = SentryTimberTree(scopes, minEventLevel, minBreadcrumbLevel) + tree = SentryTimberTree(hub, minEventLevel, minBreadcrumbLevel) Timber.plant(tree) logger.log(SentryLevel.DEBUG, "SentryTimberIntegration installed.") diff --git a/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberTree.kt b/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberTree.kt index dddab75133..f3a0f599a9 100644 --- a/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberTree.kt +++ b/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberTree.kt @@ -2,7 +2,7 @@ package io.sentry.android.timber import android.util.Log import io.sentry.Breadcrumb -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryEvent import io.sentry.SentryLevel import io.sentry.protocol.Message @@ -13,7 +13,7 @@ import timber.log.Timber */ @Suppress("TooManyFunctions") // we have to override all methods to be able to tweak logging class SentryTimberTree( - private val scopes: IScopes, + private val hub: IHub, private val minEventLevel: SentryLevel, private val minBreadcrumbLevel: SentryLevel ) : Timber.Tree() { @@ -269,7 +269,7 @@ class SentryTimberTree( logger = "Timber" } - scopes.captureEvent(sentryEvent) + hub.captureEvent(sentryEvent) } } @@ -296,7 +296,7 @@ class SentryTimberTree( else -> null } - breadCrumb?.let { scopes.addBreadcrumb(it) } + breadCrumb?.let { hub.addBreadcrumb(it) } } } diff --git a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberIntegrationTest.kt b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberIntegrationTest.kt index 8bb85aa085..a57853e059 100644 --- a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberIntegrationTest.kt +++ b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberIntegrationTest.kt @@ -1,6 +1,6 @@ package io.sentry.android.timber -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryLevel import io.sentry.SentryOptions import io.sentry.protocol.SdkVersion @@ -16,7 +16,7 @@ import kotlin.test.assertTrue class SentryTimberIntegrationTest { private class Fixture { - val scopes = mock() + val hub = mock() val options = SentryOptions().apply { sdkVersion = SdkVersion("test", "1.2.3") } @@ -41,7 +41,7 @@ class SentryTimberIntegrationTest { @Test fun `Integrations plants a tree into Timber on register`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertEquals(1, Timber.treeCount()) @@ -53,16 +53,16 @@ class SentryTimberIntegrationTest { @Test fun `Integrations plants the SentryTimberTree tree`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) Timber.e(Throwable()) - verify(fixture.scopes).captureEvent(any()) + verify(fixture.hub).captureEvent(any()) } @Test fun `Integrations removes a tree from Timber on close integration`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertEquals(1, Timber.treeCount()) @@ -84,7 +84,7 @@ class SentryTimberIntegrationTest { minEventLevel = SentryLevel.INFO, minBreadcrumbLevel = SentryLevel.DEBUG ) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertEquals(sut.minEventLevel, SentryLevel.INFO) assertEquals(sut.minBreadcrumbLevel, SentryLevel.DEBUG) @@ -93,7 +93,7 @@ class SentryTimberIntegrationTest { @Test fun `Integration adds itself to the package list`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertTrue( fixture.options.sdkVersion!!.packageSet.any { @@ -106,7 +106,7 @@ class SentryTimberIntegrationTest { @Test fun `Integration adds itself to the integration list`() { val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertTrue( fixture.options.sdkVersion!!.integrationSet.contains("Timber") diff --git a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt index 2ab7ff64db..3d82b139ec 100644 --- a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt +++ b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberTreeTest.kt @@ -1,7 +1,7 @@ package io.sentry.android.timber import io.sentry.Breadcrumb -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryLevel import io.sentry.getExc import org.mockito.kotlin.any @@ -19,13 +19,13 @@ import kotlin.test.assertNull class SentryTimberTreeTest { private class Fixture { - val scopes = mock() + val hub = mock() fun getSut( minEventLevel: SentryLevel = SentryLevel.ERROR, minBreadcrumbLevel: SentryLevel = SentryLevel.INFO ): SentryTimberTree { - return SentryTimberTree(scopes, minEventLevel, minBreadcrumbLevel) + return SentryTimberTree(hub, minEventLevel, minBreadcrumbLevel) } } @@ -40,28 +40,28 @@ class SentryTimberTreeTest { fun `Tree captures an event if min level is equal`() { val sut = fixture.getSut() sut.e(Throwable()) - verify(fixture.scopes).captureEvent(any()) + verify(fixture.hub).captureEvent(any()) } @Test fun `Tree captures an event if min level is higher`() { val sut = fixture.getSut() sut.wtf(Throwable()) - verify(fixture.scopes).captureEvent(any()) + verify(fixture.hub).captureEvent(any()) } @Test fun `Tree won't capture an event if min level is lower`() { val sut = fixture.getSut() sut.d(Throwable()) - verify(fixture.scopes, never()).captureEvent(any()) + verify(fixture.hub, never()).captureEvent(any()) } @Test fun `Tree captures debug level event`() { val sut = fixture.getSut(SentryLevel.DEBUG) sut.d(Throwable()) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { assertEquals(SentryLevel.DEBUG, it.level) } @@ -72,7 +72,7 @@ class SentryTimberTreeTest { fun `Tree captures info level event`() { val sut = fixture.getSut(SentryLevel.DEBUG) sut.i(Throwable()) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { assertEquals(SentryLevel.INFO, it.level) } @@ -83,7 +83,7 @@ class SentryTimberTreeTest { fun `Tree captures warning level event`() { val sut = fixture.getSut(SentryLevel.DEBUG) sut.w(Throwable()) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { assertEquals(SentryLevel.WARNING, it.level) } @@ -94,7 +94,7 @@ class SentryTimberTreeTest { fun `Tree captures error level event`() { val sut = fixture.getSut(SentryLevel.DEBUG) sut.e(Throwable()) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { assertEquals(SentryLevel.ERROR, it.level) } @@ -105,7 +105,7 @@ class SentryTimberTreeTest { fun `Tree captures fatal level event`() { val sut = fixture.getSut(SentryLevel.DEBUG) sut.wtf(Throwable()) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { assertEquals(SentryLevel.FATAL, it.level) } @@ -116,7 +116,7 @@ class SentryTimberTreeTest { fun `Tree captures unknown as debug level event`() { val sut = fixture.getSut(SentryLevel.DEBUG) sut.log(15, Throwable()) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { assertEquals(SentryLevel.DEBUG, it.level) } @@ -128,7 +128,7 @@ class SentryTimberTreeTest { val sut = fixture.getSut() val throwable = Throwable() sut.e(throwable) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { assertEquals(throwable, it.getExc()) } @@ -139,7 +139,7 @@ class SentryTimberTreeTest { fun `Tree captures an event without an exception`() { val sut = fixture.getSut() sut.e("message") - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { assertNull(it.getExc()) } @@ -150,7 +150,7 @@ class SentryTimberTreeTest { fun `Tree captures an event and sets Timber as a logger`() { val sut = fixture.getSut() sut.e("message") - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { assertEquals("Timber", it.logger) } @@ -164,7 +164,7 @@ class SentryTimberTreeTest { // only available thru static class Timber.tag("tag") Timber.e("message") - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { assertEquals("tag", it.getTag("TimberTag")) } @@ -176,7 +176,7 @@ class SentryTimberTreeTest { val sut = fixture.getSut() Timber.plant(sut) Timber.e("message") - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { assertNull(it.getTag("TimberTag")) } @@ -187,7 +187,7 @@ class SentryTimberTreeTest { fun `Tree captures an event with given message`() { val sut = fixture.getSut() sut.e("message") - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { assertNotNull(it.message) { message -> assertEquals("message", message.message) @@ -200,7 +200,7 @@ class SentryTimberTreeTest { fun `Tree captures an event with formatted message and arguments, when provided`() { val sut = fixture.getSut() sut.e("test count: %d", 32) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { assertNotNull(it.message) { message -> assertEquals("test count: %d", message.message) @@ -216,7 +216,7 @@ class SentryTimberTreeTest { val sut = fixture.getSut() sut.e("test count: %d", 32) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("test count: 32", it.message) } @@ -227,28 +227,28 @@ class SentryTimberTreeTest { fun `Tree adds a breadcrumb if min level is equal`() { val sut = fixture.getSut() sut.i(Throwable("test")) - verify(fixture.scopes).addBreadcrumb(any()) + verify(fixture.hub).addBreadcrumb(any()) } @Test fun `Tree adds a breadcrumb if min level is higher`() { val sut = fixture.getSut() sut.e(Throwable("test")) - verify(fixture.scopes).addBreadcrumb(any()) + verify(fixture.hub).addBreadcrumb(any()) } @Test fun `Tree won't add a breadcrumb if min level is lower`() { val sut = fixture.getSut(minBreadcrumbLevel = SentryLevel.ERROR) sut.i(Throwable("test")) - verify(fixture.scopes, never()).addBreadcrumb(any()) + verify(fixture.hub, never()).addBreadcrumb(any()) } @Test fun `Tree adds an info breadcrumb`() { val sut = fixture.getSut() sut.i("message") - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("Timber", it.category) assertEquals(SentryLevel.INFO, it.level) @@ -261,7 +261,7 @@ class SentryTimberTreeTest { fun `Tree adds an error breadcrumb`() { val sut = fixture.getSut() sut.e(Throwable("test")) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("exception", it.category) assertEquals(SentryLevel.ERROR, it.level) @@ -274,7 +274,7 @@ class SentryTimberTreeTest { fun `Tree does not add a breadcrumb, if no message provided`() { val sut = fixture.getSut() sut.e(Throwable()) - verify(fixture.scopes, never()).addBreadcrumb(any()) + verify(fixture.hub, never()).addBreadcrumb(any()) } @Test diff --git a/sentry-apollo-3/api/sentry-apollo-3.api b/sentry-apollo-3/api/sentry-apollo-3.api index e106585156..1c80e1950b 100644 --- a/sentry-apollo-3/api/sentry-apollo-3.api +++ b/sentry-apollo-3/api/sentry-apollo-3.api @@ -17,11 +17,11 @@ public final class io/sentry/apollo3/SentryApollo3HttpInterceptor : com/apollogr public static final field SENTRY_APOLLO_3_OPERATION_TYPE Ljava/lang/String; public static final field SENTRY_APOLLO_3_VARIABLES Ljava/lang/String; public fun ()V - public fun (Lio/sentry/IScopes;)V - public fun (Lio/sentry/IScopes;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;)V - public fun (Lio/sentry/IScopes;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;Z)V - public fun (Lio/sentry/IScopes;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;ZLjava/util/List;)V - public synthetic fun (Lio/sentry/IScopes;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;ZLjava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IHub;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;)V + public fun (Lio/sentry/IHub;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;Z)V + public fun (Lio/sentry/IHub;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;ZLjava/util/List;)V + public synthetic fun (Lio/sentry/IHub;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;ZLjava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun dispose ()V public fun intercept (Lcom/apollographql/apollo3/api/http/HttpRequest;Lcom/apollographql/apollo3/network/http/HttpInterceptorChain;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } @@ -40,12 +40,12 @@ public final class io/sentry/apollo3/SentryApollo3Interceptor : com/apollographq public final class io/sentry/apollo3/SentryApolloBuilderExtensionsKt { public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;)Lcom/apollographql/apollo3/ApolloClient$Builder; - public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IScopes;)Lcom/apollographql/apollo3/ApolloClient$Builder; - public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IScopes;Z)Lcom/apollographql/apollo3/ApolloClient$Builder; - public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IScopes;ZLjava/util/List;)Lcom/apollographql/apollo3/ApolloClient$Builder; - public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IScopes;ZLjava/util/List;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;)Lcom/apollographql/apollo3/ApolloClient$Builder; + public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IHub;)Lcom/apollographql/apollo3/ApolloClient$Builder; + public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IHub;Z)Lcom/apollographql/apollo3/ApolloClient$Builder; + public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IHub;ZLjava/util/List;)Lcom/apollographql/apollo3/ApolloClient$Builder; + public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IHub;ZLjava/util/List;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;)Lcom/apollographql/apollo3/ApolloClient$Builder; public static final fun sentryTracing (Lcom/apollographql/apollo3/ApolloClient$Builder;ZLjava/util/List;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;)Lcom/apollographql/apollo3/ApolloClient$Builder; - public static synthetic fun sentryTracing$default (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IScopes;ZLjava/util/List;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;ILjava/lang/Object;)Lcom/apollographql/apollo3/ApolloClient$Builder; + public static synthetic fun sentryTracing$default (Lcom/apollographql/apollo3/ApolloClient$Builder;Lio/sentry/IHub;ZLjava/util/List;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;ILjava/lang/Object;)Lcom/apollographql/apollo3/ApolloClient$Builder; public static synthetic fun sentryTracing$default (Lcom/apollographql/apollo3/ApolloClient$Builder;ZLjava/util/List;Lio/sentry/apollo3/SentryApollo3HttpInterceptor$BeforeSpanCallback;ILjava/lang/Object;)Lcom/apollographql/apollo3/ApolloClient$Builder; } diff --git a/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApollo3HttpInterceptor.kt b/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApollo3HttpInterceptor.kt index 52219cb8e1..08cab179a5 100644 --- a/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApollo3HttpInterceptor.kt +++ b/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApollo3HttpInterceptor.kt @@ -11,9 +11,9 @@ import com.apollographql.apollo3.network.http.HttpInterceptorChain import io.sentry.BaggageHeader import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.HubAdapter +import io.sentry.IHub import io.sentry.ISpan -import io.sentry.ScopesAdapter import io.sentry.SentryEvent import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryLevel @@ -41,7 +41,7 @@ import java.util.Locale private const val TRACE_ORIGIN = "auto.graphql.apollo3" class SentryApollo3HttpInterceptor @JvmOverloads constructor( - @ApiStatus.Internal private val scopes: IScopes = ScopesAdapter.getInstance(), + @ApiStatus.Internal private val hub: IHub = HubAdapter.getInstance(), private val beforeSpan: BeforeSpanCallback? = null, private val captureFailedRequests: Boolean = DEFAULT_CAPTURE_FAILED_REQUESTS, private val failedRequestTargets: List = listOf(DEFAULT_PROPAGATION_TARGETS) @@ -65,7 +65,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( request: HttpRequest, chain: HttpInterceptorChain ): HttpResponse { - val activeSpan = if (Platform.isAndroid()) scopes.transaction else scopes.span + val activeSpan = if (Platform.isAndroid()) hub.transaction else hub.span val operationName = getHeader(HEADER_APOLLO_OPERATION_NAME, request.headers) val operationType = decodeHeaderValue(request, SENTRY_APOLLO_3_OPERATION_TYPE) @@ -77,7 +77,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( span = startChild(request, activeSpan, operationName, operationType, operationId) } - val modifiedRequest = maybeAddTracingHeaders(scopes, request, span) + val modifiedRequest = maybeAddTracingHeaders(hub, request, span) var httpResponse: HttpResponse? = null var statusCode: Int? = null @@ -117,10 +117,10 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( } } - private fun maybeAddTracingHeaders(scopes: IScopes, request: HttpRequest, span: ISpan?): HttpRequest { + private fun maybeAddTracingHeaders(hub: IHub, request: HttpRequest, span: ISpan?): HttpRequest { var cleanedHeaders = removeSentryInternalHeaders(request.headers).toMutableList() - TracingUtils.traceIfAllowed(scopes, request.url, request.headers.filter { it.name == BaggageHeader.BAGGAGE_HEADER }.map { it.value }, span)?.let { + TracingUtils.traceIfAllowed(hub, request.url, request.headers.filter { it.name == BaggageHeader.BAGGAGE_HEADER }.map { it.value }, span)?.let { cleanedHeaders.add(HttpHeader(it.sentryTraceHeader.name, it.sentryTraceHeader.value)) it.baggageHeader?.let { baggageHeader -> cleanedHeaders = cleanedHeaders.filterNot { it.name == BaggageHeader.BAGGAGE_HEADER }.toMutableList().apply { @@ -179,7 +179,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( try { String(Base64.decode(it, Base64.NO_WRAP)) } catch (e: Throwable) { - scopes.options.logger.log( + hub.options.logger.log( SentryLevel.ERROR, "Error decoding internal apolloHeader $headerName", e @@ -218,7 +218,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( span.spanContext.sampled = false } } catch (e: Throwable) { - scopes.options.logger.log( + hub.options.logger.log( SentryLevel.ERROR, "An error occurred while executing beforeSpan on ApolloInterceptor", e @@ -256,7 +256,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( hint.set(APOLLO_RESPONSE, httpResponse) } - scopes.addBreadcrumb(breadcrumb, hint) + hub.addBreadcrumb(breadcrumb, hint) } // Extensions @@ -273,7 +273,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( private fun getHeaders(headers: List): MutableMap? { // Headers are only sent if isSendDefaultPii is enabled due to PII - if (!scopes.options.isSendDefaultPii) { + if (!hub.options.isSendDefaultPii) { return null } @@ -311,7 +311,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( val body = try { response.body?.peek()?.readUtf8() ?: "" } catch (e: Throwable) { - scopes.options.logger.log( + hub.options.logger.log( SentryLevel.ERROR, "Error reading the response body.", e @@ -368,7 +368,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( urlDetails.applyToRequest(this) // Cookie is only sent if isSendDefaultPii is enabled cookies = - if (scopes.options.isSendDefaultPii) getHeader("Cookie", request.headers) else null + if (hub.options.isSendDefaultPii) getHeader("Cookie", request.headers) else null method = request.method.name headers = getHeaders(request.headers) apiTarget = "graphql" @@ -382,7 +382,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( it.writeTo(buffer) data = buffer.readUtf8() } catch (e: Throwable) { - scopes.options.logger.log( + hub.options.logger.log( SentryLevel.ERROR, "Error reading the request body.", e @@ -396,7 +396,7 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( val sentryResponse = Response().apply { // Set-Cookie is only sent if isSendDefaultPii is enabled due to PII - cookies = if (scopes.options.isSendDefaultPii) { + cookies = if (hub.options.isSendDefaultPii) { getHeader( "Set-Cookie", response.headers @@ -419,9 +419,9 @@ class SentryApollo3HttpInterceptor @JvmOverloads constructor( event.contexts.setResponse(sentryResponse) event.fingerprints = fingerprints - scopes.captureEvent(event, hint) + hub.captureEvent(event, hint) } catch (e: Throwable) { - scopes.options.logger.log( + hub.options.logger.log( SentryLevel.ERROR, "Error capturing the GraphQL error.", e diff --git a/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApolloBuilderExtensions.kt b/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApolloBuilderExtensions.kt index b40b1c183d..2cdbc148fb 100644 --- a/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApolloBuilderExtensions.kt +++ b/sentry-apollo-3/src/main/java/io/sentry/apollo3/SentryApolloBuilderExtensions.kt @@ -1,14 +1,14 @@ package io.sentry.apollo3 import com.apollographql.apollo3.ApolloClient -import io.sentry.IScopes -import io.sentry.ScopesAdapter +import io.sentry.HubAdapter +import io.sentry.IHub import io.sentry.SentryOptions.DEFAULT_PROPAGATION_TARGETS import io.sentry.apollo3.SentryApollo3HttpInterceptor.Companion.DEFAULT_CAPTURE_FAILED_REQUESTS @JvmOverloads fun ApolloClient.Builder.sentryTracing( - scopes: IScopes = ScopesAdapter.getInstance(), + hub: IHub = HubAdapter.getInstance(), captureFailedRequests: Boolean = DEFAULT_CAPTURE_FAILED_REQUESTS, failedRequestTargets: List = listOf(DEFAULT_PROPAGATION_TARGETS), beforeSpan: SentryApollo3HttpInterceptor.BeforeSpanCallback? = null @@ -16,7 +16,7 @@ fun ApolloClient.Builder.sentryTracing( addInterceptor(SentryApollo3Interceptor()) addHttpInterceptor( SentryApollo3HttpInterceptor( - scopes = scopes, + hub = hub, captureFailedRequests = captureFailedRequests, failedRequestTargets = failedRequestTargets, beforeSpan = beforeSpan @@ -31,7 +31,7 @@ fun ApolloClient.Builder.sentryTracing( beforeSpan: SentryApollo3HttpInterceptor.BeforeSpanCallback? = null ): ApolloClient.Builder { return sentryTracing( - scopes = ScopesAdapter.getInstance(), + hub = HubAdapter.getInstance(), captureFailedRequests = captureFailedRequests, failedRequestTargets = failedRequestTargets, beforeSpan = beforeSpan diff --git a/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorClientErrors.kt b/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorClientErrors.kt index b3f8b6d57e..40406b77b5 100644 --- a/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorClientErrors.kt +++ b/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorClientErrors.kt @@ -5,7 +5,7 @@ import com.apollographql.apollo3.api.http.HttpRequest import com.apollographql.apollo3.api.http.HttpResponse import com.apollographql.apollo3.exception.ApolloException import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryOptions import io.sentry.SentryOptions.DEFAULT_PROPAGATION_TARGETS @@ -35,7 +35,7 @@ import kotlin.test.assertTrue class SentryApollo3InterceptorClientErrors { class Fixture { val server = MockWebServer() - lateinit var scopes: IScopes + lateinit var hub: IHub private val responseBodyOk = """{ @@ -75,7 +75,7 @@ class SentryApollo3InterceptorClientErrors { ): ApolloClient { SentryIntegrationPackageStorage.getInstance().clearStorage() - scopes = mock().apply { + hub = mock().apply { whenever(options).thenReturn( SentryOptions().apply { dsn = "https://key@sentry.io/proj" @@ -84,7 +84,7 @@ class SentryApollo3InterceptorClientErrors { } ) } - whenever(scopes.captureEvent(any(), any())).thenReturn(SentryId.EMPTY_ID) + whenever(hub.captureEvent(any(), any())).thenReturn(SentryId.EMPTY_ID) val response = MockResponse() .setBody(responseBody) @@ -102,7 +102,7 @@ class SentryApollo3InterceptorClientErrors { val builder = ApolloClient.Builder() .serverUrl(server.url("?myQuery=query#myFragment").toString()) .sentryTracing( - scopes = scopes, + hub = hub, captureFailedRequests = captureFailedRequests, failedRequestTargets = failedRequestTargets ) @@ -123,7 +123,7 @@ class SentryApollo3InterceptorClientErrors { val sut = fixture.getSut(captureFailedRequests = false, responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.scopes, never()).captureEvent(any(), any()) + verify(fixture.hub, never()).captureEvent(any(), any()) } @Test @@ -132,7 +132,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.scopes).captureEvent(any(), any()) + verify(fixture.hub).captureEvent(any(), any()) } // endregion @@ -165,7 +165,7 @@ class SentryApollo3InterceptorClientErrors { ) executeQuery(sut) - verify(fixture.scopes, never()).captureEvent(any(), any()) + verify(fixture.hub, never()).captureEvent(any(), any()) } @Test @@ -174,7 +174,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.scopes).captureEvent(any(), any()) + verify(fixture.hub).captureEvent(any(), any()) } // endregion @@ -187,7 +187,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { val throwable = (it.throwableMechanism as ExceptionMechanismException) assertEquals("SentryApollo3Interceptor", throwable.exceptionMechanism.type) @@ -202,7 +202,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { val throwable = (it.throwableMechanism as ExceptionMechanismException) assertEquals("GraphQL Request failed, name: LaunchDetails, type: query", throwable.throwable.message) @@ -217,7 +217,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { val throwable = (it.throwableMechanism as ExceptionMechanismException) assertTrue(throwable.isSnapshot) @@ -238,7 +238,7 @@ class SentryApollo3InterceptorClientErrors { {"operationName":"LaunchDetails","variables":{"id":"83"},"query":"query LaunchDetails($escapeDolar: ID!) { launch(id: $escapeDolar) { id site mission { name missionPatch(size: LARGE) } rocket { name type } } }"} """.trimIndent() - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { val request = it.request!! @@ -262,7 +262,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk, sendDefaultPii = true) executeQuery(sut) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { val request = it.request!! @@ -280,7 +280,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { val response = it.contexts.response!! @@ -300,7 +300,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk, sendDefaultPii = true) executeQuery(sut) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { val response = it.contexts.response!! @@ -318,7 +318,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { assertEquals(listOf("LaunchDetails", "query", "200"), it.fingerprints) }, @@ -337,7 +337,7 @@ class SentryApollo3InterceptorClientErrors { executeQuery(sut) // HttpInterceptor does not throw for >= 400 - verify(fixture.scopes).captureEvent(any(), any()) + verify(fixture.hub).captureEvent(any(), any()) } @Test @@ -345,7 +345,7 @@ class SentryApollo3InterceptorClientErrors { val sut = fixture.getSut(responseBody = fixture.responseBodyNotOk) - whenever(fixture.scopes.captureEvent(any(), any())).thenThrow(RuntimeException()) + whenever(fixture.hub.captureEvent(any(), any())).thenThrow(RuntimeException()) executeQuery(sut) } @@ -360,7 +360,7 @@ class SentryApollo3InterceptorClientErrors { fixture.getSut(responseBody = fixture.responseBodyNotOk) executeQuery(sut) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( any(), check { val request = it.get(TypeCheckHint.APOLLO_REQUEST) diff --git a/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorTest.kt b/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorTest.kt index 3b836f45b9..44d8bfd624 100644 --- a/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorTest.kt +++ b/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorTest.kt @@ -9,7 +9,7 @@ import com.apollographql.apollo3.network.http.HttpInterceptor import com.apollographql.apollo3.network.http.HttpInterceptorChain import io.sentry.BaggageHeader import io.sentry.Breadcrumb -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ITransaction import io.sentry.Scope import io.sentry.ScopeCallback @@ -57,11 +57,11 @@ class SentryApollo3InterceptorTest { sdkVersion = SdkVersion("test", "1.2.3") } val scope = Scope(options) - val scopes = mock().also { + val hub = mock().also { whenever(it.options).thenReturn(options) doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(it).configureScope(any()) } - private var httpInterceptor = SentryApollo3HttpInterceptor(scopes, captureFailedRequests = false) + private var httpInterceptor = SentryApollo3HttpInterceptor(hub, captureFailedRequests = false) @SuppressWarnings("LongParameterList") fun getSut( @@ -93,7 +93,7 @@ class SentryApollo3InterceptorTest { ) if (beforeSpan != null) { - httpInterceptor = SentryApollo3HttpInterceptor(scopes, beforeSpan, captureFailedRequests = false) + httpInterceptor = SentryApollo3HttpInterceptor(hub, beforeSpan, captureFailedRequests = false) } val builder = ApolloClient.Builder() @@ -124,7 +124,7 @@ class SentryApollo3InterceptorTest { fun `creates a span around the successful request`() { executeQuery() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertTransactionDetails(it, httpStatusCode = 200) assertEquals(SpanStatus.OK, it.spans.first().status) @@ -139,7 +139,7 @@ class SentryApollo3InterceptorTest { fun `creates a span around the failed request`() { executeQuery(fixture.getSut(httpStatusCode = 403)) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertTransactionDetails(it, httpStatusCode = 403) assertEquals(SpanStatus.PERMISSION_DENIED, it.spans.first().status) @@ -159,7 +159,7 @@ class SentryApollo3InterceptorTest { } executeQuery(fixture.getSut(interceptor = failingInterceptor)) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertTransactionDetails(it, httpStatusCode = 404, contentLength = null) assertEquals("POST", it.spans.first().data?.get(SpanDataConvention.HTTP_METHOD_KEY)) @@ -176,7 +176,7 @@ class SentryApollo3InterceptorTest { fun `creates a span around the request failing with network error`() { executeQuery(fixture.getSut(socketPolicy = SocketPolicy.DISCONNECT_DURING_REQUEST_BODY)) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertTransactionDetails(it, httpStatusCode = null, contentLength = null) assertEquals(SpanStatus.INTERNAL_ERROR, it.spans.first().status) @@ -241,7 +241,7 @@ class SentryApollo3InterceptorTest { ) ) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(1, it.spans.size) val httpClientSpan = it.spans.first() @@ -261,7 +261,7 @@ class SentryApollo3InterceptorTest { ) ) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(0, it.spans.size) }, @@ -281,7 +281,7 @@ class SentryApollo3InterceptorTest { ) ) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(1, it.spans.size) }, @@ -294,7 +294,7 @@ class SentryApollo3InterceptorTest { @Test fun `adds breadcrumb when http calls succeeds`() { executeQuery(fixture.getSut()) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) // response_body_size is added but mock webserver returns 0 always @@ -309,9 +309,9 @@ class SentryApollo3InterceptorTest { @Test fun `sets SDKVersion Info`() { - assertNotNull(fixture.scopes.options.sdkVersion) - assert(fixture.scopes.options.sdkVersion!!.integrationSet.contains("Apollo3")) - val packageInfo = fixture.scopes.options.sdkVersion!!.packageSet.firstOrNull { pkg -> pkg.name == "maven:io.sentry:sentry-apollo-3" } + assertNotNull(fixture.hub.options.sdkVersion) + assert(fixture.hub.options.sdkVersion!!.integrationSet.contains("Apollo3")) + val packageInfo = fixture.hub.options.sdkVersion!!.packageSet.firstOrNull { pkg -> pkg.name == "maven:io.sentry:sentry-apollo-3" } assertNotNull(packageInfo) assert(packageInfo.version == BuildConfig.VERSION_NAME) } @@ -320,14 +320,14 @@ class SentryApollo3InterceptorTest { fun `attaches to root transaction on Android`() { Apollo3PlatformTestManipulator.pretendIsAndroid(true) executeQuery(fixture.getSut()) - verify(fixture.scopes).transaction + verify(fixture.hub).transaction } @Test fun `attaches to child span on non-Android`() { Apollo3PlatformTestManipulator.pretendIsAndroid(false) executeQuery(fixture.getSut()) - verify(fixture.scopes).span + verify(fixture.hub).span } private fun assertTransactionDetails(it: SentryTransaction, httpStatusCode: Int? = 200, contentLength: Long? = 0L) { @@ -350,9 +350,9 @@ class SentryApollo3InterceptorTest { private fun executeQuery(sut: ApolloClient = fixture.getSut(), isSpanActive: Boolean = true, id: String = "83") = runBlocking { var tx: ITransaction? = null if (isSpanActive) { - tx = SentryTracer(TransactionContext("op", "desc", TracesSamplingDecision(true)), fixture.scopes) - whenever(fixture.scopes.transaction).thenReturn(tx) - whenever(fixture.scopes.span).thenReturn(tx) + tx = SentryTracer(TransactionContext("op", "desc", TracesSamplingDecision(true)), fixture.hub) + whenever(fixture.hub.transaction).thenReturn(tx) + whenever(fixture.hub.span).thenReturn(tx) } val coroutine = launch { diff --git a/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorWithVariablesTest.kt b/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorWithVariablesTest.kt index 3ac3d80d7d..81775efc18 100644 --- a/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorWithVariablesTest.kt +++ b/sentry-apollo-3/src/test/java/io/sentry/apollo3/SentryApollo3InterceptorWithVariablesTest.kt @@ -3,7 +3,7 @@ package io.sentry.apollo3 import com.apollographql.apollo3.ApolloClient import com.apollographql.apollo3.exception.ApolloException import io.sentry.Breadcrumb -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ITransaction import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -32,7 +32,7 @@ class SentryApollo3InterceptorWithVariablesTest { class Fixture { val server = MockWebServer() - val scopes = mock() + val hub = mock() @SuppressWarnings("LongParameterList") fun getSut( @@ -54,7 +54,7 @@ class SentryApollo3InterceptorWithVariablesTest { socketPolicy: SocketPolicy = SocketPolicy.KEEP_OPEN, beforeSpan: BeforeSpanCallback? = null ): ApolloClient { - whenever(scopes.options).thenReturn( + whenever(hub.options).thenReturn( SentryOptions().apply { dsn = "http://key@localhost/proj" } @@ -68,7 +68,7 @@ class SentryApollo3InterceptorWithVariablesTest { ) return ApolloClient.Builder().serverUrl(server.url("/").toString()) - .sentryTracing(scopes = scopes, beforeSpan = beforeSpan, captureFailedRequests = false) + .sentryTracing(hub = hub, beforeSpan = beforeSpan, captureFailedRequests = false) .build() } } @@ -79,7 +79,7 @@ class SentryApollo3InterceptorWithVariablesTest { fun `creates a span around the successful request`() { executeQuery() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertTransactionDetails(it) assertEquals(SpanStatus.OK, it.spans.first().status) @@ -94,7 +94,7 @@ class SentryApollo3InterceptorWithVariablesTest { fun `creates a span around the failed request`() { executeQuery(fixture.getSut(httpStatusCode = 403)) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertTransactionDetails(it) assertEquals(SpanStatus.PERMISSION_DENIED, it.spans.first().status) @@ -109,7 +109,7 @@ class SentryApollo3InterceptorWithVariablesTest { fun `creates a span around the request failing with network error`() { executeQuery(fixture.getSut(socketPolicy = SocketPolicy.DISCONNECT_DURING_REQUEST_BODY)) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertTransactionDetails(it) assertEquals(SpanStatus.INTERNAL_ERROR, it.spans.first().status) @@ -124,7 +124,7 @@ class SentryApollo3InterceptorWithVariablesTest { fun `handles non-ascii header values correctly`() { executeQuery(id = "á") - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertTransactionDetails(it) assertEquals(SpanStatus.OK, it.spans.first().status) @@ -138,7 +138,7 @@ class SentryApollo3InterceptorWithVariablesTest { @Test fun `adds breadcrumb when http calls succeeds`() { executeQuery(fixture.getSut()) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) // response_body_size is added but mock webserver returns 0 always @@ -173,8 +173,8 @@ class SentryApollo3InterceptorWithVariablesTest { private fun executeQuery(sut: ApolloClient = fixture.getSut(), isSpanActive: Boolean = true, id: String = "83") = runBlocking { var tx: ITransaction? = null if (isSpanActive) { - tx = SentryTracer(TransactionContext("op", "desc", TracesSamplingDecision(true)), fixture.scopes) - whenever(fixture.scopes.span).thenReturn(tx) + tx = SentryTracer(TransactionContext("op", "desc", TracesSamplingDecision(true)), fixture.hub) + whenever(fixture.hub.span).thenReturn(tx) } val coroutine = launch { diff --git a/sentry-apollo/api/sentry-apollo.api b/sentry-apollo/api/sentry-apollo.api index 63eac6a193..8c18bce06e 100644 --- a/sentry-apollo/api/sentry-apollo.api +++ b/sentry-apollo/api/sentry-apollo.api @@ -5,9 +5,9 @@ public final class io/sentry/apollo/BuildConfig { public final class io/sentry/apollo/SentryApolloInterceptor : com/apollographql/apollo/interceptor/ApolloInterceptor { public fun ()V - public fun (Lio/sentry/IScopes;)V - public fun (Lio/sentry/IScopes;Lio/sentry/apollo/SentryApolloInterceptor$BeforeSpanCallback;)V - public synthetic fun (Lio/sentry/IScopes;Lio/sentry/apollo/SentryApolloInterceptor$BeforeSpanCallback;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IHub;Lio/sentry/apollo/SentryApolloInterceptor$BeforeSpanCallback;)V + public synthetic fun (Lio/sentry/IHub;Lio/sentry/apollo/SentryApolloInterceptor$BeforeSpanCallback;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun (Lio/sentry/apollo/SentryApolloInterceptor$BeforeSpanCallback;)V public fun dispose ()V public fun interceptAsync (Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InterceptorRequest;Lcom/apollographql/apollo/interceptor/ApolloInterceptorChain;Ljava/util/concurrent/Executor;Lcom/apollographql/apollo/interceptor/ApolloInterceptor$CallBack;)V diff --git a/sentry-apollo/src/main/java/io/sentry/apollo/SentryApolloInterceptor.kt b/sentry-apollo/src/main/java/io/sentry/apollo/SentryApolloInterceptor.kt index fe5a6a4762..faa8a549a9 100644 --- a/sentry-apollo/src/main/java/io/sentry/apollo/SentryApolloInterceptor.kt +++ b/sentry-apollo/src/main/java/io/sentry/apollo/SentryApolloInterceptor.kt @@ -15,9 +15,9 @@ import com.apollographql.apollo.request.RequestHeaders import io.sentry.BaggageHeader import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.HubAdapter +import io.sentry.IHub import io.sentry.ISpan -import io.sentry.ScopesAdapter import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryLevel import io.sentry.SpanDataConvention @@ -32,12 +32,12 @@ import java.util.concurrent.Executor private const val TRACE_ORIGIN = "auto.graphql.apollo" class SentryApolloInterceptor( - private val scopes: IScopes = ScopesAdapter.getInstance(), + private val hub: IHub = HubAdapter.getInstance(), private val beforeSpan: BeforeSpanCallback? = null ) : ApolloInterceptor { - constructor(scopes: IScopes) : this(scopes, null) - constructor(beforeSpan: BeforeSpanCallback) : this(ScopesAdapter.getInstance(), beforeSpan) + constructor(hub: IHub) : this(hub, null) + constructor(beforeSpan: BeforeSpanCallback) : this(HubAdapter.getInstance(), beforeSpan) init { addIntegrationToSdkVersion(javaClass) @@ -45,7 +45,7 @@ class SentryApolloInterceptor( } override fun interceptAsync(request: InterceptorRequest, chain: ApolloInterceptorChain, dispatcher: Executor, callBack: CallBack) { - val activeSpan = if (io.sentry.util.Platform.isAndroid()) scopes.transaction else scopes.span + val activeSpan = if (io.sentry.util.Platform.isAndroid()) hub.transaction else hub.span if (activeSpan == null) { val headers = addTracingHeaders(request, null) val modifiedRequest = request.toBuilder().requestHeaders(headers).build() @@ -115,10 +115,10 @@ class SentryApolloInterceptor( private fun addTracingHeaders(request: InterceptorRequest, span: ISpan?): RequestHeaders { val requestHeaderBuilder = request.requestHeaders.toBuilder() - if (scopes.options.isTraceSampling) { + if (hub.options.isTraceSampling) { // we have no access to URI, no way to verify tracing origins TracingUtils.trace( - scopes, + hub, listOf(request.requestHeaders.headerValue(BaggageHeader.BAGGAGE_HEADER)), span )?.let { tracingHeaders -> @@ -154,7 +154,7 @@ class SentryApolloInterceptor( try { newSpan = beforeSpan.execute(span, request, response) } catch (e: Exception) { - scopes.options.logger.log(SentryLevel.ERROR, "An error occurred while executing beforeSpan on ApolloInterceptor", e) + hub.options.logger.log(SentryLevel.ERROR, "An error occurred while executing beforeSpan on ApolloInterceptor", e) } } if (newSpan == null) { @@ -182,7 +182,7 @@ class SentryApolloInterceptor( set(APOLLO_REQUEST, httpRequest) set(APOLLO_RESPONSE, httpResponse) } - scopes.addBreadcrumb(breadcrumb, hint) + hub.addBreadcrumb(breadcrumb, hint) } } } diff --git a/sentry-apollo/src/test/java/io/sentry/apollo/SentryApolloInterceptorTest.kt b/sentry-apollo/src/test/java/io/sentry/apollo/SentryApolloInterceptorTest.kt index b1b118c334..d22c2fd3e5 100644 --- a/sentry-apollo/src/test/java/io/sentry/apollo/SentryApolloInterceptorTest.kt +++ b/sentry-apollo/src/test/java/io/sentry/apollo/SentryApolloInterceptorTest.kt @@ -5,7 +5,7 @@ import com.apollographql.apollo.coroutines.await import com.apollographql.apollo.exception.ApolloException import io.sentry.BaggageHeader import io.sentry.Breadcrumb -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ITransaction import io.sentry.Scope import io.sentry.ScopeCallback @@ -48,13 +48,13 @@ class SentryApolloInterceptorTest { sdkVersion = SdkVersion("test", "1.2.3") } val scope = Scope(options) - val scopes = mock().also { + val hub = mock().also { whenever(it.options).thenReturn(options) doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(it).configureScope( any() ) } - private var interceptor = SentryApolloInterceptor(scopes) + private var interceptor = SentryApolloInterceptor(hub) @SuppressWarnings("LongParameterList") fun getSut( @@ -84,7 +84,7 @@ class SentryApolloInterceptorTest { ) if (beforeSpan != null) { - interceptor = SentryApolloInterceptor(scopes, beforeSpan) + interceptor = SentryApolloInterceptor(hub, beforeSpan) } return ApolloClient.builder() .serverUrl(server.url("/")) @@ -104,7 +104,7 @@ class SentryApolloInterceptorTest { fun `creates a span around the successful request`() { executeQuery() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertTransactionDetails(it) assertEquals(SpanStatus.OK, it.spans.first().status) @@ -120,7 +120,7 @@ class SentryApolloInterceptorTest { fun `creates a span around the failed request`() { executeQuery(fixture.getSut(httpStatusCode = 403)) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertTransactionDetails(it) assertEquals(SpanStatus.PERMISSION_DENIED, it.spans.first().status) @@ -138,7 +138,7 @@ class SentryApolloInterceptorTest { fun `creates a span around the request failing with network error`() { executeQuery(fixture.getSut(socketPolicy = SocketPolicy.DISCONNECT_DURING_REQUEST_BODY)) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertTransactionDetails(it) assertEquals(SpanStatus.INTERNAL_ERROR, it.spans.first().status) @@ -176,7 +176,7 @@ class SentryApolloInterceptorTest { } ) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(1, it.spans.size) val httpClientSpan = it.spans.first() @@ -196,7 +196,7 @@ class SentryApolloInterceptorTest { } ) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertTrue(it.spans.isEmpty()) }, @@ -212,7 +212,7 @@ class SentryApolloInterceptorTest { fixture.getSut { _, _, _ -> throw RuntimeException() } ) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(1, it.spans.size) }, @@ -225,7 +225,7 @@ class SentryApolloInterceptorTest { @Test fun `adds breadcrumb when http calls succeeds`() { executeQuery() - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(280L, it.data["response_body_size"]) @@ -237,9 +237,9 @@ class SentryApolloInterceptorTest { @Test fun `sets SDKVersion Info`() { - assertNotNull(fixture.scopes.options.sdkVersion) - assert(fixture.scopes.options.sdkVersion!!.integrationSet.contains("Apollo")) - val packageInfo = fixture.scopes.options.sdkVersion!!.packageSet.firstOrNull { pkg -> pkg.name == "maven:io.sentry:sentry-apollo" } + assertNotNull(fixture.hub.options.sdkVersion) + assert(fixture.hub.options.sdkVersion!!.integrationSet.contains("Apollo")) + val packageInfo = fixture.hub.options.sdkVersion!!.packageSet.firstOrNull { pkg -> pkg.name == "maven:io.sentry:sentry-apollo" } assertNotNull(packageInfo) assert(packageInfo.version == BuildConfig.VERSION_NAME) } @@ -248,14 +248,14 @@ class SentryApolloInterceptorTest { fun `attaches to root transaction on Android`() { ApolloPlatformTestManipulator.pretendIsAndroid(true) executeQuery(fixture.getSut()) - verify(fixture.scopes).transaction + verify(fixture.hub).transaction } @Test fun `attaches to child span on non-Android`() { ApolloPlatformTestManipulator.pretendIsAndroid(false) executeQuery(fixture.getSut()) - verify(fixture.scopes).span + verify(fixture.hub).span } private fun assertTransactionDetails(it: SentryTransaction) { @@ -273,9 +273,9 @@ class SentryApolloInterceptorTest { private fun executeQuery(sut: ApolloClient = fixture.getSut(), isSpanActive: Boolean = true) = runBlocking { var tx: ITransaction? = null if (isSpanActive) { - tx = SentryTracer(TransactionContext("op", "desc", TracesSamplingDecision(true)), fixture.scopes) - whenever(fixture.scopes.transaction).thenReturn(tx) - whenever(fixture.scopes.span).thenReturn(tx) + tx = SentryTracer(TransactionContext("op", "desc", TracesSamplingDecision(true)), fixture.hub) + whenever(fixture.hub.transaction).thenReturn(tx) + whenever(fixture.hub.span).thenReturn(tx) } val coroutine = launch { diff --git a/sentry-graphql/api/sentry-graphql.api b/sentry-graphql/api/sentry-graphql.api index d119256010..57c253e23c 100644 --- a/sentry-graphql/api/sentry-graphql.api +++ b/sentry-graphql/api/sentry-graphql.api @@ -9,11 +9,10 @@ public final class io/sentry/graphql/ExceptionReporter { } public final class io/sentry/graphql/ExceptionReporter$ExceptionDetails { - public fun (Lio/sentry/IScopes;Lgraphql/execution/instrumentation/parameters/InstrumentationExecutionParameters;Z)V - public fun (Lio/sentry/IScopes;Lgraphql/schema/DataFetchingEnvironment;Z)V - public fun getHub ()Lio/sentry/IScopes; + public fun (Lio/sentry/IHub;Lgraphql/execution/instrumentation/parameters/InstrumentationExecutionParameters;Z)V + public fun (Lio/sentry/IHub;Lgraphql/schema/DataFetchingEnvironment;Z)V + public fun getHub ()Lio/sentry/IHub; public fun getQuery ()Ljava/lang/String; - public fun getScopes ()Lio/sentry/IScopes; public fun getVariables ()Ljava/util/Map; public fun isSubscription ()Z } @@ -27,19 +26,19 @@ public final class io/sentry/graphql/GraphqlStringUtils { public final class io/sentry/graphql/NoOpSubscriptionHandler : io/sentry/graphql/SentrySubscriptionHandler { public static fun getInstance ()Lio/sentry/graphql/NoOpSubscriptionHandler; - public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IScopes;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; + public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IHub;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; } public final class io/sentry/graphql/SentryDataFetcherExceptionHandler : graphql/execution/DataFetcherExceptionHandler { public fun (Lgraphql/execution/DataFetcherExceptionHandler;)V - public fun (Lio/sentry/IScopes;Lgraphql/execution/DataFetcherExceptionHandler;)V + public fun (Lio/sentry/IHub;Lgraphql/execution/DataFetcherExceptionHandler;)V public fun handleException (Lgraphql/execution/DataFetcherExceptionHandlerParameters;)Ljava/util/concurrent/CompletableFuture; public fun onException (Lgraphql/execution/DataFetcherExceptionHandlerParameters;)Lgraphql/execution/DataFetcherExceptionHandlerResult; } public final class io/sentry/graphql/SentryGenericDataFetcherExceptionHandler : graphql/execution/DataFetcherExceptionHandler { public fun (Lgraphql/execution/DataFetcherExceptionHandler;)V - public fun (Lio/sentry/IScopes;Lgraphql/execution/DataFetcherExceptionHandler;)V + public fun (Lio/sentry/IHub;Lgraphql/execution/DataFetcherExceptionHandler;)V public fun handleException (Lgraphql/execution/DataFetcherExceptionHandlerParameters;)Ljava/util/concurrent/CompletableFuture; public fun onException (Lgraphql/execution/DataFetcherExceptionHandlerParameters;)Lgraphql/execution/DataFetcherExceptionHandlerResult; } @@ -52,10 +51,9 @@ public final class io/sentry/graphql/SentryGraphqlExceptionHandler { public final class io/sentry/graphql/SentryInstrumentation : graphql/execution/instrumentation/SimpleInstrumentation { public static final field SENTRY_EXCEPTIONS_CONTEXT_KEY Ljava/lang/String; public static final field SENTRY_HUB_CONTEXT_KEY Ljava/lang/String; - public static final field SENTRY_SCOPES_CONTEXT_KEY Ljava/lang/String; public fun ()V - public fun (Lio/sentry/IScopes;)V - public fun (Lio/sentry/IScopes;Lio/sentry/graphql/SentryInstrumentation$BeforeSpanCallback;)V + public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IHub;Lio/sentry/graphql/SentryInstrumentation$BeforeSpanCallback;)V public fun (Lio/sentry/graphql/SentryInstrumentation$BeforeSpanCallback;)V public fun (Lio/sentry/graphql/SentryInstrumentation$BeforeSpanCallback;Lio/sentry/graphql/SentrySubscriptionHandler;Lio/sentry/graphql/ExceptionReporter;Ljava/util/List;)V public fun (Lio/sentry/graphql/SentryInstrumentation$BeforeSpanCallback;Lio/sentry/graphql/SentrySubscriptionHandler;Z)V @@ -73,6 +71,6 @@ public abstract interface class io/sentry/graphql/SentryInstrumentation$BeforeSp } public abstract interface class io/sentry/graphql/SentrySubscriptionHandler { - public abstract fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IScopes;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; + public abstract fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IHub;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; } diff --git a/sentry-graphql/src/main/java/io/sentry/graphql/ExceptionReporter.java b/sentry-graphql/src/main/java/io/sentry/graphql/ExceptionReporter.java index 9bca0955e4..30ccb21425 100644 --- a/sentry-graphql/src/main/java/io/sentry/graphql/ExceptionReporter.java +++ b/sentry-graphql/src/main/java/io/sentry/graphql/ExceptionReporter.java @@ -5,7 +5,7 @@ import graphql.language.AstPrinter; import graphql.schema.DataFetchingEnvironment; import io.sentry.Hint; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.SentryEvent; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -33,7 +33,7 @@ public void captureThrowable( final @NotNull Throwable throwable, final @NotNull ExceptionDetails exceptionDetails, final @Nullable ExecutionResult result) { - final @NotNull IScopes scopes = exceptionDetails.getScopes(); + final @NotNull IHub hub = exceptionDetails.getHub(); final @NotNull Mechanism mechanism = new Mechanism(); mechanism.setType(MECHANISM_TYPE); mechanism.setHandled(false); @@ -43,44 +43,44 @@ public void captureThrowable( event.setLevel(SentryLevel.FATAL); final @NotNull Hint hint = new Hint(); - setRequestDetailsOnEvent(scopes, exceptionDetails, event); + setRequestDetailsOnEvent(hub, exceptionDetails, event); - if (result != null && isAllowedToAttachBody(scopes)) { + if (result != null && isAllowedToAttachBody(hub)) { final @NotNull Response response = new Response(); final @NotNull Map responseBody = result.toSpecification(); response.setData(responseBody); event.getContexts().setResponse(response); } - scopes.captureEvent(event, hint); + hub.captureEvent(event, hint); } - private boolean isAllowedToAttachBody(final @NotNull IScopes scopes) { - final @NotNull SentryOptions options = scopes.getOptions(); + private boolean isAllowedToAttachBody(final @NotNull IHub hub) { + final @NotNull SentryOptions options = hub.getOptions(); return options.isSendDefaultPii() && !SentryOptions.RequestSize.NONE.equals(options.getMaxRequestBodySize()); } private void setRequestDetailsOnEvent( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull ExceptionDetails exceptionDetails, final @NotNull SentryEvent event) { - scopes.configureScope( + hub.configureScope( (scope) -> { final @Nullable Request scopeRequest = scope.getRequest(); final @NotNull Request request = scopeRequest == null ? new Request() : scopeRequest; - setDetailsOnRequest(scopes, exceptionDetails, request); + setDetailsOnRequest(hub, exceptionDetails, request); event.setRequest(request); }); } private void setDetailsOnRequest( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull ExceptionDetails exceptionDetails, final @NotNull Request request) { request.setApiTarget("graphql"); - if (isAllowedToAttachBody(scopes) + if (isAllowedToAttachBody(hub) && (exceptionDetails.isSubscription() || captureRequestBodyForNonSubscriptions)) { final @NotNull Map data = new HashMap<>(); @@ -99,27 +99,27 @@ private void setDetailsOnRequest( public static final class ExceptionDetails { - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private final @Nullable InstrumentationExecutionParameters instrumentationExecutionParameters; private final @Nullable DataFetchingEnvironment dataFetchingEnvironment; private final boolean isSubscription; public ExceptionDetails( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @Nullable InstrumentationExecutionParameters instrumentationExecutionParameters, final boolean isSubscription) { - this.scopes = scopes; + this.hub = hub; this.instrumentationExecutionParameters = instrumentationExecutionParameters; dataFetchingEnvironment = null; this.isSubscription = isSubscription; } public ExceptionDetails( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @Nullable DataFetchingEnvironment dataFetchingEnvironment, final boolean isSubscription) { - this.scopes = scopes; + this.hub = hub; this.dataFetchingEnvironment = dataFetchingEnvironment; instrumentationExecutionParameters = null; this.isSubscription = isSubscription; @@ -149,16 +149,8 @@ public boolean isSubscription() { return isSubscription; } - /** - * @deprecated please use {@link ExceptionDetails#getScopes()} instead. - */ - @Deprecated - public @NotNull IScopes getHub() { - return scopes; - } - - public @NotNull IScopes getScopes() { - return scopes; + public @NotNull IHub getHub() { + return hub; } } } diff --git a/sentry-graphql/src/main/java/io/sentry/graphql/NoOpSubscriptionHandler.java b/sentry-graphql/src/main/java/io/sentry/graphql/NoOpSubscriptionHandler.java index 839f413719..df241ce35b 100644 --- a/sentry-graphql/src/main/java/io/sentry/graphql/NoOpSubscriptionHandler.java +++ b/sentry-graphql/src/main/java/io/sentry/graphql/NoOpSubscriptionHandler.java @@ -1,7 +1,7 @@ package io.sentry.graphql; import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; -import io.sentry.IScopes; +import io.sentry.IHub; import org.jetbrains.annotations.NotNull; public final class NoOpSubscriptionHandler implements SentrySubscriptionHandler { @@ -17,7 +17,7 @@ private NoOpSubscriptionHandler() {} @Override public @NotNull Object onSubscriptionResult( @NotNull Object result, - @NotNull IScopes scopes, + @NotNull IHub hub, @NotNull ExceptionReporter exceptionReporter, @NotNull InstrumentationFieldFetchParameters parameters) { return result; diff --git a/sentry-graphql/src/main/java/io/sentry/graphql/SentryDataFetcherExceptionHandler.java b/sentry-graphql/src/main/java/io/sentry/graphql/SentryDataFetcherExceptionHandler.java index 0813aab851..c0467c0089 100644 --- a/sentry-graphql/src/main/java/io/sentry/graphql/SentryDataFetcherExceptionHandler.java +++ b/sentry-graphql/src/main/java/io/sentry/graphql/SentryDataFetcherExceptionHandler.java @@ -6,8 +6,8 @@ import graphql.execution.DataFetcherExceptionHandlerParameters; import graphql.execution.DataFetcherExceptionHandlerResult; import io.sentry.Hint; -import io.sentry.IScopes; -import io.sentry.ScopesAdapter; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.util.Objects; import java.util.concurrent.CompletableFuture; @@ -24,18 +24,18 @@ */ @Deprecated public final class SentryDataFetcherExceptionHandler implements DataFetcherExceptionHandler { - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private final @NotNull DataFetcherExceptionHandler delegate; public SentryDataFetcherExceptionHandler( - final @NotNull IScopes scopes, final @NotNull DataFetcherExceptionHandler delegate) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + final @NotNull IHub hub, final @NotNull DataFetcherExceptionHandler delegate) { + this.hub = Objects.requireNonNull(hub, "hub is required"); this.delegate = Objects.requireNonNull(delegate, "delegate is required"); SentryIntegrationPackageStorage.getInstance().addIntegration("GrahQLLegacyExceptionHandler"); } public SentryDataFetcherExceptionHandler(final @NotNull DataFetcherExceptionHandler delegate) { - this(ScopesAdapter.getInstance(), delegate); + this(HubAdapter.getInstance(), delegate); } @Override @@ -44,7 +44,7 @@ public CompletableFuture handleException( final Hint hint = new Hint(); hint.set(GRAPHQL_HANDLER_PARAMETERS, handlerParameters); - scopes.captureException(handlerParameters.getException(), hint); + hub.captureException(handlerParameters.getException(), hint); return delegate.handleException(handlerParameters); } diff --git a/sentry-graphql/src/main/java/io/sentry/graphql/SentryGenericDataFetcherExceptionHandler.java b/sentry-graphql/src/main/java/io/sentry/graphql/SentryGenericDataFetcherExceptionHandler.java index 1287d38caa..6251d00779 100644 --- a/sentry-graphql/src/main/java/io/sentry/graphql/SentryGenericDataFetcherExceptionHandler.java +++ b/sentry-graphql/src/main/java/io/sentry/graphql/SentryGenericDataFetcherExceptionHandler.java @@ -3,7 +3,7 @@ import graphql.execution.DataFetcherExceptionHandler; import graphql.execution.DataFetcherExceptionHandlerParameters; import graphql.execution.DataFetcherExceptionHandlerResult; -import io.sentry.IScopes; +import io.sentry.IHub; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.jetbrains.annotations.NotNull; @@ -17,7 +17,7 @@ public final class SentryGenericDataFetcherExceptionHandler implements DataFetch private final @NotNull SentryGraphqlExceptionHandler handler; public SentryGenericDataFetcherExceptionHandler( - final @Nullable IScopes scopes, final @NotNull DataFetcherExceptionHandler delegate) { + final @Nullable IHub hub, final @NotNull DataFetcherExceptionHandler delegate) { this.handler = new SentryGraphqlExceptionHandler(delegate); } diff --git a/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java b/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java index 9a853faf38..d2d62c99d8 100644 --- a/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java +++ b/sentry-graphql/src/main/java/io/sentry/graphql/SentryInstrumentation.java @@ -18,15 +18,12 @@ import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLOutputType; import io.sentry.Breadcrumb; -import io.sentry.Hint; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.ISpan; -import io.sentry.NoOpScopes; +import io.sentry.NoOpHub; import io.sentry.Sentry; import io.sentry.SentryIntegrationPackageStorage; -import io.sentry.SpanOptions; import io.sentry.SpanStatus; -import io.sentry.TypeCheckHint; import io.sentry.util.StringUtils; import java.util.ArrayList; import java.util.Arrays; @@ -49,14 +46,7 @@ public final class SentryInstrumentation "INTERNAL", // Netflix DGS "DataFetchingException" // raw graphql-java ); - public static final @NotNull String SENTRY_SCOPES_CONTEXT_KEY = "sentry.scopes"; - - /** - * @deprecated please use {@link SentryInstrumentation#SENTRY_SCOPES_CONTEXT_KEY} instead. - */ - @Deprecated - public static final @NotNull String SENTRY_HUB_CONTEXT_KEY = SENTRY_SCOPES_CONTEXT_KEY; - + public static final @NotNull String SENTRY_HUB_CONTEXT_KEY = "sentry.hub"; public static final @NotNull String SENTRY_EXCEPTIONS_CONTEXT_KEY = "sentry.exceptions"; private static final String TRACE_ORIGIN = "auto.graphql.graphql"; private final @Nullable BeforeSpanCallback beforeSpan; @@ -80,7 +70,7 @@ public SentryInstrumentation() { */ @Deprecated @SuppressWarnings("InlineMeSuggester") - public SentryInstrumentation(final @Nullable IScopes scopes) { + public SentryInstrumentation(final @Nullable IHub hub) { this(null, NoOpSubscriptionHandler.getInstance(), true); } @@ -99,7 +89,7 @@ public SentryInstrumentation(final @Nullable BeforeSpanCallback beforeSpan) { @Deprecated @SuppressWarnings("InlineMeSuggester") public SentryInstrumentation( - final @Nullable IScopes scopes, final @Nullable BeforeSpanCallback beforeSpan) { + final @Nullable IHub hub, final @Nullable BeforeSpanCallback beforeSpan) { this(beforeSpan, NoOpSubscriptionHandler.getInstance(), true); } @@ -182,9 +172,9 @@ public SentryInstrumentation( public @NotNull InstrumentationContext beginExecution( final @NotNull InstrumentationExecutionParameters parameters) { final TracingState tracingState = parameters.getInstrumentationState(); - final @NotNull IScopes currentScopes = Sentry.getCurrentScopes(); - tracingState.setTransaction(currentScopes.getSpan()); - parameters.getGraphQLContext().put(SENTRY_SCOPES_CONTEXT_KEY, currentScopes); + final @NotNull IHub currentHub = Sentry.getCurrentHub(); + tracingState.setTransaction(currentHub.getSpan()); + parameters.getGraphQLContext().put(SENTRY_HUB_CONTEXT_KEY, currentHub); return super.beginExecution(parameters); } @@ -205,7 +195,7 @@ public CompletableFuture instrumentExecutionResult( exceptionReporter.captureThrowable( throwable, new ExceptionReporter.ExceptionDetails( - scopesFromContext(graphQLContext), parameters, false), + hubFromContext(graphQLContext), parameters, false), result); } } @@ -217,7 +207,7 @@ public CompletableFuture instrumentExecutionResult( exceptionReporter.captureThrowable( new RuntimeException(error.getMessage()), new ExceptionReporter.ExceptionDetails( - scopesFromContext(graphQLContext), parameters, false), + hubFromContext(graphQLContext), parameters, false), result); } } @@ -227,7 +217,7 @@ public CompletableFuture instrumentExecutionResult( exceptionReporter.captureThrowable( exception, new ExceptionReporter.ExceptionDetails( - scopesFromContext(parameters.getGraphQLContext()), parameters, false), + hubFromContext(parameters.getGraphQLContext()), parameters, false), null); } }); @@ -272,7 +262,7 @@ private boolean isIgnored(final @Nullable String errorType) { operationDefinition.getOperation(); final @Nullable String operationType = operation == null ? null : operation.name().toLowerCase(Locale.ROOT); - scopesFromContext(parameters.getExecutionContext().getGraphQLContext()) + hubFromContext(parameters.getExecutionContext().getGraphQLContext()) .addBreadcrumb( Breadcrumb.graphqlOperation( operationDefinition.getName(), @@ -283,11 +273,11 @@ private boolean isIgnored(final @Nullable String errorType) { return super.beginExecuteOperation(parameters); } - private @NotNull IScopes scopesFromContext(final @Nullable GraphQLContext context) { + private @NotNull IHub hubFromContext(final @Nullable GraphQLContext context) { if (context == null) { - return NoOpScopes.getInstance(); + return NoOpHub.getInstance(); } - return context.getOrDefault(SENTRY_SCOPES_CONTEXT_KEY, NoOpScopes.getInstance()); + return context.getOrDefault(SENTRY_HUB_CONTEXT_KEY, NoOpHub.getInstance()); } @Override @@ -303,16 +293,13 @@ private boolean isIgnored(final @Nullable String errorType) { return environment -> { final @Nullable ExecutionStepInfo executionStepInfo = environment.getExecutionStepInfo(); if (executionStepInfo != null) { - Hint hint = new Hint(); - hint.set(TypeCheckHint.GRAPHQL_DATA_FETCHING_ENVIRONMENT, environment); - scopesFromContext(parameters.getExecutionContext().getGraphQLContext()) + hubFromContext(parameters.getExecutionContext().getGraphQLContext()) .addBreadcrumb( Breadcrumb.graphqlDataFetcher( StringUtils.toString(executionStepInfo.getPath()), GraphqlStringUtils.fieldToString(executionStepInfo.getField()), GraphqlStringUtils.typeToString(executionStepInfo.getType()), - GraphqlStringUtils.objectTypeToString(executionStepInfo.getObjectType())), - hint); + GraphqlStringUtils.objectTypeToString(executionStepInfo.getObjectType()))); } final TracingState tracingState = parameters.getInstrumentationState(); final ISpan transaction = tracingState.getTransaction(); @@ -364,7 +351,7 @@ private boolean isIgnored(final @Nullable String errorType) { environment.getOperationDefinition().getOperation())) { return subscriptionHandler.onSubscriptionResult( tmpResult, - scopesFromContext(environment.getGraphQlContext()), + hubFromContext(environment.getGraphQlContext()), exceptionReporter, parameters); } @@ -403,13 +390,13 @@ private void finish( } else { parent = (GraphQLObjectType) type; } - final @NotNull SpanOptions spanOptions = new SpanOptions(); - spanOptions.setOrigin(TRACE_ORIGIN); + final @NotNull ISpan span = transaction.startChild( "graphql", - parent.getName() + "." + parameters.getExecutionStepInfo().getPath().getSegmentName(), - spanOptions); + parent.getName() + "." + parameters.getExecutionStepInfo().getPath().getSegmentName()); + + span.getSpanContext().setOrigin(TRACE_ORIGIN); return span; } diff --git a/sentry-graphql/src/main/java/io/sentry/graphql/SentrySubscriptionHandler.java b/sentry-graphql/src/main/java/io/sentry/graphql/SentrySubscriptionHandler.java index 0a5538ce22..bfc962b501 100644 --- a/sentry-graphql/src/main/java/io/sentry/graphql/SentrySubscriptionHandler.java +++ b/sentry-graphql/src/main/java/io/sentry/graphql/SentrySubscriptionHandler.java @@ -1,14 +1,14 @@ package io.sentry.graphql; import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; -import io.sentry.IScopes; +import io.sentry.IHub; import org.jetbrains.annotations.NotNull; public interface SentrySubscriptionHandler { @NotNull Object onSubscriptionResult( @NotNull Object result, - @NotNull IScopes scopes, + @NotNull IHub hub, @NotNull ExceptionReporter exceptionReporter, @NotNull InstrumentationFieldFetchParameters parameters); } diff --git a/sentry-graphql/src/test/kotlin/io/sentry/graphql/ExceptionReporterTest.kt b/sentry-graphql/src/test/kotlin/io/sentry/graphql/ExceptionReporterTest.kt index 3a798a2f86..a2b2b0f101 100644 --- a/sentry-graphql/src/test/kotlin/io/sentry/graphql/ExceptionReporterTest.kt +++ b/sentry-graphql/src/test/kotlin/io/sentry/graphql/ExceptionReporterTest.kt @@ -12,8 +12,8 @@ import graphql.schema.GraphQLObjectType import graphql.schema.GraphQLScalarType import graphql.schema.GraphQLSchema import io.sentry.Hint +import io.sentry.IHub import io.sentry.IScope -import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -39,7 +39,7 @@ class ExceptionReporterTest { it.maxRequestBodySize = SentryOptions.RequestSize.ALWAYS } val exception = IllegalStateException("some exception") - val scopes = mock() + val hub = mock() lateinit var instrumentationExecutionParameters: InstrumentationExecutionParameters lateinit var executionResult: ExecutionResult lateinit var scope: IScope @@ -47,7 +47,7 @@ class ExceptionReporterTest { val variables = mapOf("variableA" to "value a") fun getSut(options: SentryOptions = defaultOptions, captureRequestBodyForNonSubscriptions: Boolean = true): ExceptionReporter { - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) scope = Scope(options) val exceptionReporter = ExceptionReporter(captureRequestBodyForNonSubscriptions) executionResult = ExecutionResultImpl.newExecutionResult() @@ -77,7 +77,7 @@ class ExceptionReporterTest { ).build() val instrumentationState = SentryInstrumentation.TracingState() instrumentationExecutionParameters = InstrumentationExecutionParameters(executionInput, schema, instrumentationState) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) return exceptionReporter } @@ -88,9 +88,9 @@ class ExceptionReporterTest { @Test fun `captures throwable`() { val exceptionReporter = fixture.getSut() - exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.scopes, fixture.instrumentationExecutionParameters, false), fixture.executionResult) + exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.hub, fixture.instrumentationExecutionParameters, false), fixture.executionResult) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( org.mockito.kotlin.check { val ex = it.throwableMechanism as ExceptionMechanismException assertFalse(ex.exceptionMechanism.isHandled!!) @@ -112,9 +112,9 @@ class ExceptionReporterTest { val exceptionReporter = fixture.getSut() val headers = mapOf("some-header" to "some-header-value") fixture.scope.request = Request().also { it.headers = headers } - exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.scopes, fixture.instrumentationExecutionParameters, false), fixture.executionResult) + exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.hub, fixture.instrumentationExecutionParameters, false), fixture.executionResult) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( org.mockito.kotlin.check { val ex = it.throwableMechanism as ExceptionMechanismException assertFalse(ex.exceptionMechanism.isHandled!!) @@ -136,9 +136,9 @@ class ExceptionReporterTest { @Test fun `does not attach query or variables if spring`() { val exceptionReporter = fixture.getSut(captureRequestBodyForNonSubscriptions = false) - exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.scopes, fixture.instrumentationExecutionParameters, false), fixture.executionResult) + exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.hub, fixture.instrumentationExecutionParameters, false), fixture.executionResult) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( org.mockito.kotlin.check { val ex = it.throwableMechanism as ExceptionMechanismException assertFalse(ex.exceptionMechanism.isHandled!!) @@ -156,9 +156,9 @@ class ExceptionReporterTest { @Test fun `does not attach query or variables if no max body size is set`() { val exceptionReporter = fixture.getSut(SentryOptions().also { it.isSendDefaultPii = true }, false) - exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.scopes, fixture.instrumentationExecutionParameters, false), fixture.executionResult) + exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.hub, fixture.instrumentationExecutionParameters, false), fixture.executionResult) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( org.mockito.kotlin.check { val ex = it.throwableMechanism as ExceptionMechanismException assertFalse(ex.exceptionMechanism.isHandled!!) @@ -176,9 +176,9 @@ class ExceptionReporterTest { @Test fun `does not attach query or variables if sendDefaultPii is false`() { val exceptionReporter = fixture.getSut(SentryOptions().also { it.maxRequestBodySize = SentryOptions.RequestSize.ALWAYS }, false) - exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.scopes, fixture.instrumentationExecutionParameters, false), fixture.executionResult) + exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.hub, fixture.instrumentationExecutionParameters, false), fixture.executionResult) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( org.mockito.kotlin.check { val ex = it.throwableMechanism as ExceptionMechanismException assertFalse(ex.exceptionMechanism.isHandled!!) @@ -196,9 +196,9 @@ class ExceptionReporterTest { @Test fun `attaches query and variables if spring and subscription`() { val exceptionReporter = fixture.getSut(captureRequestBodyForNonSubscriptions = false) - exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.scopes, fixture.instrumentationExecutionParameters, true), fixture.executionResult) + exceptionReporter.captureThrowable(fixture.exception, ExceptionReporter.ExceptionDetails(fixture.hub, fixture.instrumentationExecutionParameters, true), fixture.executionResult) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( org.mockito.kotlin.check { val ex = it.throwableMechanism as ExceptionMechanismException assertFalse(ex.exceptionMechanism.isHandled!!) diff --git a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryDataFetcherExceptionHandlerTest.kt b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryDataFetcherExceptionHandlerTest.kt index de51abac21..b571fa8218 100644 --- a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryDataFetcherExceptionHandlerTest.kt +++ b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryDataFetcherExceptionHandlerTest.kt @@ -3,7 +3,7 @@ package io.sentry.graphql import graphql.execution.DataFetcherExceptionHandler import graphql.execution.DataFetcherExceptionHandlerParameters import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.eq import org.mockito.kotlin.mock @@ -14,15 +14,15 @@ class SentryDataFetcherExceptionHandlerTest { @Test fun `passes exception to Sentry and invokes delegate`() { - val scopes = mock() + val hub = mock() val delegate = mock() - val handler = SentryDataFetcherExceptionHandler(scopes, delegate) + val handler = SentryDataFetcherExceptionHandler(hub, delegate) val exception = RuntimeException() val parameters = DataFetcherExceptionHandlerParameters.newExceptionParameters().exception(exception).build() handler.onException(parameters) - verify(scopes).captureException(eq(exception), anyOrNull()) + verify(hub).captureException(eq(exception), anyOrNull()) verify(delegate).handleException(parameters) } } diff --git a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryGenericDataFetcherExceptionHandlerTest.kt b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryGenericDataFetcherExceptionHandlerTest.kt index 88e2f5df55..6d643baf01 100644 --- a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryGenericDataFetcherExceptionHandlerTest.kt +++ b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryGenericDataFetcherExceptionHandlerTest.kt @@ -4,7 +4,7 @@ import graphql.GraphQLContext import graphql.execution.DataFetcherExceptionHandler import graphql.execution.DataFetcherExceptionHandlerParameters import graphql.schema.DataFetchingEnvironmentImpl -import io.sentry.IScopes +import io.sentry.IHub import org.mockito.kotlin.mock import org.mockito.kotlin.verify import kotlin.test.Test @@ -15,10 +15,10 @@ class SentryGenericDataFetcherExceptionHandlerTest { @Test fun `collects exception into GraphQLContext and invokes delegate`() { - val scopes = mock() + val hub = mock() val delegate = mock() val handler = SentryGenericDataFetcherExceptionHandler( - scopes, + hub, delegate ) diff --git a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationAnotherTest.kt b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationAnotherTest.kt index 7b673a66a8..e30bbc9415 100644 --- a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationAnotherTest.kt +++ b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationAnotherTest.kt @@ -28,13 +28,11 @@ import graphql.schema.GraphQLObjectType import graphql.schema.GraphQLScalarType import graphql.schema.GraphQLSchema import io.sentry.Breadcrumb -import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.SentryTracer import io.sentry.TransactionContext -import io.sentry.TypeCheckHint import io.sentry.graphql.ExceptionReporter.ExceptionDetails import io.sentry.graphql.SentryInstrumentation.SENTRY_EXCEPTIONS_CONTEXT_KEY import io.sentry.graphql.SentryInstrumentation.TracingState @@ -54,7 +52,7 @@ import kotlin.test.assertSame class SentryInstrumentationAnotherTest { class Fixture { - val scopes = mock() + val hub = mock() lateinit var activeSpan: SentryTracer lateinit var dataFetcher: DataFetcher lateinit var fieldFetchParameters: InstrumentationFieldFetchParameters @@ -72,17 +70,17 @@ class SentryInstrumentationAnotherTest { val variables = mapOf("variableA" to "value a") fun getSut(isTransactionActive: Boolean = true, operation: OperationDefinition.Operation = OperationDefinition.Operation.QUERY, graphQLContextParam: Map? = null, addTransactionToTracingState: Boolean = true, ignoredErrors: List = emptyList()): SentryInstrumentation { - whenever(scopes.options).thenReturn(SentryOptions()) - activeSpan = SentryTracer(TransactionContext("name", "op"), scopes) + whenever(hub.options).thenReturn(SentryOptions()) + activeSpan = SentryTracer(TransactionContext("name", "op"), hub) if (isTransactionActive) { - whenever(scopes.span).thenReturn(activeSpan) + whenever(hub.span).thenReturn(activeSpan) } else { - whenever(scopes.span).thenReturn(null) + whenever(hub.span).thenReturn(null) } val defaultGraphQLContext = mapOf( - SentryInstrumentation.SENTRY_SCOPES_CONTEXT_KEY to scopes + SentryInstrumentation.SENTRY_HUB_CONTEXT_KEY to hub ) val mergedField = MergedField.newMergedField().addField(Field.newField("myFieldName").build()).build() @@ -167,7 +165,7 @@ class SentryInstrumentationAnotherTest { val result = instrumentedDataFetcher.get(fixture.environment) assertEquals("result modified by subscription handler", result) - verify(fixture.subscriptionHandler).onSubscriptionResult(eq("raw result"), same(fixture.scopes), same(fixture.exceptionReporter), same(fixture.fieldFetchParameters)) + verify(fixture.subscriptionHandler).onSubscriptionResult(eq("raw result"), same(fixture.hub), same(fixture.exceptionReporter), same(fixture.fieldFetchParameters)) } @Test @@ -177,7 +175,7 @@ class SentryInstrumentationAnotherTest { val result = instrumentedDataFetcher.get(fixture.environment) assertEquals("result modified by subscription handler", result) - verify(fixture.subscriptionHandler).onSubscriptionResult(eq("raw result"), same(fixture.scopes), same(fixture.exceptionReporter), same(fixture.fieldFetchParameters)) + verify(fixture.subscriptionHandler).onSubscriptionResult(eq("raw result"), same(fixture.hub), same(fixture.exceptionReporter), same(fixture.fieldFetchParameters)) } @Test @@ -224,7 +222,7 @@ class SentryInstrumentationAnotherTest { fun `adds a breadcrumb for operation`() { val instrumentation = fixture.getSut() instrumentation.beginExecuteOperation(fixture.instrumentationExecuteOperationParameters) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( org.mockito.kotlin.check { breadcrumb -> assertEquals("graphql", breadcrumb.type) assertEquals("query", breadcrumb.category) @@ -239,7 +237,7 @@ class SentryInstrumentationAnotherTest { fun `adds a breadcrumb for data fetcher`() { val instrumentation = fixture.getSut() instrumentation.instrumentDataFetcher(fixture.dataFetcher, fixture.fieldFetchParameters).get(fixture.environment) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( org.mockito.kotlin.check { breadcrumb -> assertEquals("graphql", breadcrumb.type) assertEquals("graphql.fetcher", breadcrumb.category) @@ -247,20 +245,16 @@ class SentryInstrumentationAnotherTest { assertEquals("myFieldName", breadcrumb.data["field"]) assertEquals("MyResponseType", breadcrumb.data["type"]) assertEquals("QUERY", breadcrumb.data["object_type"]) - }, - org.mockito.kotlin.check { hint -> - val environment = hint.getAs(TypeCheckHint.GRAPHQL_DATA_FETCHING_ENVIRONMENT, DataFetchingEnvironment::class.java) - assertNotNull(environment) } ) } @Test - fun `stores scopes in context and adds transaction to state`() { + fun `stores hub in context and adds transaction to state`() { val instrumentation = fixture.getSut(isTransactionActive = true, operation = OperationDefinition.Operation.MUTATION, graphQLContextParam = emptyMap(), addTransactionToTracingState = false) - withMockScopes { + withMockHub { instrumentation.beginExecution(fixture.instrumentationExecutionParameters) - assertSame(fixture.scopes, fixture.instrumentationExecutionParameters.graphQLContext.get(SentryInstrumentation.SENTRY_SCOPES_CONTEXT_KEY)) + assertSame(fixture.hub, fixture.instrumentationExecutionParameters.graphQLContext.get(SentryInstrumentation.SENTRY_HUB_CONTEXT_KEY)) assertNotNull(fixture.instrumentationState.transaction) } } @@ -282,7 +276,7 @@ class SentryInstrumentationAnotherTest { assertEquals("exception message", it.message) }, org.mockito.kotlin.check { - assertSame(fixture.scopes, it.scopes) + assertSame(fixture.hub, it.hub) assertSame(fixture.query, it.query) assertEquals(false, it.isSubscription) assertEquals(fixture.variables, it.variables) @@ -299,7 +293,7 @@ class SentryInstrumentationAnotherTest { val instrumentation = fixture.getSut( graphQLContextParam = mapOf( SENTRY_EXCEPTIONS_CONTEXT_KEY to listOf(exception), - SentryInstrumentation.SENTRY_SCOPES_CONTEXT_KEY to fixture.scopes + SentryInstrumentation.SENTRY_HUB_CONTEXT_KEY to fixture.hub ) ) val executionResult = ExecutionResultImpl.newExecutionResult() @@ -311,7 +305,7 @@ class SentryInstrumentationAnotherTest { assertSame(exception, it) }, org.mockito.kotlin.check { - assertSame(fixture.scopes, it.scopes) + assertSame(fixture.hub, it.hub) assertSame(fixture.query, it.query) assertEquals(false, it.isSubscription) assertEquals(fixture.variables, it.variables) @@ -362,8 +356,8 @@ class SentryInstrumentationAnotherTest { assertSame(executionResult, result) } - fun withMockScopes(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { - it.`when` { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes) + fun withMockHub(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { + it.`when` { Sentry.getCurrentHub() }.thenReturn(fixture.hub) closure.invoke() } diff --git a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationTest.kt b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationTest.kt index 7128b839e3..8a579e2687 100644 --- a/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationTest.kt +++ b/sentry-graphql/src/test/kotlin/io/sentry/graphql/SentryInstrumentationTest.kt @@ -18,7 +18,7 @@ import graphql.schema.GraphQLScalarType import graphql.schema.idl.RuntimeWiring import graphql.schema.idl.SchemaGenerator import graphql.schema.idl.SchemaParser -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -39,12 +39,12 @@ import kotlin.test.assertTrue class SentryInstrumentationTest { class Fixture { - val scopes = mock() + val hub = mock() lateinit var activeSpan: SentryTracer fun getSut(isTransactionActive: Boolean = true, dataFetcherThrows: Boolean = false, beforeSpan: SentryInstrumentation.BeforeSpanCallback? = null): GraphQL { - whenever(scopes.options).thenReturn(SentryOptions()) - activeSpan = SentryTracer(TransactionContext("name", "op"), scopes) + whenever(hub.options).thenReturn(SentryOptions()) + activeSpan = SentryTracer(TransactionContext("name", "op"), hub) val schema = """ type Query { shows: [Show] @@ -61,9 +61,9 @@ class SentryInstrumentationTest { .build() if (isTransactionActive) { - whenever(scopes.span).thenReturn(activeSpan) + whenever(hub.span).thenReturn(activeSpan) } else { - whenever(scopes.span).thenReturn(null) + whenever(hub.span).thenReturn(null) } return graphQL @@ -87,7 +87,7 @@ class SentryInstrumentationTest { fun `when transaction is active, creates inner spans`() { val sut = fixture.getSut() - withMockScopes { + withMockHub { val result = sut.execute("{ shows { id } }") assertTrue(result.errors.isEmpty()) @@ -105,7 +105,7 @@ class SentryInstrumentationTest { fun `when transaction is active, and data fetcher throws, creates inner spans`() { val sut = fixture.getSut(dataFetcherThrows = true) - withMockScopes { + withMockHub { val result = sut.execute("{ shows { id } }") assertTrue(result.errors.isNotEmpty()) @@ -122,7 +122,7 @@ class SentryInstrumentationTest { fun `when transaction is not active, does not create spans`() { val sut = fixture.getSut(isTransactionActive = false) - withMockScopes { + withMockHub { val result = sut.execute("{ shows { id } }") assertTrue(result.errors.isEmpty()) @@ -134,7 +134,7 @@ class SentryInstrumentationTest { fun `beforeSpan can drop spans`() { val sut = fixture.getSut(beforeSpan = SentryInstrumentation.BeforeSpanCallback { _, _, _ -> null }) - withMockScopes { + withMockHub { val result = sut.execute("{ shows { id } }") assertTrue(result.errors.isEmpty()) @@ -152,7 +152,7 @@ class SentryInstrumentationTest { fun `beforeSpan can modify spans`() { val sut = fixture.getSut(beforeSpan = SentryInstrumentation.BeforeSpanCallback { span, _, _ -> span.apply { description = "changed" } }) - withMockScopes { + withMockHub { val result = sut.execute("{ shows { id } }") assertTrue(result.errors.isEmpty()) @@ -208,19 +208,19 @@ class SentryInstrumentationTest { @Test fun `Integration adds itself to integration and package list`() { - withMockScopes { + withMockHub { val sut = fixture.getSut() - assertNotNull(fixture.scopes.options.sdkVersion) - assert(fixture.scopes.options.sdkVersion!!.integrationSet.contains("GraphQL")) + assertNotNull(fixture.hub.options.sdkVersion) + assert(fixture.hub.options.sdkVersion!!.integrationSet.contains("GraphQL")) val packageInfo = - fixture.scopes.options.sdkVersion!!.packageSet.firstOrNull { pkg -> pkg.name == "maven:io.sentry:sentry-graphql" } + fixture.hub.options.sdkVersion!!.packageSet.firstOrNull { pkg -> pkg.name == "maven:io.sentry:sentry-graphql" } assertNotNull(packageInfo) assert(packageInfo.version == BuildConfig.VERSION_NAME) } } - fun withMockScopes(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { - it.`when` { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes) + fun withMockHub(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { + it.`when` { Sentry.getCurrentHub() }.thenReturn(fixture.hub) closure.invoke() } diff --git a/sentry-jdbc/api/sentry-jdbc.api b/sentry-jdbc/api/sentry-jdbc.api index 700cbb2d69..cff0f37fd2 100644 --- a/sentry-jdbc/api/sentry-jdbc.api +++ b/sentry-jdbc/api/sentry-jdbc.api @@ -16,7 +16,7 @@ public final class io/sentry/jdbc/DatabaseUtils$DatabaseDetails { public class io/sentry/jdbc/SentryJdbcEventListener : com/p6spy/engine/event/SimpleJdbcEventListener { public fun ()V - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun onAfterAnyExecute (Lcom/p6spy/engine/common/StatementInformation;JLjava/sql/SQLException;)V public fun onBeforeAnyExecute (Lcom/p6spy/engine/common/StatementInformation;)V } diff --git a/sentry-jdbc/src/main/java/io/sentry/jdbc/SentryJdbcEventListener.java b/sentry-jdbc/src/main/java/io/sentry/jdbc/SentryJdbcEventListener.java index 4f45a67cc9..0346d2d0b9 100644 --- a/sentry-jdbc/src/main/java/io/sentry/jdbc/SentryJdbcEventListener.java +++ b/sentry-jdbc/src/main/java/io/sentry/jdbc/SentryJdbcEventListener.java @@ -6,12 +6,11 @@ import com.jakewharton.nopen.annotation.Open; import com.p6spy.engine.common.StatementInformation; import com.p6spy.engine.event.SimpleJdbcEventListener; -import io.sentry.IScopes; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ISpan; -import io.sentry.ScopesAdapter; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.Span; -import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; import java.sql.SQLException; @@ -22,29 +21,28 @@ @Open public class SentryJdbcEventListener extends SimpleJdbcEventListener { private static final String TRACE_ORIGIN = "auto.db.jdbc"; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private static final @NotNull ThreadLocal CURRENT_SPAN = new ThreadLocal<>(); private volatile @Nullable DatabaseUtils.DatabaseDetails cachedDatabaseDetails = null; private final @NotNull Object databaseDetailsLock = new Object(); - public SentryJdbcEventListener(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + public SentryJdbcEventListener(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "hub is required"); addPackageAndIntegrationInfo(); } public SentryJdbcEventListener() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } @Override public void onBeforeAnyExecute(final @NotNull StatementInformation statementInformation) { - final ISpan parent = scopes.getSpan(); + final ISpan parent = hub.getSpan(); if (parent != null && !parent.isNoOp()) { - final @NotNull SpanOptions spanOptions = new SpanOptions(); - spanOptions.setOrigin(TRACE_ORIGIN); - final ISpan span = parent.startChild("db.query", statementInformation.getSql(), spanOptions); + final ISpan span = parent.startChild("db.query", statementInformation.getSql()); CURRENT_SPAN.set(span); + span.getSpanContext().setOrigin(TRACE_ORIGIN); } } diff --git a/sentry-jdbc/src/test/kotlin/io/sentry/jdbc/SentryJdbcEventListenerTest.kt b/sentry-jdbc/src/test/kotlin/io/sentry/jdbc/SentryJdbcEventListenerTest.kt index 00ce03de41..78c5d4cf12 100644 --- a/sentry-jdbc/src/test/kotlin/io/sentry/jdbc/SentryJdbcEventListenerTest.kt +++ b/sentry-jdbc/src/test/kotlin/io/sentry/jdbc/SentryJdbcEventListenerTest.kt @@ -2,7 +2,7 @@ package io.sentry.jdbc import com.p6spy.engine.common.StatementInformation import com.p6spy.engine.spy.P6DataSource -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryOptions import io.sentry.SentryTracer import io.sentry.SpanDataConvention.DB_NAME_KEY @@ -26,7 +26,7 @@ import kotlin.test.assertTrue class SentryJdbcEventListenerTest { class Fixture { - val scopes = mock().apply { + val hub = mock().apply { whenever(options).thenReturn( SentryOptions().apply { sdkVersion = SdkVersion("test", "1.2.3") @@ -37,9 +37,9 @@ class SentryJdbcEventListenerTest { val actualDataSource = JDBCDataSource() fun getSut(withRunningTransaction: Boolean = true, existingRow: Int? = null): DataSource { - tx = SentryTracer(TransactionContext("name", "op"), scopes) + tx = SentryTracer(TransactionContext("name", "op"), hub) if (withRunningTransaction) { - whenever(scopes.span).thenReturn(tx) + whenever(hub.span).thenReturn(tx) } actualDataSource.setURL("jdbc:hsqldb:mem:testdb") @@ -54,7 +54,7 @@ class SentryJdbcEventListenerTest { } } - val sentryQueryExecutionListener = SentryJdbcEventListener(scopes) + val sentryQueryExecutionListener = SentryJdbcEventListener(hub) val p6spyDataSource = P6DataSource(actualDataSource) p6spyDataSource.setJdbcEventListenerFactory { sentryQueryExecutionListener } return p6spyDataSource @@ -131,9 +131,9 @@ class SentryJdbcEventListenerTest { @Test fun `sets SDKVersion Info`() { val sut = fixture.getSut() - assertNotNull(fixture.scopes.options.sdkVersion) - assert(fixture.scopes.options.sdkVersion!!.integrationSet.contains("JDBC")) - val packageInfo = fixture.scopes.options.sdkVersion!!.packageSet.firstOrNull { pkg -> pkg.name == "maven:io.sentry:sentry-jdbc" } + assertNotNull(fixture.hub.options.sdkVersion) + assert(fixture.hub.options.sdkVersion!!.integrationSet.contains("JDBC")) + val packageInfo = fixture.hub.options.sdkVersion!!.packageSet.firstOrNull { pkg -> pkg.name == "maven:io.sentry:sentry-jdbc" } assertNotNull(packageInfo) assert(packageInfo.version == BuildConfig.VERSION_NAME) } diff --git a/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java b/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java index c52b4706f2..2ca775572b 100644 --- a/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java +++ b/sentry-jul/src/main/java/io/sentry/jul/SentryHandler.java @@ -6,7 +6,7 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.ScopesAdapter; +import io.sentry.HubAdapter; import io.sentry.Sentry; import io.sentry.SentryEvent; import io.sentry.SentryIntegrationPackageStorage; @@ -210,9 +210,9 @@ SentryEvent createEvent(final @NotNull LogRecord record) { mdcProperties = CollectionUtils.filterMapEntries(mdcProperties, entry -> entry.getValue() != null); if (!mdcProperties.isEmpty()) { - // get tags from ScopesAdapter options to allow getting the correct tags if Sentry has been + // get tags from HubAdapter options to allow getting the correct tags if Sentry has been // initialized somewhere else - final List contextTags = ScopesAdapter.getInstance().getOptions().getContextTags(); + final List contextTags = HubAdapter.getInstance().getOptions().getContextTags(); if (!contextTags.isEmpty()) { for (final String contextTag : contextTags) { // if mdc tag is listed in SentryOptions, apply as event tag diff --git a/sentry-kotlin-extensions/api/sentry-kotlin-extensions.api b/sentry-kotlin-extensions/api/sentry-kotlin-extensions.api index 7e3be67279..d501240a3a 100644 --- a/sentry-kotlin-extensions/api/sentry-kotlin-extensions.api +++ b/sentry-kotlin-extensions/api/sentry-kotlin-extensions.api @@ -1,16 +1,16 @@ public final class io/sentry/kotlin/SentryContext : kotlin/coroutines/AbstractCoroutineContextElement, kotlinx/coroutines/CopyableThreadContextElement { public fun ()V - public fun (Lio/sentry/IScopes;)V - public synthetic fun (Lio/sentry/IScopes;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IHub;)V + public synthetic fun (Lio/sentry/IHub;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun copyForChild ()Lkotlinx/coroutines/CopyableThreadContextElement; public fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public fun get (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext$Element; public fun mergeForChild (Lkotlin/coroutines/CoroutineContext$Element;)Lkotlin/coroutines/CoroutineContext; public fun minusKey (Lkotlin/coroutines/CoroutineContext$Key;)Lkotlin/coroutines/CoroutineContext; public fun plus (Lkotlin/coroutines/CoroutineContext;)Lkotlin/coroutines/CoroutineContext; - public fun restoreThreadContext (Lkotlin/coroutines/CoroutineContext;Lio/sentry/IScopes;)V + public fun restoreThreadContext (Lkotlin/coroutines/CoroutineContext;Lio/sentry/IHub;)V public synthetic fun restoreThreadContext (Lkotlin/coroutines/CoroutineContext;Ljava/lang/Object;)V - public fun updateThreadContext (Lkotlin/coroutines/CoroutineContext;)Lio/sentry/IScopes; + public fun updateThreadContext (Lkotlin/coroutines/CoroutineContext;)Lio/sentry/IHub; public synthetic fun updateThreadContext (Lkotlin/coroutines/CoroutineContext;)Ljava/lang/Object; } diff --git a/sentry-kotlin-extensions/src/main/java/io/sentry/kotlin/SentryContext.kt b/sentry-kotlin-extensions/src/main/java/io/sentry/kotlin/SentryContext.kt index a77281a033..3cf22a20da 100644 --- a/sentry-kotlin-extensions/src/main/java/io/sentry/kotlin/SentryContext.kt +++ b/sentry-kotlin-extensions/src/main/java/io/sentry/kotlin/SentryContext.kt @@ -1,6 +1,6 @@ package io.sentry.kotlin -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.Sentry import kotlinx.coroutines.CopyableThreadContextElement import kotlin.coroutines.AbstractCoroutineContextElement @@ -9,28 +9,26 @@ import kotlin.coroutines.CoroutineContext /** * Sentry context element for [CoroutineContext]. */ -public class SentryContext(private val scopes: IScopes = Sentry.forkedCurrentScope("coroutine")) : - CopyableThreadContextElement, AbstractCoroutineContextElement(Key) { +public class SentryContext(private val hub: IHub = Sentry.getCurrentHub().clone()) : + CopyableThreadContextElement, AbstractCoroutineContextElement(Key) { private companion object Key : CoroutineContext.Key - @SuppressWarnings("deprecation") - override fun copyForChild(): CopyableThreadContextElement { - return SentryContext(scopes.forkedCurrentScope("coroutine.child")) + override fun copyForChild(): CopyableThreadContextElement { + return SentryContext(hub.clone()) } - @SuppressWarnings("deprecation") override fun mergeForChild(overwritingElement: CoroutineContext.Element): CoroutineContext { - return overwritingElement[Key] ?: SentryContext(scopes.forkedCurrentScope("coroutine.child")) + return overwritingElement[Key] ?: SentryContext(hub.clone()) } - override fun updateThreadContext(context: CoroutineContext): IScopes { - val oldState = Sentry.getCurrentScopes() - Sentry.setCurrentScopes(scopes) + override fun updateThreadContext(context: CoroutineContext): IHub { + val oldState = Sentry.getCurrentHub() + Sentry.setCurrentHub(hub) return oldState } - override fun restoreThreadContext(context: CoroutineContext, oldState: IScopes) { - Sentry.setCurrentScopes(oldState) + override fun restoreThreadContext(context: CoroutineContext, oldState: IHub) { + Sentry.setCurrentHub(oldState) } } diff --git a/sentry-kotlin-extensions/src/test/java/io/sentry/kotlin/SentryContextTest.kt b/sentry-kotlin-extensions/src/test/java/io/sentry/kotlin/SentryContextTest.kt index 9cffd744d8..b54ceabc51 100644 --- a/sentry-kotlin-extensions/src/test/java/io/sentry/kotlin/SentryContextTest.kt +++ b/sentry-kotlin-extensions/src/test/java/io/sentry/kotlin/SentryContextTest.kt @@ -1,6 +1,5 @@ package io.sentry.kotlin -import io.sentry.ScopeType import io.sentry.Sentry import kotlinx.coroutines.CoroutineName import kotlinx.coroutines.joinAll @@ -16,10 +15,6 @@ import kotlin.test.assertNull class SentryContextTest { - // TODO [HSM] In global hub mode SentryContext behaves differently - // because Sentry.getCurrentScopes always returns rootScopes - // What's the desired behaviour? - @BeforeTest fun init() { Sentry.init("https://key@sentry.io/123") @@ -43,40 +38,11 @@ class SentryContextTest { Sentry.setTag("c2", "c2value") assertEquals("c2value", getTag("c2")) assertEquals("parentValue", getTag("parent")) - assertNotNull(getTag("c1")) - } - listOf(c1, c2).joinAll() - assertNotNull(getTag("parent")) - assertNotNull(getTag("c1")) - assertNotNull(getTag("c2")) - return@runBlocking - } - - @Test - fun testContextIsNotPassedByDefaultBetweenCoroutinesCurrentScope() = runBlocking { - Sentry.configureScope(ScopeType.CURRENT) { scope -> - scope.setTag("parent", "parentValue") - } - val c1 = launch(SentryContext()) { - Sentry.configureScope(ScopeType.CURRENT) { scope -> - scope.setTag("c1", "c1value") - } - assertEquals("c1value", getTag("c1", ScopeType.CURRENT)) - assertEquals("parentValue", getTag("parent", ScopeType.CURRENT)) - assertNull(getTag("c2", ScopeType.CURRENT)) - } - val c2 = launch(SentryContext()) { - Sentry.configureScope(ScopeType.CURRENT) { scope -> - scope.setTag("c2", "c2value") - } - assertEquals("c2value", getTag("c2", ScopeType.CURRENT)) - assertEquals("parentValue", getTag("parent", ScopeType.CURRENT)) - assertNull(getTag("c1", ScopeType.CURRENT)) + assertNull(getTag("c1")) } listOf(c1, c2).joinAll() - assertNotNull(getTag("parent", ScopeType.CURRENT)) - assertNull(getTag("c1", ScopeType.CURRENT)) - assertNull(getTag("c2", ScopeType.CURRENT)) + assertNull(getTag("c1")) + assertNull(getTag("c2")) } @Test @@ -118,7 +84,7 @@ class SentryContextTest { } @Test - fun testContextIsClonedWhenPassedToChildCurrentScope() = runBlocking { + fun testContextIsClonedWhenPassedToChild() = runBlocking { Sentry.setTag("parent", "parentValue") launch(SentryContext()) { Sentry.setTag("c1", "c1value") @@ -136,44 +102,10 @@ class SentryContextTest { c2.join() assertNotNull(getTag("c1")) - assertNotNull(getTag("c2")) - }.join() - assertNotNull(getTag("parent")) - assertNotNull(getTag("c1")) - assertNotNull(getTag("c2")) - return@runBlocking - } - - @Test - fun testContextIsClonedWhenPassedToChild() = runBlocking { - Sentry.configureScope(ScopeType.CURRENT) { scope -> - scope.setTag("parent", "parentValue") + assertNull(getTag("c2")) } - launch(SentryContext()) { - Sentry.configureScope(ScopeType.CURRENT) { scope -> - scope.setTag("c1", "c1value") - } - assertEquals("c1value", getTag("c1", ScopeType.CURRENT)) - assertEquals("parentValue", getTag("parent", ScopeType.CURRENT)) - assertNull(getTag("c2", ScopeType.CURRENT)) - - val c2 = launch() { - Sentry.configureScope(ScopeType.CURRENT) { scope -> - scope.setTag("c2", "c2value") - } - assertEquals("c2value", getTag("c2", ScopeType.CURRENT)) - assertEquals("parentValue", getTag("parent", ScopeType.CURRENT)) - assertNotNull(getTag("c1", ScopeType.CURRENT)) - } - - c2.join() - - assertNotNull(getTag("c1", ScopeType.CURRENT)) - assertNull(getTag("c2", ScopeType.CURRENT)) - }.join() - assertNotNull(getTag("parent", ScopeType.CURRENT)) - assertNull(getTag("c1", ScopeType.CURRENT)) - assertNull(getTag("c2", ScopeType.CURRENT)) + assertNull(getTag("c1")) + assertNull(getTag("c2")) } @Test @@ -187,7 +119,7 @@ class SentryContextTest { val c2 = launch( SentryContext( - Sentry.getCurrentScopes().forkedScopes("test").also { + Sentry.getCurrentHub().clone().also { it.setTag("cloned", "clonedValue") } ) @@ -204,60 +136,16 @@ class SentryContextTest { assertNotNull(getTag("c1")) assertNull(getTag("c2")) assertNull(getTag("cloned")) - }.join() - - assertNotNull(getTag("c1")) + } + assertNull(getTag("c1")) assertNull(getTag("c2")) assertNull(getTag("cloned")) - return@runBlocking - } - - @Test - fun testExplicitlyPassedContextOverridesPropagatedContextCurrentScope() = runBlocking { - Sentry.configureScope(ScopeType.CURRENT) { scope -> - scope.setTag("parent", "parentValue") - } - launch(SentryContext()) { - Sentry.configureScope(ScopeType.CURRENT) { scope -> - scope.setTag("c1", "c1value") - } - assertEquals("c1value", getTag("c1", ScopeType.CURRENT)) - assertEquals("parentValue", getTag("parent", ScopeType.CURRENT)) - assertNull(getTag("c2", ScopeType.CURRENT)) - - val c2 = launch( - SentryContext( - Sentry.getCurrentScopes().forkedCurrentScope("test").also { - it.configureScope(ScopeType.CURRENT) { scope -> - scope.setTag("cloned", "clonedValue") - } - } - ) - ) { - Sentry.configureScope(ScopeType.CURRENT) { scope -> - scope.setTag("c2", "c2value") - } - assertEquals("c2value", getTag("c2", ScopeType.CURRENT)) - assertEquals("parentValue", getTag("parent", ScopeType.CURRENT)) - assertNotNull(getTag("c1", ScopeType.CURRENT)) - assertNotNull(getTag("cloned", ScopeType.CURRENT)) - } - - c2.join() - - assertNotNull(getTag("c1", ScopeType.CURRENT)) - assertNull(getTag("c2", ScopeType.CURRENT)) - assertNull(getTag("cloned", ScopeType.CURRENT)) - } - assertNull(getTag("c1", ScopeType.CURRENT)) - assertNull(getTag("c2", ScopeType.CURRENT)) - assertNull(getTag("cloned", ScopeType.CURRENT)) } @Test fun `mergeForChild returns copy of initial context if Key not present`() { val initialContextElement = SentryContext( - Sentry.getCurrentScopes().forkedScopes("test").also { + Sentry.getCurrentHub().clone().also { it.setTag("cloned", "clonedValue") } ) @@ -270,7 +158,7 @@ class SentryContextTest { @Test fun `mergeForChild returns passed context`() { val initialContextElement = SentryContext( - Sentry.getCurrentScopes().forkedScopes("test").also { + Sentry.getCurrentHub().clone().also { it.setTag("cloned", "clonedValue") } ) @@ -279,9 +167,9 @@ class SentryContextTest { assertEquals(initialContextElement, mergedContextElement) } - private fun getTag(tag: String, scopeType: ScopeType = ScopeType.ISOLATION): String? { + private fun getTag(tag: String): String? { var value: String? = null - Sentry.configureScope(scopeType) { + Sentry.configureScope { value = it.tags[tag] } return value diff --git a/sentry-log4j2/api/sentry-log4j2.api b/sentry-log4j2/api/sentry-log4j2.api index b7fe8b3273..76aa3e823e 100644 --- a/sentry-log4j2/api/sentry-log4j2.api +++ b/sentry-log4j2/api/sentry-log4j2.api @@ -5,7 +5,7 @@ public final class io/sentry/log4j2/BuildConfig { public class io/sentry/log4j2/SentryAppender : org/apache/logging/log4j/core/appender/AbstractAppender { public static final field MECHANISM_TYPE Ljava/lang/String; - public fun (Ljava/lang/String;Lorg/apache/logging/log4j/core/Filter;Ljava/lang/String;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Ljava/lang/Boolean;Lio/sentry/ITransportFactory;Lio/sentry/IScopes;[Ljava/lang/String;)V + public fun (Ljava/lang/String;Lorg/apache/logging/log4j/core/Filter;Ljava/lang/String;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Ljava/lang/Boolean;Lio/sentry/ITransportFactory;Lio/sentry/IHub;[Ljava/lang/String;)V public fun append (Lorg/apache/logging/log4j/core/LogEvent;)V public static fun createAppender (Ljava/lang/String;Lorg/apache/logging/log4j/Level;Lorg/apache/logging/log4j/Level;Ljava/lang/String;Ljava/lang/Boolean;Lorg/apache/logging/log4j/core/Filter;Ljava/lang/String;)Lio/sentry/log4j2/SentryAppender; protected fun createBreadcrumb (Lorg/apache/logging/log4j/core/LogEvent;)Lio/sentry/Breadcrumb; diff --git a/sentry-log4j2/src/main/java/io/sentry/log4j2/SentryAppender.java b/sentry-log4j2/src/main/java/io/sentry/log4j2/SentryAppender.java index 4ee07ab7b9..4cf4ad4a86 100644 --- a/sentry-log4j2/src/main/java/io/sentry/log4j2/SentryAppender.java +++ b/sentry-log4j2/src/main/java/io/sentry/log4j2/SentryAppender.java @@ -7,9 +7,9 @@ import io.sentry.Breadcrumb; import io.sentry.DateUtils; import io.sentry.Hint; -import io.sentry.IScopes; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ITransportFactory; -import io.sentry.ScopesAdapter; import io.sentry.Sentry; import io.sentry.SentryEvent; import io.sentry.SentryIntegrationPackageStorage; @@ -50,7 +50,7 @@ public class SentryAppender extends AbstractAppender { private @NotNull Level minimumBreadcrumbLevel = Level.INFO; private @NotNull Level minimumEventLevel = Level.ERROR; private final @Nullable Boolean debug; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private final @Nullable List contextTags; public SentryAppender( @@ -61,7 +61,7 @@ public SentryAppender( final @Nullable Level minimumEventLevel, final @Nullable Boolean debug, final @Nullable ITransportFactory transportFactory, - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @Nullable String[] contextTags) { super(name, filter, null, true, null); this.dsn = dsn; @@ -73,7 +73,7 @@ public SentryAppender( } this.debug = debug; this.transportFactory = transportFactory; - this.scopes = scopes; + this.hub = hub; this.contextTags = contextTags != null ? Arrays.asList(contextTags) : null; } @@ -110,7 +110,7 @@ public SentryAppender( minimumEventLevel, debug, null, - ScopesAdapter.getInstance(), + HubAdapter.getInstance(), contextTags != null ? contextTags.split(",") : null); } @@ -149,13 +149,13 @@ public void append(final @NotNull LogEvent eventObject) { final Hint hint = new Hint(); hint.set(SENTRY_SYNTHETIC_EXCEPTION, eventObject); - scopes.captureEvent(createEvent(eventObject), hint); + hub.captureEvent(createEvent(eventObject), hint); } if (eventObject.getLevel().isMoreSpecificThan(minimumBreadcrumbLevel)) { final Hint hint = new Hint(); hint.set(LOG4J_LOG_EVENT, eventObject); - scopes.addBreadcrumb(createBreadcrumb(eventObject), hint); + hub.addBreadcrumb(createBreadcrumb(eventObject), hint); } } @@ -199,9 +199,9 @@ public void append(final @NotNull LogEvent eventObject) { CollectionUtils.filterMapEntries( loggingEvent.getContextData().toMap(), entry -> entry.getValue() != null); if (!contextData.isEmpty()) { - // get tags from ScopesAdapter options to allow getting the correct tags if Sentry has been + // get tags from HubAdapter options to allow getting the correct tags if Sentry has been // initialized somewhere else - final List contextTags = scopes.getOptions().getContextTags(); + final List contextTags = hub.getOptions().getContextTags(); if (contextTags != null && !contextTags.isEmpty()) { for (final String contextTag : contextTags) { // if mdc tag is listed in SentryOptions, apply as event tag diff --git a/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt b/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt index e24f12d659..a99096d315 100644 --- a/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt +++ b/sentry-log4j2/src/test/kotlin/io/sentry/log4j2/SentryAppenderTest.kt @@ -1,7 +1,7 @@ package io.sentry.log4j2 +import io.sentry.HubAdapter import io.sentry.ITransportFactory -import io.sentry.ScopesAdapter import io.sentry.Sentry import io.sentry.SentryLevel import io.sentry.checkEvent @@ -49,7 +49,7 @@ class SentryAppenderTest { } loggerContext.start() val config: Configuration = loggerContext.configuration - val appender = SentryAppender("sentry", null, "http://key@localhost/proj", minimumBreadcrumbLevel, minimumEventLevel, debug, this.transportFactory, ScopesAdapter.getInstance(), contextTags?.toTypedArray()) + val appender = SentryAppender("sentry", null, "http://key@localhost/proj", minimumBreadcrumbLevel, minimumEventLevel, debug, this.transportFactory, HubAdapter.getInstance(), contextTags?.toTypedArray()) config.addAppender(appender) val ref = AppenderRef.createAppenderRef("sentry", null, null) @@ -78,7 +78,6 @@ class SentryAppenderTest { @BeforeTest fun `clear MDC`() { ThreadContext.clearAll() - Sentry.close() } @Test @@ -447,6 +446,6 @@ class SentryAppenderTest { @Test fun `sets the debug mode`() { fixture.getSut(debug = true) - assertTrue(ScopesAdapter.getInstance().options.isDebug) + assertTrue(HubAdapter.getInstance().options.isDebug) } } diff --git a/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java b/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java index 56db0b4dbc..d0be108149 100644 --- a/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java +++ b/sentry-logback/src/main/java/io/sentry/logback/SentryAppender.java @@ -12,8 +12,8 @@ import io.sentry.Breadcrumb; import io.sentry.DateUtils; import io.sentry.Hint; +import io.sentry.HubAdapter; import io.sentry.ITransportFactory; -import io.sentry.ScopesAdapter; import io.sentry.Sentry; import io.sentry.SentryEvent; import io.sentry.SentryIntegrationPackageStorage; @@ -134,9 +134,9 @@ protected void append(@NotNull ILoggingEvent eventObject) { CollectionUtils.filterMapEntries( loggingEvent.getMDCPropertyMap(), entry -> entry.getValue() != null); if (!mdcProperties.isEmpty()) { - // get tags from ScopesAdapter options to allow getting the correct tags if Sentry has been + // get tags from HubAdapter options to allow getting the correct tags if Sentry has been // initialized somewhere else - final List contextTags = ScopesAdapter.getInstance().getOptions().getContextTags(); + final List contextTags = HubAdapter.getInstance().getOptions().getContextTags(); if (!contextTags.isEmpty()) { for (final String contextTag : contextTags) { // if mdc tag is listed in SentryOptions, apply as event tag diff --git a/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt b/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt index c08837b3ff..b7797cadc7 100644 --- a/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt +++ b/sentry-logback/src/test/kotlin/io/sentry/logback/SentryAppenderTest.kt @@ -35,18 +35,17 @@ import kotlin.test.assertNull import kotlin.test.assertTrue class SentryAppenderTest { - private class Fixture(dsn: String? = "http://key@localhost/proj", minimumBreadcrumbLevel: Level? = null, minimumEventLevel: Level? = null, contextTags: List? = null, encoder: Encoder? = null, sendDefaultPii: Boolean = false, options: SentryOptions = SentryOptions(), startLater: Boolean = false) { + private class Fixture(dsn: String? = "http://key@localhost/proj", minimumBreadcrumbLevel: Level? = null, minimumEventLevel: Level? = null, contextTags: List? = null, encoder: Encoder? = null, sendDefaultPii: Boolean = false) { val logger: Logger = LoggerFactory.getLogger(SentryAppenderTest::class.java) val loggerContext = LoggerFactory.getILoggerFactory() as LoggerContext val transportFactory = mock() val transport = mock() val utcTimeZone: ZoneId = ZoneId.of("UTC") - val appender = SentryAppender() - var encoder: Encoder? = null init { whenever(this.transportFactory.create(any(), any())).thenReturn(transport) - this.encoder = encoder + val appender = SentryAppender() + val options = SentryOptions() options.dsn = dsn options.isSendDefaultPii = sendDefaultPii contextTags?.forEach { options.addContextTag(it) } @@ -60,12 +59,6 @@ class SentryAppenderTest { val rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME) rootLogger.level = Level.TRACE rootLogger.addAppender(appender) - if (!startLater) { - start() - } - } - - fun start() { appender.start() encoder?.start() loggerContext.start() @@ -84,31 +77,22 @@ class SentryAppenderTest { @BeforeTest fun `clear MDC`() { MDC.clear() - Sentry.close() } @Test fun `does not initialize Sentry if Sentry is already enabled`() { - fixture = Fixture( - startLater = true, - options = SentryOptions().also { - it.setTag("only-present-if-logger-init-was-run", "another-value") - } - ) + fixture = Fixture() Sentry.init { it.dsn = "http://key@localhost/proj" it.environment = "manual-environment" it.setTransportFactory(fixture.transportFactory) - it.setTag("tag-from-first-init", "some-value") it.isEnableBackpressureHandling = false } - fixture.start() - fixture.logger.error("testing environment field") verify(fixture.transport).send( checkEvent { event -> - assertNull(event.tags?.get("only-present-if-logger-init-was-run")) + assertEquals("manual-environment", event.environment) }, anyOrNull() ) diff --git a/sentry-okhttp/api/sentry-okhttp.api b/sentry-okhttp/api/sentry-okhttp.api index 9cb875ff34..3095659c88 100644 --- a/sentry-okhttp/api/sentry-okhttp.api +++ b/sentry-okhttp/api/sentry-okhttp.api @@ -6,12 +6,12 @@ public final class io/sentry/okhttp/BuildConfig { public class io/sentry/okhttp/SentryOkHttpEventListener : okhttp3/EventListener { public static final field Companion Lio/sentry/okhttp/SentryOkHttpEventListener$Companion; public fun ()V - public fun (Lio/sentry/IScopes;Lkotlin/jvm/functions/Function1;)V - public synthetic fun (Lio/sentry/IScopes;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Lio/sentry/IScopes;Lokhttp3/EventListener$Factory;)V - public synthetic fun (Lio/sentry/IScopes;Lokhttp3/EventListener$Factory;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Lio/sentry/IScopes;Lokhttp3/EventListener;)V - public synthetic fun (Lio/sentry/IScopes;Lokhttp3/EventListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IHub;Lkotlin/jvm/functions/Function1;)V + public synthetic fun (Lio/sentry/IHub;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IHub;Lokhttp3/EventListener$Factory;)V + public synthetic fun (Lio/sentry/IHub;Lokhttp3/EventListener$Factory;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IHub;Lokhttp3/EventListener;)V + public synthetic fun (Lio/sentry/IHub;Lokhttp3/EventListener;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun (Lokhttp3/EventListener$Factory;)V public fun (Lokhttp3/EventListener;)V public fun cacheConditionalHit (Lokhttp3/Call;Lokhttp3/Response;)V @@ -50,9 +50,9 @@ public final class io/sentry/okhttp/SentryOkHttpEventListener$Companion { public class io/sentry/okhttp/SentryOkHttpInterceptor : okhttp3/Interceptor { public fun ()V - public fun (Lio/sentry/IScopes;)V - public fun (Lio/sentry/IScopes;Lio/sentry/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;ZLjava/util/List;Ljava/util/List;)V - public synthetic fun (Lio/sentry/IScopes;Lio/sentry/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;ZLjava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IHub;Lio/sentry/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;ZLjava/util/List;Ljava/util/List;)V + public synthetic fun (Lio/sentry/IHub;Lio/sentry/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;ZLjava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun (Lio/sentry/okhttp/SentryOkHttpInterceptor$BeforeSpanCallback;)V public fun intercept (Lokhttp3/Interceptor$Chain;)Lokhttp3/Response; } diff --git a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt index 28d7375450..21a3329a14 100644 --- a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt +++ b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEvent.kt @@ -2,7 +2,7 @@ package io.sentry.okhttp import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ISpan import io.sentry.SentryDate import io.sentry.SentryLevel @@ -30,7 +30,7 @@ private const val RESPONSE_BODY_TIMEOUT_MILLIS = 800L internal const val TRACE_ORIGIN = "auto.http.okhttp" @Suppress("TooManyFunctions") -internal class SentryOkHttpEvent(private val scopes: IScopes, private val request: Request) { +internal class SentryOkHttpEvent(private val hub: IHub, private val request: Request) { private val eventSpans: MutableMap = ConcurrentHashMap() private val breadcrumb: Breadcrumb internal val callRootSpan: ISpan? @@ -49,7 +49,7 @@ internal class SentryOkHttpEvent(private val scopes: IScopes, private val reques method = request.method // We start the call span that will contain all the others - val parentSpan = if (Platform.isAndroid()) scopes.transaction else scopes.span + val parentSpan = if (Platform.isAndroid()) hub.transaction else hub.span callRootSpan = parentSpan?.startChild("http.client", "$method $url") callRootSpan?.spanContext?.origin = TRACE_ORIGIN urlDetails.applyToSpan(callRootSpan) @@ -63,7 +63,7 @@ internal class SentryOkHttpEvent(private val scopes: IScopes, private val reques callRootSpan?.setData("url", url) callRootSpan?.setData("host", host) callRootSpan?.setData("path", encodedPath) - callRootSpan?.setData(SpanDataConvention.HTTP_METHOD_KEY, method.uppercase(Locale.ROOT)) + callRootSpan?.setData(SpanDataConvention.HTTP_METHOD_KEY, method.toUpperCase(Locale.ROOT)) } /** @@ -151,13 +151,13 @@ internal class SentryOkHttpEvent(private val scopes: IScopes, private val reques response?.let { hint.set(TypeCheckHint.OKHTTP_RESPONSE, it) } // We send the breadcrumb even without spans. - scopes.addBreadcrumb(breadcrumb, hint) + hub.addBreadcrumb(breadcrumb, hint) // No span is created (e.g. no transaction is running) if (callRootSpan == null) { // We report the client error even without spans. clientErrorResponse?.let { - SentryOkHttpUtils.captureClientError(scopes, it.request, it) + SentryOkHttpUtils.captureClientError(hub, it.request, it) } return } @@ -175,7 +175,7 @@ internal class SentryOkHttpEvent(private val scopes: IScopes, private val reques // We report the client error here, after all sub-spans finished, so that it will be bound // to the root call span. clientErrorResponse?.let { - SentryOkHttpUtils.captureClientError(scopes, it.request, it) + SentryOkHttpUtils.captureClientError(hub, it.request, it) } if (finishDate != null) { callRootSpan.finish(callRootSpan.status, finishDate) @@ -206,7 +206,7 @@ internal class SentryOkHttpEvent(private val scopes: IScopes, private val reques fun scheduleFinish(timestamp: SentryDate) { try { - scopes.options.executorService.schedule({ + hub.options.executorService.schedule({ if (!isReadingResponseBody.get() && (eventSpans.values.all { it.isFinished } || callRootSpan?.isFinished != true) ) { @@ -214,7 +214,7 @@ internal class SentryOkHttpEvent(private val scopes: IScopes, private val reques } }, RESPONSE_BODY_TIMEOUT_MILLIS) } catch (e: RejectedExecutionException) { - scopes.options + hub.options .logger .log( SentryLevel.ERROR, diff --git a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEventListener.kt b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEventListener.kt index b9595af115..67a8cd8b56 100644 --- a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEventListener.kt +++ b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEventListener.kt @@ -1,7 +1,7 @@ package io.sentry.okhttp -import io.sentry.IScopes -import io.sentry.ScopesAdapter +import io.sentry.HubAdapter +import io.sentry.IHub import io.sentry.SpanDataConvention import io.sentry.SpanStatus import okhttp3.Call @@ -41,7 +41,7 @@ import java.util.concurrent.ConcurrentHashMap */ @Suppress("TooManyFunctions") public open class SentryOkHttpEventListener( - private val scopes: IScopes = ScopesAdapter.getInstance(), + private val hub: IHub = HubAdapter.getInstance(), private val originalEventListenerCreator: ((call: Call) -> EventListener)? = null ) : EventListener() { @@ -62,27 +62,27 @@ public open class SentryOkHttpEventListener( } public constructor() : this( - ScopesAdapter.getInstance(), + HubAdapter.getInstance(), originalEventListenerCreator = null ) public constructor(originalEventListener: EventListener) : this( - ScopesAdapter.getInstance(), + HubAdapter.getInstance(), originalEventListenerCreator = { originalEventListener } ) public constructor(originalEventListenerFactory: Factory) : this( - ScopesAdapter.getInstance(), + HubAdapter.getInstance(), originalEventListenerCreator = { originalEventListenerFactory.create(it) } ) - public constructor(scopes: IScopes = ScopesAdapter.getInstance(), originalEventListener: EventListener) : this( - scopes, + public constructor(hub: IHub = HubAdapter.getInstance(), originalEventListener: EventListener) : this( + hub, originalEventListenerCreator = { originalEventListener } ) - public constructor(scopes: IScopes = ScopesAdapter.getInstance(), originalEventListenerFactory: Factory) : this( - scopes, + public constructor(hub: IHub = HubAdapter.getInstance(), originalEventListenerFactory: Factory) : this( + hub, originalEventListenerCreator = { originalEventListenerFactory.create(it) } ) @@ -92,7 +92,7 @@ public open class SentryOkHttpEventListener( // If the wrapped EventListener is ours, we can just delegate the calls, // without creating other events that would create duplicates if (canCreateEventSpan()) { - eventMap[call] = SentryOkHttpEvent(scopes, call.request()) + eventMap[call] = SentryOkHttpEvent(hub, call.request()) } } @@ -318,7 +318,7 @@ public open class SentryOkHttpEventListener( it.status = SpanStatus.fromHttpStatusCode(response.code) } } - okHttpEvent.scheduleFinish(responseHeadersSpan?.finishDate ?: scopes.options.dateProvider.now()) + okHttpEvent.scheduleFinish(responseHeadersSpan?.finishDate ?: hub.options.dateProvider.now()) } override fun responseBodyStart(call: Call) { @@ -406,9 +406,6 @@ public open class SentryOkHttpEventListener( private fun canCreateEventSpan(): Boolean { // If the wrapped EventListener is ours, we shouldn't create spans, as the originalEventListener already did it - // In case SentryOkHttpEventListener from sentry-android-okhttp is used, the is check won't work so we check - // for the class name as well. - return originalEventListener !is SentryOkHttpEventListener && - "io.sentry.android.okhttp.SentryOkHttpEventListener" != originalEventListener?.javaClass?.name + return originalEventListener !is SentryOkHttpEventListener } } diff --git a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpInterceptor.kt b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpInterceptor.kt index a079864612..efa472963d 100644 --- a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpInterceptor.kt +++ b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpInterceptor.kt @@ -4,9 +4,9 @@ import io.sentry.BaggageHeader import io.sentry.Breadcrumb import io.sentry.Hint import io.sentry.HttpStatusCodeRange -import io.sentry.IScopes +import io.sentry.HubAdapter +import io.sentry.IHub import io.sentry.ISpan -import io.sentry.ScopesAdapter import io.sentry.SentryIntegrationPackageStorage import io.sentry.SentryOptions.DEFAULT_PROPAGATION_TARGETS import io.sentry.SpanDataConvention @@ -29,7 +29,7 @@ import java.io.IOException * out of the active span bound to the scope for each HTTP Request. * If [captureFailedRequests] is enabled, the SDK will capture HTTP Client errors as well. * - * @param scopes The [IScopes], internal and only used for testing. + * @param hub The [IHub], internal and only used for testing. * @param beforeSpan The [ISpan] can be customized or dropped with the [BeforeSpanCallback]. * @param captureFailedRequests The SDK will only capture HTTP Client errors if it is enabled, * Defaults to false. @@ -39,7 +39,7 @@ import java.io.IOException * is a match for any of the defined targets. */ public open class SentryOkHttpInterceptor( - private val scopes: IScopes = ScopesAdapter.getInstance(), + private val hub: IHub = HubAdapter.getInstance(), private val beforeSpan: BeforeSpanCallback? = null, private val captureFailedRequests: Boolean = true, private val failedRequestStatusCodes: List = listOf( @@ -48,9 +48,9 @@ public open class SentryOkHttpInterceptor( private val failedRequestTargets: List = listOf(DEFAULT_PROPAGATION_TARGETS) ) : Interceptor { - public constructor() : this(ScopesAdapter.getInstance()) - public constructor(scopes: IScopes) : this(scopes, null) - public constructor(beforeSpan: BeforeSpanCallback) : this(ScopesAdapter.getInstance(), beforeSpan) + public constructor() : this(HubAdapter.getInstance()) + public constructor(hub: IHub) : this(hub, null) + public constructor(beforeSpan: BeforeSpanCallback) : this(HubAdapter.getInstance(), beforeSpan) init { addIntegrationToSdkVersion(javaClass) @@ -76,7 +76,7 @@ public open class SentryOkHttpInterceptor( } else { // read the span from the bound scope okHttpEvent = null - val parentSpan = if (Platform.isAndroid()) scopes.transaction else scopes.span + val parentSpan = if (Platform.isAndroid()) hub.transaction else hub.span span = parentSpan?.startChild("http.client", "$method $url") } @@ -92,7 +92,7 @@ public open class SentryOkHttpInterceptor( val requestBuilder = request.newBuilder() TracingUtils.traceIfAllowed( - scopes, + hub, request.url.toString(), request.headers(BaggageHeader.BAGGAGE_HEADER), span @@ -121,7 +121,7 @@ public open class SentryOkHttpInterceptor( if (isFromEventListener && okHttpEvent != null) { okHttpEvent.setClientErrorResponse(response) } else { - SentryOkHttpUtils.captureClientError(scopes, request, response) + SentryOkHttpUtils.captureClientError(hub, request, response) } } @@ -157,7 +157,7 @@ public open class SentryOkHttpInterceptor( hint[OKHTTP_RESPONSE] = it } - scopes.addBreadcrumb(breadcrumb, hint) + hub.addBreadcrumb(breadcrumb, hint) } private fun finishSpan(span: ISpan?, request: Request, response: Response?, isFromEventListener: Boolean) { diff --git a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpUtils.kt b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpUtils.kt index eea35ca22e..0cfc1c5a75 100644 --- a/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpUtils.kt +++ b/sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpUtils.kt @@ -1,7 +1,7 @@ package io.sentry.okhttp import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryEvent import io.sentry.TypeCheckHint import io.sentry.exception.ExceptionMechanismException @@ -15,7 +15,7 @@ import okhttp3.Response internal object SentryOkHttpUtils { - internal fun captureClientError(scopes: IScopes, request: Request, response: Response) { + internal fun captureClientError(hub: IHub, request: Request, response: Response) { // not possible to get a parameterized url, but we remove at least the // query string and the fragment. // url example: https://api.github.com/users/getsentry/repos/#fragment?query=query @@ -40,9 +40,9 @@ internal object SentryOkHttpUtils { val sentryRequest = io.sentry.protocol.Request().apply { urlDetails.applyToRequest(this) // Cookie is only sent if isSendDefaultPii is enabled - cookies = if (scopes.options.isSendDefaultPii) request.headers["Cookie"] else null + cookies = if (hub.options.isSendDefaultPii) request.headers["Cookie"] else null method = request.method - headers = getHeaders(scopes, request.headers) + headers = getHeaders(hub, request.headers) request.body?.contentLength().ifHasValidLength { bodySize = it @@ -51,8 +51,8 @@ internal object SentryOkHttpUtils { val sentryResponse = io.sentry.protocol.Response().apply { // Set-Cookie is only sent if isSendDefaultPii is enabled due to PII - cookies = if (scopes.options.isSendDefaultPii) response.headers["Set-Cookie"] else null - headers = getHeaders(scopes, response.headers) + cookies = if (hub.options.isSendDefaultPii) response.headers["Set-Cookie"] else null + headers = getHeaders(hub, response.headers) statusCode = response.code response.body?.contentLength().ifHasValidLength { @@ -63,7 +63,7 @@ internal object SentryOkHttpUtils { event.request = sentryRequest event.contexts.setResponse(sentryResponse) - scopes.captureEvent(event, hint) + hub.captureEvent(event, hint) } private fun Long?.ifHasValidLength(fn: (Long) -> Unit) { @@ -72,9 +72,9 @@ internal object SentryOkHttpUtils { } } - private fun getHeaders(scopes: IScopes, requestHeaders: Headers): MutableMap? { + private fun getHeaders(hub: IHub, requestHeaders: Headers): MutableMap? { // Headers are only sent if isSendDefaultPii is enabled due to PII - if (!scopes.options.isSendDefaultPii) { + if (!hub.options.isSendDefaultPii) { return null } diff --git a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventListenerTest.kt b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventListenerTest.kt index 388b494547..88727c4c0d 100644 --- a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventListenerTest.kt +++ b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventListenerTest.kt @@ -1,7 +1,7 @@ package io.sentry.okhttp import io.sentry.BaggageHeader -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryOptions import io.sentry.SentryTraceHeader import io.sentry.SentryTracer @@ -36,7 +36,7 @@ import kotlin.test.assertTrue class SentryOkHttpEventListenerTest { class Fixture { - val scopes = mock() + val hub = mock() val server = MockWebServer() val mockEventListener = mock() val mockEventListenerFactory = mock() @@ -63,12 +63,12 @@ class SentryOkHttpEventListenerTest { isSendDefaultPii = sendDefaultPii configureOptions(this) } - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) - sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) + sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) if (isSpanActive) { - whenever(scopes.span).thenReturn(sentryTracer) + whenever(hub.span).thenReturn(sentryTracer) } server.enqueue( MockResponse() @@ -80,12 +80,12 @@ class SentryOkHttpEventListenerTest { val builder = OkHttpClient.Builder() if (useInterceptor) { - builder.addInterceptor(SentryOkHttpInterceptor(scopes)) + builder.addInterceptor(SentryOkHttpInterceptor(hub)) } sentryOkHttpEventListener = when { - eventListenerFactory != null -> SentryOkHttpEventListener(scopes, eventListenerFactory) - eventListener != null -> SentryOkHttpEventListener(scopes, eventListener) - else -> SentryOkHttpEventListener(scopes) + eventListenerFactory != null -> SentryOkHttpEventListener(hub, eventListenerFactory) + eventListener != null -> SentryOkHttpEventListener(hub, eventListener) + else -> SentryOkHttpEventListener(hub) } return builder.eventListener(sentryOkHttpEventListener).build() } @@ -283,7 +283,7 @@ class SentryOkHttpEventListenerTest { @Test fun `propagate all calls to the SentryOkHttpEventListener passed in the ctor`() { - val originalListener = spy(SentryOkHttpEventListener(fixture.scopes, fixture.mockEventListener)) + val originalListener = spy(SentryOkHttpEventListener(fixture.hub, fixture.mockEventListener)) val sut = fixture.getSut(eventListener = originalListener) val listener = fixture.sentryOkHttpEventListener val request = postRequest(body = "requestBody") @@ -295,7 +295,7 @@ class SentryOkHttpEventListenerTest { @Test fun `propagate all calls to the SentryOkHttpEventListener factory passed in the ctor`() { - val originalListener = spy(SentryOkHttpEventListener(fixture.scopes, fixture.mockEventListener)) + val originalListener = spy(SentryOkHttpEventListener(fixture.hub, fixture.mockEventListener)) val sut = fixture.getSut(eventListenerFactory = { originalListener }) val listener = fixture.sentryOkHttpEventListener val request = postRequest(body = "requestBody") @@ -307,7 +307,7 @@ class SentryOkHttpEventListenerTest { @Test fun `does not duplicated spans if an SentryOkHttpEventListener is passed in the ctor`() { - val originalListener = spy(SentryOkHttpEventListener(fixture.scopes, fixture.mockEventListener)) + val originalListener = spy(SentryOkHttpEventListener(fixture.hub, fixture.mockEventListener)) val sut = fixture.getSut(eventListener = originalListener) val request = postRequest(body = "requestBody") val call = sut.newCall(request) @@ -370,8 +370,8 @@ class SentryOkHttpEventListenerTest { @Test fun `responseHeadersEnd schedules event finish`() { - val listener = SentryOkHttpEventListener(fixture.scopes, fixture.mockEventListener) - whenever(fixture.scopes.options).thenReturn(SentryOptions()) + val listener = SentryOkHttpEventListener(fixture.hub, fixture.mockEventListener) + whenever(fixture.hub.options).thenReturn(SentryOptions()) val call = mock() whenever(call.request()).thenReturn(getRequest()) val okHttpEvent = mock() diff --git a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventTest.kt b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventTest.kt index 37482b824f..4b41b75c1e 100644 --- a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventTest.kt +++ b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventTest.kt @@ -2,7 +2,7 @@ package io.sentry.okhttp import io.sentry.Breadcrumb import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ISentryExecutorService import io.sentry.ISpan import io.sentry.SentryDate @@ -50,14 +50,14 @@ import kotlin.test.assertTrue class SentryOkHttpEventTest { private class Fixture { - val scopes = mock() + val hub = mock() val server = MockWebServer() val span: ISpan val mockRequest: Request val response: Response init { - whenever(scopes.options).thenReturn( + whenever(hub.options).thenReturn( SentryOptions().apply { dsn = "https://key@sentry.io/proj" } @@ -65,8 +65,9 @@ class SentryOkHttpEventTest { span = Span( TransactionContext("name", "op", TracesSamplingDecision(true)), - SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), scopes), - scopes, + SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), hub), + hub, + null, SpanOptions() ) @@ -85,7 +86,7 @@ class SentryOkHttpEventTest { } fun getSut(currentSpan: ISpan? = span, requestUrl: String ? = null): SentryOkHttpEvent { - whenever(scopes.span).thenReturn(currentSpan) + whenever(hub.span).thenReturn(currentSpan) val request = if (requestUrl == null) { mockRequest } else { @@ -95,7 +96,7 @@ class SentryOkHttpEventTest { .url(server.url(requestUrl)) .build() } - return SentryOkHttpEvent(scopes, request) + return SentryOkHttpEvent(hub, request) } } @@ -125,7 +126,7 @@ class SentryOkHttpEventTest { val sut = fixture.getSut(currentSpan = null) assertNull(sut.callRootSpan) sut.finishEvent() - verify(fixture.scopes).addBreadcrumb(any(), anyOrNull()) + verify(fixture.hub).addBreadcrumb(any(), anyOrNull()) } @Test @@ -241,7 +242,7 @@ class SentryOkHttpEventTest { fun `when finishEvent, a breadcrumb is captured with request in the hint`() { val sut = fixture.getSut() sut.finishEvent() - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals(fixture.mockRequest.url.toString(), it.data["url"]) assertEquals(fixture.mockRequest.url.host, it.data["host"]) @@ -259,7 +260,7 @@ class SentryOkHttpEventTest { val sut = fixture.getSut() sut.finishEvent() sut.finishEvent() - verify(fixture.scopes, times(1)).addBreadcrumb(any(), any()) + verify(fixture.hub, times(1)).addBreadcrumb(any(), any()) } @Test @@ -284,7 +285,7 @@ class SentryOkHttpEventTest { assertEquals(fixture.response.code, sut.callRootSpan?.getData(SpanDataConvention.HTTP_STATUS_CODE_KEY)) sut.finishEvent() - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals(fixture.response.protocol.name, it.data["protocol"]) assertEquals(fixture.response.code, it.data["status_code"]) @@ -301,7 +302,7 @@ class SentryOkHttpEventTest { sut.setProtocol("protocol") assertEquals("protocol", sut.callRootSpan?.getData("protocol")) sut.finishEvent() - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("protocol", it.data["protocol"]) }, @@ -315,7 +316,7 @@ class SentryOkHttpEventTest { sut.setProtocol(null) assertNull(sut.callRootSpan?.getData("protocol")) sut.finishEvent() - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertNull(it.data["protocol"]) }, @@ -329,7 +330,7 @@ class SentryOkHttpEventTest { sut.setRequestBodySize(10) assertEquals(10L, sut.callRootSpan?.getData("http.request_content_length")) sut.finishEvent() - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals(10L, it.data["request_content_length"]) }, @@ -343,7 +344,7 @@ class SentryOkHttpEventTest { sut.setRequestBodySize(-1) assertNull(sut.callRootSpan?.getData("http.request_content_length")) sut.finishEvent() - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertNull(it.data["request_content_length"]) }, @@ -357,7 +358,7 @@ class SentryOkHttpEventTest { sut.setResponseBodySize(10) assertEquals(10L, sut.callRootSpan?.getData(SpanDataConvention.HTTP_RESPONSE_CONTENT_LENGTH_KEY)) sut.finishEvent() - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals(10L, it.data["response_content_length"]) }, @@ -371,7 +372,7 @@ class SentryOkHttpEventTest { sut.setResponseBodySize(-1) assertNull(sut.callRootSpan?.getData(SpanDataConvention.HTTP_RESPONSE_CONTENT_LENGTH_KEY)) sut.finishEvent() - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertNull(it.data["response_content_length"]) }, @@ -385,7 +386,7 @@ class SentryOkHttpEventTest { sut.setError("errorMessage") assertEquals("errorMessage", sut.callRootSpan?.getData("error_message")) sut.finishEvent() - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("errorMessage", it.data["error_message"]) }, @@ -400,7 +401,7 @@ class SentryOkHttpEventTest { assertNotNull(sut.callRootSpan) assertNull(sut.callRootSpan.getData("error_message")) sut.finishEvent() - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertNull(it.data["error_message"]) }, @@ -533,7 +534,7 @@ class SentryOkHttpEventTest { @Test fun `scheduleFinish schedules finishEvent and finish running spans to specific timestamp`() { - fixture.scopes.options.executorService = ImmediateExecutorService() + fixture.hub.options.executorService = ImmediateExecutorService() val sut = spy(fixture.getSut()) val timestamp = mock() sut.startSpan(CONNECTION_EVENT) @@ -555,7 +556,7 @@ class SentryOkHttpEventTest { fun `scheduleFinish does not throw if executor is shut down`() { val executorService = mock() whenever(executorService.schedule(any(), any())).thenThrow(RejectedExecutionException()) - whenever(fixture.scopes.options).thenReturn(SentryOptions().apply { this.executorService = executorService }) + whenever(fixture.hub.options).thenReturn(SentryOptions().apply { this.executorService = executorService }) val sut = fixture.getSut() sut.scheduleFinish(mock()) } @@ -566,10 +567,10 @@ class SentryOkHttpEventTest { val clientErrorResponse = mock() whenever(clientErrorResponse.request).thenReturn(fixture.mockRequest) sut.setClientErrorResponse(clientErrorResponse) - verify(fixture.scopes, never()).captureEvent(any(), any()) + verify(fixture.hub, never()).captureEvent(any(), any()) sut.finishEvent() assertNotNull(sut.callRootSpan) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( argThat { throwable is SentryHttpClientException && throwable!!.message!!.startsWith("HTTP Client Error with status code: ") @@ -587,10 +588,10 @@ class SentryOkHttpEventTest { val clientErrorResponse = mock() whenever(clientErrorResponse.request).thenReturn(fixture.mockRequest) sut.setClientErrorResponse(clientErrorResponse) - verify(fixture.scopes, never()).captureEvent(any(), any()) + verify(fixture.hub, never()).captureEvent(any(), any()) sut.finishEvent() assertNull(sut.callRootSpan) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( argThat { throwable is SentryHttpClientException && throwable!!.message!!.startsWith("HTTP Client Error with status code: ") @@ -606,7 +607,7 @@ class SentryOkHttpEventTest { fun `when setClientErrorResponse is not called, no client error is captured`() { val sut = fixture.getSut() sut.finishEvent() - verify(fixture.scopes, never()).captureEvent(any(), any()) + verify(fixture.hub, never()).captureEvent(any(), any()) } /** Retrieve all the spans started in the event using reflection. */ diff --git a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt index f40b2c4cb5..fce16d9220 100644 --- a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt +++ b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpInterceptorTest.kt @@ -6,8 +6,8 @@ import io.sentry.BaggageHeader import io.sentry.Breadcrumb import io.sentry.Hint import io.sentry.HttpStatusCodeRange +import io.sentry.IHub import io.sentry.IScope -import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -47,7 +47,7 @@ import kotlin.test.fail class SentryOkHttpInterceptorTest { class Fixture { - val scopes = mock() + val hub = mock() val server = MockWebServer() lateinit var sentryTracer: SentryTracer lateinit var options: SentryOptions @@ -82,13 +82,13 @@ class SentryOkHttpInterceptorTest { isSendDefaultPii = sendDefaultPii } scope = Scope(options) - whenever(scopes.options).thenReturn(options) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) + whenever(hub.options).thenReturn(options) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) - sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) + sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) if (isSpanActive) { - whenever(scopes.span).thenReturn(sentryTracer) + whenever(hub.span).thenReturn(sentryTracer) } server.enqueue( MockResponse() @@ -100,14 +100,14 @@ class SentryOkHttpInterceptorTest { val interceptor = when (captureFailedRequests) { null -> SentryOkHttpInterceptor( - scopes, + hub, beforeSpan, failedRequestTargets = failedRequestTargets, failedRequestStatusCodes = failedRequestStatusCodes ) else -> SentryOkHttpInterceptor( - scopes, + hub, beforeSpan, captureFailedRequests = captureFailedRequests, failedRequestTargets = failedRequestTargets, @@ -281,7 +281,7 @@ class SentryOkHttpInterceptorTest { fun `adds breadcrumb when http calls succeeds`() { val sut = fixture.getSut(responseBody = "response body") sut.newCall(postRequest()).execute() - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(13L, it.data[SpanDataConvention.HTTP_RESPONSE_CONTENT_LENGTH_KEY]) @@ -296,7 +296,7 @@ class SentryOkHttpInterceptorTest { fun `adds breadcrumb when http calls results in exception`() { // to setup mocks fixture.getSut() - val interceptor = SentryOkHttpInterceptor(fixture.scopes) + val interceptor = SentryOkHttpInterceptor(fixture.hub) val chain = mock() whenever(chain.call()).thenReturn(mock()) whenever(chain.proceed(any())).thenThrow(IOException()) @@ -308,7 +308,7 @@ class SentryOkHttpInterceptorTest { } catch (e: IOException) { // ignore me } - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) }, @@ -385,7 +385,7 @@ class SentryOkHttpInterceptorTest { ) sut.newCall(getRequest()).execute() - verify(fixture.scopes).captureEvent(any(), any()) + verify(fixture.hub).captureEvent(any(), any()) } @Test @@ -396,7 +396,7 @@ class SentryOkHttpInterceptorTest { ) sut.newCall(getRequest()).execute() - verify(fixture.scopes).captureEvent(any(), any()) + verify(fixture.hub).captureEvent(any(), any()) } @Test @@ -406,7 +406,7 @@ class SentryOkHttpInterceptorTest { ) sut.newCall(getRequest()).execute() - verify(fixture.scopes, never()).captureEvent(any(), any()) + verify(fixture.hub, never()).captureEvent(any(), any()) } @Test @@ -417,7 +417,7 @@ class SentryOkHttpInterceptorTest { ) sut.newCall(getRequest()).execute() - verify(fixture.scopes, never()).captureEvent(any(), any()) + verify(fixture.hub, never()).captureEvent(any(), any()) } @Test @@ -429,7 +429,7 @@ class SentryOkHttpInterceptorTest { ) sut.newCall(getRequest()).execute() - verify(fixture.scopes, never()).captureEvent(any(), any()) + verify(fixture.hub, never()).captureEvent(any(), any()) } @Test @@ -440,7 +440,7 @@ class SentryOkHttpInterceptorTest { ) sut.newCall(getRequest()).execute() - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( any(), check { assertNotNull(it.get(TypeCheckHint.OKHTTP_REQUEST)) @@ -462,7 +462,7 @@ class SentryOkHttpInterceptorTest { val request = getRequest(url = "/hello?myQuery=myValue#myFragment") val response = sut.newCall(request).execute() - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { val sentryRequest = it.request!! assertEquals("http://localhost:${fixture.server.port}/hello", sentryRequest.url) @@ -503,7 +503,7 @@ class SentryOkHttpInterceptorTest { sut.newCall(postRequest(body = body)).execute() - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { val sentryRequest = it.request!! assertEquals(body.contentLength(), sentryRequest.bodySize) @@ -522,7 +522,7 @@ class SentryOkHttpInterceptorTest { sut.newCall(getRequest()).execute() - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( check { val sentryRequest = it.request!! assertEquals("myValue", sentryRequest.headers!!["myHeader"]) @@ -540,7 +540,7 @@ class SentryOkHttpInterceptorTest { // to setup mocks fixture.getSut() val interceptor = SentryOkHttpInterceptor( - fixture.scopes, + fixture.hub, captureFailedRequests = true ) val chain = mock() @@ -554,7 +554,7 @@ class SentryOkHttpInterceptorTest { } catch (e: IOException) { // ignore me } - verify(fixture.scopes, never()).captureEvent(any(), any()) + verify(fixture.hub, never()).captureEvent(any(), any()) } @Test @@ -565,7 +565,7 @@ class SentryOkHttpInterceptorTest { call.execute() val httpClientSpan = fixture.sentryTracer.children.firstOrNull() assertNull(httpClientSpan) - verify(fixture.scopes, never()).addBreadcrumb(any(), anyOrNull()) + verify(fixture.hub, never()).addBreadcrumb(any(), anyOrNull()) } @Test @@ -573,7 +573,7 @@ class SentryOkHttpInterceptorTest { val sut = fixture.getSut(captureFailedRequests = true, httpStatusCode = 500) val call = sut.newCall(getRequest()) call.execute() - verify(fixture.scopes).captureEvent(any(), any()) + verify(fixture.hub).captureEvent(any(), any()) } @Test @@ -582,6 +582,6 @@ class SentryOkHttpInterceptorTest { val call = sut.newCall(getRequest()) SentryOkHttpEventListener.eventMap[call] = mock() call.execute() - verify(fixture.scopes, never()).captureEvent(any(), any()) + verify(fixture.hub, never()).captureEvent(any(), any()) } } diff --git a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpUtilsTest.kt b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpUtilsTest.kt index c7194e5994..ec19454327 100644 --- a/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpUtilsTest.kt +++ b/sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpUtilsTest.kt @@ -1,7 +1,7 @@ package io.sentry.okhttp import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryOptions import io.sentry.SentryTracer import io.sentry.TransactionContext @@ -29,7 +29,7 @@ import kotlin.test.assertTrue class SentryOkHttpUtilsTest { class Fixture { - val scopes = mock() + val hub = mock() val server = MockWebServer() fun getSut( @@ -43,11 +43,11 @@ class SentryOkHttpUtilsTest { setTracePropagationTargets(listOf(server.hostName)) isSendDefaultPii = sendDefaultPii } - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) - val sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) + val sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) - whenever(scopes.span).thenReturn(sentryTracer) + whenever(hub.span).thenReturn(sentryTracer) server.enqueue( MockResponse() @@ -78,8 +78,8 @@ class SentryOkHttpUtilsTest { val request = getRequest() val response = sut.newCall(request).execute() - SentryOkHttpUtils.captureClientError(fixture.scopes, request, response) - verify(fixture.scopes).captureEvent( + SentryOkHttpUtils.captureClientError(fixture.hub, request, response) + verify(fixture.hub).captureEvent( check { val req = it.request val resp = it.contexts.response @@ -103,8 +103,8 @@ class SentryOkHttpUtilsTest { val request = getRequest() val response = sut.newCall(request).execute() - SentryOkHttpUtils.captureClientError(fixture.scopes, request, response) - verify(fixture.scopes).captureEvent( + SentryOkHttpUtils.captureClientError(fixture.hub, request, response) + verify(fixture.hub).captureEvent( check { val req = it.request val resp = it.contexts.response @@ -127,8 +127,8 @@ class SentryOkHttpUtilsTest { val request = getRequest() val response = sut.newCall(request).execute() - SentryOkHttpUtils.captureClientError(fixture.scopes, request, response) - verify(fixture.scopes).captureEvent( + SentryOkHttpUtils.captureClientError(fixture.hub, request, response) + verify(fixture.hub).captureEvent( check { val req = it.request val resp = it.contexts.response diff --git a/sentry-openfeign/api/sentry-openfeign.api b/sentry-openfeign/api/sentry-openfeign.api index 4ab65a5ca4..beb15c9e02 100644 --- a/sentry-openfeign/api/sentry-openfeign.api +++ b/sentry-openfeign/api/sentry-openfeign.api @@ -1,12 +1,12 @@ public final class io/sentry/openfeign/SentryCapability : feign/Capability { public fun ()V - public fun (Lio/sentry/IScopes;Lio/sentry/openfeign/SentryFeignClient$BeforeSpanCallback;)V + public fun (Lio/sentry/IHub;Lio/sentry/openfeign/SentryFeignClient$BeforeSpanCallback;)V public fun (Lio/sentry/openfeign/SentryFeignClient$BeforeSpanCallback;)V public fun enrich (Lfeign/Client;)Lfeign/Client; } public final class io/sentry/openfeign/SentryFeignClient : feign/Client { - public fun (Lfeign/Client;Lio/sentry/IScopes;Lio/sentry/openfeign/SentryFeignClient$BeforeSpanCallback;)V + public fun (Lfeign/Client;Lio/sentry/IHub;Lio/sentry/openfeign/SentryFeignClient$BeforeSpanCallback;)V public fun execute (Lfeign/Request;Lfeign/Request$Options;)Lfeign/Response; } diff --git a/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryCapability.java b/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryCapability.java index 1ad6b1f274..b65685c3fd 100644 --- a/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryCapability.java +++ b/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryCapability.java @@ -2,34 +2,33 @@ import feign.Capability; import feign.Client; -import io.sentry.IScopes; -import io.sentry.ScopesAdapter; +import io.sentry.HubAdapter; +import io.sentry.IHub; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** Adds Sentry tracing capability to Feign clients. */ public final class SentryCapability implements Capability { - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private final @Nullable SentryFeignClient.BeforeSpanCallback beforeSpan; public SentryCapability( - final @NotNull IScopes scopes, - final @Nullable SentryFeignClient.BeforeSpanCallback beforeSpan) { - this.scopes = scopes; + final @NotNull IHub hub, final @Nullable SentryFeignClient.BeforeSpanCallback beforeSpan) { + this.hub = hub; this.beforeSpan = beforeSpan; } public SentryCapability(final @Nullable SentryFeignClient.BeforeSpanCallback beforeSpan) { - this(ScopesAdapter.getInstance(), beforeSpan); + this(HubAdapter.getInstance(), beforeSpan); } public SentryCapability() { - this(ScopesAdapter.getInstance(), null); + this(HubAdapter.getInstance(), null); } @Override public @NotNull Client enrich(final @NotNull Client client) { - return new SentryFeignClient(client, scopes, beforeSpan); + return new SentryFeignClient(client, hub, beforeSpan); } } diff --git a/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java b/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java index a57380b29a..cb8aa3d9e0 100644 --- a/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java +++ b/sentry-openfeign/src/main/java/io/sentry/openfeign/SentryFeignClient.java @@ -9,10 +9,9 @@ import io.sentry.BaggageHeader; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.ISpan; import io.sentry.SpanDataConvention; -import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; import io.sentry.util.TracingUtils; @@ -31,15 +30,15 @@ public final class SentryFeignClient implements Client { private static final String TRACE_ORIGIN = "auto.http.openfeign"; private final @NotNull Client delegate; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private final @Nullable BeforeSpanCallback beforeSpan; public SentryFeignClient( final @NotNull Client delegate, - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @Nullable BeforeSpanCallback beforeSpan) { this.delegate = Objects.requireNonNull(delegate, "delegate is required"); - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + this.hub = Objects.requireNonNull(hub, "hub is required"); this.beforeSpan = beforeSpan; } @@ -48,16 +47,15 @@ public Response execute(final @NotNull Request request, final @NotNull Request.O throws IOException { Response response = null; try { - final ISpan activeSpan = scopes.getSpan(); + final ISpan activeSpan = hub.getSpan(); if (activeSpan == null) { final @NotNull Request modifiedRequest = maybeAddTracingHeaders(request, null); return delegate.execute(modifiedRequest, options); } - final @NotNull SpanOptions spanOptions = new SpanOptions(); - spanOptions.setOrigin(TRACE_ORIGIN); - ISpan span = activeSpan.startChild("http.client", null, spanOptions); + ISpan span = activeSpan.startChild("http.client"); + span.getSpanContext().setOrigin(TRACE_ORIGIN); final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(request.url()); final @NotNull String method = request.httpMethod().name(); span.setDescription(method + " " + urlDetails.getUrlOrFallback()); @@ -104,7 +102,7 @@ public Response execute(final @NotNull Request request, final @NotNull Request.O final @Nullable TracingUtils.TracingHeaders tracingHeaders = TracingUtils.traceIfAllowed( - scopes, + hub, request.url(), (requestBaggageHeaders != null ? new ArrayList<>(requestBaggageHeaders) : null), span); @@ -141,7 +139,7 @@ private void addBreadcrumb(final @NotNull Request request, final @Nullable Respo hint.set(OPEN_FEIGN_RESPONSE, response); } - scopes.addBreadcrumb(breadcrumb, hint); + hub.addBreadcrumb(breadcrumb, hint); } static final class RequestWrapper { diff --git a/sentry-openfeign/src/test/kotlin/io/sentry/openfeign/SentryFeignClientTest.kt b/sentry-openfeign/src/test/kotlin/io/sentry/openfeign/SentryFeignClientTest.kt index 959b890d46..65e56ab02b 100644 --- a/sentry-openfeign/src/test/kotlin/io/sentry/openfeign/SentryFeignClientTest.kt +++ b/sentry-openfeign/src/test/kotlin/io/sentry/openfeign/SentryFeignClientTest.kt @@ -7,7 +7,7 @@ import feign.HeaderMap import feign.RequestLine import io.sentry.BaggageHeader import io.sentry.Breadcrumb -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -37,7 +37,7 @@ import kotlin.test.fail class SentryFeignClientTest { class Fixture { - val scopes = mock() + val hub = mock() val server = MockWebServer() val sentryTracer: SentryTracer val sentryOptions = SentryOptions().apply { @@ -46,9 +46,9 @@ class SentryFeignClientTest { val scope = Scope(sentryOptions) init { - whenever(scopes.options).thenReturn(sentryOptions) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) - sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) + whenever(hub.options).thenReturn(sentryOptions) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) + sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) } fun getSut( @@ -59,7 +59,7 @@ class SentryFeignClientTest { beforeSpan: SentryFeignClient.BeforeSpanCallback? = null ): MockApi { if (isSpanActive) { - whenever(scopes.span).thenReturn(sentryTracer) + whenever(hub.span).thenReturn(sentryTracer) } server.enqueue( MockResponse() @@ -70,12 +70,12 @@ class SentryFeignClientTest { return if (!networkError) { Feign.builder() - .addCapability(SentryCapability(scopes, beforeSpan)) + .addCapability(SentryCapability(hub, beforeSpan)) } else { val mockClient = mock() whenever(mockClient.execute(any(), any())).thenThrow(RuntimeException::class.java) Feign.builder() - .client(SentryFeignClient(mockClient, scopes, beforeSpan)) + .client(SentryFeignClient(mockClient, hub, beforeSpan)) }.target(MockApi::class.java, server.url("/").toUrl().toString()) } } @@ -201,7 +201,7 @@ class SentryFeignClientTest { fun `adds breadcrumb when http calls succeeds`() { val sut = fixture.getSut(responseBody = "response body") sut.postWithBody("request-body") - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(13, it.data["response_body_size"]) @@ -215,7 +215,7 @@ class SentryFeignClientTest { fun `adds breadcrumb when http calls succeeds even though response body is null`() { val sut = fixture.getSut(responseBody = "") sut.postWithBody("request-body") - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(0, it.data["response_body_size"]) @@ -236,7 +236,7 @@ class SentryFeignClientTest { } catch (e: Exception) { // ignore me } - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) }, diff --git a/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts index 4c00cb8d81..80b68430db 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-agent/build.gradle.kts @@ -53,7 +53,6 @@ val upstreamAgent = configurations.create("upstreamAgent") { dependencies { bootstrapLibs(projects.sentry) - bootstrapLibs(projects.sentryOpentelemetry.sentryOpentelemetryBootstrap) javaagentLibs(projects.sentryOpentelemetry.sentryOpentelemetryAgentcustomization) upstreamAgent(Config.Libs.OpenTelemetry.otelJavaAgent) } diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts index c0e002ee31..79e3599cc8 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/build.gradle.kts @@ -24,8 +24,6 @@ dependencies { exclude(group = "io.opentelemetry") exclude(group = "io.opentelemetry.javaagent") } -// compileOnly(projects.sentryOpentelemetry.sentryOpentelemetryBootstrap) - implementation(projects.sentryOpentelemetry.sentryOpentelemetryBootstrap) compileOnly(Config.Libs.OpenTelemetry.otelSdk) compileOnly(Config.Libs.OpenTelemetry.otelExtensionAutoconfigureSpi) diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java index 019541e7e3..e808db8fcf 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryAutoConfigurationCustomizerProvider.java @@ -1,18 +1,15 @@ package io.sentry.opentelemetry; -import io.opentelemetry.context.ContextStorage; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; -import io.opentelemetry.sdk.trace.export.BatchSpanProcessor; +import io.sentry.Instrumenter; import io.sentry.Sentry; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryOptions; -import io.sentry.SentrySpanFactoryHolder; import io.sentry.protocol.SdkVersion; import io.sentry.protocol.SentryPackage; -import io.sentry.util.SpanUtils; import java.io.IOException; import java.net.URL; import java.util.ArrayList; @@ -31,29 +28,13 @@ public final class SentryAutoConfigurationCustomizerProvider @Override public void customize(AutoConfigurationCustomizer autoConfiguration) { final @Nullable VersionInfoHolder versionInfoHolder = createVersionInfo(); - - final @NotNull OtelSpanFactory spanFactory = new OtelSpanFactory(); - SentrySpanFactoryHolder.setSpanFactory(spanFactory); - /** - * We're currently overriding the storage mechanism to allow for cleanup of non closed OTel - * scopes. These happen when using e.g. Sentry static API due to getCurrentScopes() invoking - * Context.makeCurrent and then ignoring the returned lifecycle token (OTel Scope). After fixing - * the classloader problem (sentry bootstrap dependency is currently in agent classloader) we - * can revisit and try again to set the storage instead of overriding it in the wrapper. We - * should try to use OTels StorageProvider mechanism instead. - */ - // ContextStorage.addWrapper((storage) -> new SentryContextStorage(storage)); - ContextStorage.addWrapper( - (storage) -> new SentryContextStorage(new SentryOtelThreadLocalStorage())); - if (isSentryAutoInitEnabled()) { Sentry.init( options -> { options.setEnableExternalConfiguration(true); - options.setIgnoredSpanOrigins(SpanUtils.ignoredSpanOriginsForOpenTelemetry()); - options.setSpanFactory(spanFactory); + options.setInstrumenter(Instrumenter.OTEL); + options.addEventProcessor(new OpenTelemetryLinkErrorEventProcessor()); final @Nullable SdkVersion sdkVersion = createSdkVersion(options, versionInfoHolder); - // TODO [POTEL] is detecting a version mismatch between application and agent possible? if (sdkVersion != null) { options.setSdkVersion(sdkVersion); } @@ -159,10 +140,7 @@ private static class VersionInfoHolder { private SdkTracerProviderBuilder configureSdkTracerProvider( SdkTracerProviderBuilder tracerProvider, ConfigProperties config) { - return tracerProvider - .setSampler(new SentrySampler()) - .addSpanProcessor(new OtelSentrySpanProcessor()) - .addSpanProcessor(BatchSpanProcessor.builder(new SentrySpanExporter()).build()); + return tracerProvider.addSpanProcessor(new SentrySpanProcessor()); } private Map getDefaultProperties() { diff --git a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryPropagatorProvider.java b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryPropagatorProvider.java index 6aa04f31e9..49acd725fb 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryPropagatorProvider.java +++ b/sentry-opentelemetry/sentry-opentelemetry-agentcustomization/src/main/java/io/sentry/opentelemetry/SentryPropagatorProvider.java @@ -7,7 +7,7 @@ public final class SentryPropagatorProvider implements ConfigurablePropagatorProvider { @Override public TextMapPropagator getPropagator(ConfigProperties config) { - return new OtelSentryPropagator(); + return new SentryPropagator(); } @Override diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api deleted file mode 100644 index 0511fed18d..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api +++ /dev/null @@ -1,178 +0,0 @@ -public final class io/sentry/opentelemetry/InternalSemanticAttributes { - public static final field BAGGAGE Lio/opentelemetry/api/common/AttributeKey; - public static final field BAGGAGE_MUTABLE Lio/opentelemetry/api/common/AttributeKey; - public static final field IS_REMOTE_PARENT Lio/opentelemetry/api/common/AttributeKey; - public static final field PARENT_SAMPLED Lio/opentelemetry/api/common/AttributeKey; - public static final field PROFILE_SAMPLED Lio/opentelemetry/api/common/AttributeKey; - public static final field PROFILE_SAMPLE_RATE Lio/opentelemetry/api/common/AttributeKey; - public static final field SAMPLED Lio/opentelemetry/api/common/AttributeKey; - public static final field SAMPLE_RATE Lio/opentelemetry/api/common/AttributeKey; - public fun ()V -} - -public final class io/sentry/opentelemetry/OpenTelemetryUtil { - public fun ()V - public static fun applyOpenTelemetryOptions (Lio/sentry/SentryOptions;)V -} - -public final class io/sentry/opentelemetry/OtelContextScopesStorage : io/sentry/IScopesStorage { - public fun ()V - public fun close ()V - public fun get ()Lio/sentry/IScopes; - public fun set (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; -} - -public final class io/sentry/opentelemetry/OtelSpanContext : io/sentry/SpanContext { - public fun (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/TracesSamplingDecision;Lio/sentry/opentelemetry/OtelSpanWrapper;Lio/sentry/Baggage;)V - public fun getOperation ()Ljava/lang/String; - public fun getStatus ()Lio/sentry/SpanStatus; - public fun setOperation (Ljava/lang/String;)V - public fun setStatus (Lio/sentry/SpanStatus;)V -} - -public final class io/sentry/opentelemetry/OtelSpanFactory : io/sentry/ISpanFactory { - public fun ()V - public fun createSpan (Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/SpanContext;Lio/sentry/ISpan;)Lio/sentry/ISpan; - public fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; - public fun retrieveCurrentSpan (Lio/sentry/IScope;)Lio/sentry/ISpan; - public fun retrieveCurrentSpan (Lio/sentry/IScopes;)Lio/sentry/ISpan; -} - -public final class io/sentry/opentelemetry/OtelSpanWrapper : io/sentry/ISpan { - public fun (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/IScopes;Lio/sentry/SentryDate;Lio/sentry/TracesSamplingDecision;Lio/sentry/opentelemetry/OtelSpanWrapper;Lio/sentry/Baggage;)V - public fun finish ()V - public fun finish (Lio/sentry/SpanStatus;)V - public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V - public fun getContexts ()Lio/sentry/protocol/Contexts; - public fun getData ()Ljava/util/Map; - public fun getData (Ljava/lang/String;)Ljava/lang/Object; - public fun getDescription ()Ljava/lang/String; - public fun getFinishDate ()Lio/sentry/SentryDate; - public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; - public fun getMeasurements ()Ljava/util/Map; - public fun getOperation ()Ljava/lang/String; - public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision; - public fun getScopes ()Lio/sentry/IScopes; - public fun getSpanContext ()Lio/sentry/SpanContext; - public fun getStartDate ()Lio/sentry/SentryDate; - public fun getStatus ()Lio/sentry/SpanStatus; - public fun getTag (Ljava/lang/String;)Ljava/lang/String; - public fun getTags ()Ljava/util/Map; - public fun getThrowable ()Ljava/lang/Throwable; - public fun getTraceId ()Lio/sentry/protocol/SentryId; - public fun getTransactionName ()Ljava/lang/String; - public fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; - public fun isFinished ()Z - public fun isNoOp ()Z - public fun isProfileSampled ()Ljava/lang/Boolean; - public fun isSampled ()Ljava/lang/Boolean; - public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; - public fun setContext (Ljava/lang/String;Ljava/lang/Object;)V - public fun setData (Ljava/lang/String;Ljava/lang/Object;)V - public fun setDescription (Ljava/lang/String;)V - public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;)V - public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;Lio/sentry/MeasurementUnit;)V - public fun setOperation (Ljava/lang/String;)V - public fun setStatus (Lio/sentry/SpanStatus;)V - public fun setTag (Ljava/lang/String;Ljava/lang/String;)V - public fun setThrowable (Ljava/lang/Throwable;)V - public fun setTransactionName (Ljava/lang/String;)V - public fun setTransactionName (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V - public fun startChild (Lio/sentry/SpanContext;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; - 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/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; - public fun toBaggageHeader (Ljava/util/List;)Lio/sentry/BaggageHeader; - public fun toSentryTrace ()Lio/sentry/SentryTraceHeader; - public fun traceContext ()Lio/sentry/TraceContext; - public fun updateEndDate (Lio/sentry/SentryDate;)Z -} - -public final class io/sentry/opentelemetry/OtelTransactionSpanForwarder : io/sentry/ITransaction { - public fun (Lio/sentry/opentelemetry/OtelSpanWrapper;)V - public fun finish ()V - public fun finish (Lio/sentry/SpanStatus;)V - public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V - public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;ZLio/sentry/Hint;)V - public fun forceFinish (Lio/sentry/SpanStatus;ZLio/sentry/Hint;)V - public fun getContexts ()Lio/sentry/protocol/Contexts; - public fun getData (Ljava/lang/String;)Ljava/lang/Object; - public fun getDescription ()Ljava/lang/String; - public fun getEventId ()Lio/sentry/protocol/SentryId; - public fun getFinishDate ()Lio/sentry/SentryDate; - public fun getLatestActiveSpan ()Lio/sentry/ISpan; - public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; - public fun getName ()Ljava/lang/String; - public fun getOperation ()Ljava/lang/String; - public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision; - public fun getSpanContext ()Lio/sentry/SpanContext; - public fun getSpans ()Ljava/util/List; - public fun getStartDate ()Lio/sentry/SentryDate; - public fun getStatus ()Lio/sentry/SpanStatus; - public fun getTag (Ljava/lang/String;)Ljava/lang/String; - public fun getThrowable ()Ljava/lang/Throwable; - public fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; - public fun isFinished ()Z - public fun isNoOp ()Z - public fun isProfileSampled ()Ljava/lang/Boolean; - public fun isSampled ()Ljava/lang/Boolean; - public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; - public fun scheduleFinish ()V - public fun setContext (Ljava/lang/String;Ljava/lang/Object;)V - public fun setData (Ljava/lang/String;Ljava/lang/Object;)V - public fun setDescription (Ljava/lang/String;)V - public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;)V - public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;Lio/sentry/MeasurementUnit;)V - public fun setName (Ljava/lang/String;)V - public fun setName (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V - public fun setOperation (Ljava/lang/String;)V - public fun setStatus (Lio/sentry/SpanStatus;)V - public fun setTag (Ljava/lang/String;Ljava/lang/String;)V - public fun setThrowable (Ljava/lang/Throwable;)V - public fun startChild (Lio/sentry/SpanContext;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; - 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; - public fun toBaggageHeader (Ljava/util/List;)Lio/sentry/BaggageHeader; - public fun toSentryTrace ()Lio/sentry/SentryTraceHeader; - public fun traceContext ()Lio/sentry/TraceContext; - public fun updateEndDate (Lio/sentry/SentryDate;)Z -} - -public final class io/sentry/opentelemetry/SentryContextStorage : io/opentelemetry/context/ContextStorage { - public fun (Lio/opentelemetry/context/ContextStorage;)V - public fun attach (Lio/opentelemetry/context/Context;)Lio/opentelemetry/context/Scope; - public fun current ()Lio/opentelemetry/context/Context; -} - -public final class io/sentry/opentelemetry/SentryContextWrapper : io/opentelemetry/context/Context { - public fun get (Lio/opentelemetry/context/ContextKey;)Ljava/lang/Object; - public fun toString ()Ljava/lang/String; - public fun with (Lio/opentelemetry/context/ContextKey;Ljava/lang/Object;)Lio/opentelemetry/context/Context; - public static fun wrap (Lio/opentelemetry/context/Context;)Lio/sentry/opentelemetry/SentryContextWrapper; -} - -public final class io/sentry/opentelemetry/SentryOtelKeys { - public static final field SENTRY_BAGGAGE_KEY Lio/opentelemetry/context/ContextKey; - public static final field SENTRY_SCOPES_KEY Lio/opentelemetry/context/ContextKey; - public static final field SENTRY_TRACE_KEY Lio/opentelemetry/context/ContextKey; - public fun ()V -} - -public final class io/sentry/opentelemetry/SentryOtelThreadLocalStorage : io/opentelemetry/context/ContextStorage { - public fun ()V - public fun attach (Lio/opentelemetry/context/Context;)Lio/opentelemetry/context/Scope; - public fun current ()Lio/opentelemetry/context/Context; -} - -public final class io/sentry/opentelemetry/SentryWeakSpanStorage { - public static fun getInstance ()Lio/sentry/opentelemetry/SentryWeakSpanStorage; - public fun getSentrySpan (Lio/opentelemetry/api/trace/SpanContext;)Lio/sentry/opentelemetry/OtelSpanWrapper; - public fun storeSentrySpan (Lio/opentelemetry/api/trace/SpanContext;Lio/sentry/opentelemetry/OtelSpanWrapper;)V -} - diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/build.gradle.kts deleted file mode 100644 index f5aeed0b44..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/build.gradle.kts +++ /dev/null @@ -1,77 +0,0 @@ -import net.ltgt.gradle.errorprone.errorprone -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - -plugins { - `java-library` - kotlin("jvm") - jacoco - id(Config.QualityPlugins.errorProne) - id(Config.QualityPlugins.gradleVersions) -} - -configure { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - -tasks.withType().configureEach { - kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8.toString() -} - -dependencies { - compileOnly(projects.sentry) - - compileOnly(Config.Libs.OpenTelemetry.otelSdk) - - compileOnly(Config.CompileOnly.nopen) - errorprone(Config.CompileOnly.nopenChecker) - errorprone(Config.CompileOnly.errorprone) - compileOnly(Config.CompileOnly.jetbrainsAnnotations) - errorprone(Config.CompileOnly.errorProneNullAway) - - // tests - testImplementation(projects.sentryTestSupport) - testImplementation(kotlin(Config.kotlinStdLib)) - testImplementation(Config.TestLibs.kotlinTestJunit) - testImplementation(Config.TestLibs.mockitoKotlin) - testImplementation(Config.TestLibs.awaitility) - - testImplementation(Config.Libs.OpenTelemetry.otelSdk) - testImplementation(Config.Libs.OpenTelemetry.otelSemconv) -} - -configure { - test { - java.srcDir("src/test/java") - } -} - -jacoco { - toolVersion = Config.QualityPlugins.Jacoco.version -} - -tasks.jacocoTestReport { - reports { - xml.required.set(true) - html.required.set(false) - } -} - -tasks { - jacocoTestCoverageVerification { - violationRules { - rule { limit { minimum = Config.QualityPlugins.Jacoco.minimumCoverage } } - } - } - check { - dependsOn(jacocoTestCoverageVerification) - dependsOn(jacocoTestReport) - } -} - -tasks.withType().configureEach { - options.errorprone { - check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR) - option("NullAway:AnnotatedPackages", "io.sentry") - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java deleted file mode 100644 index cb64d7bfff..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/InternalSemanticAttributes.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.sentry.opentelemetry; - -import io.opentelemetry.api.common.AttributeKey; -import org.jetbrains.annotations.ApiStatus; - -@ApiStatus.Internal -public final class InternalSemanticAttributes { - public static final AttributeKey SAMPLED = AttributeKey.booleanKey("sentry.sampled"); - public static final AttributeKey SAMPLE_RATE = - AttributeKey.doubleKey("sentry.sample_rate"); - public static final AttributeKey PARENT_SAMPLED = - AttributeKey.booleanKey("sentry.parent_sampled"); - public static final AttributeKey PROFILE_SAMPLED = - AttributeKey.booleanKey("sentry.profile_sampled"); - public static final AttributeKey PROFILE_SAMPLE_RATE = - AttributeKey.doubleKey("sentry.profile_sample_rate"); - public static final AttributeKey IS_REMOTE_PARENT = - AttributeKey.booleanKey("sentry.is_remote_parent"); - public static final AttributeKey BAGGAGE = AttributeKey.stringKey("sentry.baggage"); - public static final AttributeKey BAGGAGE_MUTABLE = - AttributeKey.booleanKey("sentry.baggage_mutable"); -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OpenTelemetryUtil.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OpenTelemetryUtil.java deleted file mode 100644 index 02fc706e61..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OpenTelemetryUtil.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.sentry.opentelemetry; - -import io.sentry.SentryOptions; -import io.sentry.SentrySpanFactoryHolder; -import io.sentry.util.SpanUtils; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Nullable; - -@ApiStatus.Experimental -public final class OpenTelemetryUtil { - - public static void applyOpenTelemetryOptions(final @Nullable SentryOptions options) { - if (options != null) { - options.setSpanFactory(SentrySpanFactoryHolder.getSpanFactory()); - options.setIgnoredSpanOrigins(SpanUtils.ignoredSpanOriginsForOpenTelemetry()); - } - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java deleted file mode 100644 index 810c6c08cc..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelContextScopesStorage.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.sentry.opentelemetry; - -import static io.sentry.opentelemetry.SentryOtelKeys.SENTRY_SCOPES_KEY; - -import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; -import io.sentry.IScopes; -import io.sentry.IScopesStorage; -import io.sentry.ISentryLifecycleToken; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@ApiStatus.Internal -@SuppressWarnings("MustBeClosedChecker") -public final class OtelContextScopesStorage implements IScopesStorage { - - @Override - public @NotNull ISentryLifecycleToken set(@Nullable IScopes scopes) { - final @NotNull Scope otelScope = - Context.current().with(SENTRY_SCOPES_KEY, scopes).makeCurrent(); - return new OtelStorageToken(otelScope); - } - - @Override - public @Nullable IScopes get() { - return Context.current().get(SENTRY_SCOPES_KEY); - } - - @Override - public void close() {} -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java deleted file mode 100644 index 7b279802c9..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanContext.java +++ /dev/null @@ -1,115 +0,0 @@ -package io.sentry.opentelemetry; - -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.StatusCode; -import io.opentelemetry.sdk.trace.ReadWriteSpan; -import io.opentelemetry.sdk.trace.data.StatusData; -import io.sentry.Baggage; -import io.sentry.SpanContext; -import io.sentry.SpanId; -import io.sentry.SpanStatus; -import io.sentry.TracesSamplingDecision; -import io.sentry.protocol.SentryId; -import java.lang.ref.WeakReference; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@ApiStatus.Internal -public final class OtelSpanContext extends SpanContext { - - /** - * OpenTelemetry span which this wrapper wraps. Needs to be referenced weakly as otherwise we'd - * create a circular reference from {@link io.opentelemetry.sdk.trace.data.SpanData} to {@link - * OtelSpanWrapper} and indirectly back to {@link io.opentelemetry.sdk.trace.data.SpanData} via - * {@link Span}. Also see {@link SentryWeakSpanStorage}. - */ - private final @NotNull WeakReference span; - - public OtelSpanContext( - final @NotNull ReadWriteSpan span, - final @Nullable TracesSamplingDecision samplingDecision, - final @Nullable OtelSpanWrapper parentSpan, - final @Nullable Baggage baggage) { - super( - new SentryId(span.getSpanContext().getTraceId()), - new SpanId(span.getSpanContext().getSpanId()), - parentSpan == null ? null : parentSpan.getSpanContext().getSpanId(), - span.getName(), - null, - samplingDecision != null - ? samplingDecision - : (parentSpan == null ? null : parentSpan.getSamplingDecision()), - null, - null); - this.span = new WeakReference<>(span); - this.baggage = baggage; - } - - @Override - public @Nullable SpanStatus getStatus() { - final @Nullable ReadWriteSpan otelSpan = span.get(); - - if (otelSpan != null) { - final @NotNull StatusData otelStatus = otelSpan.toSpanData().getStatus(); - final @NotNull String otelStatusDescription = otelStatus.getDescription(); - if (otelStatusDescription.isEmpty()) { - return otelStatusCodeFallback(otelStatus); - } - final @Nullable SpanStatus spanStatus = SpanStatus.fromApiNameSafely(otelStatusDescription); - if (spanStatus == null) { - return otelStatusCodeFallback(otelStatus); - } - return spanStatus; - } - - return null; - } - - @Override - public void setStatus(@Nullable SpanStatus status) { - if (status != null) { - final @Nullable ReadWriteSpan otelSpan = span.get(); - if (otelSpan != null) { - final @NotNull StatusCode statusCode = translateStatusCode(status); - otelSpan.setStatus(statusCode, status.apiName()); - } - } - } - - @Override - public @NotNull String getOperation() { - final @Nullable ReadWriteSpan otelSpan = span.get(); - if (otelSpan != null) { - return otelSpan.getName(); - } - return ""; - } - - @Override - public void setOperation(@NotNull String operation) { - final @Nullable ReadWriteSpan otelSpan = span.get(); - if (otelSpan != null) { - otelSpan.updateName(operation); - } - } - - private @Nullable SpanStatus otelStatusCodeFallback(final @NotNull StatusData otelStatus) { - if (otelStatus.getStatusCode() == StatusCode.ERROR) { - return SpanStatus.UNKNOWN_ERROR; - } else if (otelStatus.getStatusCode() == StatusCode.OK) { - return SpanStatus.OK; - } - return null; - } - - private @NotNull StatusCode translateStatusCode(final @Nullable SpanStatus status) { - if (status == null) { - return StatusCode.UNSET; - } else if (status == SpanStatus.OK) { - return StatusCode.OK; - } else { - return StatusCode.ERROR; - } - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java deleted file mode 100644 index 7d060bb16a..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanFactory.java +++ /dev/null @@ -1,177 +0,0 @@ -package io.sentry.opentelemetry; - -import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.SpanBuilder; -import io.opentelemetry.api.trace.TraceFlags; -import io.opentelemetry.api.trace.TraceState; -import io.opentelemetry.api.trace.Tracer; -import io.opentelemetry.context.Context; -import io.sentry.Baggage; -import io.sentry.IScope; -import io.sentry.IScopes; -import io.sentry.ISpan; -import io.sentry.ISpanFactory; -import io.sentry.ITransaction; -import io.sentry.NoOpSpan; -import io.sentry.NoOpTransaction; -import io.sentry.SentryDate; -import io.sentry.SpanContext; -import io.sentry.SpanId; -import io.sentry.SpanOptions; -import io.sentry.TracesSamplingDecision; -import io.sentry.TransactionContext; -import io.sentry.TransactionOptions; -import io.sentry.TransactionPerformanceCollector; -import io.sentry.protocol.SentryId; -import io.sentry.util.SpanUtils; -import java.util.concurrent.TimeUnit; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@ApiStatus.Internal -public final class OtelSpanFactory implements ISpanFactory { - - private final @NotNull SentryWeakSpanStorage storage = SentryWeakSpanStorage.getInstance(); - - @Override - public @NotNull ITransaction createTransaction( - @NotNull TransactionContext context, - @NotNull IScopes scopes, - @NotNull TransactionOptions transactionOptions, - @Nullable TransactionPerformanceCollector transactionPerformanceCollector) { - final @Nullable OtelSpanWrapper span = - createSpanInternal( - scopes, transactionOptions, null, context.getSamplingDecision(), context); - if (span == null) { - return NoOpTransaction.getInstance(); - } - return new OtelTransactionSpanForwarder(span); - } - - @Override - public @NotNull ISpan createSpan( - final @NotNull IScopes scopes, - final @NotNull SpanOptions spanOptions, - final @NotNull SpanContext spanContext, - final @Nullable ISpan parentSpan) { - if (SpanUtils.isIgnored(scopes.getOptions().getIgnoredSpanOrigins(), spanOptions.getOrigin())) { - return NoOpSpan.getInstance(); - } - - final @Nullable TracesSamplingDecision samplingDecision = - parentSpan == null ? null : parentSpan.getSamplingDecision(); - final @Nullable OtelSpanWrapper span = - createSpanInternal(scopes, spanOptions, parentSpan, samplingDecision, spanContext); - if (span == null) { - return NoOpSpan.getInstance(); - } - return span; - } - - private @Nullable OtelSpanWrapper createSpanInternal( - final @NotNull IScopes scopes, - final @NotNull SpanOptions spanOptions, - final @Nullable ISpan parentSpan, - final @Nullable TracesSamplingDecision samplingDecision, - final @NotNull SpanContext spanContext) { - final @NotNull String name = spanContext.getOperation(); - final @NotNull SpanBuilder spanBuilder = getTracer().spanBuilder(name); - // TODO [POTEL] If performance is disabled, can we use otel.SamplingDecision.RECORD_ONLY to - // still allow otel to be used for tracing - if (parentSpan == null) { - final @NotNull SentryId traceId = spanContext.getTraceId(); - final @Nullable SpanId parentSpanId = spanContext.getParentSpanId(); - if (parentSpanId == null) { - final @NotNull io.opentelemetry.api.trace.SpanContext otelSpanContext = - io.opentelemetry.api.trace.SpanContext.create( - traceId.toString(), - io.opentelemetry.api.trace.SpanId.getInvalid(), - TraceFlags.getSampled(), - TraceState.getDefault()); - final @NotNull Span wrappedSpan = Span.wrap(otelSpanContext); - spanBuilder.setParent(Context.root().with(wrappedSpan)); - } else { - final @NotNull io.opentelemetry.api.trace.SpanContext otelSpanContext = - io.opentelemetry.api.trace.SpanContext.createFromRemoteParent( - traceId.toString(), - parentSpanId.toString(), - TraceFlags.getSampled(), - TraceState.getDefault()); - final @NotNull Span wrappedSpan = Span.wrap(otelSpanContext); - spanBuilder.setParent(Context.root().with(wrappedSpan)); - } - } else { - if (parentSpan instanceof OtelSpanWrapper) { - // TODO [POTEL] retrieve context from span - // spanBuilder.setParent() - } - } - - // note: won't go through propagators - final @Nullable Baggage baggage = spanContext.getBaggage(); - if (baggage != null) { - spanBuilder.setAttribute(InternalSemanticAttributes.BAGGAGE_MUTABLE, baggage.isMutable()); - spanBuilder.setAttribute(InternalSemanticAttributes.BAGGAGE, baggage.toHeaderString(null)); - } - - final @Nullable SentryDate startTimestampFromOptions = spanOptions.getStartTimestamp(); - final @NotNull SentryDate startTimestamp = - startTimestampFromOptions == null - ? scopes.getOptions().getDateProvider().now() - : startTimestampFromOptions; - spanBuilder.setStartTimestamp(startTimestamp.nanoTimestamp(), TimeUnit.NANOSECONDS); - - if (samplingDecision != null) { - spanBuilder.setAttribute(InternalSemanticAttributes.SAMPLED, samplingDecision.getSampled()); - spanBuilder.setAttribute( - InternalSemanticAttributes.SAMPLE_RATE, samplingDecision.getSampleRate()); - spanBuilder.setAttribute( - InternalSemanticAttributes.PROFILE_SAMPLED, samplingDecision.getProfileSampled()); - spanBuilder.setAttribute( - InternalSemanticAttributes.PROFILE_SAMPLE_RATE, samplingDecision.getProfileSampleRate()); - } - - final @NotNull Span otelSpan = spanBuilder.startSpan(); - - final @Nullable OtelSpanWrapper sentrySpan = storage.getSentrySpan(otelSpan.getSpanContext()); - if (sentrySpan != null) { - final @Nullable String description = spanContext.getDescription(); - if (description != null) { - sentrySpan.setDescription(description); - } - if (spanContext instanceof TransactionContext) { - final @NotNull TransactionContext transactionContext = (TransactionContext) spanContext; - sentrySpan.setTransactionName( - transactionContext.getName(), transactionContext.getTransactionNameSource()); - } - sentrySpan.getSpanContext().setOrigin(spanOptions.getOrigin()); - } - - return sentrySpan; - } - - @Override - public @Nullable ISpan retrieveCurrentSpan(IScopes scopes) { - final @Nullable Span span = Span.fromContextOrNull(Context.current()); - if (span == null) { - return null; - } - return storage.getSentrySpan(span.getSpanContext()); - } - - @Override - public @Nullable ISpan retrieveCurrentSpan(IScope scope) { - final @Nullable Span span = Span.fromContextOrNull(Context.current()); - if (span == null) { - return null; - } - return storage.getSentrySpan(span.getSpanContext()); - } - - private @NotNull Tracer getTracer() { - return GlobalOpenTelemetry.getTracer( - "sentry-instrumentation-scope-name", "sentry-instrumentation-scope-version"); - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java deleted file mode 100644 index b5ee906e19..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java +++ /dev/null @@ -1,498 +0,0 @@ -package io.sentry.opentelemetry; - -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.context.Scope; -import io.opentelemetry.sdk.trace.ReadWriteSpan; -import io.sentry.Baggage; -import io.sentry.BaggageHeader; -import io.sentry.IScopes; -import io.sentry.ISentryLifecycleToken; -import io.sentry.ISpan; -import io.sentry.Instrumenter; -import io.sentry.MeasurementUnit; -import io.sentry.NoOpScopesLifecycleToken; -import io.sentry.NoOpSpan; -import io.sentry.SentryDate; -import io.sentry.SentryLevel; -import io.sentry.SentryTraceHeader; -import io.sentry.SpanContext; -import io.sentry.SpanId; -import io.sentry.SpanOptions; -import io.sentry.SpanStatus; -import io.sentry.TraceContext; -import io.sentry.TracesSamplingDecision; -import io.sentry.metrics.LocalMetricsAggregator; -import io.sentry.protocol.Contexts; -import io.sentry.protocol.MeasurementValue; -import io.sentry.protocol.SentryId; -import io.sentry.protocol.TransactionNameSource; -import io.sentry.protocol.User; -import io.sentry.util.LazyEvaluator; -import io.sentry.util.Objects; -import java.lang.ref.WeakReference; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** NOTE: This wrapper is not used when using OpenTelemetry API, only when using Sentry API. */ -@ApiStatus.Internal -public final class OtelSpanWrapper implements ISpan { - - private final @NotNull IScopes scopes; - - /** The moment in time when span was started. */ - private @NotNull SentryDate startTimestamp; - - private @Nullable SentryDate finishedTimestamp = null; - - /** - * OpenTelemetry span which this wrapper wraps. Needs to be referenced weakly as otherwise we'd - * create a circular reference from {@link io.opentelemetry.sdk.trace.data.SpanData} to {@link - * OtelSpanWrapper} and indirectly back to {@link io.opentelemetry.sdk.trace.data.SpanData} via - * {@link Span}. Also see {@link SentryWeakSpanStorage}. - */ - private final @NotNull WeakReference span; // TODO [POTEL] bootstrap proxy - - private final @NotNull SpanContext context; - // private final @NotNull SpanOptions options; - private final @NotNull Contexts contexts = new Contexts(); - private @Nullable String transactionName; - private @Nullable TransactionNameSource transactionNameSource; - private final @Nullable Baggage baggage; - - // TODO [POTEL] - // private @Nullable SpanFinishedCallback spanFinishedCallback; - - private final @NotNull Map data = new ConcurrentHashMap<>(); - private final @NotNull Map measurements = new ConcurrentHashMap<>(); - - @SuppressWarnings("Convert2MethodRef") // older AGP versions do not support method references - private final @NotNull LazyEvaluator metricsAggregator = - new LazyEvaluator<>(() -> new LocalMetricsAggregator()); - - /** A throwable thrown during the execution of the span. */ - private @Nullable Throwable throwable; - - private @NotNull Deque tokensToCleanup = new ArrayDeque<>(1); - - public OtelSpanWrapper( - final @NotNull ReadWriteSpan span, - final @NotNull IScopes scopes, - final @NotNull SentryDate startTimestamp, - final @Nullable TracesSamplingDecision samplingDecision, - final @Nullable OtelSpanWrapper parentSpan, - final @Nullable Baggage baggage) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); - this.span = new WeakReference<>(span); - this.startTimestamp = startTimestamp; - - if (parentSpan != null) { - this.baggage = parentSpan.getSpanContext().getBaggage(); - } else if (baggage != null) { - this.baggage = baggage; - } else { - this.baggage = null; - } - - this.context = new OtelSpanContext(span, samplingDecision, parentSpan, this.baggage); - } - - @Override - public @NotNull ISpan startChild(@NotNull String operation) { - return startChild(operation, (String) null); - } - - @Override - public @NotNull ISpan startChild( - @NotNull String operation, @Nullable String description, @NotNull SpanOptions spanOptions) { - if (isFinished()) { - return NoOpSpan.getInstance(); - } - final @NotNull SpanContext spanContext = - context.copyForChild(operation, getSpanContext().getSpanId(), null); - spanContext.setDescription(description); - - return startChild(spanContext, spanOptions); - } - - @Override - public @NotNull ISpan startChild( - @NotNull SpanContext spanContext, @NotNull SpanOptions spanOptions) { - if (isFinished()) { - return NoOpSpan.getInstance(); - } - - return scopes.getOptions().getSpanFactory().createSpan(scopes, spanOptions, spanContext, this); - } - - @Override - public @NotNull ISpan startChild( - @NotNull String operation, - @Nullable String description, - @Nullable SentryDate timestamp, - @NotNull Instrumenter instrumenter) { - final @NotNull SpanContext spanContext = - context.copyForChild(operation, getSpanContext().getSpanId(), null); - spanContext.setDescription(description); - spanContext.setInstrumenter(instrumenter); - - final @NotNull SpanOptions spanOptions = new SpanOptions(); - spanOptions.setStartTimestamp(timestamp); - - return startChild(spanContext, spanOptions); - } - - @Override - public @NotNull ISpan startChild( - @NotNull String operation, - @Nullable String description, - @Nullable SentryDate timestamp, - @NotNull Instrumenter instrumenter, - @NotNull SpanOptions spanOptions) { - if (timestamp != null) { - spanOptions.setStartTimestamp(timestamp); - } - - final @NotNull SpanContext spanContext = - context.copyForChild(operation, getSpanContext().getSpanId(), null); - spanContext.setDescription(description); - spanContext.setInstrumenter(instrumenter); - - return startChild(spanContext, spanOptions); - } - - @Override - public @NotNull ISpan startChild(@NotNull String operation, @Nullable String description) { - final @NotNull SpanContext spanContext = - context.copyForChild(operation, getSpanContext().getSpanId(), null); - spanContext.setDescription(description); - - return startChild(spanContext, new SpanOptions()); - } - - @Override - public @NotNull SentryTraceHeader toSentryTrace() { - return new SentryTraceHeader(getTraceId(), getOtelSpanId(), isSampled()); - } - - private @NotNull SpanId getOtelSpanId() { - return context.getSpanId(); - } - - private @Nullable ReadWriteSpan getSpan() { - return span.get(); - } - - @Override - public @Nullable TraceContext traceContext() { - if (scopes.getOptions().isTraceSampling()) { - if (baggage != null) { - updateBaggageValues(); - return baggage.toTraceContext(); - } - } - return null; - } - - private void updateBaggageValues() { - synchronized (this) { - if (baggage != null && baggage.isMutable()) { - final AtomicReference userAtomicReference = new AtomicReference<>(); - scopes.configureScope( - scope -> { - userAtomicReference.set(scope.getUser()); - }); - baggage.setValuesFromTransaction( - getSpanContext().getTraceId(), - userAtomicReference.get(), - scopes.getOptions(), - this.getSamplingDecision(), - getTransactionName(), - getTransactionNameSource()); - baggage.freeze(); - } - } - } - - @Override - public @Nullable BaggageHeader toBaggageHeader(@Nullable List thirdPartyBaggageHeaders) { - if (scopes.getOptions().isTraceSampling()) { - if (baggage != null) { - updateBaggageValues(); - return BaggageHeader.fromBaggageAndOutgoingHeader(baggage, thirdPartyBaggageHeaders); - } - } - return null; - } - - @Override - public void finish() { - finish(getStatus()); - } - - @Override - public void finish(@Nullable SpanStatus status) { - setStatus(status); - final @Nullable Span otelSpan = getSpan(); - if (otelSpan != null) { - otelSpan.end(); - } - - for (ISentryLifecycleToken token : tokensToCleanup) { - token.close(); - } - } - - @Override - public void finish(@Nullable SpanStatus status, @Nullable SentryDate timestamp) { - setStatus(status); - final @Nullable Span otelSpan = getSpan(); - if (otelSpan != null) { - if (timestamp != null) { - otelSpan.end(timestamp.nanoTimestamp(), TimeUnit.NANOSECONDS); - } else { - otelSpan.end(); - } - } - } - - @Override - public void setOperation(@NotNull String operation) { - this.context.setOperation(operation); - } - - @Override - public @NotNull String getOperation() { - return context.getOperation(); - } - - @Override - public void setDescription(@Nullable String description) { - this.context.setDescription(description); - } - - @Override - public @Nullable String getDescription() { - return this.context.getDescription(); - } - - @Override - public void setStatus(final @Nullable SpanStatus status) { - context.setStatus(status); - } - - @Override - public @Nullable SpanStatus getStatus() { - return context.getStatus(); - } - - @Override - public void setThrowable(@Nullable Throwable throwable) { - this.throwable = throwable; - } - - @Override - public @Nullable Throwable getThrowable() { - return throwable; - } - - @Override - public @NotNull SpanContext getSpanContext() { - return context; - } - - @Override - public void setTag(@NotNull String key, @NotNull String value) { - context.setTag(key, value); - } - - @Override - public @Nullable String getTag(@NotNull String key) { - return context.getTags().get(key); - } - - @ApiStatus.Internal - public @NotNull Map getTags() { - return context.getTags(); - } - - @Override - public boolean isFinished() { - final @Nullable ReadWriteSpan otelSpan = getSpan(); - if (otelSpan != null) { - return otelSpan.hasEnded(); - } - - // if span is no longer available we consider it ended/finished - return true; - } - - @Override - public void setData(@NotNull String key, @NotNull Object value) { - data.put(key, value); - } - - @Override - public @Nullable Object getData(@NotNull String key) { - return data.get(key); - } - - @Override - public void setMeasurement(@NotNull String name, @NotNull Number value) { - if (isFinished()) { - scopes - .getOptions() - .getLogger() - .log( - SentryLevel.DEBUG, - "The span is already finished. Measurement %s cannot be set", - name); - return; - } - this.measurements.put(name, new MeasurementValue(value, null)); - - // TODO [POTEL] can't set on transaction - // We set the measurement in the transaction, too, but we have to check if this is the root span - // of the transaction, to avoid an infinite recursion - // if (transaction.getRoot() != this) { - // transaction.setMeasurementFromChild(name, value); - // } - } - - @Override - public void setMeasurement( - @NotNull String name, @NotNull Number value, @NotNull MeasurementUnit unit) { - if (isFinished()) { - scopes - .getOptions() - .getLogger() - .log( - SentryLevel.DEBUG, - "The span is already finished. Measurement %s cannot be set", - name); - return; - } - this.measurements.put(name, new MeasurementValue(value, unit.apiName())); - - // TODO [POTEL] can't set on transaction - // We set the measurement in the transaction, too, but we have to check if this is the root span - // of the transaction, to avoid an infinite recursion - // if (transaction.getRoot() != this) { - // transaction.setMeasurementFromChild(name, value, unit); - // } - } - - @Override - public boolean updateEndDate(@NotNull SentryDate date) { - if (this.finishedTimestamp != null) { - this.finishedTimestamp = date; - return true; - } - return false; - } - - @Override - public @NotNull SentryDate getStartDate() { - return startTimestamp; - } - - @Override - public @Nullable SentryDate getFinishDate() { - return finishedTimestamp; - } - - @Override - public boolean isNoOp() { - return false; - } - - @Override - public @Nullable LocalMetricsAggregator getLocalMetricsAggregator() { - return metricsAggregator.getValue(); - } - - @Override - public void setContext(@NotNull String key, @NotNull Object context) { - contexts.put(key, context); - } - - @Override - public @NotNull Contexts getContexts() { - // TODO [POTEL] only works for root span atm - return contexts; - } - - public void setTransactionName(@NotNull String name) { - setTransactionName(name, TransactionNameSource.CUSTOM); - } - - public void setTransactionName(@NotNull String name, @NotNull TransactionNameSource nameSource) { - this.transactionName = name; - this.transactionNameSource = nameSource; - } - - @ApiStatus.Internal - public @Nullable TransactionNameSource getTransactionNameSource() { - return transactionNameSource; - } - - @ApiStatus.Internal - public @Nullable String getTransactionName() { - return this.transactionName; - } - - @NotNull - public SentryId getTraceId() { - return context.getTraceId(); - } - - public @NotNull Map getData() { - return data; - } - - @NotNull - public Map getMeasurements() { - return measurements; - } - - @Override - public @Nullable Boolean isSampled() { - return context.getSampled(); - } - - public @Nullable Boolean isProfileSampled() { - return context.getProfileSampled(); - } - - @Override - public @Nullable TracesSamplingDecision getSamplingDecision() { - return context.getSamplingDecision(); - } - - @ApiStatus.Internal - public @NotNull IScopes getScopes() { - return scopes; - } - - @SuppressWarnings("MustBeClosedChecker") - @ApiStatus.Internal - @Override - public @NotNull ISentryLifecycleToken makeCurrent() { - final @Nullable Span otelSpan = getSpan(); - if (otelSpan != null) { - final @NotNull Scope otelScope = otelSpan.makeCurrent(); - final @NotNull OtelStorageToken token = new OtelStorageToken(otelScope); - // to iterate LIFO when closing - tokensToCleanup.addFirst(token); - return token; - } - return NoOpScopesLifecycleToken.getInstance(); - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelStorageToken.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelStorageToken.java deleted file mode 100644 index f9c7ccafad..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelStorageToken.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.sentry.opentelemetry; - -import io.opentelemetry.context.Scope; -import io.sentry.ISentryLifecycleToken; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; - -@ApiStatus.Internal -final class OtelStorageToken implements ISentryLifecycleToken { - - private final @NotNull Scope otelScope; - - OtelStorageToken(final @NotNull Scope otelScope) { - this.otelScope = otelScope; - } - - @Override - public void close() { - otelScope.close(); - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java deleted file mode 100644 index 373a4e1909..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java +++ /dev/null @@ -1,325 +0,0 @@ -package io.sentry.opentelemetry; - -import static io.sentry.TransactionContext.DEFAULT_TRANSACTION_NAME; - -import io.sentry.BaggageHeader; -import io.sentry.Hint; -import io.sentry.ISentryLifecycleToken; -import io.sentry.ISpan; -import io.sentry.ITransaction; -import io.sentry.Instrumenter; -import io.sentry.MeasurementUnit; -import io.sentry.SentryDate; -import io.sentry.SentryTraceHeader; -import io.sentry.SpanContext; -import io.sentry.SpanOptions; -import io.sentry.SpanStatus; -import io.sentry.TraceContext; -import io.sentry.TracesSamplingDecision; -import io.sentry.metrics.LocalMetricsAggregator; -import io.sentry.protocol.Contexts; -import io.sentry.protocol.SentryId; -import io.sentry.protocol.TransactionNameSource; -import io.sentry.util.Objects; -import java.util.ArrayList; -import java.util.List; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@ApiStatus.Internal -public final class OtelTransactionSpanForwarder implements ITransaction { - - private final @NotNull OtelSpanWrapper rootSpan; - - public OtelTransactionSpanForwarder(final @NotNull OtelSpanWrapper rootSpan) { - this.rootSpan = Objects.requireNonNull(rootSpan, "root span is required"); - } - - @Override - public @NotNull ISpan startChild(@NotNull String operation) { - return rootSpan.startChild(operation); - } - - @Override - public @NotNull ISpan startChild( - @NotNull String operation, @Nullable String description, @NotNull SpanOptions spanOptions) { - return rootSpan.startChild(operation, description, spanOptions); - } - - @Override - public @NotNull ISpan startChild( - @NotNull SpanContext spanContext, @NotNull SpanOptions spanOptions) { - return rootSpan.startChild(spanContext, spanOptions); - } - - @Override - public @NotNull ISpan startChild( - @NotNull String operation, - @Nullable String description, - @Nullable SentryDate timestamp, - @NotNull Instrumenter instrumenter) { - return rootSpan.startChild(operation, description, timestamp, instrumenter); - } - - @Override - public @NotNull ISpan startChild( - @NotNull String operation, - @Nullable String description, - @Nullable SentryDate timestamp, - @NotNull Instrumenter instrumenter, - @NotNull SpanOptions spanOptions) { - return rootSpan.startChild(operation, description, timestamp, instrumenter, spanOptions); - } - - @Override - public @NotNull ISpan startChild(@NotNull String operation, @Nullable String description) { - return rootSpan.startChild(operation, description); - } - - @Override - public @NotNull SentryTraceHeader toSentryTrace() { - return rootSpan.toSentryTrace(); - } - - @Override - public @Nullable TraceContext traceContext() { - return rootSpan.traceContext(); - } - - @Override - public @Nullable BaggageHeader toBaggageHeader(@Nullable List thirdPartyBaggageHeaders) { - return rootSpan.toBaggageHeader(thirdPartyBaggageHeaders); - } - - @Override - public void finish() { - // TODO [POTEL] should this finish all spans? - rootSpan.finish(); - } - - @Override - public void finish(@Nullable SpanStatus status) { - rootSpan.finish(status); - } - - @Override - public void finish(@Nullable SpanStatus status, @Nullable SentryDate timestamp) { - rootSpan.finish(status, timestamp); - } - - @Override - public void setOperation(@NotNull String operation) { - rootSpan.startChild(operation); - } - - @Override - public @NotNull String getOperation() { - return rootSpan.getOperation(); - } - - @Override - public void setDescription(@Nullable String description) { - rootSpan.setDescription(description); - } - - @Override - public @Nullable String getDescription() { - return rootSpan.getDescription(); - } - - @Override - public void setStatus(@Nullable SpanStatus status) { - rootSpan.setStatus(status); - } - - @Override - public @Nullable SpanStatus getStatus() { - return rootSpan.getStatus(); - } - - @Override - public void setThrowable(@Nullable Throwable throwable) { - rootSpan.setThrowable(throwable); - } - - @Override - public @Nullable Throwable getThrowable() { - return rootSpan.getThrowable(); - } - - @Override - public @NotNull SpanContext getSpanContext() { - return rootSpan.getSpanContext(); - } - - @Override - public void setTag(@NotNull String key, @NotNull String value) { - rootSpan.setTag(key, value); - } - - @Override - public @Nullable String getTag(@NotNull String key) { - return rootSpan.getTag(key); - } - - @Override - public boolean isFinished() { - return rootSpan.isFinished(); - } - - @Override - public void setData(@NotNull String key, @NotNull Object value) { - rootSpan.setData(key, value); - } - - @Override - public @Nullable Object getData(@NotNull String key) { - return rootSpan.getData(key); - } - - @Override - public void setMeasurement(@NotNull String name, @NotNull Number value) { - rootSpan.setMeasurement(name, value); - } - - @Override - public void setMeasurement( - @NotNull String name, @NotNull Number value, @NotNull MeasurementUnit unit) { - rootSpan.setMeasurement(name, value, unit); - } - - @Override - public boolean updateEndDate(@NotNull SentryDate date) { - return rootSpan.updateEndDate(date); - } - - @Override - public @NotNull SentryDate getStartDate() { - return rootSpan.getStartDate(); - } - - @Override - public @Nullable SentryDate getFinishDate() { - return rootSpan.getFinishDate(); - } - - @Override - public boolean isNoOp() { - return rootSpan.isNoOp(); - } - - @Override - public @Nullable LocalMetricsAggregator getLocalMetricsAggregator() { - return rootSpan.getLocalMetricsAggregator(); - } - - @Override - public @NotNull TransactionNameSource getTransactionNameSource() { - final @Nullable TransactionNameSource nameSource = rootSpan.getTransactionNameSource(); - if (nameSource == null) { - return TransactionNameSource.CUSTOM; - } - return nameSource; - } - - @Override - public @NotNull List getSpans() { - // TODO [POTEL] - return new ArrayList<>(); - } - - @Override - public @NotNull ISpan startChild( - @NotNull String operation, @Nullable String description, @Nullable SentryDate timestamp) { - return rootSpan.startChild(operation, description, timestamp, Instrumenter.SENTRY); - } - - @Override - public @Nullable Boolean isSampled() { - return rootSpan.isSampled(); - } - - @Override - public @Nullable Boolean isProfileSampled() { - return rootSpan.isProfileSampled(); - } - - @Override - public @Nullable TracesSamplingDecision getSamplingDecision() { - return rootSpan.getSamplingDecision(); - } - - @Override - public @Nullable ISpan getLatestActiveSpan() { - return rootSpan; - } - - @Override - public @NotNull SentryId getEventId() { - // TODO [POTEL] - return new SentryId(); - } - - @ApiStatus.Internal - @Override - public @NotNull ISentryLifecycleToken makeCurrent() { - return rootSpan.makeCurrent(); - } - - @Override - public void scheduleFinish() { - // TODO [POTEL] - } - - @Override - public void forceFinish( - @NotNull SpanStatus status, boolean dropIfNoChildren, @Nullable Hint hint) { - // TODO [POTEL] - rootSpan.finish(status); - } - - @Override - public void finish( - @Nullable SpanStatus status, - @Nullable SentryDate timestamp, - boolean dropIfNoChildren, - @Nullable Hint hint) { - // TODO [POTEL] - rootSpan.finish(status, timestamp); - } - - @Override - public void setContext(@NotNull String key, @NotNull Object context) { - // thoughts: - // - span would have to save it on global storage too since we can't add complex data to otel - // span - // - with span ingestion there isn't a transaction anymore, so if we still need Contexts it - // should go on the (root) span - rootSpan.setContext(key, context); - } - - @Override - public @NotNull Contexts getContexts() { - return rootSpan.getContexts(); - } - - @Override - public void setName(@NotNull String name) { - rootSpan.setTransactionName(name); - } - - @Override - public void setName(@NotNull String name, @NotNull TransactionNameSource nameSource) { - rootSpan.setTransactionName(name, nameSource); - } - - @Override - public @NotNull String getName() { - final @Nullable String name = rootSpan.getTransactionName(); - if (name == null) { - return DEFAULT_TRANSACTION_NAME; - } - return name; - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextStorage.java deleted file mode 100644 index 6ce6f88816..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextStorage.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.sentry.opentelemetry; - -import io.opentelemetry.context.Context; -import io.opentelemetry.context.ContextStorage; -import io.opentelemetry.context.Scope; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; - -@ApiStatus.Internal -public final class SentryContextStorage implements ContextStorage { - private final @NotNull Logger logger = Logger.getLogger(SentryContextStorage.class.getName()); - - private final @NotNull ContextStorage contextStorage; - - public SentryContextStorage(final @NotNull ContextStorage contextStorage) { - this.contextStorage = contextStorage; - logger.log(Level.SEVERE, "SentryContextStorage ctor called"); - } - - @Override - public Scope attach(Context toAttach) { - // TODO [POTEL] do we need to fork here as well? - // scenario: Context is propagated from thread A to thread B without changes - // OTEL likely also dosn't fork in that case so we probably also don't have to - // or maybe shouldn't even to better align with OTEL - // but since OTEL Context is immutable it doesn't have the same consequence for OTEL as for us - - // TODO [POTEL] sometimes context has already gone through forking but is still an - // ArrayBaseContext - // most likely due to OTEL bridging between agent and app - - // incoming non sentry wrapped context that already has scopes in it - if (toAttach instanceof SentryContextWrapper) { - return contextStorage.attach(toAttach); - } else { - return contextStorage.attach(SentryContextWrapper.wrap(toAttach)); - } - } - - @Override - public Context current() { - return contextStorage.current(); - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextWrapper.java deleted file mode 100644 index e2a5efaf89..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextWrapper.java +++ /dev/null @@ -1,92 +0,0 @@ -package io.sentry.opentelemetry; - -import static io.sentry.opentelemetry.SentryOtelKeys.SENTRY_SCOPES_KEY; - -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.context.Context; -import io.opentelemetry.context.ContextKey; -import io.sentry.IScopes; -import io.sentry.Sentry; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@ApiStatus.Internal -public final class SentryContextWrapper implements Context { - - private final @NotNull Context delegate; - - private SentryContextWrapper(final @NotNull Context delegate) { - this.delegate = delegate; - } - - @Override - public V get(final @NotNull ContextKey contextKey) { - return delegate.get(contextKey); - } - - @Override - public Context with(final @NotNull ContextKey contextKey, V v) { - final @NotNull Context modifiedContext = delegate.with(contextKey, v); - - if (isOpentelemetrySpan(contextKey)) { - return forkCurrentScope(modifiedContext); - } else { - return modifiedContext; - } - } - - private boolean isOpentelemetrySpan(final @NotNull ContextKey contextKey) { - return "opentelemetry-trace-span-key".equals(contextKey.toString()); - } - - private static @NotNull Context forkCurrentScope(final @NotNull Context context) { - final @Nullable IScopes scopesInContext = context.get(SENTRY_SCOPES_KEY); - final @Nullable IScopes spanScopes = getCurrentSpanScopesFromGlobalStorage(context); - - if (scopesInContext != null && spanScopes != null) { - if (scopesInContext.isAncestorOf(spanScopes)) { - return context.with( - SENTRY_SCOPES_KEY, spanScopes.forkedCurrentScope("contextwrapper.spanancestor")); - } - } - - if (scopesInContext != null) { - return context.with( - SENTRY_SCOPES_KEY, scopesInContext.forkedCurrentScope("contextwrapper.scopeincontext")); - } - - if (spanScopes != null) { - return context.with( - SENTRY_SCOPES_KEY, spanScopes.forkedCurrentScope("contextwrapper.spanscope")); - } - - return context.with(SENTRY_SCOPES_KEY, Sentry.forkedRootScopes("contextwrapper.fallback")); - } - - private static @Nullable IScopes getCurrentSpanScopesFromGlobalStorage( - final @NotNull Context context) { - @Nullable final Span span = Span.fromContextOrNull(context); - - if (span != null) { - final @Nullable OtelSpanWrapper sentrySpan = - SentryWeakSpanStorage.getInstance().getSentrySpan(span.getSpanContext()); - if (sentrySpan != null) { - return sentrySpan.getScopes(); - } - } - - return null; - } - - public static @NotNull SentryContextWrapper wrap(final @NotNull Context context) { - // we have to fork here because the first time we get to wrap a context it may already have a - // span and a scope - return new SentryContextWrapper(forkCurrentScope(context)); - } - - @Override - public String toString() { - return delegate.toString(); - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelThreadLocalStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelThreadLocalStorage.java deleted file mode 100644 index e44afc5a2d..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelThreadLocalStorage.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Adapted from https://github.com/open-telemetry/opentelemetry-java/blob/0aacc55d1e3f5cc6dbb4f8fa26bcb657b01a7bc9/context/src/main/java/io/opentelemetry/context/ThreadLocalContextStorage.java - * - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.sentry.opentelemetry; - -import io.opentelemetry.context.Context; -import io.opentelemetry.context.ContextStorage; -import io.opentelemetry.context.Scope; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Nullable; - -/** - * Workaround to make OpenTelemetry context storage work for Sentry since Sentry sometimes forks - * Context without cleaning up. We are not yet sure if this is something we can easliy fix, since - * Sentry static API makes heavy use of getCurrentScopes and there is no easy way of knowing when to - * restore previous Context. - */ -@ApiStatus.Experimental -@ApiStatus.Internal -public final class SentryOtelThreadLocalStorage implements ContextStorage { - private static final Logger logger = - Logger.getLogger(SentryOtelThreadLocalStorage.class.getName()); - - private static final ThreadLocal THREAD_LOCAL_STORAGE = new ThreadLocal<>(); - - @Override - public Scope attach(Context toAttach) { - if (toAttach == null) { - // Null context not allowed so ignore it. - return NoopScope.INSTANCE; - } - - Context beforeAttach = current(); - if (toAttach == beforeAttach) { - return NoopScope.INSTANCE; - } - - THREAD_LOCAL_STORAGE.set(toAttach); - - return new SentryScopeImpl(beforeAttach); - } - - private static class SentryScopeImpl implements Scope { - @Nullable private final Context beforeAttach; - private boolean closed; - - private SentryScopeImpl(@Nullable Context beforeAttach) { - this.beforeAttach = beforeAttach; - } - - @Override - public void close() { - // if (!closed && current() == toAttach) { - // Used to make OTel thread local storage compatible with Sentry where cleanup isn't always - // performed correctly - if (!closed) { - closed = true; - THREAD_LOCAL_STORAGE.set(beforeAttach); - } else { - logger.log( - Level.FINE, - " Trying to close scope which does not represent current context. Ignoring the call."); - } - } - } - - @Override - @Nullable - public Context current() { - return THREAD_LOCAL_STORAGE.get(); - } - - enum NoopScope implements Scope { - INSTANCE; - - @Override - public void close() {} - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryWeakSpanStorage.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryWeakSpanStorage.java deleted file mode 100644 index af9413f74f..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryWeakSpanStorage.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.sentry.opentelemetry; - -import io.opentelemetry.api.trace.SpanContext; -import io.opentelemetry.context.internal.shaded.WeakConcurrentMap; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Weakly references wrappers for OpenTelemetry spans meaning they'll be cleaned up when the - * OpenTelemetry span is garbage collected. - */ -@ApiStatus.Internal -public final class SentryWeakSpanStorage { - private static volatile @Nullable SentryWeakSpanStorage INSTANCE; - - public static @NotNull SentryWeakSpanStorage getInstance() { - if (INSTANCE == null) { - synchronized (SentryWeakSpanStorage.class) { - if (INSTANCE == null) { - INSTANCE = new SentryWeakSpanStorage(); - } - } - } - - return INSTANCE; - } - - // weak keys, spawns a thread to clean up values that have been garbage collected - private final @NotNull WeakConcurrentMap sentrySpans = - new WeakConcurrentMap<>(true); - - private SentryWeakSpanStorage() {} - - public @Nullable OtelSpanWrapper getSentrySpan(final @NotNull SpanContext spanContext) { - return sentrySpans.get(spanContext); - } - - public void storeSentrySpan( - final @NotNull SpanContext otelSpan, final @NotNull OtelSpanWrapper sentrySpan) { - this.sentrySpans.put(otelSpan, sentrySpan); - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api index 6c6fc52bad..18c73a9b68 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api +++ b/sentry-opentelemetry/sentry-opentelemetry-core/api/sentry-opentelemetry-core.api @@ -1,45 +1,21 @@ public final class io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor : io/sentry/EventProcessor { public fun ()V - public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } -public final class io/sentry/opentelemetry/OtelInternalSpanDetectionUtil { - public fun ()V - public static fun isSentryRequest (Lio/sentry/IScopes;Lio/opentelemetry/api/trace/SpanKind;Lio/opentelemetry/api/common/Attributes;)Z -} - -public final class io/sentry/opentelemetry/OtelSamplingUtil { - public fun ()V - public static fun extractSamplingDecision (Lio/opentelemetry/api/common/Attributes;)Lio/sentry/TracesSamplingDecision; - public static fun extractSamplingDecisionOrDefault (Lio/opentelemetry/api/common/Attributes;)Lio/sentry/TracesSamplingDecision; -} - -public final class io/sentry/opentelemetry/OtelSentryPropagator : io/opentelemetry/context/propagation/TextMapPropagator { - public fun ()V - public fun extract (Lio/opentelemetry/context/Context;Ljava/lang/Object;Lio/opentelemetry/context/propagation/TextMapGetter;)Lio/opentelemetry/context/Context; - public fun fields ()Ljava/util/Collection; - public fun inject (Lio/opentelemetry/context/Context;Ljava/lang/Object;Lio/opentelemetry/context/propagation/TextMapSetter;)V -} - -public final class io/sentry/opentelemetry/OtelSentrySpanProcessor : io/opentelemetry/sdk/trace/SpanProcessor { - public fun ()V - public fun isEndRequired ()Z - public fun isStartRequired ()Z - public fun onEnd (Lio/opentelemetry/sdk/trace/ReadableSpan;)V - public fun onStart (Lio/opentelemetry/context/Context;Lio/opentelemetry/sdk/trace/ReadWriteSpan;)V -} - public final class io/sentry/opentelemetry/OtelSpanInfo { public fun (Ljava/lang/String;Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V - public fun (Ljava/lang/String;Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;Ljava/util/Map;)V - public fun addDataField (Ljava/lang/String;Ljava/lang/Object;)V - public fun getDataFields ()Ljava/util/Map; public fun getDescription ()Ljava/lang/String; public fun getOp ()Ljava/lang/String; public fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; } +public final class io/sentry/opentelemetry/SentryOtelKeys { + public static final field SENTRY_BAGGAGE_KEY Lio/opentelemetry/context/ContextKey; + public static final field SENTRY_TRACE_KEY Lio/opentelemetry/context/ContextKey; + public fun ()V +} + public final class io/sentry/opentelemetry/SentryPropagator : io/opentelemetry/context/propagation/TextMapPropagator { public fun ()V public fun extract (Lio/opentelemetry/context/Context;Ljava/lang/Object;Lio/opentelemetry/context/propagation/TextMapGetter;)Lio/opentelemetry/context/Context; @@ -47,29 +23,6 @@ public final class io/sentry/opentelemetry/SentryPropagator : io/opentelemetry/c public fun inject (Lio/opentelemetry/context/Context;Ljava/lang/Object;Lio/opentelemetry/context/propagation/TextMapSetter;)V } -public final class io/sentry/opentelemetry/SentrySampler : io/opentelemetry/sdk/trace/samplers/Sampler { - public fun ()V - public fun (Lio/sentry/IScopes;)V - public fun getDescription ()Ljava/lang/String; - public fun shouldSample (Lio/opentelemetry/context/Context;Ljava/lang/String;Ljava/lang/String;Lio/opentelemetry/api/trace/SpanKind;Lio/opentelemetry/api/common/Attributes;Ljava/util/List;)Lio/opentelemetry/sdk/trace/samplers/SamplingResult; -} - -public final class io/sentry/opentelemetry/SentrySamplingResult : io/opentelemetry/sdk/trace/samplers/SamplingResult { - public fun (Lio/sentry/TracesSamplingDecision;)V - public fun getAttributes ()Lio/opentelemetry/api/common/Attributes; - public fun getDecision ()Lio/opentelemetry/sdk/trace/samplers/SamplingDecision; - public fun getSentryDecision ()Lio/sentry/TracesSamplingDecision; -} - -public final class io/sentry/opentelemetry/SentrySpanExporter : io/opentelemetry/sdk/trace/export/SpanExporter { - public static final field TRACE_ORIGIN Ljava/lang/String; - public fun ()V - public fun (Lio/sentry/IScopes;)V - public fun export (Ljava/util/Collection;)Lio/opentelemetry/sdk/common/CompletableResultCode; - public fun flush ()Lio/opentelemetry/sdk/common/CompletableResultCode; - public fun shutdown ()Lio/opentelemetry/sdk/common/CompletableResultCode; -} - public final class io/sentry/opentelemetry/SentrySpanProcessor : io/opentelemetry/sdk/trace/SpanProcessor { public fun ()V public fun isEndRequired ()Z @@ -80,19 +33,7 @@ public final class io/sentry/opentelemetry/SentrySpanProcessor : io/opentelemetr public final class io/sentry/opentelemetry/SpanDescriptionExtractor { public fun ()V - public fun extractSpanInfo (Lio/opentelemetry/sdk/trace/data/SpanData;Lio/sentry/opentelemetry/OtelSpanWrapper;)Lio/sentry/opentelemetry/OtelSpanInfo; -} - -public final class io/sentry/opentelemetry/SpanNode { - public fun (Ljava/lang/String;)V - public fun addChild (Lio/sentry/opentelemetry/SpanNode;)V - public fun addChildren (Ljava/util/List;)V - public fun getChildren ()Ljava/util/List; - public fun getId ()Ljava/lang/String; - public fun getParentNode ()Lio/sentry/opentelemetry/SpanNode; - public fun getSpan ()Lio/opentelemetry/sdk/trace/data/SpanData; - public fun setParentNode (Lio/sentry/opentelemetry/SpanNode;)V - public fun setSpan (Lio/opentelemetry/sdk/trace/data/SpanData;)V + public fun extractSpanDescription (Lio/opentelemetry/sdk/trace/ReadableSpan;)Lio/sentry/opentelemetry/OtelSpanInfo; } public final class io/sentry/opentelemetry/TraceData { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/build.gradle.kts b/sentry-opentelemetry/sentry-opentelemetry-core/build.gradle.kts index 542bc4332b..1dad433555 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/build.gradle.kts +++ b/sentry-opentelemetry/sentry-opentelemetry-core/build.gradle.kts @@ -20,8 +20,6 @@ tasks.withType().configureEach { dependencies { compileOnly(projects.sentry) - // TODO implementation? - compileOnly(projects.sentryOpentelemetry.sentryOpentelemetryBootstrap) implementation(Config.Libs.OpenTelemetry.otelSdk) compileOnly(Config.Libs.OpenTelemetry.otelSemconv) @@ -33,7 +31,6 @@ dependencies { errorprone(Config.CompileOnly.errorProneNullAway) // tests - testImplementation(projects.sentryOpentelemetry.sentryOpentelemetryBootstrap) testImplementation(projects.sentryTestSupport) testImplementation(kotlin(Config.kotlinStdLib)) testImplementation(Config.TestLibs.kotlinTestJunit) diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java index cf1e530cd9..1e373ece9c 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OpenTelemetryLinkErrorEventProcessor.java @@ -5,42 +5,36 @@ import io.opentelemetry.api.trace.TraceId; import io.sentry.EventProcessor; import io.sentry.Hint; -import io.sentry.IScopes; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ISpan; import io.sentry.Instrumenter; -import io.sentry.ScopesAdapter; import io.sentry.SentryEvent; import io.sentry.SentryLevel; +import io.sentry.SentrySpanStorage; import io.sentry.SpanContext; import io.sentry.protocol.SentryId; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; -/** - * @deprecated this is no longer needed for the latest version of our OpenTelemetry integration. - */ -@Deprecated public final class OpenTelemetryLinkErrorEventProcessor implements EventProcessor { - private final @NotNull IScopes scopes; - - @SuppressWarnings("deprecation") - private final @NotNull io.sentry.SentrySpanStorage spanStorage = - io.sentry.SentrySpanStorage.getInstance(); + private final @NotNull IHub hub; + private final @NotNull SentrySpanStorage spanStorage = SentrySpanStorage.getInstance(); public OpenTelemetryLinkErrorEventProcessor() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } @TestOnly - OpenTelemetryLinkErrorEventProcessor(final @NotNull IScopes scopes) { - this.scopes = scopes; + OpenTelemetryLinkErrorEventProcessor(final @NotNull IHub hub) { + this.hub = hub; } @Override public @Nullable SentryEvent process(final @NotNull SentryEvent event, final @NotNull Hint hint) { - final @NotNull Instrumenter instrumenter = scopes.getOptions().getInstrumenter(); + final @NotNull Instrumenter instrumenter = hub.getOptions().getInstrumenter(); if (Instrumenter.OTEL.equals(instrumenter)) { @NotNull final Span otelSpan = Span.current(); @NotNull final String traceId = otelSpan.getSpanContext().getTraceId(); @@ -61,8 +55,7 @@ public OpenTelemetryLinkErrorEventProcessor() { null); event.getContexts().setTrace(spanContext); - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -71,8 +64,7 @@ public OpenTelemetryLinkErrorEventProcessor() { spanId, traceId); } else { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -82,8 +74,7 @@ public OpenTelemetryLinkErrorEventProcessor() { traceId); } } else { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -93,8 +84,7 @@ public OpenTelemetryLinkErrorEventProcessor() { spanId); } } else { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -105,9 +95,4 @@ public OpenTelemetryLinkErrorEventProcessor() { return event; } - - @Override - public @Nullable Long getOrder() { - return 6000L; - } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelInternalSpanDetectionUtil.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelInternalSpanDetectionUtil.java deleted file mode 100644 index 7009ed144c..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelInternalSpanDetectionUtil.java +++ /dev/null @@ -1,66 +0,0 @@ -package io.sentry.opentelemetry; - -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.semconv.SemanticAttributes; -import io.sentry.DsnUtil; -import io.sentry.IScopes; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@ApiStatus.Internal -public final class OtelInternalSpanDetectionUtil { - - private static final @NotNull List spanKindsConsideredForSentryRequests = - Arrays.asList(SpanKind.CLIENT, SpanKind.INTERNAL); - - @SuppressWarnings("deprecation") - public static boolean isSentryRequest( - final @NotNull IScopes scopes, - final @NotNull SpanKind spanKind, - final @NotNull Attributes attributes) { - if (!spanKindsConsideredForSentryRequests.contains(spanKind)) { - return false; - } - - final @Nullable String httpUrl = attributes.get(SemanticAttributes.HTTP_URL); - if (DsnUtil.urlContainsDsnHost(scopes.getOptions(), httpUrl)) { - return true; - } - - final @Nullable String fullUrl = attributes.get(SemanticAttributes.URL_FULL); - if (DsnUtil.urlContainsDsnHost(scopes.getOptions(), fullUrl)) { - return true; - } - - // TODO [POTEL] should check if enabled but multi init with different options makes testing hard - // atm - // if (scopes.getOptions().isEnableSpotlight()) { - final @Nullable String optionsSpotlightUrl = scopes.getOptions().getSpotlightConnectionUrl(); - final @NotNull String spotlightUrl = - optionsSpotlightUrl != null ? optionsSpotlightUrl : "http://localhost:8969/stream"; - - if (containsSpotlightUrl(fullUrl, spotlightUrl)) { - return true; - } - if (containsSpotlightUrl(httpUrl, spotlightUrl)) { - return true; - } - // } - - return false; - } - - private static boolean containsSpotlightUrl( - final @Nullable String requestUrl, final @NotNull String spotlightUrl) { - if (requestUrl == null) { - return false; - } - - return requestUrl.toLowerCase(Locale.ROOT).contains(spotlightUrl.toLowerCase(Locale.ROOT)); - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSamplingUtil.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSamplingUtil.java deleted file mode 100644 index 45a8922741..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSamplingUtil.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.sentry.opentelemetry; - -import io.opentelemetry.api.common.Attributes; -import io.sentry.TracesSamplingDecision; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@ApiStatus.Internal -public final class OtelSamplingUtil { - - public static @NotNull TracesSamplingDecision extractSamplingDecisionOrDefault( - final @NotNull Attributes attributes) { - final @Nullable TracesSamplingDecision decision = extractSamplingDecision(attributes); - if (decision != null) { - return decision; - } else { - return new TracesSamplingDecision(false); - } - } - - public static @Nullable TracesSamplingDecision extractSamplingDecision( - final @NotNull Attributes attributes) { - final @Nullable Boolean sampled = attributes.get(InternalSemanticAttributes.SAMPLED); - if (sampled != null) { - final @Nullable Double sampleRate = attributes.get(InternalSemanticAttributes.SAMPLE_RATE); - final @Nullable Boolean profileSampled = - attributes.get(InternalSemanticAttributes.PROFILE_SAMPLED); - final @Nullable Double profileSampleRate = - attributes.get(InternalSemanticAttributes.PROFILE_SAMPLE_RATE); - - return new TracesSamplingDecision( - sampled, sampleRate, profileSampled == null ? false : profileSampled, profileSampleRate); - } else { - return null; - } - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentryPropagator.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentryPropagator.java deleted file mode 100644 index 0b9ee88e08..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentryPropagator.java +++ /dev/null @@ -1,145 +0,0 @@ -package io.sentry.opentelemetry; - -import static io.sentry.opentelemetry.SentryOtelKeys.SENTRY_SCOPES_KEY; - -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.SpanContext; -import io.opentelemetry.api.trace.TraceFlags; -import io.opentelemetry.api.trace.TraceState; -import io.opentelemetry.context.Context; -import io.opentelemetry.context.propagation.TextMapGetter; -import io.opentelemetry.context.propagation.TextMapPropagator; -import io.opentelemetry.context.propagation.TextMapSetter; -import io.sentry.Baggage; -import io.sentry.BaggageHeader; -import io.sentry.IScopes; -import io.sentry.PropagationContext; -import io.sentry.ScopesAdapter; -import io.sentry.Sentry; -import io.sentry.SentryLevel; -import io.sentry.SentryTraceHeader; -import io.sentry.exception.InvalidSentryTraceHeaderException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public final class OtelSentryPropagator implements TextMapPropagator { - - private static final @NotNull List FIELDS = - Arrays.asList(SentryTraceHeader.SENTRY_TRACE_HEADER, BaggageHeader.BAGGAGE_HEADER); - private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); - private final @NotNull IScopes scopes; - - public OtelSentryPropagator() { - this(ScopesAdapter.getInstance()); - } - - OtelSentryPropagator(final @NotNull IScopes scopes) { - this.scopes = scopes; - } - - @Override - public Collection fields() { - return FIELDS; - } - - @Override - public void inject(final Context context, final C carrier, final TextMapSetter setter) { - final @NotNull Span otelSpan = Span.fromContext(context); - final @NotNull SpanContext otelSpanContext = otelSpan.getSpanContext(); - if (!otelSpanContext.isValid()) { - scopes - .getOptions() - .getLogger() - .log( - SentryLevel.DEBUG, - "Not injecting Sentry tracing information for invalid OpenTelemetry span."); - return; - } - - final @Nullable OtelSpanWrapper sentrySpan = spanStorage.getSentrySpan(otelSpanContext); - if (sentrySpan == null || sentrySpan.isNoOp()) { - scopes - .getOptions() - .getLogger() - .log( - SentryLevel.DEBUG, - "Not injecting Sentry tracing information for span %s as no Sentry span has been found or it is a NoOp (trace %s). This might simply mean this is a request to Sentry.", - otelSpanContext.getSpanId(), - otelSpanContext.getTraceId()); - return; - } - - final @NotNull SentryTraceHeader sentryTraceHeader = sentrySpan.toSentryTrace(); - setter.set(carrier, sentryTraceHeader.getName(), sentryTraceHeader.getValue()); - final @Nullable BaggageHeader baggageHeader = - sentrySpan.toBaggageHeader(Collections.emptyList()); - if (baggageHeader != null) { - setter.set(carrier, baggageHeader.getName(), baggageHeader.getValue()); - } - } - - @Override - public Context extract( - final Context context, final C carrier, final TextMapGetter getter) { - final @Nullable IScopes scopesFromParentContext = context.get(SENTRY_SCOPES_KEY); - final @NotNull IScopes scopesToUse = - scopesFromParentContext != null - ? scopesFromParentContext.forkedScopes("propagator") - : Sentry.forkedRootScopes("propagator"); - - final @Nullable String sentryTraceString = - getter.get(carrier, SentryTraceHeader.SENTRY_TRACE_HEADER); - if (sentryTraceString == null) { - return context.with(SENTRY_SCOPES_KEY, scopesToUse); - } - - try { - SentryTraceHeader sentryTraceHeader = new SentryTraceHeader(sentryTraceString); - - final @Nullable String baggageString = getter.get(carrier, BaggageHeader.BAGGAGE_HEADER); - final Baggage baggage = Baggage.fromHeader(baggageString); - final @NotNull TraceState traceState = TraceState.getDefault(); - - SpanContext otelSpanContext = - SpanContext.createFromRemoteParent( - sentryTraceHeader.getTraceId().toString(), - sentryTraceHeader.getSpanId().toString(), - TraceFlags.getSampled(), - traceState); - - Span wrappedSpan = Span.wrap(otelSpanContext); - - final @NotNull Context modifiedContext = - context - .with(wrappedSpan) - .with(SENTRY_SCOPES_KEY, scopesToUse) - .with(SentryOtelKeys.SENTRY_TRACE_KEY, sentryTraceHeader) - .with(SentryOtelKeys.SENTRY_BAGGAGE_KEY, baggage); - - scopes - .getOptions() - .getLogger() - .log(SentryLevel.DEBUG, "Continuing Sentry trace %s", sentryTraceHeader.getTraceId()); - - final @NotNull PropagationContext propagationContext = - PropagationContext.fromHeaders( - scopes.getOptions().getLogger(), sentryTraceString, baggageString); - scopesToUse.getIsolationScope().setPropagationContext(propagationContext); - - return modifiedContext; - } catch (InvalidSentryTraceHeaderException e) { - scopes - .getOptions() - .getLogger() - .log( - SentryLevel.ERROR, - "Unable to extract Sentry tracing information from invalid header.", - e); - return context; - } - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentrySpanProcessor.java deleted file mode 100644 index 8dd4bd160a..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSentrySpanProcessor.java +++ /dev/null @@ -1,167 +0,0 @@ -package io.sentry.opentelemetry; - -import static io.sentry.opentelemetry.InternalSemanticAttributes.IS_REMOTE_PARENT; -import static io.sentry.opentelemetry.SentryOtelKeys.SENTRY_SCOPES_KEY; - -import io.opentelemetry.api.trace.SpanContext; -import io.opentelemetry.context.Context; -import io.opentelemetry.sdk.trace.ReadWriteSpan; -import io.opentelemetry.sdk.trace.ReadableSpan; -import io.opentelemetry.sdk.trace.SpanProcessor; -import io.sentry.Baggage; -import io.sentry.IScopes; -import io.sentry.PropagationContext; -import io.sentry.ScopesAdapter; -import io.sentry.Sentry; -import io.sentry.SentryDate; -import io.sentry.SentryLevel; -import io.sentry.SentryLongDate; -import io.sentry.SentryTraceHeader; -import io.sentry.SpanId; -import io.sentry.TracesSamplingDecision; -import io.sentry.protocol.SentryId; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public final class OtelSentrySpanProcessor implements SpanProcessor { - private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); - private final @NotNull IScopes scopes; - - public OtelSentrySpanProcessor() { - this(ScopesAdapter.getInstance()); - } - - OtelSentrySpanProcessor(final @NotNull IScopes scopes) { - this.scopes = scopes; - } - - @Override - public void onStart(final @NotNull Context parentContext, final @NotNull ReadWriteSpan otelSpan) { - if (!ensurePrerequisites(otelSpan)) { - return; - } - - final @Nullable IScopes scopesFromContext = parentContext.get(SENTRY_SCOPES_KEY); - final @NotNull IScopes scopes = - scopesFromContext != null - ? scopesFromContext.forkedCurrentScope("spanprocessor") - : Sentry.forkedRootScopes("spanprocessor"); - - final @Nullable OtelSpanWrapper sentryParentSpan = - spanStorage.getSentrySpan(otelSpan.getParentSpanContext()); - @NotNull - TracesSamplingDecision samplingDecision = - OtelSamplingUtil.extractSamplingDecisionOrDefault(otelSpan.toSpanData().getAttributes()); - @Nullable Baggage baggage = null; - otelSpan.setAttribute(IS_REMOTE_PARENT, otelSpan.getParentSpanContext().isRemote()); - if (sentryParentSpan == null) { - final @NotNull String traceId = otelSpan.getSpanContext().getTraceId(); - final @NotNull String spanId = otelSpan.getSpanContext().getSpanId(); - final @NotNull SpanId sentrySpanId = new SpanId(spanId); - final @NotNull String parentSpanId = otelSpan.getParentSpanContext().getSpanId(); - final @Nullable SpanId sentryParentSpanId = - io.opentelemetry.api.trace.SpanId.isValid(parentSpanId) ? new SpanId(parentSpanId) : null; - - @Nullable - SentryTraceHeader sentryTraceHeader = parentContext.get(SentryOtelKeys.SENTRY_TRACE_KEY); - @Nullable Baggage baggageFromContext = parentContext.get(SentryOtelKeys.SENTRY_BAGGAGE_KEY); - if (sentryTraceHeader != null) { - baggage = baggageFromContext; - } - - final @Nullable Boolean baggageMutable = - otelSpan.getAttribute(InternalSemanticAttributes.BAGGAGE_MUTABLE); - final @Nullable String baggageString = - otelSpan.getAttribute(InternalSemanticAttributes.BAGGAGE); - if (baggageString != null) { - baggage = Baggage.fromHeader(baggageString); - if (baggageMutable == true) { - baggage.freeze(); - } - } - - final boolean sampled = - samplingDecision != null - ? samplingDecision.getSampled() - : otelSpan.getSpanContext().isSampled(); - - final @NotNull PropagationContext propagationContext = - sentryTraceHeader == null - ? new PropagationContext( - new SentryId(traceId), sentrySpanId, sentryParentSpanId, baggage, sampled) - : PropagationContext.fromHeaders(sentryTraceHeader, baggage, sentrySpanId); - - updatePropagationContext(scopes, propagationContext); - } - - final @NotNull SpanContext spanContext = otelSpan.getSpanContext(); - final @NotNull SentryDate startTimestamp = - new SentryLongDate(otelSpan.toSpanData().getStartEpochNanos()); - final @NotNull OtelSpanWrapper sentrySpan = - new OtelSpanWrapper( - otelSpan, scopes, startTimestamp, samplingDecision, sentryParentSpan, baggage); - sentrySpan.getSpanContext().setOrigin(SentrySpanExporter.TRACE_ORIGIN); - spanStorage.storeSentrySpan(spanContext, sentrySpan); - } - - private static void updatePropagationContext( - IScopes scopes, PropagationContext propagationContext) { - scopes.configureScope( - scope -> { - scope.withPropagationContext( - oldPropagationContext -> { - scope.setPropagationContext(propagationContext); - }); - }); - } - - @Override - public boolean isStartRequired() { - return true; - } - - @Override - public void onEnd(final @NotNull ReadableSpan spanBeingEnded) { - final @Nullable OtelSpanWrapper sentrySpan = - spanStorage.getSentrySpan(spanBeingEnded.getSpanContext()); - if (sentrySpan != null) { - final @NotNull SentryDate finishDate = - new SentryLongDate(spanBeingEnded.toSpanData().getEndEpochNanos()); - sentrySpan.updateEndDate(finishDate); - } - } - - @Override - public boolean isEndRequired() { - return true; - } - - private boolean ensurePrerequisites(final @NotNull ReadableSpan otelSpan) { - if (!hasSentryBeenInitialized()) { - scopes - .getOptions() - .getLogger() - .log( - SentryLevel.DEBUG, - "Not forwarding OpenTelemetry span to Sentry as Sentry has not yet been initialized."); - return false; - } - - final @NotNull SpanContext otelSpanContext = otelSpan.getSpanContext(); - if (!otelSpanContext.isValid()) { - scopes - .getOptions() - .getLogger() - .log( - SentryLevel.DEBUG, - "Not forwarding OpenTelemetry span to Sentry as the span is invalid."); - return false; - } - - return true; - } - - private boolean hasSentryBeenInitialized() { - return scopes.isEnabled(); - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanInfo.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanInfo.java index 3a0032d16e..6ad39d3793 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanInfo.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanInfo.java @@ -1,59 +1,34 @@ package io.sentry.opentelemetry; import io.sentry.protocol.TransactionNameSource; -import java.util.HashMap; -import java.util.Map; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; @ApiStatus.Internal public final class OtelSpanInfo { private final @NotNull String op; - private final @Nullable String description; + private final @NotNull String description; private final @NotNull TransactionNameSource transactionNameSource; - private final @NotNull Map dataFields; - - public OtelSpanInfo( - final @NotNull String op, - final @Nullable String description, - final @NotNull TransactionNameSource transactionNameSource, - final @NotNull Map dataFields) { - this.op = op; - this.description = description; - this.transactionNameSource = transactionNameSource; - this.dataFields = dataFields; - } - public OtelSpanInfo( final @NotNull String op, - final @Nullable String description, + final @NotNull String description, final @NotNull TransactionNameSource transactionNameSource) { this.op = op; this.description = description; this.transactionNameSource = transactionNameSource; - this.dataFields = new HashMap<>(); } public @NotNull String getOp() { return op; } - public @Nullable String getDescription() { + public @NotNull String getDescription() { return description; } public @NotNull TransactionNameSource getTransactionNameSource() { return transactionNameSource; } - - public @NotNull Map getDataFields() { - return dataFields; - } - - public void addDataField(final @NotNull String key, final @NotNull Object value) { - dataFields.put(key, value); - } } diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelKeys.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryOtelKeys.java similarity index 79% rename from sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelKeys.java rename to sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryOtelKeys.java index 54889d1e73..51ead00c6f 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryOtelKeys.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryOtelKeys.java @@ -2,7 +2,6 @@ import io.opentelemetry.context.ContextKey; import io.sentry.Baggage; -import io.sentry.IScopes; import io.sentry.SentryTraceHeader; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -14,6 +13,4 @@ public final class SentryOtelKeys { ContextKey.named("sentry.trace"); public static final @NotNull ContextKey SENTRY_BAGGAGE_KEY = ContextKey.named("sentry.baggage"); - public static final @NotNull ContextKey SENTRY_SCOPES_KEY = - ContextKey.named("sentry.scopes"); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java index ffcab9ed54..14ac12323b 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentryPropagator.java @@ -10,10 +10,11 @@ import io.opentelemetry.context.propagation.TextMapSetter; import io.sentry.Baggage; import io.sentry.BaggageHeader; -import io.sentry.IScopes; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ISpan; -import io.sentry.ScopesAdapter; import io.sentry.SentryLevel; +import io.sentry.SentrySpanStorage; import io.sentry.SentryTraceHeader; import io.sentry.exception.InvalidSentryTraceHeaderException; import java.util.Arrays; @@ -23,27 +24,19 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** - * @deprecated please use {@link OtelSentryPropagator} instead - */ -@Deprecated public final class SentryPropagator implements TextMapPropagator { private static final @NotNull List FIELDS = Arrays.asList(SentryTraceHeader.SENTRY_TRACE_HEADER, BaggageHeader.BAGGAGE_HEADER); - - @SuppressWarnings("deprecation") - private final @NotNull io.sentry.SentrySpanStorage spanStorage = - io.sentry.SentrySpanStorage.getInstance(); - - private final @NotNull IScopes scopes; + private final @NotNull SentrySpanStorage spanStorage = SentrySpanStorage.getInstance(); + private final @NotNull IHub hub; public SentryPropagator() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } - SentryPropagator(final @NotNull IScopes scopes) { - this.scopes = scopes; + SentryPropagator(final @NotNull IHub hub) { + this.hub = hub; } @Override @@ -56,8 +49,7 @@ public void inject(final Context context, final C carrier, final TextMapSett final @NotNull Span otelSpan = Span.fromContext(context); final @NotNull SpanContext otelSpanContext = otelSpan.getSpanContext(); if (!otelSpanContext.isValid()) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -66,8 +58,7 @@ public void inject(final Context context, final C carrier, final TextMapSett } final @Nullable ISpan sentrySpan = spanStorage.get(otelSpanContext.getSpanId()); if (sentrySpan == null || sentrySpan.isNoOp()) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -115,15 +106,13 @@ public Context extract( Span wrappedSpan = Span.wrap(otelSpanContext); modifiedContext = modifiedContext.with(wrappedSpan); - scopes - .getOptions() + hub.getOptions() .getLogger() .log(SentryLevel.DEBUG, "Continuing Sentry trace %s", sentryTraceHeader.getTraceId()); return modifiedContext; } catch (InvalidSentryTraceHeaderException e) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.ERROR, diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java deleted file mode 100644 index 37df216ebb..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySampler.java +++ /dev/null @@ -1,117 +0,0 @@ -package io.sentry.opentelemetry; - -import static io.sentry.opentelemetry.OtelInternalSpanDetectionUtil.isSentryRequest; - -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.context.Context; -import io.opentelemetry.sdk.trace.data.LinkData; -import io.opentelemetry.sdk.trace.samplers.Sampler; -import io.opentelemetry.sdk.trace.samplers.SamplingDecision; -import io.opentelemetry.sdk.trace.samplers.SamplingResult; -import io.sentry.Baggage; -import io.sentry.IScopes; -import io.sentry.PropagationContext; -import io.sentry.SamplingContext; -import io.sentry.ScopesAdapter; -import io.sentry.SentryTraceHeader; -import io.sentry.SpanId; -import io.sentry.TracesSamplingDecision; -import io.sentry.TransactionContext; -import io.sentry.protocol.SentryId; -import java.util.List; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public final class SentrySampler implements Sampler { - - private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); - private final @NotNull IScopes scopes; - - public SentrySampler(final @NotNull IScopes scopes) { - this.scopes = scopes; - } - - public SentrySampler() { - this(ScopesAdapter.getInstance()); - } - - @Override - public SamplingResult shouldSample( - final @NotNull Context parentContext, - final @NotNull String traceId, - final @NotNull String name, - final @NotNull SpanKind spanKind, - final @NotNull Attributes attributes, - final @NotNull List parentLinks) { - if (isSentryRequest(scopes, spanKind, attributes)) { - return SamplingResult.drop(); - } - // note: parentLinks seems to usually be empty - final @Nullable Span parentOtelSpan = Span.fromContextOrNull(parentContext); - final @Nullable OtelSpanWrapper parentSentrySpan = - parentOtelSpan != null ? spanStorage.getSentrySpan(parentOtelSpan.getSpanContext()) : null; - - if (parentSentrySpan != null) { - return copyParentSentryDecision(parentSentrySpan); - } else { - final @Nullable TracesSamplingDecision samplingDecision = - OtelSamplingUtil.extractSamplingDecision(attributes); - if (samplingDecision != null) { - return new SentrySamplingResult(samplingDecision); - } else { - return handleRootOtelSpan(traceId, parentContext); - } - } - } - - private @NotNull SamplingResult handleRootOtelSpan( - final @NotNull String traceId, final @NotNull Context parentContext) { - if (!scopes.getOptions().isTraceSampling()) { - // TODO [POTEL] should this return RECORD_ONLY to allow tracing without performance - return SamplingResult.create(SamplingDecision.DROP); - } - @Nullable Baggage baggage = null; - @Nullable - SentryTraceHeader sentryTraceHeader = parentContext.get(SentryOtelKeys.SENTRY_TRACE_KEY); - @Nullable Baggage baggageFromContext = parentContext.get(SentryOtelKeys.SENTRY_BAGGAGE_KEY); - if (sentryTraceHeader != null) { - baggage = baggageFromContext; - } - - // there's no way to get the span id here, so we just use a random id for sampling - SpanId randomSpanId = new SpanId(); - final @NotNull PropagationContext propagationContext = - sentryTraceHeader == null - ? new PropagationContext(new SentryId(traceId), randomSpanId, null, baggage, null) - : PropagationContext.fromHeaders(sentryTraceHeader, baggage, randomSpanId); - - final @NotNull TransactionContext transactionContext = - TransactionContext.fromPropagationContext(propagationContext); - final @NotNull TracesSamplingDecision sentryDecision = - scopes - .getOptions() - .getInternalTracesSampler() - .sample(new SamplingContext(transactionContext, null)); - return new SentrySamplingResult(sentryDecision); - } - - private @NotNull SentrySamplingResult copyParentSentryDecision( - final @NotNull OtelSpanWrapper parentSentrySpan) { - final @Nullable TracesSamplingDecision parentSamplingDecision = - parentSentrySpan.getSamplingDecision(); - if (parentSamplingDecision != null) { - return new SentrySamplingResult(parentSamplingDecision); - } else { - // this should never happen and only serve to calm the compiler - // TODO [POTEL] log - return new SentrySamplingResult(new TracesSamplingDecision(true)); - } - } - - @Override - public String getDescription() { - return "SentrySampler"; - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySamplingResult.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySamplingResult.java deleted file mode 100644 index 69acf52134..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySamplingResult.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.sentry.opentelemetry; - -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.sdk.trace.samplers.SamplingDecision; -import io.opentelemetry.sdk.trace.samplers.SamplingResult; -import io.sentry.TracesSamplingDecision; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; - -@ApiStatus.Internal -public final class SentrySamplingResult implements SamplingResult { - private final TracesSamplingDecision sentryDecision; - - public SentrySamplingResult(final @NotNull TracesSamplingDecision sentryDecision) { - this.sentryDecision = sentryDecision; - } - - @Override - public SamplingDecision getDecision() { - if (sentryDecision.getSampled()) { - return SamplingDecision.RECORD_AND_SAMPLE; - } else { - return SamplingDecision.RECORD_ONLY; - } - } - - @Override - public Attributes getAttributes() { - return Attributes.builder() - .put(InternalSemanticAttributes.SAMPLED, sentryDecision.getSampled()) - .put(InternalSemanticAttributes.SAMPLE_RATE, sentryDecision.getSampleRate()) - .put(InternalSemanticAttributes.PROFILE_SAMPLED, sentryDecision.getProfileSampled()) - .put(InternalSemanticAttributes.PROFILE_SAMPLE_RATE, sentryDecision.getProfileSampleRate()) - .build(); - } - - public TracesSamplingDecision getSentryDecision() { - return sentryDecision; - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java deleted file mode 100644 index 0e86f281bc..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanExporter.java +++ /dev/null @@ -1,511 +0,0 @@ -package io.sentry.opentelemetry; - -import static io.sentry.TransactionContext.DEFAULT_TRANSACTION_NAME; -import static io.sentry.opentelemetry.InternalSemanticAttributes.IS_REMOTE_PARENT; -import static io.sentry.opentelemetry.OtelInternalSpanDetectionUtil.isSentryRequest; - -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.trace.StatusCode; -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.sdk.trace.data.StatusData; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import io.opentelemetry.semconv.SemanticAttributes; -import io.sentry.Baggage; -import io.sentry.DateUtils; -import io.sentry.DefaultSpanFactory; -import io.sentry.IScopes; -import io.sentry.ISpan; -import io.sentry.ITransaction; -import io.sentry.Instrumenter; -import io.sentry.ScopesAdapter; -import io.sentry.SentryDate; -import io.sentry.SentryInstantDate; -import io.sentry.SentryLevel; -import io.sentry.SentryLongDate; -import io.sentry.SpanContext; -import io.sentry.SpanId; -import io.sentry.SpanOptions; -import io.sentry.SpanStatus; -import io.sentry.TransactionContext; -import io.sentry.TransactionOptions; -import io.sentry.protocol.Contexts; -import io.sentry.protocol.SentryId; -import io.sentry.protocol.TransactionNameSource; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public final class SentrySpanExporter implements SpanExporter { - private volatile boolean stopped = false; - // TODO [POTEL] should we clear out old finished spans after a while? - private final List finishedSpans = new CopyOnWriteArrayList<>(); - private final @NotNull SentryWeakSpanStorage spanStorage = SentryWeakSpanStorage.getInstance(); - private final @NotNull SpanDescriptionExtractor spanDescriptionExtractor = - new SpanDescriptionExtractor(); - private final @NotNull IScopes scopes; - - private final @NotNull List attributeKeysToRemove = - Arrays.asList( - InternalSemanticAttributes.IS_REMOTE_PARENT.getKey(), - InternalSemanticAttributes.BAGGAGE.getKey(), - InternalSemanticAttributes.BAGGAGE_MUTABLE.getKey(), - InternalSemanticAttributes.SAMPLED.getKey(), - InternalSemanticAttributes.SAMPLE_RATE.getKey(), - InternalSemanticAttributes.PROFILE_SAMPLED.getKey(), - InternalSemanticAttributes.PROFILE_SAMPLE_RATE.getKey(), - InternalSemanticAttributes.PARENT_SAMPLED.getKey()); - private static final @NotNull Long SPAN_TIMEOUT = DateUtils.secondsToNanos(5 * 60); - - public static final String TRACE_ORIGIN = "auto.opentelemetry"; - - public SentrySpanExporter() { - this(ScopesAdapter.getInstance()); - } - - public SentrySpanExporter(final @NotNull IScopes scopes) { - this.scopes = scopes; - } - - @Override - public CompletableResultCode export(Collection spans) { - if (stopped) { - // TODO unsure if there's a way to attach a message - return CompletableResultCode.ofFailure(); - } - - final int openSpanCount = finishedSpans.size(); - final int newSpanCount = spans.size(); - - final @NotNull List nonSentryRequestSpans = filterOutSentrySpans(spans); - - finishedSpans.addAll(nonSentryRequestSpans); - final @NotNull List remaining = maybeSend(finishedSpans); - final int remainingSpanCount = remaining.size(); - final int sentSpanCount = openSpanCount + newSpanCount - remainingSpanCount; - - scopes - .getOptions() - .getLogger() - .log( - SentryLevel.DEBUG, - "SpanExporter exported %s spans, %s unset spans remaining.", - sentSpanCount, - remainingSpanCount); - - this.finishedSpans.clear(); - - final @NotNull SentryInstantDate now = new SentryInstantDate(); - - final @NotNull List nonExpired = - remaining.stream().filter((span) -> !isSpanTooOld(span, now)).collect(Collectors.toList()); - - this.finishedSpans.addAll(nonExpired); - - // TODO - - return CompletableResultCode.ofSuccess(); - } - - private boolean isSpanTooOld(final @NotNull SpanData span, final @NotNull SentryInstantDate now) { - final @NotNull SentryDate startDate = new SentryLongDate(span.getStartEpochNanos()); - boolean isTimedOut = now.diff(startDate) > SPAN_TIMEOUT; - if (isTimedOut) { - scopes - .getOptions() - .getLogger() - .log( - SentryLevel.DEBUG, - "Dropping span %s as it was pending for too long.", - span.getSpanId()); - } - return isTimedOut; - } - - private @NotNull List filterOutSentrySpans(final @NotNull Collection spans) { - return spans.stream() - .filter((span) -> !isSentryRequest(scopes, span.getKind(), span.getAttributes())) - .collect(Collectors.toList()); - } - - private List maybeSend(final @NotNull List spans) { - final @NotNull List grouped = groupSpansWithParents(spans); - final @NotNull List remaining = new CopyOnWriteArrayList<>(grouped); - final @NotNull List rootNodes = findCompletedRootNodes(grouped); - - for (final @NotNull SpanNode rootNode : rootNodes) { - remaining.remove(rootNode); - final @Nullable SpanData span = rootNode.getSpan(); - if (span == null) { - // TODO log - continue; - } - final @Nullable ITransaction transaction = createTransactionForOtelSpan(span); - if (transaction == null) { - // TODO log - continue; - } - - for (final @NotNull SpanNode childNode : rootNode.getChildren()) { - createAndFinishSpanForOtelSpan(childNode, transaction, remaining); - } - - transaction.finish( - mapOtelStatus(span, transaction), new SentryLongDate(span.getEndEpochNanos())); - } - - return remaining.stream() - .map((node) -> node.getSpan()) - .filter((it) -> it != null) - .collect(Collectors.toList()); - } - - private void createAndFinishSpanForOtelSpan( - final @NotNull SpanNode spanNode, - final @NotNull ISpan parentSentrySpan, - final @NotNull List remaining) { - remaining.remove(spanNode); - final @Nullable SpanData spanData = spanNode.getSpan(); - - // If this span should be dropped, we still want to create spans for the children of this - if (spanData == null) { - for (SpanNode childNode : spanNode.getChildren()) { - createAndFinishSpanForOtelSpan(childNode, parentSentrySpan, remaining); - } - return; - } - - final @NotNull String spanId = spanData.getSpanId(); - final @Nullable OtelSpanWrapper sentrySpanMaybe = - spanStorage.getSentrySpan(spanData.getSpanContext()); - final @NotNull OtelSpanInfo spanInfo = - spanDescriptionExtractor.extractSpanInfo(spanData, sentrySpanMaybe); - // TODO attributes - // TODO cleanup sentry attributes - - scopes - .getOptions() - .getLogger() - .log( - SentryLevel.DEBUG, - "Creating Sentry child span for OpenTelemetry span %s (trace %s). Parent span is %s.", - spanId, - spanData.getTraceId(), - spanData.getParentSpanId()); - final @NotNull SentryDate startDate = new SentryLongDate(spanData.getStartEpochNanos()); - final @NotNull SpanOptions spanOptions = new SpanOptions(); - // TODO [POTEL] op and description might have been overriden - final @NotNull io.sentry.SpanContext spanContext = - parentSentrySpan - .getSpanContext() - .copyForChild( - spanInfo.getOp(), - parentSentrySpan.getSpanContext().getSpanId(), - new SpanId(spanId)); - spanContext.setDescription(spanInfo.getDescription()); - spanContext.setInstrumenter(Instrumenter.OTEL); - if (sentrySpanMaybe != null) { - spanContext.setSamplingDecision(sentrySpanMaybe.getSamplingDecision()); - spanOptions.setOrigin(sentrySpanMaybe.getSpanContext().getOrigin()); - } else { - // TODO [POTEL] Check if we want to use `instrumentationScopeInfo.name` and append it to - // `auto.otel` - spanOptions.setOrigin(TRACE_ORIGIN); - } - - spanOptions.setStartTimestamp(startDate); - - final @NotNull ISpan sentryChildSpan = parentSentrySpan.startChild(spanContext, spanOptions); - - for (Map.Entry dataField : spanInfo.getDataFields().entrySet()) { - sentryChildSpan.setData(dataField.getKey(), dataField.getValue()); - } - - transferSpanDetails(sentrySpanMaybe, sentryChildSpan); - - for (SpanNode childNode : spanNode.getChildren()) { - createAndFinishSpanForOtelSpan(childNode, sentryChildSpan, remaining); - } - - sentryChildSpan.finish( - mapOtelStatus(spanData, sentryChildSpan), new SentryLongDate(spanData.getEndEpochNanos())); - } - - private void transferSpanDetails( - final @Nullable OtelSpanWrapper sourceSpanMaybe, final @NotNull ISpan targetSpan) { - if (sourceSpanMaybe != null) { - final @NotNull OtelSpanWrapper sourceSpan = sourceSpanMaybe; - - final @NotNull Contexts contexts = sourceSpan.getContexts(); - targetSpan.getContexts().putAll(contexts); - - final @NotNull Map data = sourceSpan.getData(); - for (Map.Entry entry : data.entrySet()) { - targetSpan.setData(entry.getKey(), entry.getValue()); - } - - // TODO [POTEL] this is not an OtelSpanWrapper since it's created with default span factory - // if (sentryChildSpan instanceof OtelSpanWrapper) { - // final @NotNull OtelSpanWrapper sentryChildSpanWrapper = (OtelSpanWrapper) - // sentryChildSpan; - // final @NotNull Map measurements = - // sentrySpan.getMeasurements(); - // sentryChildSpanWrapper.addAllMeasurements(measurements); - // } - - final @NotNull Map tags = sourceSpan.getTags(); - for (Map.Entry entry : tags.entrySet()) { - targetSpan.setTag(entry.getKey(), entry.getValue()); - } - - targetSpan.setStatus(sourceSpan.getStatus()); - } - } - - private @Nullable ITransaction createTransactionForOtelSpan(final @NotNull SpanData span) { - final @NotNull String spanId = span.getSpanId(); - final @NotNull String traceId = span.getTraceId(); - final @Nullable OtelSpanWrapper sentrySpanMaybe = - spanStorage.getSentrySpan(span.getSpanContext()); - - final @Nullable IScopes scopesMaybe = - sentrySpanMaybe != null ? sentrySpanMaybe.getScopes() : null; - final @NotNull IScopes scopesToUse = - scopesMaybe == null ? ScopesAdapter.getInstance() : scopesMaybe; - final @NotNull OtelSpanInfo spanInfo = - spanDescriptionExtractor.extractSpanInfo(span, sentrySpanMaybe); - - scopesToUse - .getOptions() - .getLogger() - .log( - SentryLevel.DEBUG, - "Creating Sentry transaction for OpenTelemetry span %s (trace %s).", - spanId, - traceId); - final SpanId sentrySpanId = new SpanId(spanId); - - @Nullable String transactionName = spanInfo.getDescription(); - @NotNull TransactionNameSource transactionNameSource = spanInfo.getTransactionNameSource(); - @Nullable SpanId parentSpanId = null; - @Nullable Baggage baggage = null; - - if (sentrySpanMaybe != null) { - final @NotNull OtelSpanWrapper sentrySpan = sentrySpanMaybe; - final @Nullable String transactionNameMaybe = sentrySpan.getTransactionName(); - if (transactionNameMaybe != null) { - transactionName = transactionNameMaybe; - } - final @Nullable TransactionNameSource transactionNameSourceMaybe = - sentrySpan.getTransactionNameSource(); - if (transactionNameSourceMaybe != null) { - transactionNameSource = transactionNameSourceMaybe; - } - final @NotNull SpanContext spanContext = sentrySpan.getSpanContext(); - parentSpanId = spanContext.getParentSpanId(); - baggage = spanContext.getBaggage(); - } - - // TODO [POTEL] parentSamplingDecision? - final @NotNull TransactionContext transactionContext = - new TransactionContext(new SentryId(traceId), sentrySpanId, parentSpanId, null, baggage); - - TransactionOptions transactionOptions = new TransactionOptions(); - - transactionContext.setName( - transactionName == null ? DEFAULT_TRANSACTION_NAME : transactionName); - transactionContext.setTransactionNameSource(transactionNameSource); - transactionContext.setOperation(spanInfo.getOp()); - transactionContext.setInstrumenter(Instrumenter.OTEL); - if (sentrySpanMaybe != null) { - transactionContext.setSamplingDecision(sentrySpanMaybe.getSamplingDecision()); - transactionOptions.setOrigin(sentrySpanMaybe.getSpanContext().getOrigin()); - } - - transactionOptions.setStartTimestamp(new SentryLongDate(span.getStartEpochNanos())); - transactionOptions.setSpanFactory(new DefaultSpanFactory()); - - ITransaction sentryTransaction = - scopesToUse.startTransaction(transactionContext, transactionOptions); - - final @NotNull Map otelContext = toOtelContext(span); - sentryTransaction.setContext("otel", otelContext); - - for (Map.Entry dataField : spanInfo.getDataFields().entrySet()) { - sentryTransaction.setData(dataField.getKey(), dataField.getValue()); - } - - transferSpanDetails(sentrySpanMaybe, sentryTransaction); - - return sentryTransaction; - } - - private List findCompletedRootNodes(final @NotNull List grouped) { - final @NotNull Predicate isRootPredicate = - (node) -> { - return node.getParentNode() == null && node.getSpan() != null; - }; - return grouped.stream().filter(isRootPredicate).collect(Collectors.toList()); - } - - private List groupSpansWithParents(final @NotNull List spans) { - final @NotNull Map nodeMap = new HashMap<>(); - - for (final @NotNull SpanData spanData : spans) { - createOrUpdateSpanNodeAndRefs(nodeMap, spanData); - } - - return nodeMap.values().stream().collect(Collectors.toList()); - } - - private void createOrUpdateSpanNodeAndRefs( - final @NotNull Map nodeMap, final @NotNull SpanData spanData) { - final @NotNull String spanId = spanData.getSpanId(); - final String parentId = getParentId(spanData); - if (parentId == null) { - createOrUpdateNode(nodeMap, spanId, spanData, null, null); - return; - } - - final @NotNull SpanNode parentNode = createOrGetParentNode(nodeMap, parentId); - final @NotNull SpanNode spanNode = - createOrUpdateNode(nodeMap, spanId, spanData, null, parentNode); - parentNode.addChild(spanNode); - } - - private @Nullable String getParentId(final @NotNull SpanData spanData) { - final @NotNull String parentSpanId = spanData.getParentSpanId(); - final @Nullable Boolean isRemoteParent = spanData.getAttributes().get(IS_REMOTE_PARENT); - if (isRemoteParent != null && isRemoteParent) { - return null; - } - if (io.opentelemetry.api.trace.SpanId.isValid(parentSpanId)) { - return parentSpanId; - } - return null; - } - - private @NotNull SpanNode createOrGetParentNode( - final @NotNull Map nodeMap, final @NotNull String spanId) { - final @Nullable SpanNode existingNode = nodeMap.get(spanId); - - if (existingNode == null) { - return createOrUpdateNode(nodeMap, spanId, null, null, null); - } - - return existingNode; - } - - // TODO do we ever pass children? - private @NotNull SpanNode createOrUpdateNode( - final @NotNull Map nodeMap, - final @NotNull String spanId, - final @Nullable SpanData spanData, - final @Nullable List children, - final @Nullable SpanNode parentNode) { - final @Nullable SpanNode existingNode = nodeMap.get(spanId); - - if (existingNode != null) { - final @Nullable SpanData existingNodeSpan = existingNode.getSpan(); - - if (existingNodeSpan != null) { - // If span is already set, nothing to do here - return existingNode; - } - - // If span is not set yet, we update it - existingNode.setSpan(spanData); - existingNode.setParentNode(parentNode); - - return existingNode; - } - - final @NotNull SpanNode spanNode = new SpanNode(spanId); - spanNode.setSpan(spanData); - spanNode.setParentNode(parentNode); - spanNode.addChildren(children); - - nodeMap.put(spanId, spanNode); - - return spanNode; - } - - @SuppressWarnings("deprecation") - private SpanStatus mapOtelStatus( - final @NotNull SpanData otelSpanData, final @NotNull ISpan sentrySpan) { - final @Nullable SpanStatus existingStatus = sentrySpan.getStatus(); - // TODO [POTEL] do we want the unknown error check here? - if (existingStatus != null && existingStatus != SpanStatus.UNKNOWN_ERROR) { - return existingStatus; - } - - final @NotNull StatusData otelStatus = otelSpanData.getStatus(); - final @NotNull StatusCode otelStatusCode = otelStatus.getStatusCode(); - - if (StatusCode.OK.equals(otelStatusCode) || StatusCode.UNSET.equals(otelStatusCode)) { - return SpanStatus.OK; - } - - final @Nullable Long httpStatus = - otelSpanData.getAttributes().get(SemanticAttributes.HTTP_STATUS_CODE); - if (httpStatus != null) { - final @Nullable SpanStatus spanStatus = SpanStatus.fromHttpStatusCode(httpStatus.intValue()); - if (spanStatus != null) { - return spanStatus; - } - } - - return SpanStatus.UNKNOWN_ERROR; - } - - private @NotNull Map toOtelContext(final @NotNull SpanData spanData) { - final @NotNull Map context = new HashMap<>(); - - context.put("attributes", toMapWithStringKeys(spanData.getAttributes())); - context.put("resource", toMapWithStringKeys(spanData.getResource().getAttributes())); - - return context; - } - - private @NotNull Map toMapWithStringKeys(final @Nullable Attributes attributes) { - final @NotNull Map mapWithStringKeys = new HashMap<>(); - - if (attributes != null) { - attributes.forEach( - (key, value) -> { - if (key != null) { - final @NotNull String stringKey = key.getKey(); - if (!isSentryInternalKey(stringKey)) { - mapWithStringKeys.put(stringKey, value); - } - } - }); - } - - return mapWithStringKeys; - } - - private boolean isSentryInternalKey(final @NotNull String key) { - return attributeKeysToRemove.contains(key); - } - - @Override - public CompletableResultCode flush() { - scopes.flush(10000); - return CompletableResultCode.ofSuccess(); - } - - @Override - public CompletableResultCode shutdown() { - stopped = true; - scopes.close(); - return CompletableResultCode.ofSuccess(); - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java index f09c082d72..a9e70f66a0 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SentrySpanProcessor.java @@ -1,7 +1,5 @@ package io.sentry.opentelemetry; -import static io.sentry.TransactionContext.DEFAULT_TRANSACTION_NAME; - import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.api.trace.SpanKind; @@ -15,18 +13,18 @@ import io.opentelemetry.semconv.SemanticAttributes; import io.sentry.Baggage; import io.sentry.DsnUtil; -import io.sentry.IScopes; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ISpan; import io.sentry.ITransaction; import io.sentry.Instrumenter; import io.sentry.PropagationContext; -import io.sentry.ScopesAdapter; import io.sentry.SentryDate; import io.sentry.SentryLevel; import io.sentry.SentryLongDate; +import io.sentry.SentrySpanStorage; import io.sentry.SentryTraceHeader; import io.sentry.SpanId; -import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.TransactionContext; import io.sentry.TransactionOptions; @@ -39,10 +37,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** - * @deprecated please use {@link OtelSentrySpanProcessor} instead. - */ -@Deprecated public final class SentrySpanProcessor implements SpanProcessor { private static final String TRACE_ORIGN = "auto.otel"; @@ -51,19 +45,15 @@ public final class SentrySpanProcessor implements SpanProcessor { Arrays.asList(SpanKind.CLIENT, SpanKind.INTERNAL); private final @NotNull SpanDescriptionExtractor spanDescriptionExtractor = new SpanDescriptionExtractor(); - - @SuppressWarnings("deprecation") - private final @NotNull io.sentry.SentrySpanStorage spanStorage = - io.sentry.SentrySpanStorage.getInstance(); - - private final @NotNull IScopes scopes; + private final @NotNull SentrySpanStorage spanStorage = SentrySpanStorage.getInstance(); + private final @NotNull IHub hub; public SentrySpanProcessor() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } - SentrySpanProcessor(final @NotNull IScopes scopes) { - this.scopes = scopes; + SentrySpanProcessor(final @NotNull IHub hub) { + this.hub = hub; } @Override @@ -75,8 +65,7 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri final @NotNull TraceData traceData = getTraceData(otelSpan, parentContext); if (isSentryRequest(otelSpan)) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -89,8 +78,7 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri traceData.getParentSpanId() == null ? null : spanStorage.get(traceData.getParentSpanId()); if (sentryParentSpan != null) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -100,15 +88,13 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri traceData.getParentSpanId()); final @NotNull SentryDate startDate = new SentryLongDate(otelSpan.toSpanData().getStartEpochNanos()); - final @NotNull SpanOptions spanOptions = new SpanOptions(); - spanOptions.setOrigin(TRACE_ORIGN); final @NotNull ISpan sentryChildSpan = sentryParentSpan.startChild( - otelSpan.getName(), otelSpan.getName(), startDate, Instrumenter.OTEL, spanOptions); + otelSpan.getName(), otelSpan.getName(), startDate, Instrumenter.OTEL); + sentryChildSpan.getSpanContext().setOrigin(TRACE_ORIGN); spanStorage.store(traceData.getSpanId(), sentryChildSpan); } else { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -136,9 +122,9 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setStartTimestamp( new SentryLongDate(otelSpan.toSpanData().getStartEpochNanos())); - transactionOptions.setOrigin(TRACE_ORIGN); - ISpan sentryTransaction = scopes.startTransaction(transactionContext, transactionOptions); + ISpan sentryTransaction = hub.startTransaction(transactionContext, transactionOptions); + sentryTransaction.getSpanContext().setOrigin(TRACE_ORIGN); spanStorage.store(traceData.getSpanId(), sentryTransaction); } } @@ -158,8 +144,7 @@ public void onEnd(final @NotNull ReadableSpan otelSpan) { final @Nullable ISpan sentrySpan = spanStorage.removeAndGet(traceData.getSpanId()); if (sentrySpan == null) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -170,8 +155,7 @@ public void onEnd(final @NotNull ReadableSpan otelSpan) { } if (isSentryRequest(otelSpan)) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -184,8 +168,7 @@ public void onEnd(final @NotNull ReadableSpan otelSpan) { if (sentrySpan instanceof ITransaction) { final @NotNull ITransaction sentryTransaction = (ITransaction) sentrySpan; updateTransactionWithOtelData(sentryTransaction, otelSpan); - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -195,8 +178,7 @@ public void onEnd(final @NotNull ReadableSpan otelSpan) { traceData.getTraceId()); } else { updateSpanWithOtelData(sentrySpan, otelSpan); - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -219,8 +201,7 @@ public boolean isEndRequired() { private boolean ensurePrerequisites(final @NotNull ReadableSpan otelSpan) { if (!hasSentryBeenInitialized()) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -228,10 +209,9 @@ private boolean ensurePrerequisites(final @NotNull ReadableSpan otelSpan) { return false; } - final @NotNull Instrumenter instrumenter = scopes.getOptions().getInstrumenter(); + final @NotNull Instrumenter instrumenter = hub.getOptions().getInstrumenter(); if (!Instrumenter.OTEL.equals(instrumenter)) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -242,8 +222,7 @@ private boolean ensurePrerequisites(final @NotNull ReadableSpan otelSpan) { final @NotNull SpanContext otelSpanContext = otelSpan.getSpanContext(); if (!otelSpanContext.isValid()) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -262,7 +241,7 @@ private boolean isSentryRequest(final @NotNull ReadableSpan otelSpan) { } final @Nullable String httpUrl = otelSpan.getAttribute(SemanticAttributes.HTTP_URL); - return DsnUtil.urlContainsDsnHost(scopes.getOptions(), httpUrl); + return DsnUtil.urlContainsDsnHost(hub.getOptions(), httpUrl); } private @NotNull TraceData getTraceData( @@ -293,12 +272,10 @@ private boolean isSentryRequest(final @NotNull ReadableSpan otelSpan) { private void updateTransactionWithOtelData( final @NotNull ITransaction sentryTransaction, final @NotNull ReadableSpan otelSpan) { final @NotNull OtelSpanInfo otelSpanInfo = - spanDescriptionExtractor.extractSpanInfo(otelSpan.toSpanData(), null); + spanDescriptionExtractor.extractSpanDescription(otelSpan); sentryTransaction.setOperation(otelSpanInfo.getOp()); - String transactionName = otelSpanInfo.getDescription(); sentryTransaction.setName( - transactionName == null ? DEFAULT_TRANSACTION_NAME : transactionName, - otelSpanInfo.getTransactionNameSource()); + otelSpanInfo.getDescription(), otelSpanInfo.getTransactionNameSource()); final @NotNull Map otelContext = toOtelContext(otelSpan); sentryTransaction.setContext("otel", otelContext); @@ -330,7 +307,7 @@ private void updateSpanWithOtelData( }); final @NotNull OtelSpanInfo otelSpanInfo = - spanDescriptionExtractor.extractSpanInfo(otelSpan.toSpanData(), null); + spanDescriptionExtractor.extractSpanDescription(otelSpan); sentrySpan.setOperation(otelSpanInfo.getOp()); sentrySpan.setDescription(otelSpanInfo.getDescription()); } @@ -357,7 +334,7 @@ private SpanStatus mapOtelStatus(final @NotNull ReadableSpan otelSpan) { } private boolean hasSentryBeenInitialized() { - return scopes.isEnabled(); + return hub.isEnabled(); } private @NotNull Map toMapWithStringKeys(final @Nullable Attributes attributes) { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java index 5e2c07bbdc..57db007b0a 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanDescriptionExtractor.java @@ -1,12 +1,9 @@ package io.sentry.opentelemetry; -import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.ReadableSpan; import io.opentelemetry.semconv.SemanticAttributes; import io.sentry.protocol.TransactionNameSource; -import java.util.HashMap; -import java.util.Map; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -14,119 +11,53 @@ @ApiStatus.Internal public final class SpanDescriptionExtractor { - // TODO [POTEL] remove these method overloads and pass in SpanData instead (span.toSpanData()) @SuppressWarnings("deprecation") - public @NotNull OtelSpanInfo extractSpanInfo( - final @NotNull SpanData otelSpan, final @Nullable OtelSpanWrapper sentrySpan) { - OtelSpanInfo spanInfo = extractSpanDescription(otelSpan, sentrySpan); - - final @Nullable Long threadId = otelSpan.getAttributes().get(SemanticAttributes.THREAD_ID); - if (threadId != null) { - spanInfo.addDataField("thread.id", threadId); - } - - final @Nullable String threadName = - otelSpan.getAttributes().get(SemanticAttributes.THREAD_NAME); - if (threadName != null) { - spanInfo.addDataField("thread.name", threadName); - } - - final @Nullable String dbSystem = otelSpan.getAttributes().get(SemanticAttributes.DB_SYSTEM); - if (dbSystem != null) { - spanInfo.addDataField("db.system", dbSystem); - } - - final @Nullable String dbName = otelSpan.getAttributes().get(SemanticAttributes.DB_NAME); - if (dbName != null) { - spanInfo.addDataField("db.name", dbName); - } - - return spanInfo; - } - - @SuppressWarnings("deprecation") - private OtelSpanInfo extractSpanDescription( - final @NotNull SpanData otelSpan, final @Nullable OtelSpanWrapper sentrySpan) { - final @NotNull Attributes attributes = otelSpan.getAttributes(); + public @NotNull OtelSpanInfo extractSpanDescription(final @NotNull ReadableSpan otelSpan) { + final @NotNull String name = otelSpan.getName(); - final @Nullable String httpMethod = attributes.get(SemanticAttributes.HTTP_METHOD); + final @Nullable String httpMethod = otelSpan.getAttribute(SemanticAttributes.HTTP_METHOD); if (httpMethod != null) { return descriptionForHttpMethod(otelSpan, httpMethod); } - final @Nullable String httpRequestMethod = - attributes.get(SemanticAttributes.HTTP_REQUEST_METHOD); - if (httpRequestMethod != null) { - return descriptionForHttpMethod(otelSpan, httpRequestMethod); - } - - final @Nullable String dbSystem = attributes.get(SemanticAttributes.DB_SYSTEM); + final @Nullable String dbSystem = otelSpan.getAttribute(SemanticAttributes.DB_SYSTEM); if (dbSystem != null) { return descriptionForDbSystem(otelSpan); } - final @NotNull String name = otelSpan.getName(); - final @Nullable String description = sentrySpan != null ? sentrySpan.getDescription() : name; - return new OtelSpanInfo(name, description, TransactionNameSource.CUSTOM); + return new OtelSpanInfo(name, name, TransactionNameSource.CUSTOM); } @SuppressWarnings("deprecation") private OtelSpanInfo descriptionForHttpMethod( - final @NotNull SpanData otelSpan, final @NotNull String httpMethod) { + final @NotNull ReadableSpan otelSpan, final @NotNull String httpMethod) { final @NotNull String name = otelSpan.getName(); final @NotNull SpanKind kind = otelSpan.getKind(); final @NotNull StringBuilder opBuilder = new StringBuilder("http"); - final @NotNull Attributes attributes = otelSpan.getAttributes(); - final @NotNull Map dataFields = new HashMap<>(); - dataFields.put("http.request.method", httpMethod); if (SpanKind.CLIENT.equals(kind)) { opBuilder.append(".client"); } else if (SpanKind.SERVER.equals(kind)) { opBuilder.append(".server"); } - final @Nullable String httpTarget = attributes.get(SemanticAttributes.HTTP_TARGET); - final @Nullable String httpRoute = attributes.get(SemanticAttributes.HTTP_ROUTE); - @Nullable String httpPath = httpRoute; - if (httpPath == null) { - httpPath = httpTarget; - } + final @Nullable String httpTarget = otelSpan.getAttribute(SemanticAttributes.HTTP_TARGET); + final @Nullable String httpRoute = otelSpan.getAttribute(SemanticAttributes.HTTP_ROUTE); + final @Nullable String httpPath = httpRoute != null ? httpRoute : httpTarget; final @NotNull String op = opBuilder.toString(); - final @Nullable Long httpStatusCode = - attributes.get(SemanticAttributes.HTTP_RESPONSE_STATUS_CODE); - if (httpStatusCode != null) { - dataFields.put("http.response.status_code", httpStatusCode); - } - - final @Nullable String serverAddress = attributes.get(SemanticAttributes.SERVER_ADDRESS); - if (serverAddress != null) { - dataFields.put("server.address", serverAddress); - } - - final @Nullable String urlFull = attributes.get(SemanticAttributes.URL_FULL); - if (urlFull != null) { - dataFields.put("url.full", urlFull); - if (httpPath == null) { - httpPath = urlFull; - } - } - if (httpPath == null) { - return new OtelSpanInfo(op, name, TransactionNameSource.CUSTOM, dataFields); + return new OtelSpanInfo(op, name, TransactionNameSource.CUSTOM); } final @NotNull String description = httpMethod + " " + httpPath; final @NotNull TransactionNameSource transactionNameSource = httpRoute != null ? TransactionNameSource.ROUTE : TransactionNameSource.URL; - return new OtelSpanInfo(op, description, transactionNameSource, dataFields); + return new OtelSpanInfo(op, description, transactionNameSource); } - @SuppressWarnings("deprecation") - private OtelSpanInfo descriptionForDbSystem(final @NotNull SpanData otelSpan) { - final @NotNull Attributes attributes = otelSpan.getAttributes(); - @Nullable String dbStatement = attributes.get(SemanticAttributes.DB_STATEMENT); + private OtelSpanInfo descriptionForDbSystem(final @NotNull ReadableSpan otelSpan) { + @Nullable String dbStatement = otelSpan.getAttribute(SemanticAttributes.DB_STATEMENT); @NotNull String description = dbStatement != null ? dbStatement : otelSpan.getName(); return new OtelSpanInfo("db", description, TransactionNameSource.TASK); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanNode.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanNode.java deleted file mode 100644 index e74747d8a1..0000000000 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/SpanNode.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.sentry.opentelemetry; - -import io.opentelemetry.sdk.trace.data.SpanData; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@ApiStatus.Internal -public final class SpanNode { - private final @NotNull String id; - private @Nullable SpanData span; - private @Nullable SpanNode parentNode; - private @NotNull List children = new CopyOnWriteArrayList<>(); - - public SpanNode(final @NotNull String spanId) { - this.id = spanId; - } - - public @NotNull String getId() { - return id; - } - - public @Nullable SpanData getSpan() { - return span; - } - - public void setSpan(final @Nullable SpanData span) { - this.span = span; - } - - public @Nullable SpanNode getParentNode() { - return parentNode; - } - - public void setParentNode(final @Nullable SpanNode parentNode) { - this.parentNode = parentNode; - } - - public @NotNull List getChildren() { - return children; - } - - public void addChildren(final @Nullable List children) { - if (children != null) { - this.children.addAll(children); - } - } - - public void addChild(final @Nullable SpanNode child) { - if (child != null) { - this.children.add(child); - } - } -} diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/TraceData.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/TraceData.java index 5904db39e7..08751b5609 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/TraceData.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/TraceData.java @@ -6,7 +6,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -@Deprecated @ApiStatus.Internal public final class TraceData { diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SentrySpanProcessorTest.kt b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SentrySpanProcessorTest.kt index acead00460..5ed757ba16 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SentrySpanProcessorTest.kt +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SentrySpanProcessorTest.kt @@ -21,7 +21,7 @@ import io.opentelemetry.semconv.SemanticAttributes import io.sentry.Baggage import io.sentry.BaggageHeader import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ISpan import io.sentry.ITransaction import io.sentry.Instrumenter @@ -29,7 +29,6 @@ import io.sentry.SentryDate import io.sentry.SentryEvent import io.sentry.SentryOptions import io.sentry.SentryTraceHeader -import io.sentry.SpanOptions import io.sentry.SpanStatus import io.sentry.TransactionContext import io.sentry.TransactionOptions @@ -66,7 +65,7 @@ class SentrySpanProcessorTest { it.dsn = "https://key@sentry.io/proj" it.instrumenter = Instrumenter.OTEL } - val scopes = mock() + val hub = mock() val transaction = mock() val span = mock() val spanContext = mock() @@ -76,9 +75,9 @@ class SentrySpanProcessorTest { val baggage = Baggage.fromHeader(BAGGAGE_HEADER_STRING) fun setup() { - whenever(scopes.isEnabled).thenReturn(true) - whenever(scopes.options).thenReturn(options) - whenever(scopes.startTransaction(any(), any())).thenReturn(transaction) + whenever(hub.isEnabled).thenReturn(true) + whenever(hub.options).thenReturn(options) + whenever(hub.startTransaction(any(), any())).thenReturn(transaction) whenever(spanContext.operation).thenReturn("spanContextOp") whenever(spanContext.parentSpanId).thenReturn(io.sentry.SpanId("cedf5b7571cb4972")) @@ -92,10 +91,10 @@ class SentrySpanProcessorTest { whenever(span.toBaggageHeader(any())).thenReturn(baggageHeader) whenever(transaction.toBaggageHeader(any())).thenReturn(baggageHeader) - whenever(transaction.startChild(any(), anyOrNull(), anyOrNull(), eq(Instrumenter.OTEL), any())).thenReturn(span) + whenever(transaction.startChild(any(), anyOrNull(), anyOrNull(), eq(Instrumenter.OTEL))).thenReturn(span) val sdkTracerProvider = SdkTracerProvider.builder() - .addSpanProcessor(SentrySpanProcessor(scopes)) + .addSpanProcessor(SentrySpanProcessor(hub)) .build() openTelemetry = OpenTelemetrySdk.builder() @@ -147,13 +146,13 @@ class SentrySpanProcessorTest { val context = mock() val span = mock() - whenever(fixture.scopes.isEnabled).thenReturn(false) + whenever(fixture.hub.isEnabled).thenReturn(false) - SentrySpanProcessor(fixture.scopes).onStart(context, span) + SentrySpanProcessor(fixture.hub).onStart(context, span) - verify(fixture.scopes).isEnabled - verify(fixture.scopes).options - verifyNoMoreInteractions(fixture.scopes) + verify(fixture.hub).isEnabled + verify(fixture.hub).options + verifyNoMoreInteractions(fixture.hub) verifyNoInteractions(context, span) } @@ -162,13 +161,13 @@ class SentrySpanProcessorTest { fixture.setup() val span = mock() - whenever(fixture.scopes.isEnabled).thenReturn(false) + whenever(fixture.hub.isEnabled).thenReturn(false) - SentrySpanProcessor(fixture.scopes).onEnd(span) + SentrySpanProcessor(fixture.hub).onEnd(span) - verify(fixture.scopes).isEnabled - verify(fixture.scopes).options - verifyNoMoreInteractions(fixture.scopes) + verify(fixture.hub).isEnabled + verify(fixture.hub).options + verifyNoMoreInteractions(fixture.hub) verifyNoInteractions(span) } @@ -179,7 +178,7 @@ class SentrySpanProcessorTest { val mockSpanContext = mock() whenever(mockSpanContext.spanId).thenReturn(SpanId.getInvalid()) whenever(mockSpan.spanContext).thenReturn(mockSpanContext) - SentrySpanProcessor(fixture.scopes).onStart(Context.current(), mockSpan) + SentrySpanProcessor(fixture.hub).onStart(Context.current(), mockSpan) thenNoTransactionIsStarted() } @@ -191,7 +190,7 @@ class SentrySpanProcessorTest { whenever(mockSpanContext.spanId).thenReturn(SpanId.fromBytes("seed".toByteArray())) whenever(mockSpanContext.traceId).thenReturn(TraceId.getInvalid()) whenever(mockSpan.spanContext).thenReturn(mockSpanContext) - SentrySpanProcessor(fixture.scopes).onStart(Context.current(), mockSpan) + SentrySpanProcessor(fixture.hub).onStart(Context.current(), mockSpan) thenNoTransactionIsStarted() } @@ -343,7 +342,7 @@ class SentrySpanProcessorTest { thenTransactionIsStarted(otelSpan, isContinued = true) otelSpan.makeCurrent().use { _ -> - val processedEvent = OpenTelemetryLinkErrorEventProcessor(fixture.scopes).process(SentryEvent(), Hint()) + val processedEvent = OpenTelemetryLinkErrorEventProcessor(fixture.hub).process(SentryEvent(), Hint()) val traceContext = processedEvent!!.contexts.trace!! assertEquals("2722d9f6ec019ade60c776169d9a8904", traceContext.traceId.toString()) @@ -362,7 +361,7 @@ class SentrySpanProcessorTest { fixture.options.instrumenter = Instrumenter.SENTRY fixture.setup() - val processedEvent = OpenTelemetryLinkErrorEventProcessor(fixture.scopes).process(SentryEvent(), Hint()) + val processedEvent = OpenTelemetryLinkErrorEventProcessor(fixture.hub).process(SentryEvent(), Hint()) thenNoTraceContextHasBeenAddedToEvent(processedEvent) } @@ -394,7 +393,7 @@ class SentrySpanProcessorTest { private fun thenTransactionIsStarted(otelSpan: Span, isContinued: Boolean = false, continuesWithFilledBaggage: Boolean = true) { if (isContinued) { - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("testspan", it.name) assertEquals(TransactionNameSource.CUSTOM, it.transactionNameSource) @@ -424,7 +423,7 @@ class SentrySpanProcessorTest { } ) } else { - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("testspan", it.name) assertEquals(TransactionNameSource.CUSTOM, it.transactionNameSource) @@ -452,7 +451,7 @@ class SentrySpanProcessorTest { } private fun thenNoTransactionIsStarted() { - verify(fixture.scopes, never()).startTransaction( + verify(fixture.hub, never()).startTransaction( any(), any() ) @@ -463,8 +462,7 @@ class SentrySpanProcessorTest { eq("childspan"), eq("childspan"), any(), - eq(Instrumenter.OTEL), - any() + eq(Instrumenter.OTEL) ) } diff --git a/sentry-quartz/api/sentry-quartz.api b/sentry-quartz/api/sentry-quartz.api index bb8b142a91..ff32280dc5 100644 --- a/sentry-quartz/api/sentry-quartz.api +++ b/sentry-quartz/api/sentry-quartz.api @@ -5,10 +5,9 @@ public final class io/sentry/quartz/BuildConfig { public final class io/sentry/quartz/SentryJobListener : org/quartz/JobListener { public static final field SENTRY_CHECK_IN_ID_KEY Ljava/lang/String; - public static final field SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY Ljava/lang/String; public static final field SENTRY_SLUG_KEY Ljava/lang/String; public fun ()V - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun getName ()Ljava/lang/String; public fun jobExecutionVetoed (Lorg/quartz/JobExecutionContext;)V public fun jobToBeExecuted (Lorg/quartz/JobExecutionContext;)V diff --git a/sentry-quartz/src/main/java/io/sentry/quartz/SentryJobListener.java b/sentry-quartz/src/main/java/io/sentry/quartz/SentryJobListener.java index 38dbffdc8e..28a0e51200 100644 --- a/sentry-quartz/src/main/java/io/sentry/quartz/SentryJobListener.java +++ b/sentry-quartz/src/main/java/io/sentry/quartz/SentryJobListener.java @@ -3,13 +3,11 @@ import io.sentry.BuildConfig; import io.sentry.CheckIn; import io.sentry.CheckInStatus; -import io.sentry.IScopes; -import io.sentry.ISentryLifecycleToken; -import io.sentry.ScopesAdapter; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryLevel; import io.sentry.protocol.SentryId; -import io.sentry.util.LifecycleHelper; import io.sentry.util.Objects; import io.sentry.util.TracingUtils; import org.jetbrains.annotations.ApiStatus; @@ -25,16 +23,15 @@ public final class SentryJobListener implements JobListener { public static final String SENTRY_CHECK_IN_ID_KEY = "sentry-checkin-id"; public static final String SENTRY_SLUG_KEY = "sentry-slug"; - public static final String SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY = "sentry-scope-lifecycle"; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; public SentryJobListener() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } - public SentryJobListener(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + public SentryJobListener(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "hub is required"); SentryIntegrationPackageStorage.getInstance().addIntegration("Quartz"); SentryIntegrationPackageStorage.getInstance() .addPackage("maven:io.sentry:sentry-quartz", BuildConfig.VERSION_NAME); @@ -52,18 +49,15 @@ public void jobToBeExecuted(final @NotNull JobExecutionContext context) { if (maybeSlug == null) { return; } - final @NotNull ISentryLifecycleToken lifecycleToken = - scopes.forkedScopes("SentryJobListener").makeCurrent(); - TracingUtils.startNewTrace(scopes); + hub.pushScope(); + TracingUtils.startNewTrace(hub); final @NotNull String slug = maybeSlug; final @NotNull CheckIn checkIn = new CheckIn(slug, CheckInStatus.IN_PROGRESS); - final @NotNull SentryId checkInId = scopes.captureCheckIn(checkIn); + final @NotNull SentryId checkInId = hub.captureCheckIn(checkIn); context.put(SENTRY_CHECK_IN_ID_KEY, checkInId); context.put(SENTRY_SLUG_KEY, slug); - context.put(SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY, lifecycleToken); } catch (Throwable t) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log(SentryLevel.ERROR, "Unable to capture check-in in jobToBeExecuted.", t); } @@ -100,15 +94,14 @@ public void jobWasExecuted(JobExecutionContext context, JobExecutionException jo if (slug != null) { final boolean isFailed = jobException != null; final @NotNull CheckInStatus status = isFailed ? CheckInStatus.ERROR : CheckInStatus.OK; - scopes.captureCheckIn(new CheckIn(checkInId, slug, status)); + hub.captureCheckIn(new CheckIn(checkInId, slug, status)); } } catch (Throwable t) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log(SentryLevel.ERROR, "Unable to capture check-in in jobWasExecuted.", t); } finally { - LifecycleHelper.close(context.get(SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY)); + hub.popScope(); } } } diff --git a/sentry-samples/sentry-samples-android/CMakeLists.txt b/sentry-samples/sentry-samples-android/CMakeLists.txt index 19dca2b80d..ad170fe404 100644 --- a/sentry-samples/sentry-samples-android/CMakeLists.txt +++ b/sentry-samples/sentry-samples-android/CMakeLists.txt @@ -3,12 +3,15 @@ project(Sentry-Sample LANGUAGES C CXX) add_library(native-sample SHARED src/main/cpp/native-sample.cpp) -find_package(sentry-native-ndk REQUIRED CONFIG) +# make sure that we build it as a shared lib instead of a static lib +set(BUILD_SHARED_LIBS ON) +set(SENTRY_BUILD_SHARED_LIBS ON) + +add_subdirectory(../../sentry-android-ndk/${SENTRY_NATIVE_SRC} sentry_build) find_library(LOG_LIB log) target_link_libraries(native-sample PRIVATE ${LOG_LIB} - sentry-native-ndk::sentry-android - sentry-native-ndk::sentry + $ ) diff --git a/sentry-samples/sentry-samples-android/build.gradle.kts b/sentry-samples/sentry-samples-android/build.gradle.kts index e86dab253d..a8d8897519 100644 --- a/sentry-samples/sentry-samples-android/build.gradle.kts +++ b/sentry-samples/sentry-samples-android/build.gradle.kts @@ -15,8 +15,16 @@ android { versionName = project.version.toString() externalNativeBuild { + val sentryNativeSrc = if (File("${project.projectDir}/../../sentry-android-ndk/sentry-native-local").exists()) { + "sentry-native-local" + } else { + "sentry-native" + } + println("sentry-samples-android: $sentryNativeSrc") + cmake { - arguments.add(0, "-DANDROID_STL=c++_shared") + arguments.add(0, "-DANDROID_STL=c++_static") + arguments.add(0, "-DSENTRY_NATIVE_SRC=$sentryNativeSrc") } } @@ -30,7 +38,6 @@ android { // Note that the viewBinding.enabled property is now deprecated. viewBinding = true compose = true - prefab = true } composeOptions { @@ -100,11 +107,11 @@ dependencies { implementation(kotlin(Config.kotlinStdLib, org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION)) implementation(projects.sentryAndroid) + implementation(projects.sentryAndroidOkhttp) implementation(projects.sentryAndroidFragment) implementation(projects.sentryAndroidTimber) implementation(projects.sentryCompose) implementation(projects.sentryComposeHelper) - implementation(projects.sentryOkhttp) implementation(Config.Libs.fragment) implementation(Config.Libs.timber) @@ -127,6 +134,4 @@ dependencies { implementation(Config.Libs.composeMaterial) debugImplementation(Config.Libs.leakCanary) - - implementation("io.sentry:sentry-native-ndk:0.7.5") } diff --git a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/ProfilingActivity.kt b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/ProfilingActivity.kt index 64e3f48441..610fc1534d 100644 --- a/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/ProfilingActivity.kt +++ b/sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/ProfilingActivity.kt @@ -75,7 +75,7 @@ class ProfilingActivity : AppCompatActivity() { private fun finishTransactionAndPrintResults(t: ITransaction) { t.finish() profileFinished = true - val profilesDirPath = Sentry.getCurrentScopes().options.profilingTracesDirPath + val profilesDirPath = Sentry.getCurrentHub().options.profilingTracesDirPath if (profilesDirPath == null) { Toast.makeText(this, R.string.profiling_no_dir_set, Toast.LENGTH_SHORT).show() return @@ -84,7 +84,7 @@ class ProfilingActivity : AppCompatActivity() { // We have concurrent profiling now. We have to wait for all transactions to finish (e.g. button click) // before reading the profile, otherwise it's empty and a crash occurs if (Sentry.getSpan() != null) { - val timeout = Sentry.getCurrentScopes().options.idleTimeout ?: 0 + val timeout = Sentry.getCurrentHub().options.idleTimeout ?: 0 val duration = (getProfileDuration() * 1000).toLong() Thread.sleep((timeout - duration).coerceAtLeast(0)) } @@ -100,7 +100,7 @@ class ProfilingActivity : AppCompatActivity() { val traceData = ProfilingTraceData(profile, t) // Create envelope item from copied profile val item = - SentryEnvelopeItem.fromProfilingTrace(traceData, Long.MAX_VALUE, Sentry.getCurrentScopes().options.serializer) + SentryEnvelopeItem.fromProfilingTrace(traceData, Long.MAX_VALUE, Sentry.getCurrentHub().options.serializer) val itemData = item.data // Compress the envelope item using Gzip diff --git a/sentry-samples/sentry-samples-spring-jakarta/src/main/java/io/sentry/samples/spring/jakarta/AppConfig.java b/sentry-samples/sentry-samples-spring-jakarta/src/main/java/io/sentry/samples/spring/jakarta/AppConfig.java index 72ecb14e2f..f78c3f71d5 100644 --- a/sentry-samples/sentry-samples-spring-jakarta/src/main/java/io/sentry/samples/spring/jakarta/AppConfig.java +++ b/sentry-samples/sentry-samples-spring-jakarta/src/main/java/io/sentry/samples/spring/jakarta/AppConfig.java @@ -1,6 +1,6 @@ package io.sentry.samples.spring.jakarta; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.spring.jakarta.SentryUserFilter; import io.sentry.spring.jakarta.SentryUserProvider; import java.util.List; @@ -14,7 +14,7 @@ public class AppConfig { @Bean SentryUserFilter sentryUserFilter( - final IScopes scopes, final List sentryUserProviders) { - return new SentryUserFilter(scopes, sentryUserProviders); + final IHub hub, final List sentryUserProviders) { + return new SentryUserFilter(hub, sentryUserProviders); } } diff --git a/sentry-samples/sentry-samples-spring-jakarta/src/main/java/io/sentry/samples/spring/jakarta/WebConfig.java b/sentry-samples/sentry-samples-spring-jakarta/src/main/java/io/sentry/samples/spring/jakarta/WebConfig.java index 73d425b286..92b48b138c 100644 --- a/sentry-samples/sentry-samples-spring-jakarta/src/main/java/io/sentry/samples/spring/jakarta/WebConfig.java +++ b/sentry-samples/sentry-samples-spring-jakarta/src/main/java/io/sentry/samples/spring/jakarta/WebConfig.java @@ -1,6 +1,6 @@ package io.sentry.samples.spring.jakarta; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.spring.jakarta.tracing.SentrySpanClientHttpRequestInterceptor; import java.util.Collections; import org.springframework.context.annotation.Bean; @@ -20,14 +20,14 @@ public class WebConfig { * Creates a {@link RestTemplate} which calls are intercepted with {@link * SentrySpanClientHttpRequestInterceptor} to create spans around HTTP calls. * - * @param scopes - sentry scopes + * @param hub - sentry hub * @return RestTemplate */ @Bean - RestTemplate restTemplate(IScopes scopes) { + RestTemplate restTemplate(IHub hub) { RestTemplate restTemplate = new RestTemplate(); SentrySpanClientHttpRequestInterceptor sentryRestTemplateInterceptor = - new SentrySpanClientHttpRequestInterceptor(scopes); + new SentrySpanClientHttpRequestInterceptor(hub); restTemplate.setInterceptors(Collections.singletonList(sentryRestTemplateInterceptor)); return restTemplate; } diff --git a/sentry-samples/sentry-samples-spring/src/main/java/io/sentry/samples/spring/AppConfig.java b/sentry-samples/sentry-samples-spring/src/main/java/io/sentry/samples/spring/AppConfig.java index 89a968834a..7d46f09fb9 100644 --- a/sentry-samples/sentry-samples-spring/src/main/java/io/sentry/samples/spring/AppConfig.java +++ b/sentry-samples/sentry-samples-spring/src/main/java/io/sentry/samples/spring/AppConfig.java @@ -1,6 +1,6 @@ package io.sentry.samples.spring; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.spring.SentryUserFilter; import io.sentry.spring.SentryUserProvider; import java.util.List; @@ -14,7 +14,7 @@ public class AppConfig { @Bean SentryUserFilter sentryUserFilter( - final IScopes scopes, final List sentryUserProviders) { - return new SentryUserFilter(scopes, sentryUserProviders); + final IHub hub, final List sentryUserProviders) { + return new SentryUserFilter(hub, sentryUserProviders); } } diff --git a/sentry-samples/sentry-samples-spring/src/main/java/io/sentry/samples/spring/WebConfig.java b/sentry-samples/sentry-samples-spring/src/main/java/io/sentry/samples/spring/WebConfig.java index 2990ba8a38..e135cbe233 100644 --- a/sentry-samples/sentry-samples-spring/src/main/java/io/sentry/samples/spring/WebConfig.java +++ b/sentry-samples/sentry-samples-spring/src/main/java/io/sentry/samples/spring/WebConfig.java @@ -1,6 +1,6 @@ package io.sentry.samples.spring; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.spring.tracing.SentrySpanClientHttpRequestInterceptor; import java.util.Collections; import org.springframework.context.annotation.Bean; @@ -20,14 +20,14 @@ public class WebConfig { * Creates a {@link RestTemplate} which calls are intercepted with {@link * SentrySpanClientHttpRequestInterceptor} to create spans around HTTP calls. * - * @param scopes - sentry scopes + * @param hub - sentry hub * @return RestTemplate */ @Bean - RestTemplate restTemplate(IScopes scopes) { + RestTemplate restTemplate(IHub hub) { RestTemplate restTemplate = new RestTemplate(); SentrySpanClientHttpRequestInterceptor sentryRestTemplateInterceptor = - new SentrySpanClientHttpRequestInterceptor(scopes); + new SentrySpanClientHttpRequestInterceptor(hub); restTemplate.setInterceptors(Collections.singletonList(sentryRestTemplateInterceptor)); return restTemplate; } diff --git a/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api b/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api index d89edeec60..d0367e5195 100644 --- a/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api +++ b/sentry-servlet-jakarta/api/sentry-servlet-jakarta.api @@ -9,9 +9,8 @@ public class io/sentry/servlet/jakarta/SentryServletContainerInitializer : jakar } public class io/sentry/servlet/jakarta/SentryServletRequestListener : jakarta/servlet/ServletRequestListener { - public static final field SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY Ljava/lang/String; public fun ()V - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun requestDestroyed (Ljakarta/servlet/ServletRequestEvent;)V public fun requestInitialized (Ljakarta/servlet/ServletRequestEvent;)V } diff --git a/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryRequestHttpServletRequestProcessor.java b/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryRequestHttpServletRequestProcessor.java index 1ee536cb92..17ac102090 100644 --- a/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryRequestHttpServletRequestProcessor.java +++ b/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryRequestHttpServletRequestProcessor.java @@ -56,9 +56,4 @@ public SentryRequestHttpServletRequestProcessor(@NotNull HttpServletRequest http private static @Nullable String toString(final @Nullable Enumeration enumeration) { return enumeration != null ? String.join(",", Collections.list(enumeration)) : null; } - - @Override - public @Nullable Long getOrder() { - return 4000L; - } } diff --git a/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java b/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java index 9c8edeaf71..e3811157f8 100644 --- a/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java +++ b/sentry-servlet-jakarta/src/main/java/io/sentry/servlet/jakarta/SentryServletRequestListener.java @@ -5,10 +5,8 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IScopes; -import io.sentry.ISentryLifecycleToken; -import io.sentry.ScopesAdapter; -import io.sentry.util.LifecycleHelper; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.util.Objects; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletRequestEvent; @@ -23,41 +21,36 @@ @Open public class SentryServletRequestListener implements ServletRequestListener { - public static final String SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY = "sentry-scope-lifecycle"; + private final IHub hub; - private final IScopes scopes; - - public SentryServletRequestListener(@NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + public SentryServletRequestListener(@NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "hub is required"); } public SentryServletRequestListener() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } @Override public void requestDestroyed(@NotNull ServletRequestEvent servletRequestEvent) { - final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); - LifecycleHelper.close(servletRequest.getAttribute(SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY)); + hub.popScope(); } @Override public void requestInitialized(@NotNull ServletRequestEvent servletRequestEvent) { - final @NotNull ISentryLifecycleToken lifecycleToken = - scopes.forkedScopes("SentryServletRequestListener").makeCurrent(); + hub.pushScope(); final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); - servletRequest.setAttribute(SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY, lifecycleToken); if (servletRequest instanceof HttpServletRequest) { final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; final Hint hint = new Hint(); hint.set(SERVLET_REQUEST, httpRequest); - scopes.addBreadcrumb( + hub.addBreadcrumb( Breadcrumb.http(httpRequest.getRequestURI(), httpRequest.getMethod()), hint); - scopes.configureScope( + hub.configureScope( scope -> { scope.addEventProcessor(new SentryRequestHttpServletRequestProcessor(httpRequest)); }); diff --git a/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt b/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt index 322c410117..b87ea218a2 100644 --- a/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt +++ b/sentry-servlet-jakarta/src/test/kotlin/io/sentry/servlet/jakarta/SentryServletRequestListenerTest.kt @@ -1,15 +1,11 @@ package io.sentry.servlet.jakarta import io.sentry.Breadcrumb -import io.sentry.IScopes -import io.sentry.ISentryLifecycleToken +import io.sentry.IHub import jakarta.servlet.ServletRequestEvent -import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.check -import org.mockito.kotlin.eq import org.mockito.kotlin.mock -import org.mockito.kotlin.same import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import kotlin.test.Test @@ -17,10 +13,9 @@ import kotlin.test.assertEquals class SentryServletRequestListenerTest { private class Fixture { - val scopes = mock() - val lifecycleToken = mock() + val hub = mock() val listener = - SentryServletRequestListener(scopes) + SentryServletRequestListener(hub) val request = mockRequest( url = "http://localhost:8080/some-uri", method = "POST" @@ -29,8 +24,6 @@ class SentryServletRequestListenerTest { init { whenever(event.servletRequest).thenReturn(request) - whenever(scopes.forkedScopes(any())).thenReturn(scopes) - whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) } } @@ -40,15 +33,14 @@ class SentryServletRequestListenerTest { fun `pushes scope when request gets initialized`() { fixture.listener.requestInitialized(fixture.event) - verify(fixture.scopes).forkedScopes(any()) - verify(fixture.scopes).makeCurrent() + verify(fixture.hub).pushScope() } @Test fun `adds breadcrumb when request gets initialized`() { fixture.listener.requestInitialized(fixture.event) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { it: Breadcrumb -> assertEquals("/some-uri", it.getData("url")) assertEquals("POST", it.getData("method")) @@ -56,14 +48,12 @@ class SentryServletRequestListenerTest { }, anyOrNull() ) - verify(fixture.request).setAttribute(eq("sentry-scope-lifecycle"), same(fixture.lifecycleToken)) } @Test fun `pops scope when request gets destroyed`() { - whenever(fixture.request.getAttribute(eq("sentry-scope-lifecycle"))).thenReturn(fixture.lifecycleToken) - fixture.listener.requestDestroyed(fixture.event) - verify(fixture.lifecycleToken).close() + + verify(fixture.hub).popScope() } } diff --git a/sentry-servlet/api/sentry-servlet.api b/sentry-servlet/api/sentry-servlet.api index 3bbffa1b5d..a0a2a1e0d2 100644 --- a/sentry-servlet/api/sentry-servlet.api +++ b/sentry-servlet/api/sentry-servlet.api @@ -9,9 +9,8 @@ public class io/sentry/servlet/SentryServletContainerInitializer : javax/servlet } public class io/sentry/servlet/SentryServletRequestListener : javax/servlet/ServletRequestListener { - public static final field SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY Ljava/lang/String; public fun ()V - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun requestDestroyed (Ljavax/servlet/ServletRequestEvent;)V public fun requestInitialized (Ljavax/servlet/ServletRequestEvent;)V } diff --git a/sentry-servlet/src/main/java/io/sentry/servlet/SentryRequestHttpServletRequestProcessor.java b/sentry-servlet/src/main/java/io/sentry/servlet/SentryRequestHttpServletRequestProcessor.java index a005d50c0a..b24c0446b0 100644 --- a/sentry-servlet/src/main/java/io/sentry/servlet/SentryRequestHttpServletRequestProcessor.java +++ b/sentry-servlet/src/main/java/io/sentry/servlet/SentryRequestHttpServletRequestProcessor.java @@ -56,9 +56,4 @@ public SentryRequestHttpServletRequestProcessor(@NotNull HttpServletRequest http private static @Nullable String toString(final @Nullable Enumeration enumeration) { return enumeration != null ? String.join(",", Collections.list(enumeration)) : null; } - - @Override - public @Nullable Long getOrder() { - return 4000L; - } } diff --git a/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java b/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java index 0a2a2f5d23..9b981676c4 100644 --- a/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java +++ b/sentry-servlet/src/main/java/io/sentry/servlet/SentryServletRequestListener.java @@ -5,10 +5,8 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IScopes; -import io.sentry.ISentryLifecycleToken; -import io.sentry.ScopesAdapter; -import io.sentry.util.LifecycleHelper; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.util.Objects; import javax.servlet.ServletRequest; import javax.servlet.ServletRequestEvent; @@ -23,41 +21,36 @@ @Open public class SentryServletRequestListener implements ServletRequestListener { - public static final String SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY = "sentry-scope-lifecycle"; + private final IHub hub; - private final IScopes scopes; - - public SentryServletRequestListener(@NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + public SentryServletRequestListener(@NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "hub is required"); } public SentryServletRequestListener() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } @Override public void requestDestroyed(@NotNull ServletRequestEvent servletRequestEvent) { - final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); - LifecycleHelper.close(servletRequest.getAttribute(SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY)); + hub.popScope(); } @Override public void requestInitialized(@NotNull ServletRequestEvent servletRequestEvent) { - final @NotNull ISentryLifecycleToken lifecycleToken = - scopes.forkedScopes("SentryServletRequestListener").makeCurrent(); + hub.pushScope(); final ServletRequest servletRequest = servletRequestEvent.getServletRequest(); - servletRequest.setAttribute(SENTRY_SCOPE_LIFECYCLE_TOKEN_KEY, lifecycleToken); if (servletRequest instanceof HttpServletRequest) { final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; final Hint hint = new Hint(); hint.set(SERVLET_REQUEST, httpRequest); - scopes.addBreadcrumb( + hub.addBreadcrumb( Breadcrumb.http(httpRequest.getRequestURI(), httpRequest.getMethod()), hint); - scopes.configureScope( + hub.configureScope( scope -> { scope.addEventProcessor(new SentryRequestHttpServletRequestProcessor(httpRequest)); }); diff --git a/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt b/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt index 07327599d3..b94e73f2ef 100644 --- a/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt +++ b/sentry-servlet/src/test/kotlin/io/sentry/servlet/SentryServletRequestListenerTest.kt @@ -1,10 +1,8 @@ package io.sentry.servlet import io.sentry.Breadcrumb -import io.sentry.IScopes -import io.sentry.ISentryLifecycleToken +import io.sentry.IHub import org.assertj.core.api.Assertions.assertThat -import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.check import org.mockito.kotlin.mock @@ -13,13 +11,11 @@ import org.mockito.kotlin.whenever import org.springframework.mock.web.MockHttpServletRequest import javax.servlet.ServletRequestEvent import kotlin.test.Test -import kotlin.test.assertSame class SentryServletRequestListenerTest { private class Fixture { - val scopes = mock() - val lifecycleToken = mock() - val listener = SentryServletRequestListener(scopes) + val hub = mock() + val listener = SentryServletRequestListener(hub) val request = MockHttpServletRequest() val event = mock() @@ -27,8 +23,6 @@ class SentryServletRequestListenerTest { request.requestURI = "http://localhost:8080/some-uri" request.method = "post" whenever(event.servletRequest).thenReturn(request) - whenever(scopes.forkedScopes(any())).thenReturn(scopes) - whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) } } @@ -38,15 +32,14 @@ class SentryServletRequestListenerTest { fun `pushes scope when request gets initialized`() { fixture.listener.requestInitialized(fixture.event) - verify(fixture.scopes).forkedScopes(any()) - verify(fixture.scopes).makeCurrent() + verify(fixture.hub).pushScope() } @Test fun `adds breadcrumb when request gets initialized`() { fixture.listener.requestInitialized(fixture.event) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { it: Breadcrumb -> assertThat(it.getData("url")).isEqualTo("http://localhost:8080/some-uri") assertThat(it.getData("method")).isEqualTo("POST") @@ -54,14 +47,12 @@ class SentryServletRequestListenerTest { }, anyOrNull() ) - assertSame(fixture.lifecycleToken, fixture.request.getAttribute("sentry-scope-lifecycle")) } @Test fun `pops scope when request gets destroyed`() { - fixture.request.setAttribute("sentry-scope-lifecycle", fixture.lifecycleToken) - fixture.listener.requestDestroyed(fixture.event) - verify(fixture.lifecycleToken).close() + + verify(fixture.hub).popScope() } } diff --git a/sentry-spring-boot-jakarta/api/sentry-spring-boot-jakarta.api b/sentry-spring-boot-jakarta/api/sentry-spring-boot-jakarta.api index 009036082e..a392ff75ee 100644 --- a/sentry-spring-boot-jakarta/api/sentry-spring-boot-jakarta.api +++ b/sentry-spring-boot-jakarta/api/sentry-spring-boot-jakarta.api @@ -62,7 +62,7 @@ public class io/sentry/spring/boot/jakarta/SentryProperties$Reactive { public class io/sentry/spring/boot/jakarta/SentryWebfluxAutoConfiguration { public fun ()V - public fun sentryWebExceptionHandler (Lio/sentry/IScopes;)Lio/sentry/spring/jakarta/webflux/SentryWebExceptionHandler; + public fun sentryWebExceptionHandler (Lio/sentry/IHub;)Lio/sentry/spring/jakarta/webflux/SentryWebExceptionHandler; } public class io/sentry/spring/boot/jakarta/graphql/SentryGraphqlAutoConfiguration { diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java index f38682cf6d..7893cf7c7c 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryAutoConfiguration.java @@ -3,14 +3,15 @@ import com.jakewharton.nopen.annotation.Open; import graphql.GraphQLError; import io.sentry.EventProcessor; -import io.sentry.IScopes; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ITransportFactory; import io.sentry.Integration; -import io.sentry.ScopesAdapter; import io.sentry.Sentry; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryOptions; import io.sentry.graphql.SentryGraphqlExceptionHandler; +import io.sentry.opentelemetry.OpenTelemetryLinkErrorEventProcessor; import io.sentry.protocol.SdkVersion; import io.sentry.quartz.SentryJobListener; import io.sentry.spring.boot.jakarta.graphql.SentryGraphqlAutoConfiguration; @@ -117,7 +118,7 @@ static class HubConfiguration { } @Bean - public @NotNull IScopes sentryHub( + public @NotNull IHub sentryHub( final @NotNull List> optionsConfigurations, final @NotNull SentryProperties options, final @NotNull ObjectProvider gitProperties) { @@ -140,7 +141,7 @@ static class HubConfiguration { // here we make sure that only classes that extend throwable are set on this field options.getIgnoredExceptionsForType().removeIf(it -> !Throwable.class.isAssignableFrom(it)); Sentry.init(options); - return ScopesAdapter.getInstance(); + return HubAdapter.getInstance(); } @Configuration(proxyBeanMethods = false) @@ -157,16 +158,14 @@ static class ContextTagsEventProcessorConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(name = "sentry.auto-init", havingValue = "false") - @ConditionalOnClass(io.sentry.opentelemetry.OpenTelemetryLinkErrorEventProcessor.class) - @SuppressWarnings("deprecation") + @ConditionalOnClass(OpenTelemetryLinkErrorEventProcessor.class) @Open static class OpenTelemetryLinkErrorEventProcessorConfiguration { @Bean @ConditionalOnMissingBean - public @NotNull io.sentry.opentelemetry.OpenTelemetryLinkErrorEventProcessor - openTelemetryLinkErrorEventProcessor() { - return new io.sentry.opentelemetry.OpenTelemetryLinkErrorEventProcessor(); + public @NotNull OpenTelemetryLinkErrorEventProcessor openTelemetryLinkErrorEventProcessor() { + return new OpenTelemetryLinkErrorEventProcessor(); } } @@ -242,7 +241,7 @@ static class SentrySecurityConfiguration { * HttpServletRequest#getUserPrincipal()}. If Spring Security is auto-configured, its order is * set to run after Spring Security. * - * @param scopes the Sentry scopes + * @param hub the Sentry hub * @param sentryProperties the Sentry properties * @param sentryUserProvider the user provider * @return {@link SentryUserFilter} registration bean @@ -250,11 +249,11 @@ static class SentrySecurityConfiguration { @Bean @ConditionalOnBean(SentryUserProvider.class) public @NotNull FilterRegistrationBean sentryUserFilter( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull SentryProperties sentryProperties, final @NotNull List sentryUserProvider) { final FilterRegistrationBean filter = new FilterRegistrationBean<>(); - filter.setFilter(new SentryUserFilter(scopes, sentryUserProvider)); + filter.setFilter(new SentryUserFilter(hub, sentryUserProvider)); filter.setOrder(resolveUserFilterOrder(sentryProperties)); return filter; } @@ -266,19 +265,19 @@ static class SentrySecurityConfiguration { } @Bean - public @NotNull SentryRequestResolver sentryRequestResolver(final @NotNull IScopes scopes) { - return new SentryRequestResolver(scopes); + public @NotNull SentryRequestResolver sentryRequestResolver(final @NotNull IHub hub) { + return new SentryRequestResolver(hub); } @Bean @ConditionalOnMissingBean(name = "sentrySpringFilter") public @NotNull FilterRegistrationBean sentrySpringFilter( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull SentryRequestResolver requestResolver, final @NotNull TransactionNameProvider transactionNameProvider) { FilterRegistrationBean filter = new FilterRegistrationBean<>( - new SentrySpringFilter(scopes, requestResolver, transactionNameProvider)); + new SentrySpringFilter(hub, requestResolver, transactionNameProvider)); filter.setOrder(SENTRY_SPRING_FILTER_PRECEDENCE); return filter; } @@ -286,10 +285,9 @@ static class SentrySecurityConfiguration { @Bean @ConditionalOnMissingBean(name = "sentryTracingFilter") public FilterRegistrationBean sentryTracingFilter( - final @NotNull IScopes scopes, - final @NotNull TransactionNameProvider transactionNameProvider) { + final @NotNull IHub hub, final @NotNull TransactionNameProvider transactionNameProvider) { FilterRegistrationBean filter = - new FilterRegistrationBean<>(new SentryTracingFilter(scopes, transactionNameProvider)); + new FilterRegistrationBean<>(new SentryTracingFilter(hub, transactionNameProvider)); filter.setOrder(SENTRY_SPRING_FILTER_PRECEDENCE + 1); // must run after SentrySpringFilter return filter; } @@ -302,11 +300,11 @@ static class SentryMvcModeConfig { @Bean @ConditionalOnMissingBean public @NotNull SentryExceptionResolver sentryExceptionResolver( - final @NotNull IScopes scopes, + final @NotNull IHub sentryHub, final @NotNull TransactionNameProvider transactionNameProvider, final @NotNull SentryProperties options) { return new SentryExceptionResolver( - scopes, transactionNameProvider, options.getExceptionResolverOrder()); + sentryHub, transactionNameProvider, options.getExceptionResolverOrder()); } @Bean @@ -375,8 +373,8 @@ static class SentrySpanPointcutAutoConfiguration {} @Open static class SentryPerformanceRestTemplateConfiguration { @Bean - public SentrySpanRestTemplateCustomizer sentrySpanRestTemplateCustomizer(IScopes scopes) { - return new SentrySpanRestTemplateCustomizer(scopes); + public SentrySpanRestTemplateCustomizer sentrySpanRestTemplateCustomizer(IHub hub) { + return new SentrySpanRestTemplateCustomizer(hub); } } @@ -386,8 +384,8 @@ public SentrySpanRestTemplateCustomizer sentrySpanRestTemplateCustomizer(IScopes @Open static class SentrySpanRestClientConfiguration { @Bean - public SentrySpanRestClientCustomizer sentrySpanRestClientCustomizer(IScopes scopes) { - return new SentrySpanRestClientCustomizer(scopes); + public SentrySpanRestClientCustomizer sentrySpanRestClientCustomizer(IHub hub) { + return new SentrySpanRestClientCustomizer(hub); } } @@ -397,8 +395,8 @@ public SentrySpanRestClientCustomizer sentrySpanRestClientCustomizer(IScopes sco @Open static class SentryPerformanceWebClientConfiguration { @Bean - public SentrySpanWebClientCustomizer sentrySpanWebClientCustomizer(IScopes scopes) { - return new SentrySpanWebClientCustomizer(scopes); + public SentrySpanWebClientCustomizer sentrySpanWebClientCustomizer(IHub hub) { + return new SentrySpanWebClientCustomizer(hub); } } diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryProperties.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryProperties.java index 80ea79932c..7b3469d7f1 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryProperties.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryProperties.java @@ -163,8 +163,7 @@ public void setLoggers(final @NotNull List loggers) { @Open public static class Reactive { /** - * Enable/Disable usage of {@link io.micrometer.context.ThreadLocalAccessor} for Scopes - * propagation + * Enable/Disable usage of {@link io.micrometer.context.ThreadLocalAccessor} for Hub propagation */ private boolean threadLocalAccessorEnabled = true; diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizer.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizer.java index 304fb6911e..243a4d1f50 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizer.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizer.java @@ -1,7 +1,7 @@ package io.sentry.spring.boot.jakarta; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.spring.jakarta.tracing.SentrySpanClientHttpRequestInterceptor; import org.jetbrains.annotations.NotNull; import org.springframework.boot.web.client.RestClientCustomizer; @@ -11,8 +11,8 @@ class SentrySpanRestClientCustomizer implements RestClientCustomizer { private final @NotNull SentrySpanClientHttpRequestInterceptor interceptor; - public SentrySpanRestClientCustomizer(final @NotNull IScopes scopes) { - this.interceptor = new SentrySpanClientHttpRequestInterceptor(scopes, false); + public SentrySpanRestClientCustomizer(final @NotNull IHub hub) { + this.interceptor = new SentrySpanClientHttpRequestInterceptor(hub, false); } @Override diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizer.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizer.java index 4a0faa9875..c1ded006e8 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizer.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizer.java @@ -1,7 +1,7 @@ package io.sentry.spring.boot.jakarta; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.spring.jakarta.tracing.SentrySpanClientHttpRequestInterceptor; import java.util.ArrayList; import java.util.List; @@ -14,8 +14,8 @@ class SentrySpanRestTemplateCustomizer implements RestTemplateCustomizer { private final @NotNull SentrySpanClientHttpRequestInterceptor interceptor; - public SentrySpanRestTemplateCustomizer(final @NotNull IScopes scopes) { - this.interceptor = new SentrySpanClientHttpRequestInterceptor(scopes); + public SentrySpanRestTemplateCustomizer(final @NotNull IHub hub) { + this.interceptor = new SentrySpanClientHttpRequestInterceptor(hub); } @Override diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizer.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizer.java index d349ac4c6e..afbe2eb6c4 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizer.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizer.java @@ -1,7 +1,7 @@ package io.sentry.spring.boot.jakarta; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.spring.jakarta.tracing.SentrySpanClientWebRequestFilter; import org.jetbrains.annotations.NotNull; import org.springframework.boot.web.reactive.function.client.WebClientCustomizer; @@ -11,8 +11,8 @@ class SentrySpanWebClientCustomizer implements WebClientCustomizer { private final @NotNull SentrySpanClientWebRequestFilter filter; - public SentrySpanWebClientCustomizer(final @NotNull IScopes scopes) { - this.filter = new SentrySpanClientWebRequestFilter(scopes); + public SentrySpanWebClientCustomizer(final @NotNull IHub hub) { + this.filter = new SentrySpanClientWebRequestFilter(hub); } @Override diff --git a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryWebfluxAutoConfiguration.java b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryWebfluxAutoConfiguration.java index 2bc8bc87ed..d1cda8b4d2 100644 --- a/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryWebfluxAutoConfiguration.java +++ b/sentry-spring-boot-jakarta/src/main/java/io/sentry/spring/boot/jakarta/SentryWebfluxAutoConfiguration.java @@ -1,8 +1,8 @@ package io.sentry.spring.boot.jakarta; import com.jakewharton.nopen.annotation.Open; +import io.sentry.IHub; import io.sentry.IScope; -import io.sentry.IScopes; import io.sentry.spring.jakarta.webflux.SentryScheduleHook; import io.sentry.spring.jakarta.webflux.SentryWebExceptionHandler; import io.sentry.spring.jakarta.webflux.SentryWebFilter; @@ -28,7 +28,7 @@ /** Configures Sentry integration for Spring Webflux and Project Reactor. */ @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) -@ConditionalOnBean(IScopes.class) +@ConditionalOnBean(IHub.class) @ConditionalOnClass(Schedulers.class) @Open @ApiStatus.Experimental @@ -44,14 +44,14 @@ static class SentryWebfluxFilterThreadLocalAccessorConfiguration { * Configures a filter that sets up Sentry {@link IScope} for each request. * *

    Makes use of newer reactor-core and context-propagation library feature - * ThreadLocalAccessor to propagate the Sentry scopes. + * ThreadLocalAccessor to propagate the Sentry hub. */ @Bean @Order(SENTRY_SPRING_FILTER_PRECEDENCE) public @NotNull SentryWebFilterWithThreadLocalAccessor sentryWebFilterWithContextPropagation( - final @NotNull IScopes scopes) { + final @NotNull IHub hub) { Hooks.enableAutomaticContextPropagation(); - return new SentryWebFilterWithThreadLocalAccessor(scopes); + return new SentryWebFilterWithThreadLocalAccessor(hub); } } @@ -60,7 +60,7 @@ static class SentryWebfluxFilterThreadLocalAccessorConfiguration { @Open static class SentryWebfluxFilterConfiguration { - /** Configures hook that sets correct scopes on the executing thread. */ + /** Configures hook that sets correct hub on the executing thread. */ @Bean public @NotNull ApplicationRunner sentryScheduleHookApplicationRunner() { return args -> { @@ -71,16 +71,15 @@ static class SentryWebfluxFilterConfiguration { /** Configures a filter that sets up Sentry {@link IScope} for each request. */ @Bean @Order(SENTRY_SPRING_FILTER_PRECEDENCE) - public @NotNull SentryWebFilter sentryWebFilter(final @NotNull IScopes scopes) { - return new SentryWebFilter(scopes); + public @NotNull SentryWebFilter sentryWebFilter(final @NotNull IHub hub) { + return new SentryWebFilter(hub); } } /** Configures exception handler that handles unhandled exceptions and sends them to Sentry. */ @Bean - public @NotNull SentryWebExceptionHandler sentryWebExceptionHandler( - final @NotNull IScopes scopes) { - return new SentryWebExceptionHandler(scopes); + public @NotNull SentryWebExceptionHandler sentryWebExceptionHandler(final @NotNull IHub hub) { + return new SentryWebExceptionHandler(hub); } static final class SentryLegacyFilterConfigurationCondition extends AnyNestedCondition { diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt index 9805de6e21..3d4b16d419 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentryAutoConfigurationTest.kt @@ -5,7 +5,7 @@ import io.sentry.AsyncHttpTransportFactory import io.sentry.Breadcrumb import io.sentry.EventProcessor import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ITransportFactory import io.sentry.Integration import io.sentry.NoOpTransportFactory @@ -79,18 +79,18 @@ class SentryAutoConfigurationTest { .withConfiguration(AutoConfigurations.of(SentryAutoConfiguration::class.java, WebMvcAutoConfiguration::class.java)) @Test - fun `scopes is not created when auto-configuration dsn is not set`() { + fun `hub is not created when auto-configuration dsn is not set`() { contextRunner .run { - assertThat(it).doesNotHaveBean(IScopes::class.java) + assertThat(it).doesNotHaveBean(IHub::class.java) } } @Test - fun `scopes is created when dsn is provided`() { + fun `hub is created when dsn is provided`() { contextRunner.withPropertyValues("sentry.dsn=http://key@localhost/proj") .run { - assertThat(it).hasSingleBean(IScopes::class.java) + assertThat(it).hasSingleBean(IHub::class.java) } } @@ -972,7 +972,7 @@ class SentryAutoConfigurationTest { } class CustomIntegration : Integration { - override fun register(scopes: IScopes, options: SentryOptions) {} + override fun register(hub: IHub, options: SentryOptions) {} } @Configuration(proxyBeanMethods = false) diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizerTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizerTest.kt index 9ba9bf57d2..f9f55ffede 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizerTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestClientCustomizerTest.kt @@ -2,7 +2,7 @@ package io.sentry.spring.boot.jakarta import io.sentry.BaggageHeader import io.sentry.Breadcrumb -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -36,18 +36,18 @@ import kotlin.test.assertTrue class SentrySpanRestClientCustomizerTest { class Fixture { val sentryOptions = SentryOptions() - val scopes = mock() + val hub = mock() val restClientBuilder = RestClient.builder() var mockServer = MockWebServer() val transaction: SentryTracer - internal val customizer = SentrySpanRestClientCustomizer(scopes) + internal val customizer = SentrySpanRestClientCustomizer(hub) val url = mockServer.url("/test/123").toString() val scope = Scope(sentryOptions) init { - whenever(scopes.options).thenReturn(sentryOptions) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) - transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), scopes) + whenever(hub.options).thenReturn(sentryOptions) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) + transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), hub) } fun getSut( @@ -75,7 +75,7 @@ class SentrySpanRestClientCustomizerTest { ) if (isTransactionActive) { - whenever(scopes.span).thenReturn(transaction) + whenever(hub.span).thenReturn(transaction) } return restClientBuilder.apply { @@ -247,7 +247,7 @@ class SentrySpanRestClientCustomizerTest { .body("content") .retrieve() .toEntity(String::class.java) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -269,7 +269,7 @@ class SentrySpanRestClientCustomizerTest { .toEntity(String::class.java) } catch (e: Throwable) { } - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -287,7 +287,7 @@ class SentrySpanRestClientCustomizerTest { .body("content") .retrieve() .toEntity(String::class.java) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -309,7 +309,7 @@ class SentrySpanRestClientCustomizerTest { .toEntity(String::class.java) } catch (e: Throwable) { } - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizerTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizerTest.kt index 4ab3205b31..db5b25de44 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizerTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanRestTemplateCustomizerTest.kt @@ -2,7 +2,7 @@ package io.sentry.spring.boot.jakarta import io.sentry.BaggageHeader import io.sentry.Breadcrumb -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -37,21 +37,21 @@ import kotlin.test.assertTrue class SentrySpanRestTemplateCustomizerTest { class Fixture { val sentryOptions = SentryOptions() - val scopes = mock() + val hub = mock() val restTemplate = RestTemplateBuilder() .setConnectTimeout(Duration.ofSeconds(2)) .setReadTimeout(Duration.ofSeconds(2)) .build() var mockServer = MockWebServer() val transaction: SentryTracer - internal val customizer = SentrySpanRestTemplateCustomizer(scopes) + internal val customizer = SentrySpanRestTemplateCustomizer(hub) val url = mockServer.url("/test/123").toString() val scope = Scope(sentryOptions) init { - whenever(scopes.options).thenReturn(sentryOptions) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) - transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), scopes) + whenever(hub.options).thenReturn(sentryOptions) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) + transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), hub) } fun getSut(isTransactionActive: Boolean, status: HttpStatus = HttpStatus.OK, socketPolicy: SocketPolicy = SocketPolicy.KEEP_OPEN, includeMockServerInTracingOrigins: Boolean = true): RestTemplate { @@ -74,7 +74,7 @@ class SentrySpanRestTemplateCustomizerTest { ) if (isTransactionActive) { - whenever(scopes.span).thenReturn(transaction) + whenever(hub.span).thenReturn(transaction) } return restTemplate @@ -209,7 +209,7 @@ class SentrySpanRestTemplateCustomizerTest { @Test fun `when transaction is active adds breadcrumb when http calls succeeds`() { fixture.getSut(isTransactionActive = true).postForObject(fixture.url, "content", String::class.java) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -227,7 +227,7 @@ class SentrySpanRestTemplateCustomizerTest { fixture.getSut(isTransactionActive = true, status = HttpStatus.INTERNAL_SERVER_ERROR).getForObject(fixture.url, String::class.java) } catch (e: Throwable) { } - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -240,7 +240,7 @@ class SentrySpanRestTemplateCustomizerTest { @Test fun `when transaction is not active adds breadcrumb when http calls succeeds`() { fixture.getSut(isTransactionActive = false).postForObject(fixture.url, "content", String::class.java) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -258,7 +258,7 @@ class SentrySpanRestTemplateCustomizerTest { fixture.getSut(isTransactionActive = false, status = HttpStatus.INTERNAL_SERVER_ERROR).getForObject(fixture.url, String::class.java) } catch (e: Throwable) { } - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizerTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizerTest.kt index d3fb5f7d31..51f0a6cb3d 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizerTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/SentrySpanWebClientCustomizerTest.kt @@ -2,8 +2,8 @@ package io.sentry.spring.boot.jakarta import io.sentry.BaggageHeader import io.sentry.Breadcrumb +import io.sentry.IHub import io.sentry.IScope -import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -39,10 +39,10 @@ class SentrySpanWebClientCustomizerTest { class Fixture { lateinit var sentryOptions: SentryOptions lateinit var scope: IScope - val scopes = mock() + val hub = mock() var mockServer = MockWebServer() lateinit var transaction: SentryTracer - private val customizer = SentrySpanWebClientCustomizer(scopes) + private val customizer = SentrySpanWebClientCustomizer(hub) fun getSut(isTransactionActive: Boolean, status: HttpStatus = HttpStatus.OK, throwIOException: Boolean = false, includeMockServerInTracingOrigins: Boolean = true): WebClient { sentryOptions = SentryOptions().apply { @@ -54,9 +54,9 @@ class SentrySpanWebClientCustomizerTest { dsn = "http://key@localhost/proj" } scope = Scope(sentryOptions) - whenever(scopes.options).thenReturn(sentryOptions) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) - transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), scopes) + whenever(hub.options).thenReturn(sentryOptions) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) + transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), hub) val webClientBuilder = WebClient.builder() customizer.customize(webClientBuilder) val webClient = webClientBuilder.build() @@ -64,7 +64,7 @@ class SentrySpanWebClientCustomizerTest { if (isTransactionActive) { val scope = Scope(sentryOptions) scope.transaction = transaction - whenever(scopes.span).thenReturn(transaction) + whenever(hub.span).thenReturn(transaction) } val dispatcher: Dispatcher = object : Dispatcher() { @@ -236,7 +236,7 @@ class SentrySpanWebClientCustomizerTest { .retrieve() .bodyToMono(String::class.java) .block() - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(uri.toString(), it.data["url"]) @@ -259,7 +259,7 @@ class SentrySpanWebClientCustomizerTest { .block() } catch (e: Throwable) { } - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(uri.toString(), it.data["url"]) @@ -279,7 +279,7 @@ class SentrySpanWebClientCustomizerTest { .retrieve() .bodyToMono(String::class.java) .block() - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(uri.toString(), it.data["url"]) @@ -302,7 +302,7 @@ class SentrySpanWebClientCustomizerTest { .block() } catch (e: Throwable) { } - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(uri.toString(), it.data["url"]) diff --git a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/it/SentrySpringIntegrationTest.kt b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/it/SentrySpringIntegrationTest.kt index 1b997737ab..288864abcb 100644 --- a/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/it/SentrySpringIntegrationTest.kt +++ b/sentry-spring-boot-jakarta/src/test/kotlin/io/sentry/spring/boot/jakarta/it/SentrySpringIntegrationTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.boot.jakarta.it -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ITransportFactory import io.sentry.Sentry import io.sentry.checkEvent @@ -60,7 +60,7 @@ class SentrySpringIntegrationTest { lateinit var transport: ITransport @SpyBean - lateinit var scopes: IScopes + lateinit var hub: IHub @LocalServerPort var port: Int? = null @@ -188,7 +188,7 @@ class SentrySpringIntegrationTest { restTemplate.getForEntity("http://localhost:$port/throws-handled", String::class.java) - verify(scopes, never()).captureEvent(any()) + verify(hub, never()).captureEvent(any()) } @Test diff --git a/sentry-spring-boot/api/sentry-spring-boot.api b/sentry-spring-boot/api/sentry-spring-boot.api index d97e2e1111..32d7cc8c60 100644 --- a/sentry-spring-boot/api/sentry-spring-boot.api +++ b/sentry-spring-boot/api/sentry-spring-boot.api @@ -53,8 +53,8 @@ public class io/sentry/spring/boot/SentryProperties$Logging { public class io/sentry/spring/boot/SentryWebfluxAutoConfiguration { public fun ()V public fun sentryScheduleHookApplicationRunner ()Lorg/springframework/boot/ApplicationRunner; - public fun sentryWebExceptionHandler (Lio/sentry/IScopes;)Lio/sentry/spring/webflux/SentryWebExceptionHandler; - public fun sentryWebFilter (Lio/sentry/IScopes;)Lio/sentry/spring/webflux/SentryWebFilter; + public fun sentryWebExceptionHandler (Lio/sentry/IHub;)Lio/sentry/spring/webflux/SentryWebExceptionHandler; + public fun sentryWebFilter (Lio/sentry/IHub;)Lio/sentry/spring/webflux/SentryWebFilter; } public class io/sentry/spring/boot/graphql/SentryGraphqlAutoConfiguration { diff --git a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java index 62f8e8457d..1c66a98c50 100644 --- a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java +++ b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryAutoConfiguration.java @@ -3,14 +3,15 @@ import com.jakewharton.nopen.annotation.Open; import graphql.GraphQLError; import io.sentry.EventProcessor; -import io.sentry.IScopes; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ITransportFactory; import io.sentry.Integration; -import io.sentry.ScopesAdapter; import io.sentry.Sentry; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryOptions; import io.sentry.graphql.SentryGraphqlExceptionHandler; +import io.sentry.opentelemetry.OpenTelemetryLinkErrorEventProcessor; import io.sentry.protocol.SdkVersion; import io.sentry.quartz.SentryJobListener; import io.sentry.spring.ContextTagsEventProcessor; @@ -115,7 +116,7 @@ static class HubConfiguration { } @Bean - public @NotNull IScopes sentryHub( + public @NotNull IHub sentryHub( final @NotNull List> optionsConfigurations, final @NotNull SentryProperties options, final @NotNull ObjectProvider gitProperties) { @@ -138,7 +139,7 @@ static class HubConfiguration { // here we make sure that only classes that extend throwable are set on this field options.getIgnoredExceptionsForType().removeIf(it -> !Throwable.class.isAssignableFrom(it)); Sentry.init(options); - return ScopesAdapter.getInstance(); + return HubAdapter.getInstance(); } @Configuration(proxyBeanMethods = false) @@ -155,16 +156,14 @@ static class ContextTagsEventProcessorConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(name = "sentry.auto-init", havingValue = "false") - @ConditionalOnClass(io.sentry.opentelemetry.OpenTelemetryLinkErrorEventProcessor.class) - @SuppressWarnings("deprecation") + @ConditionalOnClass(OpenTelemetryLinkErrorEventProcessor.class) @Open static class OpenTelemetryLinkErrorEventProcessorConfiguration { @Bean @ConditionalOnMissingBean - public @NotNull io.sentry.opentelemetry.OpenTelemetryLinkErrorEventProcessor - openTelemetryLinkErrorEventProcessor() { - return new io.sentry.opentelemetry.OpenTelemetryLinkErrorEventProcessor(); + public @NotNull OpenTelemetryLinkErrorEventProcessor openTelemetryLinkErrorEventProcessor() { + return new OpenTelemetryLinkErrorEventProcessor(); } } @@ -240,7 +239,7 @@ static class SentrySecurityConfiguration { * HttpServletRequest#getUserPrincipal()}. If Spring Security is auto-configured, its order is * set to run after Spring Security. * - * @param scopes the Sentry scopes + * @param hub the Sentry hub * @param sentryProperties the Sentry properties * @param sentryUserProvider the user provider * @return {@link SentryUserFilter} registration bean @@ -248,11 +247,11 @@ static class SentrySecurityConfiguration { @Bean @ConditionalOnBean(SentryUserProvider.class) public @NotNull FilterRegistrationBean sentryUserFilter( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull SentryProperties sentryProperties, final @NotNull List sentryUserProvider) { final FilterRegistrationBean filter = new FilterRegistrationBean<>(); - filter.setFilter(new SentryUserFilter(scopes, sentryUserProvider)); + filter.setFilter(new SentryUserFilter(hub, sentryUserProvider)); filter.setOrder(resolveUserFilterOrder(sentryProperties)); return filter; } @@ -264,19 +263,19 @@ static class SentrySecurityConfiguration { } @Bean - public @NotNull SentryRequestResolver sentryRequestResolver(final @NotNull IScopes scopes) { - return new SentryRequestResolver(scopes); + public @NotNull SentryRequestResolver sentryRequestResolver(final @NotNull IHub hub) { + return new SentryRequestResolver(hub); } @Bean @ConditionalOnMissingBean(name = "sentrySpringFilter") public @NotNull FilterRegistrationBean sentrySpringFilter( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull SentryRequestResolver requestResolver, final @NotNull TransactionNameProvider transactionNameProvider) { FilterRegistrationBean filter = new FilterRegistrationBean<>( - new SentrySpringFilter(scopes, requestResolver, transactionNameProvider)); + new SentrySpringFilter(hub, requestResolver, transactionNameProvider)); filter.setOrder(SENTRY_SPRING_FILTER_PRECEDENCE); return filter; } @@ -284,10 +283,9 @@ static class SentrySecurityConfiguration { @Bean @ConditionalOnMissingBean(name = "sentryTracingFilter") public FilterRegistrationBean sentryTracingFilter( - final @NotNull IScopes scopes, - final @NotNull TransactionNameProvider transactionNameProvider) { + final @NotNull IHub hub, final @NotNull TransactionNameProvider transactionNameProvider) { FilterRegistrationBean filter = - new FilterRegistrationBean<>(new SentryTracingFilter(scopes, transactionNameProvider)); + new FilterRegistrationBean<>(new SentryTracingFilter(hub, transactionNameProvider)); filter.setOrder(SENTRY_SPRING_FILTER_PRECEDENCE + 1); // must run after SentrySpringFilter return filter; } @@ -300,11 +298,11 @@ static class SentryMvcModeConfig { @Bean @ConditionalOnMissingBean public @NotNull SentryExceptionResolver sentryExceptionResolver( - final @NotNull IScopes scopes, + final @NotNull IHub sentryHub, final @NotNull TransactionNameProvider transactionNameProvider, final @NotNull SentryProperties options) { return new SentryExceptionResolver( - scopes, transactionNameProvider, options.getExceptionResolverOrder()); + sentryHub, transactionNameProvider, options.getExceptionResolverOrder()); } @Bean @@ -369,8 +367,8 @@ static class SentrySpanPointcutAutoConfiguration {} @Open static class SentryPerformanceRestTemplateConfiguration { @Bean - public SentrySpanRestTemplateCustomizer sentrySpanRestTemplateCustomizer(IScopes scopes) { - return new SentrySpanRestTemplateCustomizer(scopes); + public SentrySpanRestTemplateCustomizer sentrySpanRestTemplateCustomizer(IHub hub) { + return new SentrySpanRestTemplateCustomizer(hub); } } @@ -380,8 +378,8 @@ public SentrySpanRestTemplateCustomizer sentrySpanRestTemplateCustomizer(IScopes @Open static class SentryPerformanceWebClientConfiguration { @Bean - public SentrySpanWebClientCustomizer sentrySpanWebClientCustomizer(IScopes scopes) { - return new SentrySpanWebClientCustomizer(scopes); + public SentrySpanWebClientCustomizer sentrySpanWebClientCustomizer(IHub hub) { + return new SentrySpanWebClientCustomizer(hub); } } diff --git a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentrySpanRestTemplateCustomizer.java b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentrySpanRestTemplateCustomizer.java index 2a5e4f1be5..bd311c55f1 100644 --- a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentrySpanRestTemplateCustomizer.java +++ b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentrySpanRestTemplateCustomizer.java @@ -1,7 +1,7 @@ package io.sentry.spring.boot; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.spring.tracing.SentrySpanClientHttpRequestInterceptor; import java.util.ArrayList; import java.util.List; @@ -14,8 +14,8 @@ class SentrySpanRestTemplateCustomizer implements RestTemplateCustomizer { private final @NotNull SentrySpanClientHttpRequestInterceptor interceptor; - public SentrySpanRestTemplateCustomizer(final @NotNull IScopes scopes) { - this.interceptor = new SentrySpanClientHttpRequestInterceptor(scopes); + public SentrySpanRestTemplateCustomizer(final @NotNull IHub hub) { + this.interceptor = new SentrySpanClientHttpRequestInterceptor(hub); } @Override diff --git a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentrySpanWebClientCustomizer.java b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentrySpanWebClientCustomizer.java index 79e59f1cf0..0b8aa4055c 100644 --- a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentrySpanWebClientCustomizer.java +++ b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentrySpanWebClientCustomizer.java @@ -1,7 +1,7 @@ package io.sentry.spring.boot; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.spring.tracing.SentrySpanClientWebRequestFilter; import org.jetbrains.annotations.NotNull; import org.springframework.boot.web.reactive.function.client.WebClientCustomizer; @@ -11,8 +11,8 @@ class SentrySpanWebClientCustomizer implements WebClientCustomizer { private final @NotNull SentrySpanClientWebRequestFilter filter; - public SentrySpanWebClientCustomizer(final @NotNull IScopes scopes) { - this.filter = new SentrySpanClientWebRequestFilter(scopes); + public SentrySpanWebClientCustomizer(final @NotNull IHub hub) { + this.filter = new SentrySpanClientWebRequestFilter(hub); } @Override diff --git a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryWebfluxAutoConfiguration.java b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryWebfluxAutoConfiguration.java index e7f6a444b8..3d507f1b6b 100644 --- a/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryWebfluxAutoConfiguration.java +++ b/sentry-spring-boot/src/main/java/io/sentry/spring/boot/SentryWebfluxAutoConfiguration.java @@ -1,8 +1,8 @@ package io.sentry.spring.boot; import com.jakewharton.nopen.annotation.Open; +import io.sentry.IHub; import io.sentry.IScope; -import io.sentry.IScopes; import io.sentry.spring.webflux.SentryScheduleHook; import io.sentry.spring.webflux.SentryWebExceptionHandler; import io.sentry.spring.webflux.SentryWebFilter; @@ -21,14 +21,14 @@ /** Configures Sentry integration for Spring Webflux and Project Reactor. */ @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) -@ConditionalOnBean(IScopes.class) +@ConditionalOnBean(IHub.class) @ConditionalOnClass(Schedulers.class) @Open @ApiStatus.Experimental public class SentryWebfluxAutoConfiguration { private static final int SENTRY_SPRING_FILTER_PRECEDENCE = Ordered.HIGHEST_PRECEDENCE; - /** Configures hook that sets correct scopes on the executing thread. */ + /** Configures hook that sets correct hub on the executing thread. */ @Bean public @NotNull ApplicationRunner sentryScheduleHookApplicationRunner() { return args -> { @@ -39,14 +39,13 @@ public class SentryWebfluxAutoConfiguration { /** Configures a filter that sets up Sentry {@link IScope} for each request. */ @Bean @Order(SENTRY_SPRING_FILTER_PRECEDENCE) - public @NotNull SentryWebFilter sentryWebFilter(final @NotNull IScopes scopes) { - return new SentryWebFilter(scopes); + public @NotNull SentryWebFilter sentryWebFilter(final @NotNull IHub hub) { + return new SentryWebFilter(hub); } /** Configures exception handler that handles unhandled exceptions and sends them to Sentry. */ @Bean - public @NotNull SentryWebExceptionHandler sentryWebExceptionHandler( - final @NotNull IScopes scopes) { - return new SentryWebExceptionHandler(scopes); + public @NotNull SentryWebExceptionHandler sentryWebExceptionHandler(final @NotNull IHub hub) { + return new SentryWebExceptionHandler(hub); } } diff --git a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt index a65926c934..86785f409b 100644 --- a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt +++ b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentryAutoConfigurationTest.kt @@ -5,7 +5,7 @@ import io.sentry.AsyncHttpTransportFactory import io.sentry.Breadcrumb import io.sentry.EventProcessor import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ITransportFactory import io.sentry.Integration import io.sentry.NoOpTransportFactory @@ -78,18 +78,18 @@ class SentryAutoConfigurationTest { .withConfiguration(AutoConfigurations.of(SentryAutoConfiguration::class.java, WebMvcAutoConfiguration::class.java)) @Test - fun `scopes is not created when auto-configuration dsn is not set`() { + fun `hub is not created when auto-configuration dsn is not set`() { contextRunner .run { - assertThat(it).doesNotHaveBean(IScopes::class.java) + assertThat(it).doesNotHaveBean(IHub::class.java) } } @Test - fun `scopes is created when dsn is provided`() { + fun `hub is created when dsn is provided`() { contextRunner.withPropertyValues("sentry.dsn=http://key@localhost/proj") .run { - assertThat(it).hasSingleBean(IScopes::class.java) + assertThat(it).hasSingleBean(IHub::class.java) } } @@ -956,7 +956,7 @@ class SentryAutoConfigurationTest { } class CustomIntegration : Integration { - override fun register(scopes: IScopes, options: SentryOptions) {} + override fun register(hub: IHub, options: SentryOptions) {} } @Configuration(proxyBeanMethods = false) diff --git a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanRestTemplateCustomizerTest.kt b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanRestTemplateCustomizerTest.kt index 33d7974d8a..0d675b6841 100644 --- a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanRestTemplateCustomizerTest.kt +++ b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanRestTemplateCustomizerTest.kt @@ -2,7 +2,7 @@ package io.sentry.spring.boot import io.sentry.BaggageHeader import io.sentry.Breadcrumb -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -37,23 +37,23 @@ import kotlin.test.assertTrue class SentrySpanRestTemplateCustomizerTest { class Fixture { val sentryOptions = SentryOptions() - val scopes = mock() + val hub = mock() val restTemplate = RestTemplateBuilder() .setConnectTimeout(Duration.ofSeconds(2)) .setReadTimeout(Duration.ofSeconds(2)) .build() var mockServer = MockWebServer() val transaction: SentryTracer - internal val customizer = SentrySpanRestTemplateCustomizer(scopes) + internal val customizer = SentrySpanRestTemplateCustomizer(hub) val url = mockServer.url("/test/123").toString() val scope = Scope(sentryOptions) init { - whenever(scopes.options).thenReturn(sentryOptions) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope( + whenever(hub.options).thenReturn(sentryOptions) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope( any() ) - transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), scopes) + transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), hub) } fun getSut(isTransactionActive: Boolean, status: HttpStatus = HttpStatus.OK, socketPolicy: SocketPolicy = SocketPolicy.KEEP_OPEN, includeMockServerInTracingOrigins: Boolean = true): RestTemplate { @@ -76,7 +76,7 @@ class SentrySpanRestTemplateCustomizerTest { ) if (isTransactionActive) { - whenever(scopes.span).thenReturn(transaction) + whenever(hub.span).thenReturn(transaction) } return restTemplate @@ -211,7 +211,7 @@ class SentrySpanRestTemplateCustomizerTest { @Test fun `when transaction is active adds breadcrumb when http calls succeeds`() { fixture.getSut(isTransactionActive = true).postForObject(fixture.url, "content", String::class.java) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -229,7 +229,7 @@ class SentrySpanRestTemplateCustomizerTest { fixture.getSut(isTransactionActive = true, status = HttpStatus.INTERNAL_SERVER_ERROR).getForObject(fixture.url, String::class.java) } catch (e: Throwable) { } - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -242,7 +242,7 @@ class SentrySpanRestTemplateCustomizerTest { @Test fun `when transaction is not active adds breadcrumb when http calls succeeds`() { fixture.getSut(isTransactionActive = false).postForObject(fixture.url, "content", String::class.java) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) @@ -260,7 +260,7 @@ class SentrySpanRestTemplateCustomizerTest { fixture.getSut(isTransactionActive = false, status = HttpStatus.INTERNAL_SERVER_ERROR).getForObject(fixture.url, String::class.java) } catch (e: Throwable) { } - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(fixture.url, it.data["url"]) diff --git a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanWebClientCustomizerTest.kt b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanWebClientCustomizerTest.kt index 4f1f70d75e..f925435fd3 100644 --- a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanWebClientCustomizerTest.kt +++ b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/SentrySpanWebClientCustomizerTest.kt @@ -2,8 +2,8 @@ package io.sentry.spring.boot import io.sentry.BaggageHeader import io.sentry.Breadcrumb +import io.sentry.IHub import io.sentry.IScope -import io.sentry.IScopes import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -39,10 +39,10 @@ class SentrySpanWebClientCustomizerTest { class Fixture { lateinit var sentryOptions: SentryOptions lateinit var scope: IScope - val scopes = mock() + val hub = mock() var mockServer = MockWebServer() lateinit var transaction: SentryTracer - private val customizer = SentrySpanWebClientCustomizer(scopes) + private val customizer = SentrySpanWebClientCustomizer(hub) fun getSut(isTransactionActive: Boolean, status: HttpStatus = HttpStatus.OK, throwIOException: Boolean = false, includeMockServerInTracingOrigins: Boolean = true): WebClient { sentryOptions = SentryOptions().apply { @@ -54,11 +54,11 @@ class SentrySpanWebClientCustomizerTest { dsn = "http://key@localhost/proj" } scope = Scope(sentryOptions) - whenever(scopes.options).thenReturn(sentryOptions) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope( + whenever(hub.options).thenReturn(sentryOptions) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope( any() ) - transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), scopes) + transaction = SentryTracer(TransactionContext("aTransaction", "op", TracesSamplingDecision(true)), hub) val webClientBuilder = WebClient.builder() customizer.customize(webClientBuilder) val webClient = webClientBuilder.build() @@ -66,7 +66,7 @@ class SentrySpanWebClientCustomizerTest { if (isTransactionActive) { val scope = Scope(sentryOptions) scope.transaction = transaction - whenever(scopes.span).thenReturn(transaction) + whenever(hub.span).thenReturn(transaction) } val dispatcher: Dispatcher = object : Dispatcher() { @@ -238,7 +238,7 @@ class SentrySpanWebClientCustomizerTest { .retrieve() .bodyToMono(String::class.java) .block() - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(uri.toString(), it.data["url"]) @@ -261,7 +261,7 @@ class SentrySpanWebClientCustomizerTest { .block() } catch (e: Throwable) { } - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(uri.toString(), it.data["url"]) @@ -281,7 +281,7 @@ class SentrySpanWebClientCustomizerTest { .retrieve() .bodyToMono(String::class.java) .block() - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(uri.toString(), it.data["url"]) @@ -304,7 +304,7 @@ class SentrySpanWebClientCustomizerTest { .block() } catch (e: Throwable) { } - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { assertEquals("http", it.type) assertEquals(uri.toString(), it.data["url"]) diff --git a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt index bb6aa99fe9..a1a54ea49b 100644 --- a/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt +++ b/sentry-spring-boot/src/test/kotlin/io/sentry/spring/boot/it/SentrySpringIntegrationTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.boot.it -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ITransportFactory import io.sentry.Sentry import io.sentry.checkEvent @@ -60,7 +60,7 @@ class SentrySpringIntegrationTest { lateinit var transport: ITransport @SpyBean - lateinit var scopes: IScopes + lateinit var hub: IHub @LocalServerPort var port: Int? = null @@ -188,7 +188,7 @@ class SentrySpringIntegrationTest { restTemplate.getForEntity("http://localhost:$port/throws-handled", String::class.java) - verify(scopes, never()).captureEvent(any()) + verify(hub, never()).captureEvent(any()) } @Test diff --git a/sentry-spring-jakarta/api/sentry-spring-jakarta.api b/sentry-spring-jakarta/api/sentry-spring-jakarta.api index e445aac284..543f14823b 100644 --- a/sentry-spring-jakarta/api/sentry-spring-jakarta.api +++ b/sentry-spring-jakarta/api/sentry-spring-jakarta.api @@ -5,7 +5,6 @@ public final class io/sentry/spring/jakarta/BuildConfig { public final class io/sentry/spring/jakarta/ContextTagsEventProcessor : io/sentry/EventProcessor { public fun (Lio/sentry/SentryOptions;)V - public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } @@ -23,7 +22,7 @@ public final class io/sentry/spring/jakarta/HttpServletRequestSentryUserProvider public class io/sentry/spring/jakarta/SentryExceptionResolver : org/springframework/core/Ordered, org/springframework/web/servlet/HandlerExceptionResolver { public static final field MECHANISM_TYPE Ljava/lang/String; - public fun (Lio/sentry/IScopes;Lio/sentry/spring/jakarta/tracing/TransactionNameProvider;I)V + public fun (Lio/sentry/IHub;Lio/sentry/spring/jakarta/tracing/TransactionNameProvider;I)V protected fun createEvent (Ljakarta/servlet/http/HttpServletRequest;Ljava/lang/Exception;)Lio/sentry/SentryEvent; protected fun createHint (Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;)Lio/sentry/Hint; public fun getOrder ()I @@ -44,19 +43,18 @@ public class io/sentry/spring/jakarta/SentryInitBeanPostProcessor : org/springfr public class io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessor : io/sentry/EventProcessor { public fun (Lio/sentry/spring/jakarta/tracing/TransactionNameProvider;Ljakarta/servlet/http/HttpServletRequest;)V - public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } public class io/sentry/spring/jakarta/SentryRequestResolver { - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun resolveSentryRequest (Ljakarta/servlet/http/HttpServletRequest;)Lio/sentry/protocol/Request; } public class io/sentry/spring/jakarta/SentrySpringFilter : org/springframework/web/filter/OncePerRequestFilter { public fun ()V - public fun (Lio/sentry/IScopes;)V - public fun (Lio/sentry/IScopes;Lio/sentry/spring/jakarta/SentryRequestResolver;Lio/sentry/spring/jakarta/tracing/TransactionNameProvider;)V + public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IHub;Lio/sentry/spring/jakarta/SentryRequestResolver;Lio/sentry/spring/jakarta/tracing/TransactionNameProvider;)V protected fun doFilterInternal (Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;Ljakarta/servlet/FilterChain;)V } @@ -71,7 +69,7 @@ public final class io/sentry/spring/jakarta/SentryTaskDecorator : org/springfram } public class io/sentry/spring/jakarta/SentryUserFilter : org/springframework/web/filter/OncePerRequestFilter { - public fun (Lio/sentry/IScopes;Ljava/util/List;)V + public fun (Lio/sentry/IHub;Ljava/util/List;)V protected fun doFilterInternal (Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;Ljakarta/servlet/FilterChain;)V public fun getSentryUserProviders ()Ljava/util/List; } @@ -98,7 +96,7 @@ public abstract interface annotation class io/sentry/spring/jakarta/checkin/Sent public class io/sentry/spring/jakarta/checkin/SentryCheckInAdvice : org/aopalliance/intercept/MethodInterceptor, org/springframework/context/EmbeddedValueResolverAware { public fun ()V - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun invoke (Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object; public fun setEmbeddedValueResolver (Lorg/springframework/util/StringValueResolver;)V } @@ -129,7 +127,7 @@ public abstract interface annotation class io/sentry/spring/jakarta/exception/Se public class io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdvice : org/aopalliance/intercept/MethodInterceptor { public fun ()V - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun invoke (Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object; } @@ -171,7 +169,7 @@ public final class io/sentry/spring/jakarta/graphql/SentryDataFetcherExceptionRe public final class io/sentry/spring/jakarta/graphql/SentryDgsSubscriptionHandler : io/sentry/graphql/SentrySubscriptionHandler { public fun ()V - public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IScopes;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; + public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IHub;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; } public final class io/sentry/spring/jakarta/graphql/SentryGraphqlBeanPostProcessor : org/springframework/beans/factory/config/BeanPostProcessor, org/springframework/core/PriorityOrdered { @@ -190,7 +188,7 @@ public class io/sentry/spring/jakarta/graphql/SentryGraphqlConfiguration { public final class io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandler : io/sentry/graphql/SentrySubscriptionHandler { public fun ()V - public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IScopes;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; + public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IHub;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; } public class io/sentry/spring/jakarta/tracing/SentryAdviceConfiguration { @@ -209,18 +207,18 @@ public abstract interface annotation class io/sentry/spring/jakarta/tracing/Sent public class io/sentry/spring/jakarta/tracing/SentrySpanAdvice : org/aopalliance/intercept/MethodInterceptor { public fun ()V - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun invoke (Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object; } public class io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor : org/springframework/http/client/ClientHttpRequestInterceptor { - public fun (Lio/sentry/IScopes;)V - public fun (Lio/sentry/IScopes;Z)V + public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IHub;Z)V public fun intercept (Lorg/springframework/http/HttpRequest;[BLorg/springframework/http/client/ClientHttpRequestExecution;)Lorg/springframework/http/client/ClientHttpResponse; } public class io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter : org/springframework/web/reactive/function/client/ExchangeFilterFunction { - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun filter (Lorg/springframework/web/reactive/function/client/ClientRequest;Lorg/springframework/web/reactive/function/client/ExchangeFunction;)Lreactor/core/publisher/Mono; } @@ -235,8 +233,8 @@ public class io/sentry/spring/jakarta/tracing/SentryTracingConfiguration { public class io/sentry/spring/jakarta/tracing/SentryTracingFilter : org/springframework/web/filter/OncePerRequestFilter { public fun ()V - public fun (Lio/sentry/IScopes;)V - public fun (Lio/sentry/IScopes;Lio/sentry/spring/jakarta/tracing/TransactionNameProvider;)V + public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IHub;Lio/sentry/spring/jakarta/tracing/TransactionNameProvider;)V protected fun doFilterInternal (Ljakarta/servlet/http/HttpServletRequest;Ljakarta/servlet/http/HttpServletResponse;Ljakarta/servlet/FilterChain;)V } @@ -248,7 +246,7 @@ public abstract interface annotation class io/sentry/spring/jakarta/tracing/Sent public class io/sentry/spring/jakarta/tracing/SentryTransactionAdvice : org/aopalliance/intercept/MethodInterceptor { public fun ()V - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun invoke (Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object; } @@ -276,39 +274,38 @@ public abstract interface class io/sentry/spring/jakarta/tracing/TransactionName public abstract class io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter : org/springframework/web/server/WebFilter { public static final field SENTRY_HUB_KEY Ljava/lang/String; - public static final field SENTRY_SCOPES_KEY Ljava/lang/String; - public fun (Lio/sentry/IScopes;)V - protected fun doFinally (Lorg/springframework/web/server/ServerWebExchange;Lio/sentry/IScopes;Lio/sentry/ITransaction;)V - protected fun doFirst (Lorg/springframework/web/server/ServerWebExchange;Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V + protected fun doFinally (Lorg/springframework/web/server/ServerWebExchange;Lio/sentry/IHub;Lio/sentry/ITransaction;)V + protected fun doFirst (Lorg/springframework/web/server/ServerWebExchange;Lio/sentry/IHub;)V protected fun doOnError (Lio/sentry/ITransaction;Ljava/lang/Throwable;)V - protected fun maybeStartTransaction (Lio/sentry/IScopes;Lorg/springframework/http/server/reactive/ServerHttpRequest;Ljava/lang/String;)Lio/sentry/ITransaction; - protected fun shouldTraceRequest (Lio/sentry/IScopes;Lorg/springframework/http/server/reactive/ServerHttpRequest;)Z - protected fun startTransaction (Lio/sentry/IScopes;Lorg/springframework/http/server/reactive/ServerHttpRequest;Lio/sentry/TransactionContext;Ljava/lang/String;)Lio/sentry/ITransaction; + protected fun maybeStartTransaction (Lio/sentry/IHub;Lorg/springframework/http/server/reactive/ServerHttpRequest;)Lio/sentry/ITransaction; + protected fun shouldTraceRequest (Lio/sentry/IHub;Lorg/springframework/http/server/reactive/ServerHttpRequest;)Z + protected fun startTransaction (Lio/sentry/IHub;Lorg/springframework/http/server/reactive/ServerHttpRequest;Lio/sentry/TransactionContext;)Lio/sentry/ITransaction; } public final class io/sentry/spring/jakarta/webflux/ReactorUtils { public fun ()V public static fun withSentry (Lreactor/core/publisher/Flux;)Lreactor/core/publisher/Flux; public static fun withSentry (Lreactor/core/publisher/Mono;)Lreactor/core/publisher/Mono; - public static fun withSentryForkedRoots (Lreactor/core/publisher/Flux;)Lreactor/core/publisher/Flux; - public static fun withSentryForkedRoots (Lreactor/core/publisher/Mono;)Lreactor/core/publisher/Mono; - public static fun withSentryScopes (Lreactor/core/publisher/Flux;Lio/sentry/IScopes;)Lreactor/core/publisher/Flux; - public static fun withSentryScopes (Lreactor/core/publisher/Mono;Lio/sentry/IScopes;)Lreactor/core/publisher/Mono; + public static fun withSentryHub (Lreactor/core/publisher/Flux;Lio/sentry/IHub;)Lreactor/core/publisher/Flux; + public static fun withSentryHub (Lreactor/core/publisher/Mono;Lio/sentry/IHub;)Lreactor/core/publisher/Mono; + public static fun withSentryNewMainHubClone (Lreactor/core/publisher/Flux;)Lreactor/core/publisher/Flux; + public static fun withSentryNewMainHubClone (Lreactor/core/publisher/Mono;)Lreactor/core/publisher/Mono; } public final class io/sentry/spring/jakarta/webflux/SentryReactorThreadLocalAccessor : io/micrometer/context/ThreadLocalAccessor { public static final field KEY Ljava/lang/String; public fun ()V - public fun getValue ()Lio/sentry/IScopes; + public fun getValue ()Lio/sentry/IHub; public synthetic fun getValue ()Ljava/lang/Object; public fun key ()Ljava/lang/Object; public fun reset ()V - public fun setValue (Lio/sentry/IScopes;)V + public fun setValue (Lio/sentry/IHub;)V public synthetic fun setValue (Ljava/lang/Object;)V } public class io/sentry/spring/jakarta/webflux/SentryRequestResolver { - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun resolveSentryRequest (Lorg/springframework/http/server/reactive/ServerHttpRequest;)Lio/sentry/protocol/Request; } @@ -320,18 +317,18 @@ public final class io/sentry/spring/jakarta/webflux/SentryScheduleHook : java/ut public final class io/sentry/spring/jakarta/webflux/SentryWebExceptionHandler : org/springframework/web/server/WebExceptionHandler { public static final field MECHANISM_TYPE Ljava/lang/String; - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun handle (Lorg/springframework/web/server/ServerWebExchange;Ljava/lang/Throwable;)Lreactor/core/publisher/Mono; } public class io/sentry/spring/jakarta/webflux/SentryWebFilter : io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter { - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun filter (Lorg/springframework/web/server/ServerWebExchange;Lorg/springframework/web/server/WebFilterChain;)Lreactor/core/publisher/Mono; } public final class io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor : io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter { public static final field TRACE_ORIGIN Ljava/lang/String; - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun filter (Lorg/springframework/web/server/ServerWebExchange;Lorg/springframework/web/server/WebFilterChain;)Lreactor/core/publisher/Mono; } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/ContextTagsEventProcessor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/ContextTagsEventProcessor.java index 94f49d8319..0f00b4e9f7 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/ContextTagsEventProcessor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/ContextTagsEventProcessor.java @@ -38,9 +38,4 @@ public ContextTagsEventProcessor(final @NotNull SentryOptions options) { } return event; } - - @Override - public @Nullable Long getOrder() { - return 14000L; - } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/EnableSentry.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/EnableSentry.java index e8cd91f3f4..1395e035a1 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/EnableSentry.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/EnableSentry.java @@ -12,7 +12,7 @@ * *

      *
    • creates bean of type {@link io.sentry.SentryOptions} - *
    • registers {@link io.sentry.IScopes} for sending Sentry events + *
    • registers {@link io.sentry.IHub} for sending Sentry events *
    • registers {@link SentryExceptionResolver} to send Sentry event for any uncaught exception * in Spring MVC flow. *
    diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryExceptionResolver.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryExceptionResolver.java index efa98ef581..6d4098d624 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryExceptionResolver.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryExceptionResolver.java @@ -5,7 +5,7 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.Hint; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.SentryEvent; import io.sentry.SentryLevel; import io.sentry.exception.ExceptionMechanismException; @@ -29,15 +29,15 @@ public class SentryExceptionResolver implements HandlerExceptionResolver, Ordered { public static final String MECHANISM_TYPE = "Spring6ExceptionResolver"; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private final @NotNull TransactionNameProvider transactionNameProvider; private final int order; public SentryExceptionResolver( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull TransactionNameProvider transactionNameProvider, final int order) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + this.hub = Objects.requireNonNull(hub, "hub is required"); this.transactionNameProvider = Objects.requireNonNull(transactionNameProvider, "transactionNameProvider is required"); this.order = order; @@ -53,7 +53,7 @@ public SentryExceptionResolver( final SentryEvent event = createEvent(request, ex); final Hint hint = createHint(request, response); - scopes.captureEvent(event, hint); + hub.captureEvent(event, hint); // null = run other HandlerExceptionResolvers to actually handle the exception return null; diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryHubRegistrar.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryHubRegistrar.java index 9598f0c926..4a8789c812 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryHubRegistrar.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryHubRegistrar.java @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta; import com.jakewharton.nopen.annotation.Open; -import io.sentry.ScopesAdapter; +import io.sentry.HubAdapter; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryOptions; import io.sentry.protocol.SdkVersion; @@ -60,7 +60,7 @@ private void registerSentryOptions( private void registerSentryHubBean(final @NotNull BeanDefinitionRegistry registry) { final BeanDefinitionBuilder builder = - BeanDefinitionBuilder.genericBeanDefinition(ScopesAdapter.class); + BeanDefinitionBuilder.genericBeanDefinition(HubAdapter.class); builder.setInitMethodName("getInstance"); registry.registerBeanDefinition("sentryHub", builder.getBeanDefinition()); diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryInitBeanPostProcessor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryInitBeanPostProcessor.java index d33dfca8d8..9937bfbf1a 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryInitBeanPostProcessor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryInitBeanPostProcessor.java @@ -2,10 +2,10 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.EventProcessor; -import io.sentry.IScopes; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ITransportFactory; import io.sentry.Integration; -import io.sentry.ScopesAdapter; import io.sentry.Sentry; import io.sentry.SentryOptions; import io.sentry.SentryOptions.TracesSamplerCallback; @@ -27,15 +27,15 @@ public class SentryInitBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware, DisposableBean { private @Nullable ApplicationContext applicationContext; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; public SentryInitBeanPostProcessor() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } - SentryInitBeanPostProcessor(final @NotNull IScopes scopes) { - Objects.requireNonNull(scopes, "Scopes are required"); - this.scopes = scopes; + SentryInitBeanPostProcessor(final @NotNull IHub hub) { + Objects.requireNonNull(hub, "hub is required"); + this.hub = hub; } @Override @@ -86,6 +86,6 @@ public void setApplicationContext(final @NotNull ApplicationContext applicationC @Override public void destroy() { - scopes.close(); + hub.close(); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessor.java index 91b27ddeac..fa782030c0 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessor.java @@ -8,7 +8,6 @@ import io.sentry.util.Objects; import jakarta.servlet.http.HttpServletRequest; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** Attaches transaction name from the HTTP request to {@link SentryEvent}. */ @Open @@ -31,9 +30,4 @@ public SentryRequestHttpServletRequestProcessor( } return event; } - - @Override - public @Nullable Long getOrder() { - return 5000L; - } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryRequestResolver.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryRequestResolver.java index 71f9079f9f..e1bc45ac64 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryRequestResolver.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryRequestResolver.java @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.SentryLevel; import io.sentry.protocol.Request; import io.sentry.util.HttpUtils; @@ -20,11 +20,11 @@ @Open public class SentryRequestResolver { - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private volatile @Nullable List extraSecurityCookies; - public SentryRequestResolver(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "options is required"); + public SentryRequestResolver(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "options is required"); } // httpRequest.getRequestURL() returns StringBuffer which is considered an obsolete class. @@ -40,7 +40,7 @@ public SentryRequestResolver(final @NotNull IScopes scopes) { extractSecurityCookieNamesOrUseCached(httpRequest); sentryRequest.setHeaders(resolveHeadersMap(httpRequest, additionalSecurityCookieNames)); - if (scopes.getOptions().isSendDefaultPii()) { + if (hub.getOptions().isSendDefaultPii()) { String cookieName = HttpUtils.COOKIE_HEADER_NAME; final @Nullable List filteredHeaders = HttpUtils.filterOutSecurityCookiesFromHeader( @@ -57,8 +57,7 @@ Map resolveHeadersMap( final Map headersMap = new HashMap<>(); for (String headerName : Collections.list(request.getHeaderNames())) { // do not copy personal information identifiable headers - if (scopes.getOptions().isSendDefaultPii() - || !HttpUtils.containsSensitiveHeader(headerName)) { + if (hub.getOptions().isSendDefaultPii() || !HttpUtils.containsSensitiveHeader(headerName)) { final @Nullable List filteredHeaders = HttpUtils.filterOutSecurityCookiesFromHeader( request.getHeaders(headerName), headerName, additionalSecurityCookieNames); @@ -95,8 +94,7 @@ private List extractSecurityCookieNames(final @NotNull HttpServletReques } } } catch (Throwable t) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log(SentryLevel.WARNING, "Failed to extract session cookie name from request.", t); } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java index c7573701ec..129ea739d6 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentrySpringFilter.java @@ -8,9 +8,8 @@ import io.sentry.Breadcrumb; import io.sentry.EventProcessor; import io.sentry.Hint; -import io.sentry.IScopes; -import io.sentry.ISentryLifecycleToken; -import io.sentry.ScopesAdapter; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.SentryEvent; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -24,33 +23,32 @@ import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.springframework.http.MediaType; import org.springframework.util.MimeType; import org.springframework.web.filter.OncePerRequestFilter; @Open public class SentrySpringFilter extends OncePerRequestFilter { - private final @NotNull IScopes scopesBeforeForking; + private final @NotNull IHub hub; private final @NotNull SentryRequestResolver requestResolver; private final @NotNull TransactionNameProvider transactionNameProvider; public SentrySpringFilter( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull SentryRequestResolver requestResolver, final @NotNull TransactionNameProvider transactionNameProvider) { - this.scopesBeforeForking = Objects.requireNonNull(scopes, "scopes are required"); + this.hub = Objects.requireNonNull(hub, "hub is required"); this.requestResolver = Objects.requireNonNull(requestResolver, "requestResolver is required"); this.transactionNameProvider = Objects.requireNonNull(transactionNameProvider, "transactionNameProvider is required"); } - public SentrySpringFilter(final @NotNull IScopes scopes) { - this(scopes, new SentryRequestResolver(scopes), new SpringMvcTransactionNameProvider()); + public SentrySpringFilter(final @NotNull IHub hub) { + this(hub, new SentryRequestResolver(hub), new SpringMvcTransactionNameProvider()); } public SentrySpringFilter() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } @Override @@ -59,30 +57,29 @@ protected void doFilterInternal( final @NotNull HttpServletResponse response, final @NotNull FilterChain filterChain) throws ServletException, IOException { - if (scopesBeforeForking.isEnabled()) { + if (hub.isEnabled()) { // request may qualify for caching request body, if so resolve cached request - final HttpServletRequest request = - resolveHttpServletRequest(scopesBeforeForking, servletRequest); - final @NotNull IScopes forkedScopes = scopesBeforeForking.forkedScopes("SentrySpringFilter"); - try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) { + final HttpServletRequest request = resolveHttpServletRequest(servletRequest); + hub.pushScope(); + try { final Hint hint = new Hint(); hint.set(SPRING_REQUEST_FILTER_REQUEST, servletRequest); hint.set(SPRING_REQUEST_FILTER_RESPONSE, response); - forkedScopes.addBreadcrumb( - Breadcrumb.http(request.getRequestURI(), request.getMethod()), hint); - configureScope(forkedScopes, request); + hub.addBreadcrumb(Breadcrumb.http(request.getRequestURI(), request.getMethod()), hint); + configureScope(request); filterChain.doFilter(request, response); + } finally { + hub.popScope(); } } else { filterChain.doFilter(servletRequest, response); } } - private void configureScope( - final @NotNull IScopes scopes, final @NotNull HttpServletRequest request) { + private void configureScope(HttpServletRequest request) { try { - scopes.configureScope( + hub.configureScope( scope -> { // set basic request information on the scope scope.setRequest(requestResolver.resolveSentryRequest(request)); @@ -95,26 +92,24 @@ private void configureScope( // request processing if (request instanceof CachedBodyHttpServletRequest) { scope.addEventProcessor( - new RequestBodyExtractingEventProcessor(request, scopes.getOptions())); + new RequestBodyExtractingEventProcessor(request, hub.getOptions())); } }); } catch (Throwable e) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log(SentryLevel.ERROR, "Failed to set scope for HTTP request", e); } } private @NotNull HttpServletRequest resolveHttpServletRequest( - final @NotNull IScopes scopes, final @NotNull HttpServletRequest request) { - if (scopes.getOptions().isSendDefaultPii() - && qualifiesForCaching(request, scopes.getOptions().getMaxRequestBodySize())) { + final @NotNull HttpServletRequest request) { + if (hub.getOptions().isSendDefaultPii() + && qualifiesForCaching(request, hub.getOptions().getMaxRequestBodySize())) { try { return new CachedBodyHttpServletRequest(request); } catch (IOException e) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -159,10 +154,5 @@ public RequestBodyExtractingEventProcessor( } return event; } - - @Override - public @Nullable Long getOrder() { - return 3000L; - } } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java index ba75795260..5c4f37e521 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryTaskDecorator.java @@ -1,7 +1,6 @@ package io.sentry.spring.jakarta; -import io.sentry.IScopes; -import io.sentry.ISentryLifecycleToken; +import io.sentry.IHub; import io.sentry.Sentry; import java.util.concurrent.Callable; import org.jetbrains.annotations.NotNull; @@ -9,18 +8,22 @@ import org.springframework.scheduling.annotation.Async; /** - * Forks scopes for a thread running a {@link Runnable} given by parameter. Used to propagate the - * current {@link IScopes} on the thread executing async task - like MVC controller methods + * Sets a current hub on a thread running a {@link Runnable} given by parameter. Used to propagate + * the current {@link IHub} on the thread executing async task - like MVC controller methods * returning a {@link Callable} or Spring beans methods annotated with {@link Async}. */ public final class SentryTaskDecorator implements TaskDecorator { @Override public @NotNull Runnable decorate(final @NotNull Runnable runnable) { - final IScopes newScopes = Sentry.getCurrentScopes().forkedScopes("SentryTaskDecorator"); + final IHub newHub = Sentry.getCurrentHub().clone(); return () -> { - try (final @NotNull ISentryLifecycleToken ignored = newScopes.makeCurrent()) { + final IHub oldState = Sentry.getCurrentHub(); + Sentry.setCurrentHub(newHub); + try { runnable.run(); + } finally { + Sentry.setCurrentHub(oldState); } }; } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryUserFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryUserFilter.java index 31cc73a346..f7b8bc62d6 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryUserFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/SentryUserFilter.java @@ -1,8 +1,8 @@ package io.sentry.spring.jakarta; import com.jakewharton.nopen.annotation.Open; +import io.sentry.IHub; import io.sentry.IScope; -import io.sentry.IScopes; import io.sentry.IpAddressUtils; import io.sentry.protocol.User; import io.sentry.util.Objects; @@ -26,12 +26,12 @@ */ @Open public class SentryUserFilter extends OncePerRequestFilter { - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private final @NotNull List sentryUserProviders; public SentryUserFilter( - final @NotNull IScopes scopes, final @NotNull List sentryUserProviders) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + final @NotNull IHub hub, final @NotNull List sentryUserProviders) { + this.hub = Objects.requireNonNull(hub, "hub is required"); this.sentryUserProviders = Objects.requireNonNull(sentryUserProviders, "sentryUserProviders list is required"); } @@ -46,13 +46,13 @@ protected void doFilterInternal( for (final SentryUserProvider provider : sentryUserProviders) { apply(user, provider.provideUser()); } - if (scopes.getOptions().isSendDefaultPii()) { + if (hub.getOptions().isSendDefaultPii()) { if (IpAddressUtils.isDefault(user.getIpAddress())) { // unset {{auto}} as it would set the server's ip address as a user ip address user.setIpAddress(null); } } - scopes.setUser(user); + hub.setUser(user); chain.doFilter(request, response); } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/checkin/SentryCheckInAdvice.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/checkin/SentryCheckInAdvice.java index dd22f4dc5d..5a4e329fa8 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/checkin/SentryCheckInAdvice.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/checkin/SentryCheckInAdvice.java @@ -4,9 +4,8 @@ import io.sentry.CheckIn; import io.sentry.CheckInStatus; import io.sentry.DateUtils; -import io.sentry.IScopes; -import io.sentry.ISentryLifecycleToken; -import io.sentry.ScopesAdapter; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.SentryLevel; import io.sentry.protocol.SentryId; import io.sentry.util.Objects; @@ -31,16 +30,16 @@ @ApiStatus.Experimental @Open public class SentryCheckInAdvice implements MethodInterceptor, EmbeddedValueResolverAware { - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private @Nullable StringValueResolver resolver; public SentryCheckInAdvice() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } - public SentryCheckInAdvice(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + public SentryCheckInAdvice(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "hub is required"); } @Override @@ -67,8 +66,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl // expressions. Testing shows this can also happen if properties cannot be resolved (without // an exception being thrown). Sentry should alert the user about missed checkins in this // case since the monitor slug won't match what is configured in Sentry. - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -78,8 +76,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } if (ObjectUtils.isEmpty(monitorSlug)) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -87,28 +84,27 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl return invocation.proceed(); } - try (final @NotNull ISentryLifecycleToken ignored = - scopes.forkedScopes("SentryCheckInAdvice").makeCurrent()) { - TracingUtils.startNewTrace(scopes); + hub.pushScope(); + TracingUtils.startNewTrace(hub); - @Nullable SentryId checkInId = null; - final long startTime = System.currentTimeMillis(); - boolean didError = false; + @Nullable SentryId checkInId = null; + final long startTime = System.currentTimeMillis(); + boolean didError = false; - try { - if (!isHeartbeatOnly) { - checkInId = scopes.captureCheckIn(new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS)); - } - return invocation.proceed(); - } catch (Throwable e) { - didError = true; - throw e; - } finally { - final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK; - CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); - checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); - scopes.captureCheckIn(checkIn); + try { + if (!isHeartbeatOnly) { + checkInId = hub.captureCheckIn(new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS)); } + return invocation.proceed(); + } catch (Throwable e) { + didError = true; + throw e; + } finally { + final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK; + CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); + checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); + hub.captureCheckIn(checkIn); + hub.popScope(); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdvice.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdvice.java index c6537f853c..f989382045 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdvice.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdvice.java @@ -1,8 +1,8 @@ package io.sentry.spring.jakarta.exception; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IScopes; -import io.sentry.ScopesAdapter; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.exception.ExceptionMechanismException; import io.sentry.protocol.Mechanism; import io.sentry.util.Objects; @@ -22,14 +22,14 @@ @Open public class SentryCaptureExceptionParameterAdvice implements MethodInterceptor { private static final String MECHANISM_TYPE = "SentrySpring6CaptureExceptionParameterAdvice"; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; public SentryCaptureExceptionParameterAdvice() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } - public SentryCaptureExceptionParameterAdvice(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + public SentryCaptureExceptionParameterAdvice(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "hub is required"); } @Override @@ -58,6 +58,6 @@ private void captureException(final @NotNull Throwable throwable) { mechanism.setHandled(true); final Throwable mechanismException = new ExceptionMechanismException(mechanism, throwable, Thread.currentThread()); - scopes.captureException(mechanismException); + hub.captureException(mechanismException); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryBatchLoaderRegistry.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryBatchLoaderRegistry.java index 3e2223b694..1d5576c596 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryBatchLoaderRegistry.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryBatchLoaderRegistry.java @@ -1,11 +1,11 @@ package io.sentry.spring.jakarta.graphql; -import static io.sentry.graphql.SentryInstrumentation.SENTRY_SCOPES_CONTEXT_KEY; +import static io.sentry.graphql.SentryInstrumentation.SENTRY_HUB_CONTEXT_KEY; import graphql.GraphQLContext; import io.sentry.Breadcrumb; -import io.sentry.IScopes; -import io.sentry.NoOpScopes; +import io.sentry.IHub; +import io.sentry.NoOpHub; import java.util.List; import java.util.Map; import java.util.Set; @@ -89,7 +89,7 @@ public BatchLoaderRegistry.RegistrationSpec withOptions(DataLoaderOptions public void registerBatchLoader(BiFunction, BatchLoaderEnvironment, Flux> loader) { delegate.registerBatchLoader( (keys, batchLoaderEnvironment) -> { - scopesFromContext(batchLoaderEnvironment) + hubFromContext(batchLoaderEnvironment) .addBreadcrumb(Breadcrumb.graphqlDataLoader(keys, keyType, valueType, name)); return loader.apply(keys, batchLoaderEnvironment); }); @@ -100,20 +100,20 @@ public void registerMappedBatchLoader( BiFunction, BatchLoaderEnvironment, Mono>> loader) { delegate.registerMappedBatchLoader( (keys, batchLoaderEnvironment) -> { - scopesFromContext(batchLoaderEnvironment) + hubFromContext(batchLoaderEnvironment) .addBreadcrumb(Breadcrumb.graphqlDataLoader(keys, keyType, valueType, name)); return loader.apply(keys, batchLoaderEnvironment); }); } - private @NotNull IScopes scopesFromContext(final @NotNull BatchLoaderEnvironment environment) { + private @NotNull IHub hubFromContext(final @NotNull BatchLoaderEnvironment environment) { Object context = environment.getContext(); if (context instanceof GraphQLContext) { GraphQLContext graphqlContext = (GraphQLContext) context; - return graphqlContext.getOrDefault(SENTRY_SCOPES_CONTEXT_KEY, NoOpScopes.getInstance()); + return graphqlContext.getOrDefault(SENTRY_HUB_CONTEXT_KEY, NoOpHub.getInstance()); } - return NoOpScopes.getInstance(); + return NoOpHub.getInstance(); } } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryDgsSubscriptionHandler.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryDgsSubscriptionHandler.java index a7a6cccd3e..83a090954d 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryDgsSubscriptionHandler.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentryDgsSubscriptionHandler.java @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta.graphql; import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.graphql.ExceptionReporter; import io.sentry.graphql.SentrySubscriptionHandler; @@ -17,7 +17,7 @@ public SentryDgsSubscriptionHandler() { @Override public @NotNull Object onSubscriptionResult( final @NotNull Object result, - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull ExceptionReporter exceptionReporter, final @NotNull InstrumentationFieldFetchParameters parameters) { if (result instanceof Flux) { @@ -25,7 +25,7 @@ public SentryDgsSubscriptionHandler() { return flux.doOnError( throwable -> { final @NotNull ExceptionReporter.ExceptionDetails exceptionDetails = - new ExceptionReporter.ExceptionDetails(scopes, parameters.getEnvironment(), true); + new ExceptionReporter.ExceptionDetails(hub, parameters.getEnvironment(), true); exceptionReporter.captureThrowable(throwable, exceptionDetails, null); }); } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandler.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandler.java index eec86f5e8b..4c51981035 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandler.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandler.java @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta.graphql; import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.graphql.ExceptionReporter; import io.sentry.graphql.SentrySubscriptionHandler; import org.jetbrains.annotations.NotNull; @@ -13,7 +13,7 @@ public final class SentrySpringSubscriptionHandler implements SentrySubscription @Override public @NotNull Object onSubscriptionResult( final @NotNull Object result, - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull ExceptionReporter exceptionReporter, final @NotNull InstrumentationFieldFetchParameters parameters) { if (result instanceof Flux) { @@ -21,7 +21,7 @@ public final class SentrySpringSubscriptionHandler implements SentrySubscription return flux.doOnError( throwable -> { final @NotNull ExceptionReporter.ExceptionDetails exceptionDetails = - new ExceptionReporter.ExceptionDetails(scopes, parameters.getEnvironment(), true); + new ExceptionReporter.ExceptionDetails(hub, parameters.getEnvironment(), true); if (throwable instanceof SubscriptionPublisherException && throwable.getCause() != null) { exceptionReporter.captureThrowable(throwable.getCause(), exceptionDetails, null); diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanAdvice.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanAdvice.java index 668c8d1b0b..5f345a4e02 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanAdvice.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanAdvice.java @@ -1,10 +1,9 @@ package io.sentry.spring.jakarta.tracing; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IScopes; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ISpan; -import io.sentry.ScopesAdapter; -import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; import java.lang.reflect.Method; @@ -23,20 +22,20 @@ @Open public class SentrySpanAdvice implements MethodInterceptor { private static final String TRACE_ORIGIN = "auto.function.spring_jakarta.advice"; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; public SentrySpanAdvice() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } - public SentrySpanAdvice(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + public SentrySpanAdvice(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "hub is required"); } @SuppressWarnings("deprecation") @Override public Object invoke(final @NotNull MethodInvocation invocation) throws Throwable { - final ISpan activeSpan = scopes.getSpan(); + final ISpan activeSpan = hub.getSpan(); if (activeSpan == null || activeSpan.isNoOp()) { // there is no active transaction, we do not start new span @@ -52,9 +51,8 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl mostSpecificMethod.getDeclaringClass(), SentrySpan.class); } final String operation = resolveSpanOperation(targetClass, mostSpecificMethod, sentrySpan); - SpanOptions spanOptions = new SpanOptions(); - spanOptions.setOrigin(TRACE_ORIGIN); - final ISpan span = activeSpan.startChild(operation, null, spanOptions); + final ISpan span = activeSpan.startChild(operation); + span.getSpanContext().setOrigin(TRACE_ORIGIN); if (sentrySpan != null && !StringUtils.isEmpty(sentrySpan.description())) { span.setDescription(sentrySpan.description()); } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java index 6feac7eec7..59c1926ff3 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientHttpRequestInterceptor.java @@ -8,10 +8,9 @@ import io.sentry.BaggageHeader; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.ISpan; import io.sentry.SpanDataConvention; -import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; import io.sentry.util.TracingUtils; @@ -29,16 +28,16 @@ public class SentrySpanClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { private static final String TRACE_ORIGIN_REST_TEMPLATE = "auto.http.spring_jakarta.resttemplate"; private static final String TRACE_ORIGIN_REST_CLIENT = "auto.http.spring_jakarta.restclient"; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private final @NotNull String traceOrigin; - public SentrySpanClientHttpRequestInterceptor(final @NotNull IScopes scopes) { - this(scopes, true); + public SentrySpanClientHttpRequestInterceptor(final @NotNull IHub hub) { + this(hub, true); } public SentrySpanClientHttpRequestInterceptor( - final @NotNull IScopes scopes, final @NotNull boolean isRestTemplate) { - this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); + final @NotNull IHub hub, final @NotNull boolean isRestTemplate) { + this.hub = Objects.requireNonNull(hub, "hub is required"); this.traceOrigin = isRestTemplate ? TRACE_ORIGIN_REST_TEMPLATE : TRACE_ORIGIN_REST_CLIENT; } @@ -51,14 +50,14 @@ public SentrySpanClientHttpRequestInterceptor( Integer responseStatusCode = null; ClientHttpResponse response = null; try { - final ISpan activeSpan = scopes.getSpan(); + final ISpan activeSpan = hub.getSpan(); if (activeSpan == null) { maybeAddTracingHeaders(request, null); return execution.execute(request, body); } - final @NotNull SpanOptions spanOptions = new SpanOptions(); - spanOptions.setOrigin(traceOrigin); - final ISpan span = activeSpan.startChild("http.client", null, spanOptions); + + final ISpan span = activeSpan.startChild("http.client"); + span.getSpanContext().setOrigin(traceOrigin); final String methodName = request.getMethod() != null ? request.getMethod().name() : "unknown"; final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(request.getURI().toString()); @@ -92,7 +91,7 @@ private void maybeAddTracingHeaders( final @NotNull HttpRequest request, final @Nullable ISpan span) { final @Nullable TracingUtils.TracingHeaders tracingHeaders = TracingUtils.traceIfAllowed( - scopes, + hub, request.getURI().toString(), request.getHeaders().get(BaggageHeader.BAGGAGE_HEADER), span); @@ -129,6 +128,6 @@ private void addBreadcrumb( hint.set(SPRING_REQUEST_INTERCEPTOR_RESPONSE, response); } - scopes.addBreadcrumb(breadcrumb, hint); + hub.addBreadcrumb(breadcrumb, hint); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java index 6744cf174e..4ac511721e 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentrySpanClientWebRequestFilter.java @@ -7,10 +7,9 @@ import io.sentry.BaggageHeader; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.ISpan; import io.sentry.SpanDataConvention; -import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; import io.sentry.util.TracingUtils; @@ -26,24 +25,24 @@ @Open public class SentrySpanClientWebRequestFilter implements ExchangeFilterFunction { private static final String TRACE_ORIGIN = "auto.http.spring_jakarta.webclient"; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; - public SentrySpanClientWebRequestFilter(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); + public SentrySpanClientWebRequestFilter(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "hub is required"); } @Override public @NotNull Mono filter( final @NotNull ClientRequest request, final @NotNull ExchangeFunction next) { - final ISpan activeSpan = scopes.getSpan(); + final ISpan activeSpan = hub.getSpan(); if (activeSpan == null) { final @NotNull ClientRequest modifiedRequest = maybeAddTracingHeaders(request, null); addBreadcrumb(modifiedRequest, null); return next.exchange(modifiedRequest); } - final @NotNull SpanOptions spanOptions = new SpanOptions(); - spanOptions.setOrigin(TRACE_ORIGIN); - final ISpan span = activeSpan.startChild("http.client", null, spanOptions); + + final ISpan span = activeSpan.startChild("http.client"); + span.getSpanContext().setOrigin(TRACE_ORIGIN); final @NotNull String method = request.method().name(); span.setDescription(method + " " + request.url()); span.setData(SpanDataConvention.HTTP_METHOD_KEY, method.toUpperCase(Locale.ROOT)); @@ -75,7 +74,7 @@ public SentrySpanClientWebRequestFilter(final @NotNull IScopes scopes) { final @Nullable TracingUtils.TracingHeaders tracingHeaders = TracingUtils.traceIfAllowed( - scopes, + hub, request.url().toString(), request.headers().get(BaggageHeader.BAGGAGE_HEADER), span); @@ -112,6 +111,6 @@ private void addBreadcrumb( hint.set(SPRING_EXCHANGE_FILTER_RESPONSE, response); } - scopes.addBreadcrumb(breadcrumb, hint); + hub.addBreadcrumb(breadcrumb, hint); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java index fdfef1030c..ed91f85a29 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTracingFilter.java @@ -3,9 +3,9 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.BaggageHeader; import io.sentry.CustomSamplingContext; -import io.sentry.IScopes; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ITransaction; -import io.sentry.ScopesAdapter; import io.sentry.SentryTraceHeader; import io.sentry.SpanStatus; import io.sentry.TransactionContext; @@ -38,7 +38,7 @@ public class SentryTracingFilter extends OncePerRequestFilter { private static final String TRACE_ORIGIN = "auto.http.spring_jakarta.webmvc"; private final @NotNull TransactionNameProvider transactionNameProvider; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; /** * Creates filter that resolves transaction name using {@link SpringMvcTransactionNameProvider}. @@ -49,26 +49,25 @@ public class SentryTracingFilter extends OncePerRequestFilter { * jakarta.servlet.Filter}. */ public SentryTracingFilter() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } /** * Creates filter that resolves transaction name using transaction name provider given by * parameter. * - * @param scopes - the scopes + * @param hub - the hub * @param transactionNameProvider - transaction name provider. */ public SentryTracingFilter( - final @NotNull IScopes scopes, - final @NotNull TransactionNameProvider transactionNameProvider) { - this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); + final @NotNull IHub hub, final @NotNull TransactionNameProvider transactionNameProvider) { + this.hub = Objects.requireNonNull(hub, "hub is required"); this.transactionNameProvider = Objects.requireNonNull(transactionNameProvider, "transactionNameProvider is required"); } - public SentryTracingFilter(final @NotNull IScopes scopes) { - this(scopes, new SpringMvcTransactionNameProvider()); + public SentryTracingFilter(final @NotNull IHub hub) { + this(hub, new SpringMvcTransactionNameProvider()); } @Override @@ -77,14 +76,14 @@ protected void doFilterInternal( final @NotNull HttpServletResponse httpResponse, final @NotNull FilterChain filterChain) throws ServletException, IOException { - if (scopes.isEnabled()) { + if (hub.isEnabled()) { final @Nullable String sentryTraceHeader = httpRequest.getHeader(SentryTraceHeader.SENTRY_TRACE_HEADER); final @Nullable List baggageHeader = Collections.list(httpRequest.getHeaders(BaggageHeader.BAGGAGE_HEADER)); final @Nullable TransactionContext transactionContext = - scopes.continueTrace(sentryTraceHeader, baggageHeader); - if (scopes.getOptions().isTracingEnabled() && shouldTraceRequest(httpRequest)) { + hub.continueTrace(sentryTraceHeader, baggageHeader); + if (hub.getOptions().isTracingEnabled() && shouldTraceRequest(httpRequest)) { doFilterWithTransaction(httpRequest, httpResponse, filterChain, transactionContext); } else { filterChain.doFilter(httpRequest, httpResponse); @@ -102,6 +101,7 @@ private void doFilterWithTransaction( throws IOException, ServletException { // at this stage we are not able to get real transaction name final ITransaction transaction = startTransaction(httpRequest, transactionContext); + transaction.getSpanContext().setOrigin(TRACE_ORIGIN); try { filterChain.doFilter(httpRequest, httpResponse); @@ -130,7 +130,7 @@ private void doFilterWithTransaction( } private boolean shouldTraceRequest(final @NotNull HttpServletRequest request) { - return scopes.getOptions().isTraceOptionsRequests() + return hub.getOptions().isTraceOptionsRequests() || !HttpMethod.OPTIONS.name().equals(request.getMethod()); } @@ -143,20 +143,23 @@ private ITransaction startTransaction( final CustomSamplingContext customSamplingContext = new CustomSamplingContext(); customSamplingContext.set("request", request); - final TransactionOptions transactionOptions = new TransactionOptions(); - transactionOptions.setCustomSamplingContext(customSamplingContext); - transactionOptions.setBindToScope(true); - transactionOptions.setOrigin(TRACE_ORIGIN); - if (transactionContext != null) { transactionContext.setName(name); transactionContext.setTransactionNameSource(TransactionNameSource.URL); transactionContext.setOperation("http.server"); - return scopes.startTransaction(transactionContext, transactionOptions); + final TransactionOptions transactionOptions = new TransactionOptions(); + transactionOptions.setCustomSamplingContext(customSamplingContext); + transactionOptions.setBindToScope(true); + + return hub.startTransaction(transactionContext, transactionOptions); } - return scopes.startTransaction( + final TransactionOptions transactionOptions = new TransactionOptions(); + transactionOptions.setCustomSamplingContext(customSamplingContext); + transactionOptions.setBindToScope(true); + + return hub.startTransaction( new TransactionContext(name, TransactionNameSource.URL, "http.server"), transactionOptions); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java index 95618f76fd..5b264defa4 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/tracing/SentryTransactionAdvice.java @@ -1,10 +1,9 @@ package io.sentry.spring.jakarta.tracing; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IScopes; -import io.sentry.ISentryLifecycleToken; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ITransaction; -import io.sentry.ScopesAdapter; import io.sentry.SpanStatus; import io.sentry.TransactionContext; import io.sentry.TransactionOptions; @@ -29,14 +28,14 @@ public class SentryTransactionAdvice implements MethodInterceptor { private static final String TRACE_ORIGIN = "auto.function.spring_jakarta.advice"; - private final @NotNull IScopes scopesBeforeForking; + private final @NotNull IHub hub; public SentryTransactionAdvice() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } - public SentryTransactionAdvice(final @NotNull IScopes scopes) { - this.scopesBeforeForking = Objects.requireNonNull(scopes, "scopes are required"); + public SentryTransactionAdvice(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "hub is required"); } @SuppressWarnings("deprecation") @@ -57,7 +56,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl final TransactionNameAndSource nameAndSource = resolveTransactionName(invocation, sentryTransaction); - final boolean isTransactionActive = isTransactionActive(scopesBeforeForking); + final boolean isTransactionActive = isTransactionActive(); if (isTransactionActive) { // transaction is already active, we do not start new transaction @@ -69,27 +68,25 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } else { operation = "bean"; } - final @NotNull IScopes forkedScopes = - scopesBeforeForking.forkedScopes("SentryTransactionAdvice"); - try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) { - final TransactionOptions transactionOptions = new TransactionOptions(); - transactionOptions.setBindToScope(true); - transactionOptions.setOrigin(TRACE_ORIGIN); - final ITransaction transaction = - forkedScopes.startTransaction( - new TransactionContext(nameAndSource.name, nameAndSource.source, operation), - transactionOptions); - try { - final Object result = invocation.proceed(); - transaction.setStatus(SpanStatus.OK); - return result; - } catch (Throwable e) { - transaction.setStatus(SpanStatus.INTERNAL_ERROR); - transaction.setThrowable(e); - throw e; - } finally { - transaction.finish(); - } + hub.pushScope(); + final TransactionOptions transactionOptions = new TransactionOptions(); + transactionOptions.setBindToScope(true); + final ITransaction transaction = + hub.startTransaction( + new TransactionContext(nameAndSource.name, nameAndSource.source, operation), + transactionOptions); + transaction.getSpanContext().setOrigin(TRACE_ORIGIN); + try { + final Object result = invocation.proceed(); + transaction.setStatus(SpanStatus.OK); + return result; + } catch (Throwable e) { + transaction.setStatus(SpanStatus.INTERNAL_ERROR); + transaction.setThrowable(e); + throw e; + } finally { + transaction.finish(); + hub.popScope(); } } } @@ -108,8 +105,8 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } } - private boolean isTransactionActive(final @NotNull IScopes scopes) { - return scopes.getSpan() != null; + private boolean isTransactionActive() { + return hub.getSpan() != null; } private static class TransactionNameAndSource { diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java index 2d43b41f85..bf25aa5f49 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/AbstractSentryWebFilter.java @@ -7,10 +7,10 @@ import io.sentry.Breadcrumb; import io.sentry.CustomSamplingContext; import io.sentry.Hint; +import io.sentry.IHub; import io.sentry.IScope; -import io.sentry.IScopes; import io.sentry.ITransaction; -import io.sentry.NoOpScopes; +import io.sentry.NoOpHub; import io.sentry.Sentry; import io.sentry.SentryTraceHeader; import io.sentry.SpanStatus; @@ -34,35 +34,26 @@ @ApiStatus.Experimental public abstract class AbstractSentryWebFilter implements WebFilter { private final @NotNull SentryRequestResolver sentryRequestResolver; - public static final String SENTRY_SCOPES_KEY = "sentry-scopes"; - - /** - * @deprecated please use {@link AbstractSentryWebFilter#SENTRY_SCOPES_KEY} instead. - */ - @Deprecated public static final String SENTRY_HUB_KEY = SENTRY_SCOPES_KEY; - + public static final String SENTRY_HUB_KEY = "sentry-hub"; private static final String TRANSACTION_OP = "http.server"; - public AbstractSentryWebFilter(final @NotNull IScopes scopes) { - Objects.requireNonNull(scopes, "scopes are required"); - this.sentryRequestResolver = new SentryRequestResolver(scopes); + public AbstractSentryWebFilter(final @NotNull IHub hub) { + Objects.requireNonNull(hub, "hub is required"); + this.sentryRequestResolver = new SentryRequestResolver(hub); } protected @Nullable ITransaction maybeStartTransaction( - final @NotNull IScopes requestScopes, - final @NotNull ServerHttpRequest request, - final @NotNull String origin) { - if (requestScopes.isEnabled()) { + final @NotNull IHub requestHub, final @NotNull ServerHttpRequest request) { + if (requestHub.isEnabled()) { final @NotNull HttpHeaders headers = request.getHeaders(); final @Nullable String sentryTraceHeader = headers.getFirst(SentryTraceHeader.SENTRY_TRACE_HEADER); final @Nullable List baggageHeaders = headers.get(BaggageHeader.BAGGAGE_HEADER); final @Nullable TransactionContext transactionContext = - requestScopes.continueTrace(sentryTraceHeader, baggageHeaders); + requestHub.continueTrace(sentryTraceHeader, baggageHeaders); - if (requestScopes.getOptions().isTracingEnabled() - && shouldTraceRequest(requestScopes, request)) { - return startTransaction(requestScopes, request, transactionContext, origin); + if (requestHub.getOptions().isTracingEnabled() && shouldTraceRequest(requestHub, request)) { + return startTransaction(requestHub, request, transactionContext); } } @@ -71,18 +62,22 @@ && shouldTraceRequest(requestScopes, request)) { protected void doFinally( final @NotNull ServerWebExchange serverWebExchange, - final @NotNull IScopes requestScopes, + final @NotNull IHub requestHub, final @Nullable ITransaction transaction) { if (transaction != null) { finishTransaction(serverWebExchange, transaction); } - Sentry.setCurrentScopes(NoOpScopes.getInstance()); + if (requestHub.isEnabled()) { + requestHub.popScope(); + } + Sentry.setCurrentHub(NoOpHub.getInstance()); } protected void doFirst( - final @NotNull ServerWebExchange serverWebExchange, final @NotNull IScopes requestScopes) { - if (requestScopes.isEnabled()) { - serverWebExchange.getAttributes().put(SENTRY_SCOPES_KEY, requestScopes); + final @NotNull ServerWebExchange serverWebExchange, final @NotNull IHub requestHub) { + if (requestHub.isEnabled()) { + serverWebExchange.getAttributes().put(SENTRY_HUB_KEY, requestHub); + requestHub.pushScope(); final ServerHttpRequest request = serverWebExchange.getRequest(); final ServerHttpResponse response = serverWebExchange.getResponse(); @@ -91,8 +86,8 @@ protected void doFirst( hint.set(WEBFLUX_FILTER_RESPONSE, response); final String methodName = request.getMethod() != null ? request.getMethod().name() : "unknown"; - requestScopes.addBreadcrumb(Breadcrumb.http(request.getURI().toString(), methodName), hint); - requestScopes.configureScope( + requestHub.addBreadcrumb(Breadcrumb.http(request.getURI().toString(), methodName), hint); + requestHub.configureScope( scope -> scope.setRequest(sentryRequestResolver.resolveSentryRequest(request))); } } @@ -105,8 +100,8 @@ protected void doOnError(final @Nullable ITransaction transaction, final @NotNul } protected boolean shouldTraceRequest( - final @NotNull IScopes scopes, final @NotNull ServerHttpRequest request) { - return scopes.getOptions().isTraceOptionsRequests() + final @NotNull IHub hub, final @NotNull ServerHttpRequest request) { + return hub.getOptions().isTraceOptionsRequests() || !HttpMethod.OPTIONS.equals(request.getMethod()); } @@ -135,10 +130,9 @@ private void finishTransaction(ServerWebExchange exchange, ITransaction transact } protected @NotNull ITransaction startTransaction( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull ServerHttpRequest request, - final @Nullable TransactionContext transactionContext, - final @NotNull String origin) { + final @Nullable TransactionContext transactionContext) { final @NotNull String name = request.getMethod() + " " + request.getURI().getPath(); final @NotNull CustomSamplingContext customSamplingContext = new CustomSamplingContext(); customSamplingContext.set("request", request); @@ -146,17 +140,16 @@ private void finishTransaction(ServerWebExchange exchange, ITransaction transact final TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setCustomSamplingContext(customSamplingContext); transactionOptions.setBindToScope(true); - transactionOptions.setOrigin(origin); if (transactionContext != null) { transactionContext.setName(name); transactionContext.setTransactionNameSource(TransactionNameSource.URL); transactionContext.setOperation(TRANSACTION_OP); - return scopes.startTransaction(transactionContext, transactionOptions); + return hub.startTransaction(transactionContext, transactionOptions); } - return scopes.startTransaction( + return hub.startTransaction( new TransactionContext(name, TransactionNameSource.URL, TRANSACTION_OP), transactionOptions); } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java index 1c2bb0afcf..4af641af29 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/ReactorUtils.java @@ -1,6 +1,6 @@ package io.sentry.spring.jakarta.webflux; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.Sentry; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -12,97 +12,103 @@ public final class ReactorUtils { /** - * Writes the current Sentry {@link IScopes} to the {@link Context} and uses {@link + * Writes the current Sentry {@link IHub} to the {@link Context} and uses {@link * io.micrometer.context.ThreadLocalAccessor} to propagate it. * *

    This requires - reactor.core.publisher.Hooks#enableAutomaticContextPropagation() to be * enabled - having `io.micrometer:context-propagation:1.0.2+` (provided by Spring Boot 3.0.3+) - * having `io.projectreactor:reactor-core:3.5.3+` (provided by Spring Boot 3.0.3+) */ + @ApiStatus.Experimental public static Mono withSentry(final @NotNull Mono mono) { - final @NotNull IScopes oldScopes = Sentry.getCurrentScopes(); - final @NotNull IScopes forkedScopes = oldScopes.forkedCurrentScope("reactor.withSentry"); - return withSentryScopes(mono, forkedScopes); + final @NotNull IHub oldHub = Sentry.getCurrentHub(); + final @NotNull IHub clonedHub = oldHub.clone(); + return withSentryHub(mono, clonedHub); } /** - * Writes a new Sentry {@link IScopes} forked from the main scopes to the {@link Context} and uses + * Writes a new Sentry {@link IHub} cloned from the main hub to the {@link Context} and uses * {@link io.micrometer.context.ThreadLocalAccessor} to propagate it. * *

    This requires - reactor.core.publisher.Hooks#enableAutomaticContextPropagation() to be * enabled - having `io.micrometer:context-propagation:1.0.2+` (provided by Spring Boot 3.0.3+) - * having `io.projectreactor:reactor-core:3.5.3+` (provided by Spring Boot 3.0.3+) */ - public static Mono withSentryForkedRoots(final @NotNull Mono mono) { - final @NotNull IScopes scopes = Sentry.forkedRootScopes("reactor"); - return withSentryScopes(mono, scopes); + @ApiStatus.Experimental + public static Mono withSentryNewMainHubClone(final @NotNull Mono mono) { + final @NotNull IHub hub = Sentry.cloneMainHub(); + return withSentryHub(mono, hub); } /** - * Writes the given Sentry {@link IScopes} to the {@link Context} and uses {@link + * Writes the given Sentry {@link IHub} to the {@link Context} and uses {@link * io.micrometer.context.ThreadLocalAccessor} to propagate it. * *

    This requires - reactor.core.publisher.Hooks#enableAutomaticContextPropagation() to be * enabled - having `io.micrometer:context-propagation:1.0.2+` (provided by Spring Boot 3.0.3+) - * having `io.projectreactor:reactor-core:3.5.3+` (provided by Spring Boot 3.0.3+) */ - public static Mono withSentryScopes( - final @NotNull Mono mono, final @NotNull IScopes scopes) { + @ApiStatus.Experimental + public static Mono withSentryHub(final @NotNull Mono mono, final @NotNull IHub hub) { /** - * WARNING: Cannot set the scopes as current. It would be used by others to clone again causing - * shared scopes and thus leading to issues like unrelated breadcrumbs showing up in events. + * WARNING: Cannot set the hub as current. It would be used by others to clone again causing + * shared hubs and scopes and thus leading to issues like unrelated breadcrumbs showing up in + * events. */ - // Sentry.setCurrentScopes(forkedScopes); + // Sentry.setCurrentHub(clonedHub); return Mono.deferContextual(ctx -> mono) - .contextWrite(Context.of(SentryReactorThreadLocalAccessor.KEY, scopes)); + .contextWrite(Context.of(SentryReactorThreadLocalAccessor.KEY, hub)); } /** - * Writes the current Sentry {@link IScopes} to the {@link Context} and uses {@link + * Writes the current Sentry {@link IHub} to the {@link Context} and uses {@link * io.micrometer.context.ThreadLocalAccessor} to propagate it. * *

    This requires - reactor.core.publisher.Hooks#enableAutomaticContextPropagation() to be * enabled - having `io.micrometer:context-propagation:1.0.2+` (provided by Spring Boot 3.0.3+) - * having `io.projectreactor:reactor-core:3.5.3+` (provided by Spring Boot 3.0.3+) */ + @ApiStatus.Experimental public static Flux withSentry(final @NotNull Flux flux) { - final @NotNull IScopes oldScopes = Sentry.getCurrentScopes(); - final @NotNull IScopes forkedScopes = oldScopes.forkedCurrentScope("reactor.withSentry"); + final @NotNull IHub oldHub = Sentry.getCurrentHub(); + final @NotNull IHub clonedHub = oldHub.clone(); - return withSentryScopes(flux, forkedScopes); + return withSentryHub(flux, clonedHub); } /** - * Writes a new Sentry {@link IScopes} forked from the main scopes to the {@link Context} and uses + * Writes a new Sentry {@link IHub} cloned from the main hub to the {@link Context} and uses * {@link io.micrometer.context.ThreadLocalAccessor} to propagate it. * *

    This requires - reactor.core.publisher.Hooks#enableAutomaticContextPropagation() to be * enabled - having `io.micrometer:context-propagation:1.0.2+` (provided by Spring Boot 3.0.3+) - * having `io.projectreactor:reactor-core:3.5.3+` (provided by Spring Boot 3.0.3+) */ - public static Flux withSentryForkedRoots(final @NotNull Flux flux) { - final @NotNull IScopes scopes = Sentry.forkedRootScopes("reactor"); - return withSentryScopes(flux, scopes); + @ApiStatus.Experimental + public static Flux withSentryNewMainHubClone(final @NotNull Flux flux) { + final @NotNull IHub hub = Sentry.cloneMainHub(); + return withSentryHub(flux, hub); } /** - * Writes the given Sentry {@link IScopes} to the {@link Context} and uses {@link + * Writes the given Sentry {@link IHub} to the {@link Context} and uses {@link * io.micrometer.context.ThreadLocalAccessor} to propagate it. * *

    This requires - reactor.core.publisher.Hooks#enableAutomaticContextPropagation() to be * enabled - having `io.micrometer:context-propagation:1.0.2+` (provided by Spring Boot 3.0.3+) - * having `io.projectreactor:reactor-core:3.5.3+` (provided by Spring Boot 3.0.3+) */ - public static Flux withSentryScopes( - final @NotNull Flux flux, final @NotNull IScopes scopes) { + @ApiStatus.Experimental + public static Flux withSentryHub(final @NotNull Flux flux, final @NotNull IHub hub) { /** - * WARNING: Cannot set the scopes as current. It would be used by others to fork again causing - * shared scopes and thus leading to issues like unrelated breadcrumbs showing up in events. + * WARNING: Cannot set the hub as current. It would be used by others to clone again causing + * shared hubs and scopes and thus leading to issues like unrelated breadcrumbs showing up in + * events. */ - // Sentry.setCurrentScopes(forkedScopes); + // Sentry.setCurrentHub(clonedHub); return Flux.deferContextual(ctx -> flux) - .contextWrite(Context.of(SentryReactorThreadLocalAccessor.KEY, scopes)); + .contextWrite(Context.of(SentryReactorThreadLocalAccessor.KEY, hub)); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryReactorThreadLocalAccessor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryReactorThreadLocalAccessor.java index 9b7e51db73..c64cf3d634 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryReactorThreadLocalAccessor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryReactorThreadLocalAccessor.java @@ -1,15 +1,15 @@ package io.sentry.spring.jakarta.webflux; import io.micrometer.context.ThreadLocalAccessor; -import io.sentry.IScopes; -import io.sentry.NoOpScopes; +import io.sentry.IHub; +import io.sentry.NoOpHub; import io.sentry.Sentry; import org.jetbrains.annotations.ApiStatus; @ApiStatus.Experimental -public final class SentryReactorThreadLocalAccessor implements ThreadLocalAccessor { +public final class SentryReactorThreadLocalAccessor implements ThreadLocalAccessor { - public static final String KEY = "sentry-scopes"; + public static final String KEY = "sentry-hub"; @Override public Object key() { @@ -17,18 +17,18 @@ public Object key() { } @Override - public IScopes getValue() { - return Sentry.getCurrentScopes(); + public IHub getValue() { + return Sentry.getCurrentHub(); } @Override - public void setValue(IScopes value) { - Sentry.setCurrentScopes(value); + public void setValue(IHub value) { + Sentry.setCurrentHub(value); } @Override @SuppressWarnings("deprecation") public void reset() { - Sentry.setCurrentScopes(NoOpScopes.getInstance()); + Sentry.setCurrentHub(NoOpHub.getInstance()); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryRequestResolver.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryRequestResolver.java index d58291ade6..2f41ba93ca 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryRequestResolver.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryRequestResolver.java @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta.webflux; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.protocol.Request; import io.sentry.util.HttpUtils; import io.sentry.util.Objects; @@ -20,10 +20,10 @@ @Open @ApiStatus.Experimental public class SentryRequestResolver { - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; - public SentryRequestResolver(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + public SentryRequestResolver(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "options is required"); } public @NotNull Request resolveSentryRequest(final @NotNull ServerHttpRequest httpRequest) { @@ -36,7 +36,7 @@ public SentryRequestResolver(final @NotNull IScopes scopes) { urlDetails.applyToRequest(sentryRequest); sentryRequest.setHeaders(resolveHeadersMap(httpRequest.getHeaders())); - if (scopes.getOptions().isSendDefaultPii()) { + if (hub.getOptions().isSendDefaultPii()) { String headerName = HttpUtils.COOKIE_HEADER_NAME; sentryRequest.setCookies( toString( @@ -52,8 +52,7 @@ Map resolveHeadersMap(final HttpHeaders request) { for (Map.Entry> entry : request.entrySet()) { // do not copy personal information identifiable headers String headerName = entry.getKey(); - if (scopes.getOptions().isSendDefaultPii() - || !HttpUtils.containsSensitiveHeader(headerName)) { + if (hub.getOptions().isSendDefaultPii() || !HttpUtils.containsSensitiveHeader(headerName)) { headersMap.put( headerName, toString( diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryScheduleHook.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryScheduleHook.java index 57a74732ea..2bf27f246d 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryScheduleHook.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryScheduleHook.java @@ -1,7 +1,6 @@ package io.sentry.spring.jakarta.webflux; -import io.sentry.IScopes; -import io.sentry.ISentryLifecycleToken; +import io.sentry.IHub; import io.sentry.Sentry; import java.util.function.Function; import org.jetbrains.annotations.ApiStatus; @@ -9,17 +8,21 @@ /** * Hook meant to used with {@link reactor.core.scheduler.Schedulers#onScheduleHook(String, - * Function)} to configure Reactor to copy correct scopes into the operating thread. + * Function)} to configure Reactor to copy correct hub into the operating thread. */ @ApiStatus.Experimental public final class SentryScheduleHook implements Function { @Override public Runnable apply(final @NotNull Runnable runnable) { - final IScopes newScopes = Sentry.getCurrentScopes().forkedCurrentScope("spring.scheduleHook"); + final IHub newHub = Sentry.getCurrentHub().clone(); return () -> { - try (final @NotNull ISentryLifecycleToken ignored = newScopes.makeCurrent()) { + final IHub oldState = Sentry.getCurrentHub(); + Sentry.setCurrentHub(newHub); + try { runnable.run(); + } finally { + Sentry.setCurrentHub(oldState); } }; } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebExceptionHandler.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebExceptionHandler.java index 15c73ab625..24439cd0e9 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebExceptionHandler.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebExceptionHandler.java @@ -5,7 +5,7 @@ import static io.sentry.TypeCheckHint.WEBFLUX_EXCEPTION_HANDLER_RESPONSE; import io.sentry.Hint; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.SentryEvent; import io.sentry.SentryLevel; import io.sentry.exception.ExceptionMechanismException; @@ -27,20 +27,20 @@ @ApiStatus.Experimental public final class SentryWebExceptionHandler implements WebExceptionHandler { public static final String MECHANISM_TYPE = "Spring6WebFluxExceptionResolver"; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; - public SentryWebExceptionHandler(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + public SentryWebExceptionHandler(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "hub is required"); } @Override public @NotNull Mono handle( final @NotNull ServerWebExchange serverWebExchange, final @NotNull Throwable ex) { - final @Nullable IScopes requestScopes = - serverWebExchange.getAttributeOrDefault(SentryWebFilter.SENTRY_SCOPES_KEY, null); - final @NotNull IScopes scopesToUse = requestScopes != null ? requestScopes : scopes; + final @Nullable IHub requestHub = + serverWebExchange.getAttributeOrDefault(SentryWebFilter.SENTRY_HUB_KEY, null); + final @NotNull IHub hubToUse = requestHub != null ? requestHub : hub; - return ReactorUtils.withSentryScopes( + return ReactorUtils.withSentryHub( Mono.just(ex) .map( it -> { @@ -61,12 +61,12 @@ public SentryWebExceptionHandler(final @NotNull IScopes scopes) { WEBFLUX_EXCEPTION_HANDLER_RESPONSE, serverWebExchange.getResponse()); hint.set(WEBFLUX_EXCEPTION_HANDLER_EXCHANGE, serverWebExchange); - scopes.captureEvent(event, hint); + hub.captureEvent(event, hint); } return it; }), - scopesToUse) + hubToUse) .flatMap(it -> Mono.error(ex)); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilter.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilter.java index 0ec4b44a0e..a57a389499 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilter.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilter.java @@ -1,8 +1,8 @@ package io.sentry.spring.jakarta.webflux; import com.jakewharton.nopen.annotation.Open; +import io.sentry.IHub; import io.sentry.IScope; -import io.sentry.IScopes; import io.sentry.ITransaction; import io.sentry.Sentry; import org.jetbrains.annotations.ApiStatus; @@ -20,26 +20,28 @@ public class SentryWebFilter extends AbstractSentryWebFilter { private static final String TRACE_ORIGIN = "auto.spring_jakarta.webflux"; - public SentryWebFilter(final @NotNull IScopes scopes) { - super(scopes); + public SentryWebFilter(final @NotNull IHub hub) { + super(hub); } @Override public Mono filter( final @NotNull ServerWebExchange serverWebExchange, final @NotNull WebFilterChain webFilterChain) { - @NotNull IScopes requestScopes = Sentry.forkedRootScopes("request.webflux"); + @NotNull IHub requestHub = Sentry.cloneMainHub(); final ServerHttpRequest request = serverWebExchange.getRequest(); - final @Nullable ITransaction transaction = - maybeStartTransaction(requestScopes, request, TRACE_ORIGIN); + final @Nullable ITransaction transaction = maybeStartTransaction(requestHub, request); + if (transaction != null) { + transaction.getSpanContext().setOrigin(TRACE_ORIGIN); + } return webFilterChain .filter(serverWebExchange) - .doFinally(__ -> doFinally(serverWebExchange, requestScopes, transaction)) + .doFinally(__ -> doFinally(serverWebExchange, requestHub, transaction)) .doOnError(e -> doOnError(transaction, e)) .doFirst( () -> { - Sentry.setCurrentScopes(requestScopes); - doFirst(serverWebExchange, requestScopes); + Sentry.setCurrentHub(requestHub); + doFirst(serverWebExchange, requestHub); }); } } diff --git a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor.java b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor.java index 5408f6dbec..278c2b8e7e 100644 --- a/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor.java +++ b/sentry-spring-jakarta/src/main/java/io/sentry/spring/jakarta/webflux/SentryWebFilterWithThreadLocalAccessor.java @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta.webflux; +import io.sentry.IHub; import io.sentry.IScope; -import io.sentry.IScopes; import io.sentry.ITransaction; import io.sentry.Sentry; import org.jetbrains.annotations.ApiStatus; @@ -17,8 +17,8 @@ public final class SentryWebFilterWithThreadLocalAccessor extends AbstractSentry public static final String TRACE_ORIGIN = "auto.spring_jakarta.webflux"; - public SentryWebFilterWithThreadLocalAccessor(final @NotNull IScopes scopes) { - super(scopes); + public SentryWebFilterWithThreadLocalAccessor(final @NotNull IHub hub) { + super(hub); } @Override @@ -26,23 +26,25 @@ public Mono filter( final @NotNull ServerWebExchange serverWebExchange, final @NotNull WebFilterChain webFilterChain) { final @NotNull TransactionContainer transactionContainer = new TransactionContainer(); - return ReactorUtils.withSentryForkedRoots( + return ReactorUtils.withSentryNewMainHubClone( webFilterChain .filter(serverWebExchange) .doFinally( __ -> doFinally( serverWebExchange, - Sentry.getCurrentScopes(), + Sentry.getCurrentHub(), transactionContainer.transaction)) .doOnError(e -> doOnError(transactionContainer.transaction, e)) .doFirst( () -> { - doFirst(serverWebExchange, Sentry.getCurrentScopes()); + doFirst(serverWebExchange, Sentry.getCurrentHub()); final ITransaction transaction = - maybeStartTransaction( - Sentry.getCurrentScopes(), serverWebExchange.getRequest(), TRACE_ORIGIN); + maybeStartTransaction(Sentry.getCurrentHub(), serverWebExchange.getRequest()); transactionContainer.transaction = transaction; + if (transaction != null) { + transaction.getSpanContext().setOrigin(TRACE_ORIGIN); + } })); } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/EnableSentryTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/EnableSentryTest.kt index 37853c3101..7b51bbc1e7 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/EnableSentryTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/EnableSentryTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta import io.sentry.EventProcessor -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ITransportFactory import io.sentry.Integration import io.sentry.Sentry @@ -67,7 +67,7 @@ class EnableSentryTest { @Test fun `creates Sentry Hub`() { contextRunner.run { - assertThat(it).hasSingleBean(IScopes::class.java) + assertThat(it).hasSingleBean(IHub::class.java) } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryCheckInAdviceTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryCheckInAdviceTest.kt index e02f1cb62a..05b97ba24c 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryCheckInAdviceTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryCheckInAdviceTest.kt @@ -2,8 +2,7 @@ package io.sentry.spring.jakarta import io.sentry.CheckIn import io.sentry.CheckInStatus -import io.sentry.IScopes -import io.sentry.ISentryLifecycleToken +import io.sentry.IHub import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.protocol.SentryId @@ -55,23 +54,19 @@ class SentryCheckInAdviceTest { lateinit var sampleServiceSpringProperties: SampleServiceSpringProperties @Autowired - lateinit var scopes: IScopes - - val lifecycleToken = mock() + lateinit var hub: IHub @BeforeTest fun setup() { - reset(scopes) - whenever(scopes.options).thenReturn(SentryOptions()) - whenever(scopes.forkedScopes(any())).thenReturn(scopes) - whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) + reset(hub) + whenever(hub.options).thenReturn(SentryOptions()) } @Test fun `when method is annotated with @SentryCheckIn, every method call creates two check-ins`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleService.hello() assertEquals(1, result) assertEquals(2, checkInCaptor.allValues.size) @@ -84,18 +79,17 @@ class SentryCheckInAdviceTest { assertEquals("monitor_slug_1", doneCheckIn.monitorSlug) assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) - val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).forkedScopes(any()) - order.verify(scopes).makeCurrent() - order.verify(scopes, times(2)).captureCheckIn(any()) - order.verify(lifecycleToken).close() + val order = inOrder(hub) + order.verify(hub).pushScope() + order.verify(hub, times(2)).captureCheckIn(any()) + order.verify(hub).popScope() } @Test fun `when method is annotated with @SentryCheckIn, every method call creates two check-ins error`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) assertThrows { sampleService.oops() } @@ -109,18 +103,17 @@ class SentryCheckInAdviceTest { assertEquals("monitor_slug_1e", doneCheckIn.monitorSlug) assertEquals(CheckInStatus.ERROR.apiName(), doneCheckIn.status) - val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).forkedScopes(any()) - order.verify(scopes).makeCurrent() - order.verify(scopes, times(2)).captureCheckIn(any()) - order.verify(lifecycleToken).close() + val order = inOrder(hub) + order.verify(hub).pushScope() + order.verify(hub, times(2)).captureCheckIn(any()) + order.verify(hub).popScope() } @Test fun `when method is annotated with @SentryCheckIn and heartbeat only, every method call creates only one check-in at the end`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceHeartbeat.hello() assertEquals(1, result) assertEquals(1, checkInCaptor.allValues.size) @@ -130,18 +123,17 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).forkedScopes(any()) - order.verify(scopes).makeCurrent() - order.verify(scopes).captureCheckIn(any()) - order.verify(lifecycleToken).close() + val order = inOrder(hub) + order.verify(hub).pushScope() + order.verify(hub).captureCheckIn(any()) + order.verify(hub).popScope() } @Test fun `when method is annotated with @SentryCheckIn and heartbeat only, every method call creates only one check-in at the end with error`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) assertThrows { sampleServiceHeartbeat.oops() } @@ -152,33 +144,31 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.ERROR.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).forkedScopes(any()) - order.verify(scopes).makeCurrent() - order.verify(scopes).captureCheckIn(any()) - order.verify(lifecycleToken).close() + val order = inOrder(hub) + order.verify(hub).pushScope() + order.verify(hub).captureCheckIn(any()) + order.verify(hub).popScope() } @Test fun `when method is annotated with @SentryCheckIn but slug is missing, does not create check-in`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceNoSlug.hello() assertEquals(1, result) assertEquals(0, checkInCaptor.allValues.size) - verify(scopes, never()).forkedScopes(any()) - verify(scopes, never()).makeCurrent() - verify(scopes, never()).captureCheckIn(any()) - verify(lifecycleToken, never()).close() + verify(hub, never()).pushScope() + verify(hub, never()).captureCheckIn(any()) + verify(hub, never()).popScope() } @Test fun `when @SentryCheckIn is passed a spring property it is resolved correctly`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceSpringProperties.hello() assertEquals(1, result) assertEquals(1, checkInCaptor.allValues.size) @@ -188,18 +178,17 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).forkedScopes(any()) - order.verify(scopes).makeCurrent() - order.verify(scopes).captureCheckIn(any()) - order.verify(lifecycleToken).close() + val order = inOrder(hub) + order.verify(hub).pushScope() + order.verify(hub).captureCheckIn(any()) + order.verify(hub).popScope() } @Test fun `when @SentryCheckIn is passed a spring property that does not exist, raw value is used`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceSpringProperties.helloUnresolvedProperty() assertEquals(1, result) assertEquals(1, checkInCaptor.allValues.size) @@ -209,18 +198,17 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).forkedScopes(any()) - order.verify(scopes).makeCurrent() - order.verify(scopes).captureCheckIn(any()) - order.verify(lifecycleToken).close() + val order = inOrder(hub) + order.verify(hub).pushScope() + order.verify(hub).captureCheckIn(any()) + order.verify(hub).popScope() } @Test fun `when @SentryCheckIn is passed a spring property that causes an exception, raw value is used`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceSpringProperties.helloExceptionProperty() assertEquals(1, result) assertEquals(1, checkInCaptor.allValues.size) @@ -230,11 +218,10 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).forkedScopes(any()) - order.verify(scopes).makeCurrent() - order.verify(scopes).captureCheckIn(any()) - order.verify(lifecycleToken).close() + val order = inOrder(hub) + order.verify(hub).pushScope() + order.verify(hub).captureCheckIn(any()) + order.verify(hub).popScope() } @Configuration @@ -255,10 +242,10 @@ class SentryCheckInAdviceTest { open fun sampleServiceSpringProperties() = SampleServiceSpringProperties() @Bean - open fun scopes(): IScopes { - val scopes = mock() - Sentry.setCurrentScopes(scopes) - return scopes + open fun hub(): IHub { + val hub = mock() + Sentry.setCurrentHub(hub) + return hub } companion object { diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryExceptionResolverTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryExceptionResolverTest.kt index 431137aabd..797d5aa5ac 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryExceptionResolverTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryExceptionResolverTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryEvent import io.sentry.SentryLevel import io.sentry.exception.ExceptionMechanismException @@ -17,7 +17,7 @@ import org.mockito.kotlin.whenever import kotlin.test.Test class SentryExceptionResolverTest { - private val scopes = mock() + private val hub = mock() private val transactionNameProvider = mock() private val request = mock() @@ -26,10 +26,10 @@ class SentryExceptionResolverTest { @Test fun `when handles exception, sets wrapped exception for event`() { val eventCaptor = argumentCaptor() - whenever(scopes.captureEvent(eventCaptor.capture(), any())).thenReturn(null) + whenever(hub.captureEvent(eventCaptor.capture(), any())).thenReturn(null) val expectedCause = RuntimeException("test") - SentryExceptionResolver(scopes, transactionNameProvider, 1) + SentryExceptionResolver(hub, transactionNameProvider, 1) .resolveException(request, response, null, expectedCause) assertThat(eventCaptor.firstValue.throwable).isEqualTo(expectedCause) @@ -46,9 +46,9 @@ class SentryExceptionResolverTest { @Test fun `when handles exception, sets fatal level for event`() { val eventCaptor = argumentCaptor() - whenever(scopes.captureEvent(eventCaptor.capture(), any())).thenReturn(null) + whenever(hub.captureEvent(eventCaptor.capture(), any())).thenReturn(null) - SentryExceptionResolver(scopes, transactionNameProvider, 1) + SentryExceptionResolver(hub, transactionNameProvider, 1) .resolveException(request, response, null, RuntimeException("test")) assertThat(eventCaptor.firstValue.level).isEqualTo(SentryLevel.FATAL) @@ -59,9 +59,9 @@ class SentryExceptionResolverTest { val expectedTransactionName = "test-transaction" whenever(transactionNameProvider.provideTransactionName(any())).thenReturn(expectedTransactionName) val eventCaptor = argumentCaptor() - whenever(scopes.captureEvent(eventCaptor.capture(), any())).thenReturn(null) + whenever(hub.captureEvent(eventCaptor.capture(), any())).thenReturn(null) - SentryExceptionResolver(scopes, transactionNameProvider, 1) + SentryExceptionResolver(hub, transactionNameProvider, 1) .resolveException(request, response, null, RuntimeException("test")) assertThat(eventCaptor.firstValue.transaction).isEqualTo(expectedTransactionName) @@ -71,9 +71,9 @@ class SentryExceptionResolverTest { @Test fun `when handles exception, provides spring resolver hint`() { val hintCaptor = argumentCaptor() - whenever(scopes.captureEvent(any(), hintCaptor.capture())).thenReturn(null) + whenever(hub.captureEvent(any(), hintCaptor.capture())).thenReturn(null) - SentryExceptionResolver(scopes, transactionNameProvider, 1) + SentryExceptionResolver(hub, transactionNameProvider, 1) .resolveException(request, response, null, RuntimeException("test")) with(hintCaptor.firstValue) { @@ -86,8 +86,8 @@ class SentryExceptionResolverTest { fun `when custom create event method provided, uses it to capture event`() { val expectedEvent = SentryEvent() val eventCaptor = argumentCaptor() - whenever(scopes.captureEvent(eventCaptor.capture(), any())).thenReturn(null) - val resolver = object : SentryExceptionResolver(scopes, transactionNameProvider, 1) { + whenever(hub.captureEvent(eventCaptor.capture(), any())).thenReturn(null) + val resolver = object : SentryExceptionResolver(hub, transactionNameProvider, 1) { override fun createEvent(request: HttpServletRequest, ex: Exception) = expectedEvent } @@ -100,8 +100,8 @@ class SentryExceptionResolverTest { fun `when custom create hint method provided, uses it to capture event`() { val expectedHint = Hint() val hintCaptor = argumentCaptor() - whenever(scopes.captureEvent(any(), hintCaptor.capture())).thenReturn(null) - val resolver = object : SentryExceptionResolver(scopes, transactionNameProvider, 1) { + whenever(hub.captureEvent(any(), hintCaptor.capture())).thenReturn(null) + val resolver = object : SentryExceptionResolver(hub, transactionNameProvider, 1) { override fun createHint(request: HttpServletRequest, response: HttpServletResponse) = expectedHint } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryInitBeanPostProcessorTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryInitBeanPostProcessorTest.kt index 54b6acb0f3..4168605823 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryInitBeanPostProcessorTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryInitBeanPostProcessorTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.jakarta -import io.sentry.IScopes +import io.sentry.IHub import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.springframework.context.annotation.AnnotationConfigApplicationContext @@ -13,18 +13,18 @@ class SentryInitBeanPostProcessorTest { @Test fun closesSentryOnApplicationContextDestroy() { val ctx = AnnotationConfigApplicationContext(TestConfig::class.java) - val scopes = ctx.getBean(IScopes::class.java) + val hub = ctx.getBean(IHub::class.java) ctx.close() - verify(scopes).close() + verify(hub).close() } @Configuration open class TestConfig { @Bean(destroyMethod = "") - open fun scopes() = mock() + open fun hub() = mock() @Bean - open fun sentryInitBeanPostProcessor() = SentryInitBeanPostProcessor(scopes()) + open fun sentryInitBeanPostProcessor() = SentryInitBeanPostProcessor(hub()) } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessorTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessorTest.kt index 8faa243f83..279abce417 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessorTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryRequestHttpServletRequestProcessorTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryEvent import io.sentry.SentryOptions import io.sentry.spring.jakarta.tracing.SpringMvcTransactionNameProvider @@ -19,10 +19,10 @@ import kotlin.test.assertNotNull class SentryRequestHttpServletRequestProcessorTest { private class Fixture { - val scopes = mock() + val hub = mock() fun getSut(request: HttpServletRequest, options: SentryOptions = SentryOptions()): SentryRequestHttpServletRequestProcessor { - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) return SentryRequestHttpServletRequestProcessor(SpringMvcTransactionNameProvider(), request) } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentrySpringFilterTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentrySpringFilterTest.kt index 1c1f2b5c13..4e1bbb0ee5 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentrySpringFilterTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentrySpringFilterTest.kt @@ -1,9 +1,8 @@ package io.sentry.spring.jakarta import io.sentry.Breadcrumb +import io.sentry.IHub import io.sentry.IScope -import io.sentry.IScopes -import io.sentry.ISentryLifecycleToken import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -38,29 +37,23 @@ import kotlin.test.fail class SentrySpringFilterTest { private class Fixture { - val scopes = mock() - val scopesBeforeForking = mock() + val hub = mock() val response = MockHttpServletResponse() - val lifecycleToken = mock() val chain = mock() lateinit var scope: IScope lateinit var request: HttpServletRequest fun getSut(request: HttpServletRequest? = null, options: SentryOptions = SentryOptions()): SentrySpringFilter { scope = Scope(options) - whenever(scopesBeforeForking.options).thenReturn(options) - whenever(scopesBeforeForking.isEnabled).thenReturn(true) - whenever(scopes.options).thenReturn(options) - whenever(scopes.isEnabled).thenReturn(true) - whenever(scopesBeforeForking.forkedScopes(any())).thenReturn(scopes) - whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) + whenever(hub.options).thenReturn(options) + whenever(hub.isEnabled).thenReturn(true) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) this.request = request ?: MockHttpServletRequest().apply { this.requestURI = "http://localhost:8080/some-uri" this.method = "post" } - return SentrySpringFilter(scopesBeforeForking) + return SentrySpringFilter(hub) } } @@ -71,8 +64,7 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopesBeforeForking).forkedScopes(any()) - verify(fixture.scopes).makeCurrent() + verify(fixture.hub).pushScope() } @Test @@ -80,7 +72,7 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { it: Breadcrumb -> Assertions.assertThat(it.getData("url")).isEqualTo("http://localhost:8080/some-uri") Assertions.assertThat(it.getData("method")).isEqualTo("POST") @@ -95,7 +87,7 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.lifecycleToken).close() + verify(fixture.hub).popScope() } @Test @@ -107,7 +99,7 @@ class SentrySpringFilterTest { listener.doFilter(fixture.request, fixture.response, fixture.chain) fail() } catch (e: Exception) { - verify(fixture.lifecycleToken).close() + verify(fixture.hub).popScope() } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryTaskDecoratorTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryTaskDecoratorTest.kt index d44e64f78d..aa809259d1 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryTaskDecoratorTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryTaskDecoratorTest.kt @@ -25,35 +25,35 @@ class SentryTaskDecoratorTest { } @Test - fun `scopes is reset to its state within the thread after decoration is done`() { + fun `hub is reset to its state within the thread after decoration is done`() { Sentry.init { it.dsn = dsn } val sut = SentryTaskDecorator() - val mainScopes = Sentry.getCurrentScopes() - val threadedScopes = Sentry.getCurrentScopes().forkedCurrentScope("test") + val mainHub = Sentry.getCurrentHub() + val threadedHub = Sentry.getCurrentHub().clone() executor.submit { - Sentry.setCurrentScopes(threadedScopes) + Sentry.setCurrentHub(threadedHub) }.get() - assertEquals(mainScopes, Sentry.getCurrentScopes()) + assertEquals(mainHub, Sentry.getCurrentHub()) val callableFuture = executor.submit( sut.decorate { - assertNotEquals(mainScopes, Sentry.getCurrentScopes()) - assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) + assertNotEquals(mainHub, Sentry.getCurrentHub()) + assertNotEquals(threadedHub, Sentry.getCurrentHub()) } ) callableFuture.get() executor.submit { - assertNotEquals(mainScopes, Sentry.getCurrentScopes()) - assertEquals(threadedScopes, Sentry.getCurrentScopes()) + assertNotEquals(mainHub, Sentry.getCurrentHub()) + assertEquals(threadedHub, Sentry.getCurrentHub()) }.get() } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryUserFilterTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryUserFilterTest.kt index b30dc937e5..2f128cc4bb 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryUserFilterTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/SentryUserFilterTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.jakarta -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryOptions import io.sentry.protocol.User import jakarta.servlet.FilterChain @@ -16,7 +16,7 @@ import kotlin.test.assertNull class SentryUserFilterTest { class Fixture { - val scopes = mock() + val hub = mock() val request = MockHttpServletRequest() val response = MockHttpServletResponse() val chain = mock() @@ -25,8 +25,8 @@ class SentryUserFilterTest { val options = SentryOptions().apply { this.isSendDefaultPii = isSendDefaultPii } - whenever(scopes.options).thenReturn(options) - return SentryUserFilter(scopes, userProviders) + whenever(hub.options).thenReturn(options) + return SentryUserFilter(hub, userProviders) } } @@ -52,7 +52,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).setUser( + verify(fixture.hub).setUser( check { assertEquals(sampleUser, it) } @@ -72,7 +72,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).setUser( + verify(fixture.hub).setUser( check { assertEquals(sampleUser, it) } @@ -92,7 +92,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).setUser( + verify(fixture.hub).setUser( check { assertEquals(sampleUser, it) } @@ -118,7 +118,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).setUser( + verify(fixture.hub).setUser( check { assertEquals(mapOf("key" to "value", "new-key" to "new-value"), it.others) } @@ -140,7 +140,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).setUser( + verify(fixture.hub).setUser( check { assertEquals("192.168.0.1", it.ipAddress) } @@ -162,7 +162,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).setUser( + verify(fixture.hub).setUser( check { assertNull(it.ipAddress) } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdviceTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdviceTest.kt index 3f8371ca3d..0da5047251 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdviceTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/exception/SentryCaptureExceptionParameterAdviceTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta.exception import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.Sentry import io.sentry.exception.ExceptionMechanismException import org.junit.runner.RunWith @@ -30,18 +30,18 @@ class SentryCaptureExceptionParameterAdviceTest { lateinit var sampleService: SampleService @Autowired - lateinit var scopes: IScopes + lateinit var hub: IHub @BeforeTest fun setup() { - reset(scopes) + reset(hub) } @Test fun `captures exception passed to method annotated with @SentryCaptureException`() { val exception = RuntimeException("test exception") sampleService.methodTakingAnException(exception) - verify(scopes).captureException( + verify(hub).captureException( check { assertTrue(it is ExceptionMechanismException) assertEquals(exception, it.throwable) @@ -60,10 +60,10 @@ class SentryCaptureExceptionParameterAdviceTest { open fun sampleService() = SampleService() @Bean - open fun scopes(): IScopes { - val scopes = mock() - Sentry.setCurrentScopes(scopes) - return scopes + open fun hub(): IHub { + val hub = mock() + Sentry.setCurrentHub(hub) + return hub } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandlerTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandlerTest.kt index c2ebb95e48..3f71eaf23e 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandlerTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/graphql/SentrySpringSubscriptionHandlerTest.kt @@ -4,7 +4,7 @@ import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchPar import graphql.language.Document import graphql.language.OperationDefinition import graphql.schema.DataFetchingEnvironment -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.graphql.ExceptionReporter import io.sentry.spring.jakarta.graphql.SentrySpringSubscriptionHandler import org.junit.jupiter.api.assertThrows @@ -24,7 +24,7 @@ class SentrySpringSubscriptionHandlerTest { @Test fun `reports exception`() { val exception = IllegalStateException("some exception") - val scopes = mock() + val hub = mock() val exceptionReporter = mock() val parameters = mock() val dataFetchingEnvironment = mock() @@ -33,7 +33,7 @@ class SentrySpringSubscriptionHandlerTest { .build() whenever(dataFetchingEnvironment.document).thenReturn(document) whenever(parameters.environment).thenReturn(dataFetchingEnvironment) - val resultObject = SentrySpringSubscriptionHandler().onSubscriptionResult(Flux.error(exception), scopes, exceptionReporter, parameters) + val resultObject = SentrySpringSubscriptionHandler().onSubscriptionResult(Flux.error(exception), hub, exceptionReporter, parameters) assertThrows { (resultObject as Flux).blockFirst() } @@ -42,7 +42,7 @@ class SentrySpringSubscriptionHandlerTest { same(exception), org.mockito.kotlin.check { assertEquals(true, it.isSubscription) - assertSame(scopes, it.scopes) + assertSame(hub, it.hub) assertEquals("query testQuery\n", it.query) }, anyOrNull() @@ -53,7 +53,7 @@ class SentrySpringSubscriptionHandlerTest { fun `unwraps SubscriptionPublisherException and reports cause`() { val exception = IllegalStateException("some exception") val wrappedException = SubscriptionPublisherException(emptyList(), exception) - val scopes = mock() + val hub = mock() val exceptionReporter = mock() val parameters = mock() val dataFetchingEnvironment = mock() @@ -62,7 +62,7 @@ class SentrySpringSubscriptionHandlerTest { .build() whenever(dataFetchingEnvironment.document).thenReturn(document) whenever(parameters.environment).thenReturn(dataFetchingEnvironment) - val resultObject = SentrySpringSubscriptionHandler().onSubscriptionResult(Flux.error(wrappedException), scopes, exceptionReporter, parameters) + val resultObject = SentrySpringSubscriptionHandler().onSubscriptionResult(Flux.error(wrappedException), hub, exceptionReporter, parameters) assertThrows { (resultObject as Flux).blockFirst() } @@ -71,7 +71,7 @@ class SentrySpringSubscriptionHandlerTest { same(exception), org.mockito.kotlin.check { assertEquals(true, it.isSubscription) - assertSame(scopes, it.scopes) + assertSame(hub, it.hub) assertEquals("query testQuery\n", it.query) }, anyOrNull() diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/mvc/SentrySpringIntegrationTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/mvc/SentrySpringIntegrationTest.kt index 0b4be869d0..f472a75a65 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/mvc/SentrySpringIntegrationTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/mvc/SentrySpringIntegrationTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.jakarta.mvc -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ITransportFactory import io.sentry.Sentry import io.sentry.SentryOptions @@ -104,7 +104,7 @@ class SentrySpringIntegrationTest { lateinit var anotherService: AnotherService @Autowired - lateinit var scopes: IScopes + lateinit var hub: IHub @LocalServerPort var port: Int? = null @@ -260,7 +260,7 @@ class SentrySpringIntegrationTest { try { someService.aMethodThrowing() } catch (e: Exception) { - scopes.captureException(e) + hub.captureException(e) } verify(transport).send( checkEvent { @@ -276,7 +276,7 @@ class SentrySpringIntegrationTest { try { someService.aMethodWithInnerSpanThrowing() } catch (e: Exception) { - scopes.captureException(e) + hub.captureException(e) } verify(transport).send( checkEvent { @@ -370,20 +370,20 @@ open class App { open fun springSecuritySentryUserProvider(sentryOptions: SentryOptions) = SpringSecuritySentryUserProvider(sentryOptions) @Bean - open fun sentryUserFilter(scopes: IScopes, @Lazy sentryUserProviders: List) = FilterRegistrationBean().apply { - this.filter = SentryUserFilter(scopes, sentryUserProviders) + open fun sentryUserFilter(hub: IHub, @Lazy sentryUserProviders: List) = FilterRegistrationBean().apply { + this.filter = SentryUserFilter(hub, sentryUserProviders) this.order = Ordered.LOWEST_PRECEDENCE } @Bean - open fun sentrySpringFilter(scopes: IScopes) = FilterRegistrationBean().apply { - this.filter = SentrySpringFilter(scopes) + open fun sentrySpringFilter(hub: IHub) = FilterRegistrationBean().apply { + this.filter = SentrySpringFilter(hub) this.order = Ordered.HIGHEST_PRECEDENCE } @Bean - open fun sentryTracingFilter(scopes: IScopes) = FilterRegistrationBean().apply { - this.filter = SentryTracingFilter(scopes) + open fun sentryTracingFilter(hub: IHub) = FilterRegistrationBean().apply { + this.filter = SentryTracingFilter(hub) this.order = Ordered.HIGHEST_PRECEDENCE + 1 // must run after SentrySpringFilter } @@ -391,13 +391,13 @@ open class App { open fun sentryTaskDecorator() = SentryTaskDecorator() @Bean - open fun webClient(scopes: IScopes): WebClient { + open fun webClient(hub: IHub): WebClient { return WebClient.builder() .filter( ExchangeFilterFunctions .basicAuthentication("user", "password") ) - .filter(SentrySpanClientWebRequestFilter(scopes)).build() + .filter(SentrySpanClientWebRequestFilter(hub)).build() } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentrySpanAdviceTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentrySpanAdviceTest.kt index 8b74b08fb1..2910e7aac6 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentrySpanAdviceTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentrySpanAdviceTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.jakarta.tracing -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.Scope import io.sentry.Sentry import io.sentry.SentryOptions @@ -37,20 +37,20 @@ class SentrySpanAdviceTest { lateinit var classAnnotatedWithOperationSampleService: ClassAnnotatedWithOperationSampleService @Autowired - lateinit var scopes: IScopes + lateinit var hub: IHub @BeforeTest fun setup() { - whenever(scopes.options).thenReturn(SentryOptions()) + whenever(hub.options).thenReturn(SentryOptions()) } @Test fun `when class is annotated with @SentrySpan, every method call attaches span to existing transaction`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) scope.setTransaction(tx) - whenever(scopes.span).thenReturn(tx) + whenever(hub.span).thenReturn(tx) val result = classAnnotatedSampleService.hello() assertEquals(1, result) assertEquals(1, tx.spans.size) @@ -62,10 +62,10 @@ class SentrySpanAdviceTest { @Test fun `when class is annotated with @SentrySpan with operation set, every method call attaches span to existing transaction`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) scope.setTransaction(tx) - whenever(scopes.span).thenReturn(tx) + whenever(hub.span).thenReturn(tx) val result = classAnnotatedWithOperationSampleService.hello() assertEquals(1, result) assertEquals(1, tx.spans.size) @@ -76,10 +76,10 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan with properties set, attaches span to existing transaction`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) scope.setTransaction(tx) - whenever(scopes.span).thenReturn(tx) + whenever(hub.span).thenReturn(tx) val result = sampleService.methodWithSpanDescriptionSet() assertEquals(1, result) assertEquals(1, tx.spans.size) @@ -90,10 +90,10 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan without properties set, attaches span to existing transaction and sets Span description as className dot methodName`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) scope.setTransaction(tx) - whenever(scopes.span).thenReturn(tx) + whenever(hub.span).thenReturn(tx) val result = sampleService.methodWithoutSpanDescriptionSet() assertEquals(2, result) assertEquals(1, tx.spans.size) @@ -104,10 +104,10 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan and returns, attached span has status OK`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) scope.setTransaction(tx) - whenever(scopes.span).thenReturn(tx) + whenever(hub.span).thenReturn(tx) sampleService.methodWithSpanDescriptionSet() assertEquals(SpanStatus.OK, tx.spans.first().status) } @@ -115,10 +115,10 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan and throws exception, attached span has throwable set and INTERNAL_ERROR status`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) scope.setTransaction(tx) - whenever(scopes.span).thenReturn(tx) + whenever(hub.span).thenReturn(tx) var throwable: Throwable? = null try { sampleService.methodThrowingException() @@ -131,7 +131,7 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan and there is no active transaction, span is not created and method is executed`() { - whenever(scopes.span).thenReturn(null) + whenever(hub.span).thenReturn(null) val result = sampleService.methodWithSpanDescriptionSet() assertEquals(1, result) } @@ -151,10 +151,10 @@ class SentrySpanAdviceTest { open fun classAnnotatedWithOperationSampleService() = ClassAnnotatedWithOperationSampleService() @Bean - open fun scopes(): IScopes { - val scopes = mock() - Sentry.setCurrentScopes(scopes) - return scopes + open fun hub(): IHub { + val hub = mock() + Sentry.setCurrentHub(hub) + return hub } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTracingFilterTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTracingFilterTest.kt index 678dd238cf..0424696fad 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTracingFilterTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTracingFilterTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring.jakarta.tracing +import io.sentry.IHub import io.sentry.ILogger -import io.sentry.IScopes import io.sentry.PropagationContext import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -38,7 +38,7 @@ import kotlin.test.fail class SentryTracingFilterTest { private class Fixture { - val scopes = mock() + val hub = mock() val request = MockHttpServletRequest() val response = MockHttpServletResponse() val chain = mock() @@ -50,7 +50,7 @@ class SentryTracingFilterTest { val logger = mock() init { - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) } fun getSut(isEnabled: Boolean = true, status: Int = 200, sentryTraceHeader: String? = null, baggageHeaders: List? = null): SentryTracingFilter { @@ -61,16 +61,16 @@ class SentryTracingFilterTest { whenever(transactionNameProvider.provideTransactionSource()).thenReturn(TransactionNameSource.CUSTOM) if (sentryTraceHeader != null) { request.addHeader("sentry-trace", sentryTraceHeader) - whenever(scopes.startTransaction(any(), check { it.isBindToScope })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } + whenever(hub.startTransaction(any(), check { it.isBindToScope })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } } if (baggageHeaders != null) { request.addHeader("baggage", baggageHeaders) } response.status = status - whenever(scopes.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } - whenever(scopes.isEnabled).thenReturn(isEnabled) - whenever(scopes.continueTrace(any(), any())).thenAnswer { TransactionContext.fromPropagationContext(PropagationContext.fromHeaders(logger, it.arguments[0] as String?, it.arguments[1] as List?)) } - return SentryTracingFilter(scopes, transactionNameProvider) + whenever(hub.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } + whenever(hub.isEnabled).thenReturn(isEnabled) + whenever(hub.continueTrace(any(), any())).thenAnswer { TransactionContext.fromPropagationContext(PropagationContext.fromHeaders(logger, it.arguments[0] as String?, it.arguments[1] as List?)) } + return SentryTracingFilter(hub, transactionNameProvider) } } @@ -82,7 +82,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("POST /product/12", it.name) assertEquals(TransactionNameSource.URL, it.transactionNameSource) @@ -92,15 +92,15 @@ class SentryTracingFilterTest { assertNotNull(it.customSamplingContext?.get("request")) assertTrue(it.customSamplingContext?.get("request") is HttpServletRequest) assertTrue(it.isBindToScope) - assertThat(it.origin).isEqualTo("auto.http.spring_jakarta.webmvc") } ) verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.transaction).isEqualTo("POST /product/{id}") assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.OK) assertThat(it.contexts.trace!!.operation).isEqualTo("http.server") + assertThat(it.contexts.trace!!.origin).isEqualTo("auto.http.spring_jakarta.webmvc") }, anyOrNull(), anyOrNull(), @@ -114,7 +114,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.INTERNAL_ERROR) }, @@ -130,7 +130,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.status).isNull() }, @@ -146,7 +146,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -163,7 +163,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isEqualTo(parentSpanId) }, @@ -174,15 +174,15 @@ class SentryTracingFilterTest { } @Test - fun `when scopes is disabled, components are not invoked`() { + fun `when hub is disabled, components are not invoked`() { val filter = fixture.getSut(isEnabled = false) filter.doFilter(fixture.request, fixture.response, fixture.chain) verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.scopes).isEnabled - verifyNoMoreInteractions(fixture.scopes) + verify(fixture.hub).isEnabled + verifyNoMoreInteractions(fixture.hub) verify(fixture.transactionNameProvider, never()).provideTransactionName(any()) } @@ -196,7 +196,7 @@ class SentryTracingFilterTest { fail("filter is expected to rethrow exception") } catch (_: Exception) { } - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.status).isEqualTo(SpanStatus.INTERNAL_ERROR) }, @@ -216,10 +216,10 @@ class SentryTracingFilterTest { verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.scopes).isEnabled - verify(fixture.scopes).continueTrace(anyOrNull(), anyOrNull()) - verify(fixture.scopes, times(2)).options - verifyNoMoreInteractions(fixture.scopes) + verify(fixture.hub).isEnabled + verify(fixture.hub).continueTrace(anyOrNull(), anyOrNull()) + verify(fixture.hub, times(2)).options + verifyNoMoreInteractions(fixture.hub) verify(fixture.transactionNameProvider, never()).provideTransactionName(any()) } @@ -233,7 +233,7 @@ class SentryTracingFilterTest { verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -253,7 +253,7 @@ class SentryTracingFilterTest { verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -275,9 +275,9 @@ class SentryTracingFilterTest { verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.scopes).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) + verify(fixture.hub).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) - verify(fixture.scopes, never()).captureTransaction( + verify(fixture.hub, never()).captureTransaction( anyOrNull(), anyOrNull(), anyOrNull(), diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt index 545a5e1390..5d2863310f 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/tracing/SentryTransactionAdviceTest.kt @@ -1,7 +1,6 @@ package io.sentry.spring.jakarta.tracing -import io.sentry.IScopes -import io.sentry.ISentryLifecycleToken +import io.sentry.IHub import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -45,39 +44,28 @@ class SentryTransactionAdviceTest { lateinit var classAnnotatedWithOperationSampleService: ClassAnnotatedWithOperationSampleService @Autowired - lateinit var scopes: IScopes - - val lifecycleToken = mock() + lateinit var hub: IHub @BeforeTest fun setup() { - reset(scopes) - whenever( - scopes.startTransaction( - any(), - check { - assertTrue(it.isBindToScope) - assertThat(it.origin).isEqualTo("auto.function.spring_jakarta.advice") - } - ) - ).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } - whenever(scopes.options).thenReturn( + reset(hub) + whenever(hub.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } + whenever(hub.options).thenReturn( SentryOptions().apply { dsn = "https://key@sentry.io/proj" } ) - whenever(scopes.forkedScopes(any())).thenReturn(scopes) - whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) } @Test fun `creates transaction around method annotated with @SentryTransaction`() { sampleService.methodWithTransactionNameSet() - verify(scopes).captureTransaction( + verify(hub).captureTransaction( check { assertThat(it.transaction).isEqualTo("customName") assertThat(it.contexts.trace!!.operation).isEqualTo("bean") assertThat(it.status).isEqualTo(SpanStatus.OK) + assertThat(it.contexts.trace!!.origin).isEqualTo("auto.function.spring_jakarta.advice") }, anyOrNull(), anyOrNull(), @@ -88,7 +76,7 @@ class SentryTransactionAdviceTest { @Test fun `when method annotated with @SentryTransaction throws exception, sets error status on transaction`() { assertThrows { sampleService.methodThrowingException() } - verify(scopes).captureTransaction( + verify(hub).captureTransaction( check { assertThat(it.status).isEqualTo(SpanStatus.INTERNAL_ERROR) }, @@ -101,7 +89,7 @@ class SentryTransactionAdviceTest { @Test fun `when @SentryTransaction has no name set, sets transaction name as className dot methodName`() { sampleService.methodWithoutTransactionNameSet() - verify(scopes).captureTransaction( + verify(hub).captureTransaction( check { assertThat(it.transaction).isEqualTo("SampleService.methodWithoutTransactionNameSet") assertThat(it.contexts.trace!!.operation).isEqualTo("op") @@ -114,18 +102,18 @@ class SentryTransactionAdviceTest { @Test fun `when transaction is already active, does not start new transaction`() { - whenever(scopes.options).thenReturn(SentryOptions()) - whenever(scopes.span).then { SentryTracer(TransactionContext("aTransaction", "op"), scopes) } + whenever(hub.options).thenReturn(SentryOptions()) + whenever(hub.span).then { SentryTracer(TransactionContext("aTransaction", "op"), hub) } sampleService.methodWithTransactionNameSet() - verify(scopes, times(0)).captureTransaction(any(), any()) + verify(hub, times(0)).captureTransaction(any(), any()) } @Test fun `creates transaction around method in class annotated with @SentryTransaction`() { classAnnotatedSampleService.hello() - verify(scopes).captureTransaction( + verify(hub).captureTransaction( check { assertThat(it.transaction).isEqualTo("ClassAnnotatedSampleService.hello") assertThat(it.contexts.trace!!.operation).isEqualTo("op") @@ -139,7 +127,7 @@ class SentryTransactionAdviceTest { @Test fun `creates transaction with operation set around method in class annotated with @SentryTransaction`() { classAnnotatedWithOperationSampleService.hello() - verify(scopes).captureTransaction( + verify(hub).captureTransaction( check { assertThat(it.transaction).isEqualTo("ClassAnnotatedWithOperationSampleService.hello") assertThat(it.contexts.trace!!.operation).isEqualTo("my-op") @@ -153,14 +141,13 @@ class SentryTransactionAdviceTest { @Test fun `pushes the scope when advice starts`() { classAnnotatedSampleService.hello() - verify(scopes).forkedScopes(any()) - verify(scopes).makeCurrent() + verify(hub).pushScope() } @Test fun `pops the scope when advice finishes`() { classAnnotatedSampleService.hello() - verify(lifecycleToken).close() + verify(hub).popScope() } @Configuration @@ -178,10 +165,10 @@ class SentryTransactionAdviceTest { open fun classAnnotatedWithOperationSampleService() = ClassAnnotatedWithOperationSampleService() @Bean - open fun scopes(): IScopes { - val scopes = mock() - Sentry.setCurrentScopes(scopes) - return scopes + open fun hub(): IHub { + val hub = mock() + Sentry.setCurrentHub(hub) + return hub } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/ReactorUtilsTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/ReactorUtilsTest.kt index f3bd5d2653..ad335333ed 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/ReactorUtilsTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/ReactorUtilsTest.kt @@ -1,9 +1,8 @@ package io.sentry.spring.jakarta.webflux -import io.sentry.IScopes -import io.sentry.NoOpScopes +import io.sentry.IHub +import io.sentry.NoOpHub import io.sentry.Sentry -import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @@ -27,92 +26,92 @@ class ReactorUtilsTest { @AfterTest fun teardown() { - Sentry.setCurrentScopes(NoOpScopes.getInstance()) + Sentry.setCurrentHub(NoOpHub.getInstance()) } @Test - fun `propagates scopes inside mono`() { - val scopesToUse = mock() - var scopesInside: IScopes? = null - val mono = ReactorUtils.withSentryScopes( + fun `propagates hub inside mono`() { + val hubToUse = mock() + var hubInside: IHub? = null + val mono = ReactorUtils.withSentryHub( Mono.just("hello") .publishOn(Schedulers.boundedElastic()) .map { it -> - scopesInside = Sentry.getCurrentScopes() + hubInside = Sentry.getCurrentHub() it }, - scopesToUse + hubToUse ) assertEquals("hello", mono.block()) - assertSame(scopesToUse, scopesInside) + assertSame(hubToUse, hubInside) } @Test - fun `propagates scopes inside flux`() { - val scopesToUse = mock() - var scopesInside: IScopes? = null - val flux = ReactorUtils.withSentryScopes( + fun `propagates hub inside flux`() { + val hubToUse = mock() + var hubInside: IHub? = null + val flux = ReactorUtils.withSentryHub( Flux.just("hello") .publishOn(Schedulers.boundedElastic()) .map { it -> - scopesInside = Sentry.getCurrentScopes() + hubInside = Sentry.getCurrentHub() it }, - scopesToUse + hubToUse ) assertEquals("hello", flux.blockFirst()) - assertSame(scopesToUse, scopesInside) + assertSame(hubToUse, hubInside) } @Test - fun `without reactive utils scopes is not propagated to mono`() { - val scopesToUse = mock() - var scopesInside: IScopes? = null + fun `without reactive utils hub is not propagated to mono`() { + val hubToUse = mock() + var hubInside: IHub? = null val mono = Mono.just("hello") .publishOn(Schedulers.boundedElastic()) .map { it -> - scopesInside = Sentry.getCurrentScopes() + hubInside = Sentry.getCurrentHub() it } assertEquals("hello", mono.block()) - assertNotSame(scopesToUse, scopesInside) + assertNotSame(hubToUse, hubInside) } @Test - fun `without reactive utils scopes is not propagated to flux`() { - val scopesToUse = mock() - var scopesInside: IScopes? = null + fun `without reactive utils hub is not propagated to flux`() { + val hubToUse = mock() + var hubInside: IHub? = null val flux = Flux.just("hello") .publishOn(Schedulers.boundedElastic()) .map { it -> - scopesInside = Sentry.getCurrentScopes() + hubInside = Sentry.getCurrentHub() it } assertEquals("hello", flux.blockFirst()) - assertNotSame(scopesToUse, scopesInside) + assertNotSame(hubToUse, hubInside) } @Test - fun `clones scopes for mono`() { - val mockScopes = mock() - whenever(mockScopes.forkedCurrentScope(any())).thenReturn(mock()) - Sentry.setCurrentScopes(mockScopes) + fun `clones hub for mono`() { + val mockHub = mock() + whenever(mockHub.clone()).thenReturn(mock()) + Sentry.setCurrentHub(mockHub) ReactorUtils.withSentry(Mono.just("hello")).block() - verify(mockScopes).forkedCurrentScope(any()) + verify(mockHub).clone() } @Test - fun `clones scopes for flux`() { - val mockScopes = mock() - whenever(mockScopes.forkedCurrentScope(any())).thenReturn(mock()) - Sentry.setCurrentScopes(mockScopes) + fun `clones hub for flux`() { + val mockHub = mock() + whenever(mockHub.clone()).thenReturn(mock()) + Sentry.setCurrentHub(mockHub) ReactorUtils.withSentry(Flux.just("hello")).blockFirst() - verify(mockScopes).forkedCurrentScope(any()) + verify(mockHub).clone() } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryScheduleHookTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryScheduleHookTest.kt index 4b540da1aa..1eb06d0afe 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryScheduleHookTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryScheduleHookTest.kt @@ -26,35 +26,35 @@ class SentryScheduleHookTest { } @Test - fun `scopes is reset to its state within the thread after hook is done`() { + fun `hub is reset to its state within the thread after hook is done`() { Sentry.init { it.dsn = dsn } val sut = SentryScheduleHook() - val mainScopes = Sentry.getCurrentScopes() - val threadedScopes = Sentry.getCurrentScopes().forkedCurrentScope("test") + val mainHub = Sentry.getCurrentHub() + val threadedHub = Sentry.getCurrentHub().clone() executor.submit { - Sentry.setCurrentScopes(threadedScopes) + Sentry.setCurrentHub(threadedHub) }.get() - assertEquals(mainScopes, Sentry.getCurrentScopes()) + assertEquals(mainHub, Sentry.getCurrentHub()) val callableFuture = executor.submit( sut.apply { - assertNotEquals(mainScopes, Sentry.getCurrentScopes()) - assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) + assertNotEquals(mainHub, Sentry.getCurrentHub()) + assertNotEquals(threadedHub, Sentry.getCurrentHub()) } ) callableFuture.get() executor.submit { - assertNotEquals(mainScopes, Sentry.getCurrentScopes()) - assertEquals(threadedScopes, Sentry.getCurrentScopes()) + assertNotEquals(mainHub, Sentry.getCurrentHub()) + assertEquals(threadedHub, Sentry.getCurrentHub()) }.get() } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt index 2b18b38651..eac0b9c60f 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebFluxTracingFilterTest.kt @@ -2,8 +2,8 @@ package io.sentry.spring.jakarta.webflux import io.sentry.Breadcrumb import io.sentry.Hint +import io.sentry.IHub import io.sentry.ILogger -import io.sentry.IScopes import io.sentry.PropagationContext import io.sentry.ScopeCallback import io.sentry.Sentry @@ -17,7 +17,7 @@ import io.sentry.TransactionOptions import io.sentry.protocol.SentryId import io.sentry.protocol.SentryTransaction import io.sentry.protocol.TransactionNameSource -import io.sentry.spring.jakarta.webflux.AbstractSentryWebFilter.SENTRY_SCOPES_KEY +import io.sentry.spring.jakarta.webflux.AbstractSentryWebFilter.SENTRY_HUB_KEY import org.assertj.core.api.Assertions.assertThat import org.mockito.Mockito import org.mockito.kotlin.any @@ -47,7 +47,7 @@ import kotlin.test.fail class SentryWebFluxTracingFilterTest { private class Fixture { - val scopes = mock() + val hub = mock() lateinit var request: MockServerHttpRequest lateinit var exchange: MockServerWebExchange val chain = mock() @@ -58,46 +58,45 @@ class SentryWebFluxTracingFilterTest { val logger = mock() init { - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) } fun getSut(isEnabled: Boolean = true, status: HttpStatus = HttpStatus.OK, sentryTraceHeader: String? = null, baggageHeaders: List? = null, method: HttpMethod = HttpMethod.POST): SentryWebFilter { var requestBuilder = MockServerHttpRequest.method(method, "/product/{id}", 12) if (sentryTraceHeader != null) { requestBuilder = requestBuilder.header("sentry-trace", sentryTraceHeader) - whenever(scopes.startTransaction(any(), check { it.isBindToScope })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } + whenever(hub.startTransaction(any(), check { it.isBindToScope })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } } if (baggageHeaders != null) { requestBuilder = requestBuilder.header("baggage", *baggageHeaders.toTypedArray()) } request = requestBuilder.build() exchange = MockServerWebExchange.builder(request).build() - exchange.attributes.put(SENTRY_SCOPES_KEY, scopes) + exchange.attributes.put(SENTRY_HUB_KEY, hub) exchange.attributes.put(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, PathPatternParser().parse("/product/{id}")) exchange.response.statusCode = status - whenever(scopes.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } - whenever(scopes.isEnabled).thenReturn(isEnabled) + whenever(hub.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } + whenever(hub.isEnabled).thenReturn(isEnabled) whenever(chain.filter(any())).thenReturn(Mono.create { s -> s.success() }) - whenever(scopes.continueTrace(anyOrNull(), anyOrNull())).thenAnswer { TransactionContext.fromPropagationContext(PropagationContext.fromHeaders(logger, it.arguments[0] as String?, it.arguments[1] as List?)) } - return SentryWebFilter(scopes) + whenever(hub.continueTrace(anyOrNull(), anyOrNull())).thenAnswer { TransactionContext.fromPropagationContext(PropagationContext.fromHeaders(logger, it.arguments[0] as String?, it.arguments[1] as List?)) } + return SentryWebFilter(hub) } } private val fixture = Fixture() - fun withMockScopes(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { - it.`when` { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes) - it.`when` { Sentry.forkedRootScopes(any()) }.thenReturn(fixture.scopes) + fun withMockHub(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { + it.`when` { Sentry.cloneMainHub() }.thenReturn(fixture.hub) closure.invoke() } @Test fun `creates transaction around the request`() { val filter = fixture.getSut() - withMockScopes { + withMockHub { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("POST /product/12", it.name) assertEquals(TransactionNameSource.URL, it.transactionNameSource) @@ -107,15 +106,15 @@ class SentryWebFluxTracingFilterTest { assertNotNull(it.customSamplingContext?.get("request")) assertTrue(it.customSamplingContext?.get("request") is ServerHttpRequest) assertTrue(it.isBindToScope) - assertThat(it.origin).isEqualTo("auto.spring_jakarta.webflux") } ) verify(fixture.chain).filter(fixture.exchange) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.transaction).isEqualTo("POST /product/{id}") assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.OK) assertThat(it.contexts.trace!!.operation).isEqualTo("http.server") + assertThat(it.contexts.trace!!.origin).isEqualTo("auto.spring_jakarta.webflux") assertThat(it.contexts.response!!.statusCode).isEqualTo(200) }, anyOrNull(), @@ -129,10 +128,10 @@ class SentryWebFluxTracingFilterTest { fun `sets correct span status based on the response status`() { val filter = fixture.getSut(status = HttpStatus.INTERNAL_SERVER_ERROR) - withMockScopes { + withMockHub { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.INTERNAL_ERROR) assertThat(it.contexts.response!!.statusCode).isEqualTo(500) @@ -148,10 +147,10 @@ class SentryWebFluxTracingFilterTest { fun `does not set span status for response status that dont match predefined span statuses`() { val filter = fixture.getSut(status = HttpStatus.FOUND) - withMockScopes { + withMockHub { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.status).isNull() }, @@ -166,10 +165,10 @@ class SentryWebFluxTracingFilterTest { fun `when sentry trace is not present, transaction does not have parentSpanId set`() { val filter = fixture.getSut() - withMockScopes { + withMockHub { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -185,10 +184,10 @@ class SentryWebFluxTracingFilterTest { val parentSpanId = SpanId() val filter = fixture.getSut(sentryTraceHeader = "${SentryId()}-$parentSpanId-1") - withMockScopes { + withMockHub { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isEqualTo(parentSpanId) }, @@ -200,16 +199,16 @@ class SentryWebFluxTracingFilterTest { } @Test - fun `when scopes is disabled, components are not invoked`() { + fun `when hub is disabled, components are not invoked`() { val filter = fixture.getSut(isEnabled = false) - withMockScopes { + withMockHub { filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.scopes, times(2)).isEnabled - verifyNoMoreInteractions(fixture.scopes) + verify(fixture.hub, times(3)).isEnabled + verifyNoMoreInteractions(fixture.hub) } } @@ -217,7 +216,7 @@ class SentryWebFluxTracingFilterTest { fun `sets status to internal server error when chain throws exception`() { val filter = fixture.getSut() - withMockScopes { + withMockHub { whenever(fixture.chain.filter(any())).thenReturn(Mono.error(RuntimeException("error"))) try { @@ -225,7 +224,7 @@ class SentryWebFluxTracingFilterTest { fail("filter is expected to rethrow exception") } catch (_: Exception) { } - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.status).isEqualTo(SpanStatus.INTERNAL_ERROR) }, @@ -240,19 +239,21 @@ class SentryWebFluxTracingFilterTest { fun `does not track OPTIONS request with traceOptionsRequests=false`() { val filter = fixture.getSut(method = HttpMethod.OPTIONS) - withMockScopes { + withMockHub { fixture.options.isTraceOptionsRequests = false filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.scopes, times(2)).isEnabled - verify(fixture.scopes, times(2)).options - verify(fixture.scopes).continueTrace(anyOrNull(), anyOrNull()) - verify(fixture.scopes).addBreadcrumb(any(), any()) - verify(fixture.scopes).configureScope(any()) - verifyNoMoreInteractions(fixture.scopes) + verify(fixture.hub, times(3)).isEnabled + verify(fixture.hub, times(2)).options + verify(fixture.hub).continueTrace(anyOrNull(), anyOrNull()) + verify(fixture.hub).pushScope() + verify(fixture.hub).addBreadcrumb(any(), any()) + verify(fixture.hub).configureScope(any()) + verify(fixture.hub).popScope() + verifyNoMoreInteractions(fixture.hub) } } @@ -260,14 +261,14 @@ class SentryWebFluxTracingFilterTest { fun `tracks OPTIONS request with traceOptionsRequests=true`() { val filter = fixture.getSut(method = HttpMethod.OPTIONS) - withMockScopes { + withMockHub { fixture.options.isTraceOptionsRequests = true filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -282,14 +283,14 @@ class SentryWebFluxTracingFilterTest { fun `tracks POST request with traceOptionsRequests=false`() { val filter = fixture.getSut(method = HttpMethod.POST) - withMockScopes { + withMockHub { fixture.options.isTraceOptionsRequests = false filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -308,19 +309,19 @@ class SentryWebFluxTracingFilterTest { fixture.options.enableTracing = false val filter = fixture.getSut(sentryTraceHeader = sentryTraceHeaderString, baggageHeaders = baggageHeaderStrings) - withMockScopes { + withMockHub { filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.scopes, never()).captureTransaction( + verify(fixture.hub, never()).captureTransaction( anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull() ) - verify(fixture.scopes).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) + verify(fixture.hub).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) } } } diff --git a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebfluxIntegrationTest.kt b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebfluxIntegrationTest.kt index 9033028dfc..2a74356415 100644 --- a/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebfluxIntegrationTest.kt +++ b/sentry-spring-jakarta/src/test/kotlin/io/sentry/spring/jakarta/webflux/SentryWebfluxIntegrationTest.kt @@ -1,8 +1,8 @@ package io.sentry.spring.jakarta.webflux -import io.sentry.IScopes +import io.sentry.HubAdapter +import io.sentry.IHub import io.sentry.ITransportFactory -import io.sentry.ScopesAdapter import io.sentry.Sentry import io.sentry.checkEvent import io.sentry.checkTransaction @@ -95,7 +95,7 @@ class SentryWebfluxIntegrationTest { checkEvent { event -> assertEquals("GET /throws", event.transaction) assertNotNull(event.exceptions) { - val ex = it.last() + val ex = it.first() assertEquals("something went wrong", ex.value) assertNotNull(ex.mechanism) { assertThat(it.isHandled).isFalse() @@ -160,13 +160,13 @@ open class App { open fun mockTransport() = transport @Bean - open fun scopes() = ScopesAdapter.getInstance() + open fun hub() = HubAdapter.getInstance() @Bean - open fun sentryFilter(scopes: IScopes) = SentryWebFilter(scopes) + open fun sentryFilter(hub: IHub) = SentryWebFilter(hub) @Bean - open fun sentryWebExceptionHandler(scopes: IScopes) = SentryWebExceptionHandler(scopes) + open fun sentryWebExceptionHandler(hub: IHub) = SentryWebExceptionHandler(hub) @Bean open fun sentryScheduleHookRegistrar() = ApplicationRunner { diff --git a/sentry-spring/api/sentry-spring.api b/sentry-spring/api/sentry-spring.api index 9d31cdd62a..f0d792724d 100644 --- a/sentry-spring/api/sentry-spring.api +++ b/sentry-spring/api/sentry-spring.api @@ -5,7 +5,6 @@ public final class io/sentry/spring/BuildConfig { public final class io/sentry/spring/ContextTagsEventProcessor : io/sentry/EventProcessor { public fun (Lio/sentry/SentryOptions;)V - public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } @@ -23,7 +22,7 @@ public final class io/sentry/spring/HttpServletRequestSentryUserProvider : io/se public class io/sentry/spring/SentryExceptionResolver : org/springframework/core/Ordered, org/springframework/web/servlet/HandlerExceptionResolver { public static final field MECHANISM_TYPE Ljava/lang/String; - public fun (Lio/sentry/IScopes;Lio/sentry/spring/tracing/TransactionNameProvider;I)V + public fun (Lio/sentry/IHub;Lio/sentry/spring/tracing/TransactionNameProvider;I)V protected fun createEvent (Ljavax/servlet/http/HttpServletRequest;Ljava/lang/Exception;)Lio/sentry/SentryEvent; protected fun createHint (Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)Lio/sentry/Hint; public fun getOrder ()I @@ -44,19 +43,18 @@ public class io/sentry/spring/SentryInitBeanPostProcessor : org/springframework/ public class io/sentry/spring/SentryRequestHttpServletRequestProcessor : io/sentry/EventProcessor { public fun (Lio/sentry/spring/tracing/TransactionNameProvider;Ljavax/servlet/http/HttpServletRequest;)V - public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } public class io/sentry/spring/SentryRequestResolver { - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun resolveSentryRequest (Ljavax/servlet/http/HttpServletRequest;)Lio/sentry/protocol/Request; } public class io/sentry/spring/SentrySpringFilter : org/springframework/web/filter/OncePerRequestFilter { public fun ()V - public fun (Lio/sentry/IScopes;)V - public fun (Lio/sentry/IScopes;Lio/sentry/spring/SentryRequestResolver;Lio/sentry/spring/tracing/TransactionNameProvider;)V + public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IHub;Lio/sentry/spring/SentryRequestResolver;Lio/sentry/spring/tracing/TransactionNameProvider;)V protected fun doFilterInternal (Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;Ljavax/servlet/FilterChain;)V } @@ -71,7 +69,7 @@ public final class io/sentry/spring/SentryTaskDecorator : org/springframework/co } public class io/sentry/spring/SentryUserFilter : org/springframework/web/filter/OncePerRequestFilter { - public fun (Lio/sentry/IScopes;Ljava/util/List;)V + public fun (Lio/sentry/IHub;Ljava/util/List;)V protected fun doFilterInternal (Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;Ljavax/servlet/FilterChain;)V public fun getSentryUserProviders ()Ljava/util/List; } @@ -98,7 +96,7 @@ public abstract interface annotation class io/sentry/spring/checkin/SentryCheckI public class io/sentry/spring/checkin/SentryCheckInAdvice : org/aopalliance/intercept/MethodInterceptor, org/springframework/context/EmbeddedValueResolverAware { public fun ()V - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun invoke (Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object; public fun setEmbeddedValueResolver (Lorg/springframework/util/StringValueResolver;)V } @@ -129,7 +127,7 @@ public abstract interface annotation class io/sentry/spring/exception/SentryCapt public class io/sentry/spring/exception/SentryCaptureExceptionParameterAdvice : org/aopalliance/intercept/MethodInterceptor { public fun ()V - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun invoke (Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object; } @@ -171,7 +169,7 @@ public final class io/sentry/spring/graphql/SentryDataFetcherExceptionResolverAd public final class io/sentry/spring/graphql/SentryDgsSubscriptionHandler : io/sentry/graphql/SentrySubscriptionHandler { public fun ()V - public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IScopes;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; + public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IHub;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; } public final class io/sentry/spring/graphql/SentryGraphqlBeanPostProcessor : org/springframework/beans/factory/config/BeanPostProcessor, org/springframework/core/PriorityOrdered { @@ -190,7 +188,7 @@ public class io/sentry/spring/graphql/SentryGraphqlConfiguration { public final class io/sentry/spring/graphql/SentrySpringSubscriptionHandler : io/sentry/graphql/SentrySubscriptionHandler { public fun ()V - public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IScopes;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; + public fun onSubscriptionResult (Ljava/lang/Object;Lio/sentry/IHub;Lio/sentry/graphql/ExceptionReporter;Lgraphql/execution/instrumentation/parameters/InstrumentationFieldFetchParameters;)Ljava/lang/Object; } public class io/sentry/spring/tracing/SentryAdviceConfiguration { @@ -209,17 +207,17 @@ public abstract interface annotation class io/sentry/spring/tracing/SentrySpan : public class io/sentry/spring/tracing/SentrySpanAdvice : org/aopalliance/intercept/MethodInterceptor { public fun ()V - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun invoke (Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object; } public class io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor : org/springframework/http/client/ClientHttpRequestInterceptor { - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun intercept (Lorg/springframework/http/HttpRequest;[BLorg/springframework/http/client/ClientHttpRequestExecution;)Lorg/springframework/http/client/ClientHttpResponse; } public class io/sentry/spring/tracing/SentrySpanClientWebRequestFilter : org/springframework/web/reactive/function/client/ExchangeFilterFunction { - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun filter (Lorg/springframework/web/reactive/function/client/ClientRequest;Lorg/springframework/web/reactive/function/client/ExchangeFunction;)Lreactor/core/publisher/Mono; } @@ -234,8 +232,8 @@ public class io/sentry/spring/tracing/SentryTracingConfiguration { public class io/sentry/spring/tracing/SentryTracingFilter : org/springframework/web/filter/OncePerRequestFilter { public fun ()V - public fun (Lio/sentry/IScopes;)V - public fun (Lio/sentry/IScopes;Lio/sentry/spring/tracing/TransactionNameProvider;)V + public fun (Lio/sentry/IHub;)V + public fun (Lio/sentry/IHub;Lio/sentry/spring/tracing/TransactionNameProvider;)V protected fun doFilterInternal (Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;Ljavax/servlet/FilterChain;)V } @@ -247,7 +245,7 @@ public abstract interface annotation class io/sentry/spring/tracing/SentryTransa public class io/sentry/spring/tracing/SentryTransactionAdvice : org/aopalliance/intercept/MethodInterceptor { public fun ()V - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun invoke (Lorg/aopalliance/intercept/MethodInvocation;)Ljava/lang/Object; } @@ -274,7 +272,7 @@ public abstract interface class io/sentry/spring/tracing/TransactionNameProvider } public class io/sentry/spring/webflux/SentryRequestResolver { - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun resolveSentryRequest (Lorg/springframework/http/server/reactive/ServerHttpRequest;)Lio/sentry/protocol/Request; } @@ -286,14 +284,13 @@ public final class io/sentry/spring/webflux/SentryScheduleHook : java/util/funct public final class io/sentry/spring/webflux/SentryWebExceptionHandler : org/springframework/web/server/WebExceptionHandler { public static final field MECHANISM_TYPE Ljava/lang/String; - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun handle (Lorg/springframework/web/server/ServerWebExchange;Ljava/lang/Throwable;)Lreactor/core/publisher/Mono; } public final class io/sentry/spring/webflux/SentryWebFilter : org/springframework/web/server/WebFilter { public static final field SENTRY_HUB_KEY Ljava/lang/String; - public static final field SENTRY_SCOPES_KEY Ljava/lang/String; - public fun (Lio/sentry/IScopes;)V + public fun (Lio/sentry/IHub;)V public fun filter (Lorg/springframework/web/server/ServerWebExchange;Lorg/springframework/web/server/WebFilterChain;)Lreactor/core/publisher/Mono; } diff --git a/sentry-spring/src/main/java/io/sentry/spring/ContextTagsEventProcessor.java b/sentry-spring/src/main/java/io/sentry/spring/ContextTagsEventProcessor.java index 41ff04d0c4..79330bdf51 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/ContextTagsEventProcessor.java +++ b/sentry-spring/src/main/java/io/sentry/spring/ContextTagsEventProcessor.java @@ -38,9 +38,4 @@ public ContextTagsEventProcessor(final @NotNull SentryOptions options) { } return event; } - - @Override - public @Nullable Long getOrder() { - return 14000L; - } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/EnableSentry.java b/sentry-spring/src/main/java/io/sentry/spring/EnableSentry.java index 055afd3484..efb9ce529b 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/EnableSentry.java +++ b/sentry-spring/src/main/java/io/sentry/spring/EnableSentry.java @@ -12,7 +12,7 @@ * *

      *
    • creates bean of type {@link io.sentry.SentryOptions} - *
    • registers {@link io.sentry.IScopes} for sending Sentry events + *
    • registers {@link io.sentry.IHub} for sending Sentry events *
    • registers {@link SentryExceptionResolver} to send Sentry event for any uncaught exception * in Spring MVC flow. *
    diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryExceptionResolver.java b/sentry-spring/src/main/java/io/sentry/spring/SentryExceptionResolver.java index ba0586eade..fc9da87933 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryExceptionResolver.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryExceptionResolver.java @@ -5,7 +5,7 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.Hint; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.SentryEvent; import io.sentry.SentryLevel; import io.sentry.exception.ExceptionMechanismException; @@ -29,15 +29,15 @@ public class SentryExceptionResolver implements HandlerExceptionResolver, Ordered { public static final String MECHANISM_TYPE = "Spring5ExceptionResolver"; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private final @NotNull TransactionNameProvider transactionNameProvider; private final int order; public SentryExceptionResolver( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull TransactionNameProvider transactionNameProvider, final int order) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + this.hub = Objects.requireNonNull(hub, "hub is required"); this.transactionNameProvider = Objects.requireNonNull(transactionNameProvider, "transactionNameProvider is required"); this.order = order; @@ -53,7 +53,7 @@ public SentryExceptionResolver( final SentryEvent event = createEvent(request, ex); final Hint hint = createHint(request, response); - scopes.captureEvent(event, hint); + hub.captureEvent(event, hint); // null = run other HandlerExceptionResolvers to actually handle the exception return null; diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryHubRegistrar.java b/sentry-spring/src/main/java/io/sentry/spring/SentryHubRegistrar.java index 195a88e277..e597d175b6 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryHubRegistrar.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryHubRegistrar.java @@ -1,7 +1,7 @@ package io.sentry.spring; import com.jakewharton.nopen.annotation.Open; -import io.sentry.ScopesAdapter; +import io.sentry.HubAdapter; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryOptions; import io.sentry.protocol.SdkVersion; @@ -60,7 +60,7 @@ private void registerSentryOptions( private void registerSentryHubBean(final @NotNull BeanDefinitionRegistry registry) { final BeanDefinitionBuilder builder = - BeanDefinitionBuilder.genericBeanDefinition(ScopesAdapter.class); + BeanDefinitionBuilder.genericBeanDefinition(HubAdapter.class); builder.setInitMethodName("getInstance"); registry.registerBeanDefinition("sentryHub", builder.getBeanDefinition()); diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryInitBeanPostProcessor.java b/sentry-spring/src/main/java/io/sentry/spring/SentryInitBeanPostProcessor.java index ca431aae14..9e455dfb04 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryInitBeanPostProcessor.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryInitBeanPostProcessor.java @@ -2,10 +2,10 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.EventProcessor; -import io.sentry.IScopes; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ITransportFactory; import io.sentry.Integration; -import io.sentry.ScopesAdapter; import io.sentry.Sentry; import io.sentry.SentryOptions; import io.sentry.SentryOptions.TracesSamplerCallback; @@ -27,15 +27,15 @@ public class SentryInitBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware, DisposableBean { private @Nullable ApplicationContext applicationContext; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; public SentryInitBeanPostProcessor() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } - SentryInitBeanPostProcessor(final @NotNull IScopes scopes) { - Objects.requireNonNull(scopes, "scopes are required"); - this.scopes = scopes; + SentryInitBeanPostProcessor(final @NotNull IHub hub) { + Objects.requireNonNull(hub, "hub is required"); + this.hub = hub; } @Override @@ -86,6 +86,6 @@ public void setApplicationContext(final @NotNull ApplicationContext applicationC @Override public void destroy() { - scopes.close(); + hub.close(); } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryRequestHttpServletRequestProcessor.java b/sentry-spring/src/main/java/io/sentry/spring/SentryRequestHttpServletRequestProcessor.java index 426571ba6e..38c1067cab 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryRequestHttpServletRequestProcessor.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryRequestHttpServletRequestProcessor.java @@ -8,7 +8,6 @@ import io.sentry.util.Objects; import javax.servlet.http.HttpServletRequest; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** Attaches transaction name from the HTTP request to {@link SentryEvent}. */ @Open @@ -31,9 +30,4 @@ public SentryRequestHttpServletRequestProcessor( } return event; } - - @Override - public @Nullable Long getOrder() { - return 5000L; - } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryRequestResolver.java b/sentry-spring/src/main/java/io/sentry/spring/SentryRequestResolver.java index 2d9e2996f7..442644fcac 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryRequestResolver.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryRequestResolver.java @@ -1,7 +1,7 @@ package io.sentry.spring; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.SentryLevel; import io.sentry.protocol.Request; import io.sentry.util.HttpUtils; @@ -20,11 +20,11 @@ @Open public class SentryRequestResolver { - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private volatile @Nullable List extraSecurityCookies; - public SentryRequestResolver(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + public SentryRequestResolver(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "options is required"); } // httpRequest.getRequestURL() returns StringBuffer which is considered an obsolete class. @@ -40,7 +40,7 @@ public SentryRequestResolver(final @NotNull IScopes scopes) { extractSecurityCookieNamesOrUseCached(httpRequest); sentryRequest.setHeaders(resolveHeadersMap(httpRequest, additionalSecurityCookieNames)); - if (scopes.getOptions().isSendDefaultPii()) { + if (hub.getOptions().isSendDefaultPii()) { String cookieName = HttpUtils.COOKIE_HEADER_NAME; final @Nullable List filteredHeaders = HttpUtils.filterOutSecurityCookiesFromHeader( @@ -57,8 +57,7 @@ Map resolveHeadersMap( final Map headersMap = new HashMap<>(); for (String headerName : Collections.list(request.getHeaderNames())) { // do not copy personal information identifiable headers - if (scopes.getOptions().isSendDefaultPii() - || !HttpUtils.containsSensitiveHeader(headerName)) { + if (hub.getOptions().isSendDefaultPii() || !HttpUtils.containsSensitiveHeader(headerName)) { final @Nullable List filteredHeaders = HttpUtils.filterOutSecurityCookiesFromHeader( request.getHeaders(headerName), headerName, additionalSecurityCookieNames); @@ -95,8 +94,7 @@ private List extractSecurityCookieNames(final @NotNull HttpServletReques } } } catch (Throwable t) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log(SentryLevel.WARNING, "Failed to extract session cookie name from request.", t); } diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java b/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java index d450e1451a..8fd8180941 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentrySpringFilter.java @@ -8,9 +8,8 @@ import io.sentry.Breadcrumb; import io.sentry.EventProcessor; import io.sentry.Hint; -import io.sentry.IScopes; -import io.sentry.ISentryLifecycleToken; -import io.sentry.ScopesAdapter; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.SentryEvent; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -24,33 +23,32 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.springframework.http.MediaType; import org.springframework.util.MimeType; import org.springframework.web.filter.OncePerRequestFilter; @Open public class SentrySpringFilter extends OncePerRequestFilter { - private final @NotNull IScopes scopesBeforeForking; + private final @NotNull IHub hub; private final @NotNull SentryRequestResolver requestResolver; private final @NotNull TransactionNameProvider transactionNameProvider; public SentrySpringFilter( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull SentryRequestResolver requestResolver, final @NotNull TransactionNameProvider transactionNameProvider) { - this.scopesBeforeForking = Objects.requireNonNull(scopes, "scopes are required"); + this.hub = Objects.requireNonNull(hub, "hub is required"); this.requestResolver = Objects.requireNonNull(requestResolver, "requestResolver is required"); this.transactionNameProvider = Objects.requireNonNull(transactionNameProvider, "transactionNameProvider is required"); } - public SentrySpringFilter(final @NotNull IScopes scopes) { - this(scopes, new SentryRequestResolver(scopes), new SpringMvcTransactionNameProvider()); + public SentrySpringFilter(final @NotNull IHub hub) { + this(hub, new SentryRequestResolver(hub), new SpringMvcTransactionNameProvider()); } public SentrySpringFilter() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } @Override @@ -59,30 +57,29 @@ protected void doFilterInternal( final @NotNull HttpServletResponse response, final @NotNull FilterChain filterChain) throws ServletException, IOException { - if (scopesBeforeForking.isEnabled()) { + if (hub.isEnabled()) { // request may qualify for caching request body, if so resolve cached request - final HttpServletRequest request = - resolveHttpServletRequest(scopesBeforeForking, servletRequest); - final @NotNull IScopes forkedScopes = scopesBeforeForking.forkedScopes("SentrySpringFilter"); - try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) { + final HttpServletRequest request = resolveHttpServletRequest(servletRequest); + hub.pushScope(); + try { final Hint hint = new Hint(); hint.set(SPRING_REQUEST_FILTER_REQUEST, servletRequest); hint.set(SPRING_REQUEST_FILTER_RESPONSE, response); - forkedScopes.addBreadcrumb( - Breadcrumb.http(request.getRequestURI(), request.getMethod()), hint); - configureScope(forkedScopes, request); + hub.addBreadcrumb(Breadcrumb.http(request.getRequestURI(), request.getMethod()), hint); + configureScope(request); filterChain.doFilter(request, response); + } finally { + hub.popScope(); } } else { filterChain.doFilter(servletRequest, response); } } - private void configureScope( - final @NotNull IScopes scopes, final @NotNull HttpServletRequest request) { + private void configureScope(HttpServletRequest request) { try { - scopes.configureScope( + hub.configureScope( scope -> { // set basic request information on the scope scope.setRequest(requestResolver.resolveSentryRequest(request)); @@ -95,26 +92,24 @@ private void configureScope( // request processing if (request instanceof CachedBodyHttpServletRequest) { scope.addEventProcessor( - new RequestBodyExtractingEventProcessor(request, scopes.getOptions())); + new RequestBodyExtractingEventProcessor(request, hub.getOptions())); } }); } catch (Throwable e) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log(SentryLevel.ERROR, "Failed to set scope for HTTP request", e); } } private @NotNull HttpServletRequest resolveHttpServletRequest( - final @NotNull IScopes scopes, final @NotNull HttpServletRequest request) { - if (scopes.getOptions().isSendDefaultPii() - && qualifiesForCaching(request, scopes.getOptions().getMaxRequestBodySize())) { + final @NotNull HttpServletRequest request) { + if (hub.getOptions().isSendDefaultPii() + && qualifiesForCaching(request, hub.getOptions().getMaxRequestBodySize())) { try { return new CachedBodyHttpServletRequest(request); } catch (IOException e) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -159,10 +154,5 @@ public RequestBodyExtractingEventProcessor( } return event; } - - @Override - public @Nullable Long getOrder() { - return 3000L; - } } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java b/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java index 3b0960cb44..cb4a700696 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryTaskDecorator.java @@ -1,7 +1,6 @@ package io.sentry.spring; -import io.sentry.IScopes; -import io.sentry.ISentryLifecycleToken; +import io.sentry.IHub; import io.sentry.Sentry; import java.util.concurrent.Callable; import org.jetbrains.annotations.NotNull; @@ -9,19 +8,22 @@ import org.springframework.scheduling.annotation.Async; /** - * Forks scopes for the thread running a {@link Runnable} given by parameter. Used to propagate the - * current {@link IScopes} on the thread executing async task - like MVC controller methods + * Sets a current hub on a thread running a {@link Runnable} given by parameter. Used to propagate + * the current {@link IHub} on the thread executing async task - like MVC controller methods * returning a {@link Callable} or Spring beans methods annotated with {@link Async}. */ public final class SentryTaskDecorator implements TaskDecorator { @Override public @NotNull Runnable decorate(final @NotNull Runnable runnable) { - final @NotNull IScopes forkedScopes = - Sentry.getCurrentScopes().forkedScopes("SentryTaskDecorator"); + final IHub newHub = Sentry.getCurrentHub().clone(); return () -> { - try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) { + final IHub oldState = Sentry.getCurrentHub(); + Sentry.setCurrentHub(newHub); + try { runnable.run(); + } finally { + Sentry.setCurrentHub(oldState); } }; } diff --git a/sentry-spring/src/main/java/io/sentry/spring/SentryUserFilter.java b/sentry-spring/src/main/java/io/sentry/spring/SentryUserFilter.java index e0b4e9c1ba..55c5826d40 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/SentryUserFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/SentryUserFilter.java @@ -1,8 +1,8 @@ package io.sentry.spring; import com.jakewharton.nopen.annotation.Open; +import io.sentry.IHub; import io.sentry.IScope; -import io.sentry.IScopes; import io.sentry.IpAddressUtils; import io.sentry.protocol.User; import io.sentry.util.Objects; @@ -26,12 +26,12 @@ */ @Open public class SentryUserFilter extends OncePerRequestFilter { - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private final @NotNull List sentryUserProviders; public SentryUserFilter( - final @NotNull IScopes scopes, final @NotNull List sentryUserProviders) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + final @NotNull IHub hub, final @NotNull List sentryUserProviders) { + this.hub = Objects.requireNonNull(hub, "hub is required"); this.sentryUserProviders = Objects.requireNonNull(sentryUserProviders, "sentryUserProviders list is required"); } @@ -46,13 +46,13 @@ protected void doFilterInternal( for (final SentryUserProvider provider : sentryUserProviders) { apply(user, provider.provideUser()); } - if (scopes.getOptions().isSendDefaultPii()) { + if (hub.getOptions().isSendDefaultPii()) { if (IpAddressUtils.isDefault(user.getIpAddress())) { // unset {{auto}} as it would set the server's ip address as a user ip address user.setIpAddress(null); } } - scopes.setUser(user); + hub.setUser(user); chain.doFilter(request, response); } diff --git a/sentry-spring/src/main/java/io/sentry/spring/checkin/SentryCheckInAdvice.java b/sentry-spring/src/main/java/io/sentry/spring/checkin/SentryCheckInAdvice.java index 58e7e9430e..c29ad44900 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/checkin/SentryCheckInAdvice.java +++ b/sentry-spring/src/main/java/io/sentry/spring/checkin/SentryCheckInAdvice.java @@ -4,9 +4,8 @@ import io.sentry.CheckIn; import io.sentry.CheckInStatus; import io.sentry.DateUtils; -import io.sentry.IScopes; -import io.sentry.ISentryLifecycleToken; -import io.sentry.ScopesAdapter; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.SentryLevel; import io.sentry.protocol.SentryId; import io.sentry.util.Objects; @@ -31,16 +30,16 @@ @ApiStatus.Experimental @Open public class SentryCheckInAdvice implements MethodInterceptor, EmbeddedValueResolverAware { - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private @Nullable StringValueResolver resolver; public SentryCheckInAdvice() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } - public SentryCheckInAdvice(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + public SentryCheckInAdvice(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "hub is required"); } @Override @@ -70,8 +69,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl // Sentry should alert the user about missed checkins in this case since the monitor slug // won't match // what is configured in Sentry. - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -81,8 +79,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } if (ObjectUtils.isEmpty(monitorSlug)) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -90,28 +87,27 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl return invocation.proceed(); } - try (final @NotNull ISentryLifecycleToken ignored = - scopes.forkedScopes("SentryCheckInAdvice").makeCurrent()) { - TracingUtils.startNewTrace(scopes); + hub.pushScope(); + TracingUtils.startNewTrace(hub); - @Nullable SentryId checkInId = null; - final long startTime = System.currentTimeMillis(); - boolean didError = false; + @Nullable SentryId checkInId = null; + final long startTime = System.currentTimeMillis(); + boolean didError = false; - try { - if (!isHeartbeatOnly) { - checkInId = scopes.captureCheckIn(new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS)); - } - return invocation.proceed(); - } catch (Throwable e) { - didError = true; - throw e; - } finally { - final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK; - CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); - checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); - scopes.captureCheckIn(checkIn); + try { + if (!isHeartbeatOnly) { + checkInId = hub.captureCheckIn(new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS)); } + return invocation.proceed(); + } catch (Throwable e) { + didError = true; + throw e; + } finally { + final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK; + CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); + checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); + hub.captureCheckIn(checkIn); + hub.popScope(); } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/exception/SentryCaptureExceptionParameterAdvice.java b/sentry-spring/src/main/java/io/sentry/spring/exception/SentryCaptureExceptionParameterAdvice.java index 119f39ba6c..bababbce77 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/exception/SentryCaptureExceptionParameterAdvice.java +++ b/sentry-spring/src/main/java/io/sentry/spring/exception/SentryCaptureExceptionParameterAdvice.java @@ -1,8 +1,8 @@ package io.sentry.spring.exception; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IScopes; -import io.sentry.ScopesAdapter; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.exception.ExceptionMechanismException; import io.sentry.protocol.Mechanism; import io.sentry.util.Objects; @@ -22,14 +22,14 @@ @Open public class SentryCaptureExceptionParameterAdvice implements MethodInterceptor { private static final String MECHANISM_TYPE = "SentrySpring5CaptureExceptionParameterAdvice"; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; public SentryCaptureExceptionParameterAdvice() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } - public SentryCaptureExceptionParameterAdvice(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + public SentryCaptureExceptionParameterAdvice(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "hub is required"); } @Override @@ -58,6 +58,6 @@ private void captureException(final @NotNull Throwable throwable) { mechanism.setHandled(true); final Throwable mechanismException = new ExceptionMechanismException(mechanism, throwable, Thread.currentThread()); - scopes.captureException(mechanismException); + hub.captureException(mechanismException); } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/graphql/SentryBatchLoaderRegistry.java b/sentry-spring/src/main/java/io/sentry/spring/graphql/SentryBatchLoaderRegistry.java index f1d8717598..62a8669f89 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/graphql/SentryBatchLoaderRegistry.java +++ b/sentry-spring/src/main/java/io/sentry/spring/graphql/SentryBatchLoaderRegistry.java @@ -1,11 +1,11 @@ package io.sentry.spring.graphql; -import static io.sentry.graphql.SentryInstrumentation.SENTRY_SCOPES_CONTEXT_KEY; +import static io.sentry.graphql.SentryInstrumentation.SENTRY_HUB_CONTEXT_KEY; import graphql.GraphQLContext; import io.sentry.Breadcrumb; -import io.sentry.IScopes; -import io.sentry.NoOpScopes; +import io.sentry.IHub; +import io.sentry.NoOpHub; import java.util.List; import java.util.Map; import java.util.Set; @@ -89,7 +89,7 @@ public BatchLoaderRegistry.RegistrationSpec withOptions(DataLoaderOptions public void registerBatchLoader(BiFunction, BatchLoaderEnvironment, Flux> loader) { delegate.registerBatchLoader( (keys, batchLoaderEnvironment) -> { - scopesFromContext(batchLoaderEnvironment) + hubFromContext(batchLoaderEnvironment) .addBreadcrumb(Breadcrumb.graphqlDataLoader(keys, keyType, valueType, name)); return loader.apply(keys, batchLoaderEnvironment); }); @@ -100,20 +100,20 @@ public void registerMappedBatchLoader( BiFunction, BatchLoaderEnvironment, Mono>> loader) { delegate.registerMappedBatchLoader( (keys, batchLoaderEnvironment) -> { - scopesFromContext(batchLoaderEnvironment) + hubFromContext(batchLoaderEnvironment) .addBreadcrumb(Breadcrumb.graphqlDataLoader(keys, keyType, valueType, name)); return loader.apply(keys, batchLoaderEnvironment); }); } - private @NotNull IScopes scopesFromContext(final @NotNull BatchLoaderEnvironment environment) { + private @NotNull IHub hubFromContext(final @NotNull BatchLoaderEnvironment environment) { Object context = environment.getContext(); if (context instanceof GraphQLContext) { GraphQLContext graphqlContext = (GraphQLContext) context; - return graphqlContext.getOrDefault(SENTRY_SCOPES_CONTEXT_KEY, NoOpScopes.getInstance()); + return graphqlContext.getOrDefault(SENTRY_HUB_CONTEXT_KEY, NoOpHub.getInstance()); } - return NoOpScopes.getInstance(); + return NoOpHub.getInstance(); } } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/graphql/SentryDgsSubscriptionHandler.java b/sentry-spring/src/main/java/io/sentry/spring/graphql/SentryDgsSubscriptionHandler.java index eef5fdae69..fb4e09e889 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/graphql/SentryDgsSubscriptionHandler.java +++ b/sentry-spring/src/main/java/io/sentry/spring/graphql/SentryDgsSubscriptionHandler.java @@ -1,7 +1,7 @@ package io.sentry.spring.graphql; import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.graphql.ExceptionReporter; import io.sentry.graphql.SentrySubscriptionHandler; @@ -17,7 +17,7 @@ public SentryDgsSubscriptionHandler() { @Override public @NotNull Object onSubscriptionResult( final @NotNull Object result, - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull ExceptionReporter exceptionReporter, final @NotNull InstrumentationFieldFetchParameters parameters) { if (result instanceof Flux) { @@ -25,7 +25,7 @@ public SentryDgsSubscriptionHandler() { return flux.doOnError( throwable -> { final @NotNull ExceptionReporter.ExceptionDetails exceptionDetails = - new ExceptionReporter.ExceptionDetails(scopes, parameters.getEnvironment(), true); + new ExceptionReporter.ExceptionDetails(hub, parameters.getEnvironment(), true); exceptionReporter.captureThrowable(throwable, exceptionDetails, null); }); } diff --git a/sentry-spring/src/main/java/io/sentry/spring/graphql/SentrySpringSubscriptionHandler.java b/sentry-spring/src/main/java/io/sentry/spring/graphql/SentrySpringSubscriptionHandler.java index b3f0b9830e..a7809eb230 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/graphql/SentrySpringSubscriptionHandler.java +++ b/sentry-spring/src/main/java/io/sentry/spring/graphql/SentrySpringSubscriptionHandler.java @@ -1,7 +1,7 @@ package io.sentry.spring.graphql; import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.graphql.ExceptionReporter; import io.sentry.graphql.SentrySubscriptionHandler; import org.jetbrains.annotations.NotNull; @@ -13,7 +13,7 @@ public final class SentrySpringSubscriptionHandler implements SentrySubscription @Override public @NotNull Object onSubscriptionResult( final @NotNull Object result, - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull ExceptionReporter exceptionReporter, final @NotNull InstrumentationFieldFetchParameters parameters) { if (result instanceof Flux) { @@ -21,7 +21,7 @@ public final class SentrySpringSubscriptionHandler implements SentrySubscription return flux.doOnError( throwable -> { final @NotNull ExceptionReporter.ExceptionDetails exceptionDetails = - new ExceptionReporter.ExceptionDetails(scopes, parameters.getEnvironment(), true); + new ExceptionReporter.ExceptionDetails(hub, parameters.getEnvironment(), true); if (throwable instanceof SubscriptionPublisherException && throwable.getCause() != null) { exceptionReporter.captureThrowable(throwable.getCause(), exceptionDetails, null); diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanAdvice.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanAdvice.java index af57e999f9..96c4275836 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanAdvice.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanAdvice.java @@ -1,10 +1,9 @@ package io.sentry.spring.tracing; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IScopes; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ISpan; -import io.sentry.ScopesAdapter; -import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; import java.lang.reflect.Method; @@ -23,20 +22,20 @@ @Open public class SentrySpanAdvice implements MethodInterceptor { private static final String TRACE_ORIGIN = "auto.function.spring.advice"; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; public SentrySpanAdvice() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } - public SentrySpanAdvice(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + public SentrySpanAdvice(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "hub is required"); } @SuppressWarnings("deprecation") @Override public Object invoke(final @NotNull MethodInvocation invocation) throws Throwable { - final ISpan activeSpan = scopes.getSpan(); + final ISpan activeSpan = hub.getSpan(); if (activeSpan == null || activeSpan.isNoOp()) { // there is no active transaction, we do not start new span @@ -52,9 +51,8 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl mostSpecificMethod.getDeclaringClass(), SentrySpan.class); } final String operation = resolveSpanOperation(targetClass, mostSpecificMethod, sentrySpan); - final @NotNull SpanOptions spanOptions = new SpanOptions(); - spanOptions.setOrigin(TRACE_ORIGIN); - final ISpan span = activeSpan.startChild(operation, null, spanOptions); + final ISpan span = activeSpan.startChild(operation); + span.getSpanContext().setOrigin(TRACE_ORIGIN); if (sentrySpan != null && !StringUtils.isEmpty(sentrySpan.description())) { span.setDescription(sentrySpan.description()); } diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java index 91ca625d41..4067c69c8c 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientHttpRequestInterceptor.java @@ -8,10 +8,9 @@ import io.sentry.BaggageHeader; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.ISpan; import io.sentry.SpanDataConvention; -import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; import io.sentry.util.TracingUtils; @@ -28,10 +27,10 @@ @Open public class SentrySpanClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { private static final String TRACE_ORIGIN = "auto.http.spring.resttemplate"; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; - public SentrySpanClientHttpRequestInterceptor(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + public SentrySpanClientHttpRequestInterceptor(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "hub is required"); } @Override @@ -43,14 +42,14 @@ public SentrySpanClientHttpRequestInterceptor(final @NotNull IScopes scopes) { Integer responseStatusCode = null; ClientHttpResponse response = null; try { - final ISpan activeSpan = scopes.getSpan(); + final ISpan activeSpan = hub.getSpan(); if (activeSpan == null) { maybeAddTracingHeaders(request, null); return execution.execute(request, body); } - final @NotNull SpanOptions spanOptions = new SpanOptions(); - spanOptions.setOrigin(TRACE_ORIGIN); - final ISpan span = activeSpan.startChild("http.client", null, spanOptions); + + final ISpan span = activeSpan.startChild("http.client"); + span.getSpanContext().setOrigin(TRACE_ORIGIN); final String methodName = request.getMethod() != null ? request.getMethod().name() : "unknown"; final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(request.getURI().toString()); @@ -84,7 +83,7 @@ private void maybeAddTracingHeaders( final @NotNull HttpRequest request, final @Nullable ISpan span) { final @Nullable TracingUtils.TracingHeaders tracingHeaders = TracingUtils.traceIfAllowed( - scopes, + hub, request.getURI().toString(), request.getHeaders().get(BaggageHeader.BAGGAGE_HEADER), span); @@ -121,6 +120,6 @@ private void addBreadcrumb( hint.set(SPRING_REQUEST_INTERCEPTOR_RESPONSE, response); } - scopes.addBreadcrumb(breadcrumb, hint); + hub.addBreadcrumb(breadcrumb, hint); } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java index d401041544..00ad91bbb0 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentrySpanClientWebRequestFilter.java @@ -7,10 +7,9 @@ import io.sentry.BaggageHeader; import io.sentry.Breadcrumb; import io.sentry.Hint; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.ISpan; import io.sentry.SpanDataConvention; -import io.sentry.SpanOptions; import io.sentry.SpanStatus; import io.sentry.util.Objects; import io.sentry.util.TracingUtils; @@ -27,23 +26,23 @@ @Open public class SentrySpanClientWebRequestFilter implements ExchangeFilterFunction { private static final String TRACE_ORIGIN = "auto.http.spring.webclient"; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; - public SentrySpanClientWebRequestFilter(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + public SentrySpanClientWebRequestFilter(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "hub is required"); } @Override public @NotNull Mono filter( final @NotNull ClientRequest request, final @NotNull ExchangeFunction next) { - final ISpan activeSpan = scopes.getSpan(); + final ISpan activeSpan = hub.getSpan(); if (activeSpan == null) { addBreadcrumb(request, null); return next.exchange(maybeAddHeaders(request, null)); } - final @NotNull SpanOptions spanOptions = new SpanOptions(); - spanOptions.setOrigin(TRACE_ORIGIN); - final ISpan span = activeSpan.startChild("http.client", null, spanOptions); + + final ISpan span = activeSpan.startChild("http.client"); + span.getSpanContext().setOrigin(TRACE_ORIGIN); final @NotNull UrlUtils.UrlDetails urlDetails = UrlUtils.parse(request.url().toString()); final @NotNull String method = request.method().name(); span.setDescription(method + " " + urlDetails.getUrlOrFallback()); @@ -77,7 +76,7 @@ private ClientRequest maybeAddHeaders( final @Nullable TracingUtils.TracingHeaders tracingHeaders = TracingUtils.traceIfAllowed( - scopes, + hub, request.url().toString(), request.headers().get(BaggageHeader.BAGGAGE_HEADER), span); @@ -114,6 +113,6 @@ private void addBreadcrumb( hint.set(SPRING_EXCHANGE_FILTER_RESPONSE, response); } - scopes.addBreadcrumb(breadcrumb, hint); + hub.addBreadcrumb(breadcrumb, hint); } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java index 1e5cffc568..5af329f600 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTracingFilter.java @@ -3,9 +3,9 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.BaggageHeader; import io.sentry.CustomSamplingContext; -import io.sentry.IScopes; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ITransaction; -import io.sentry.ScopesAdapter; import io.sentry.SentryTraceHeader; import io.sentry.SpanStatus; import io.sentry.TransactionContext; @@ -35,7 +35,7 @@ public class SentryTracingFilter extends OncePerRequestFilter { private static final String TRACE_ORIGIN = "auto.http.spring.webmvc"; private final @NotNull TransactionNameProvider transactionNameProvider; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; /** * Creates filter that resolves transaction name using {@link SpringMvcTransactionNameProvider}. @@ -46,26 +46,25 @@ public class SentryTracingFilter extends OncePerRequestFilter { * javax.servlet.Filter}. */ public SentryTracingFilter() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } /** * Creates filter that resolves transaction name using transaction name provider given by * parameter. * - * @param scopes - the scopes + * @param hub - the hub * @param transactionNameProvider - transaction name provider. */ public SentryTracingFilter( - final @NotNull IScopes scopes, - final @NotNull TransactionNameProvider transactionNameProvider) { - this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); + final @NotNull IHub hub, final @NotNull TransactionNameProvider transactionNameProvider) { + this.hub = Objects.requireNonNull(hub, "hub is required"); this.transactionNameProvider = Objects.requireNonNull(transactionNameProvider, "transactionNameProvider is required"); } - public SentryTracingFilter(final @NotNull IScopes scopes) { - this(scopes, new SpringMvcTransactionNameProvider()); + public SentryTracingFilter(final @NotNull IHub hub) { + this(hub, new SpringMvcTransactionNameProvider()); } @Override @@ -75,15 +74,15 @@ protected void doFilterInternal( final @NotNull FilterChain filterChain) throws ServletException, IOException { - if (scopes.isEnabled()) { + if (hub.isEnabled()) { final @Nullable String sentryTraceHeader = httpRequest.getHeader(SentryTraceHeader.SENTRY_TRACE_HEADER); final @Nullable List baggageHeader = Collections.list(httpRequest.getHeaders(BaggageHeader.BAGGAGE_HEADER)); final @Nullable TransactionContext transactionContext = - scopes.continueTrace(sentryTraceHeader, baggageHeader); + hub.continueTrace(sentryTraceHeader, baggageHeader); - if (scopes.getOptions().isTracingEnabled() && shouldTraceRequest(httpRequest)) { + if (hub.getOptions().isTracingEnabled() && shouldTraceRequest(httpRequest)) { doFilterWithTransaction(httpRequest, httpResponse, filterChain, transactionContext); } else { filterChain.doFilter(httpRequest, httpResponse); @@ -101,6 +100,7 @@ private void doFilterWithTransaction( throws IOException, ServletException { // at this stage we are not able to get real transaction name final ITransaction transaction = startTransaction(httpRequest, transactionContext); + transaction.getSpanContext().setOrigin(TRACE_ORIGIN); try { filterChain.doFilter(httpRequest, httpResponse); @@ -129,7 +129,7 @@ private void doFilterWithTransaction( } private boolean shouldTraceRequest(final @NotNull HttpServletRequest request) { - return scopes.getOptions().isTraceOptionsRequests() + return hub.getOptions().isTraceOptionsRequests() || !HttpMethod.OPTIONS.name().equals(request.getMethod()); } @@ -142,20 +142,23 @@ private ITransaction startTransaction( final CustomSamplingContext customSamplingContext = new CustomSamplingContext(); customSamplingContext.set("request", request); - final TransactionOptions transactionOptions = new TransactionOptions(); - transactionOptions.setCustomSamplingContext(customSamplingContext); - transactionOptions.setBindToScope(true); - transactionOptions.setOrigin(TRACE_ORIGIN); - if (transactionContext != null) { transactionContext.setName(name); transactionContext.setTransactionNameSource(TransactionNameSource.URL); transactionContext.setOperation("http.server"); - return scopes.startTransaction(transactionContext, transactionOptions); + final TransactionOptions transactionOptions = new TransactionOptions(); + transactionOptions.setCustomSamplingContext(customSamplingContext); + transactionOptions.setBindToScope(true); + + return hub.startTransaction(transactionContext, transactionOptions); } - return scopes.startTransaction( + final TransactionOptions transactionOptions = new TransactionOptions(); + transactionOptions.setCustomSamplingContext(customSamplingContext); + transactionOptions.setBindToScope(true); + + return hub.startTransaction( new TransactionContext(name, TransactionNameSource.URL, "http.server"), transactionOptions); } } diff --git a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java index 180d5df00a..c417f14a14 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java +++ b/sentry-spring/src/main/java/io/sentry/spring/tracing/SentryTransactionAdvice.java @@ -1,10 +1,9 @@ package io.sentry.spring.tracing; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IScopes; -import io.sentry.ISentryLifecycleToken; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ITransaction; -import io.sentry.ScopesAdapter; import io.sentry.SpanStatus; import io.sentry.TransactionContext; import io.sentry.TransactionOptions; @@ -28,14 +27,14 @@ @Open public class SentryTransactionAdvice implements MethodInterceptor { private static final String TRACE_ORIGIN = "auto.function.spring.advice"; - private final @NotNull IScopes scopesBeforeForking; + private final @NotNull IHub hub; public SentryTransactionAdvice() { - this(ScopesAdapter.getInstance()); + this(HubAdapter.getInstance()); } - public SentryTransactionAdvice(final @NotNull IScopes scopes) { - this.scopesBeforeForking = Objects.requireNonNull(scopes, "scopes are required"); + public SentryTransactionAdvice(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "hub is required"); } @SuppressWarnings("deprecation") @@ -56,7 +55,7 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl final TransactionNameAndSource nameAndSource = resolveTransactionName(invocation, sentryTransaction); - final boolean isTransactionActive = isTransactionActive(scopesBeforeForking); + final boolean isTransactionActive = isTransactionActive(); if (isTransactionActive) { // transaction is already active, we do not start new transaction @@ -68,27 +67,25 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } else { operation = "bean"; } - final @NotNull IScopes forkedScopes = - scopesBeforeForking.forkedScopes("SentryTransactionAdvice"); - try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) { - final TransactionOptions transactionOptions = new TransactionOptions(); - transactionOptions.setBindToScope(true); - transactionOptions.setOrigin(TRACE_ORIGIN); - final ITransaction transaction = - forkedScopes.startTransaction( - new TransactionContext(nameAndSource.name, nameAndSource.source, operation), - transactionOptions); - try { - final Object result = invocation.proceed(); - transaction.setStatus(SpanStatus.OK); - return result; - } catch (Throwable e) { - transaction.setStatus(SpanStatus.INTERNAL_ERROR); - transaction.setThrowable(e); - throw e; - } finally { - transaction.finish(); - } + hub.pushScope(); + final TransactionOptions transactionOptions = new TransactionOptions(); + transactionOptions.setBindToScope(true); + final ITransaction transaction = + hub.startTransaction( + new TransactionContext(nameAndSource.name, nameAndSource.source, operation), + transactionOptions); + transaction.getSpanContext().setOrigin(TRACE_ORIGIN); + try { + final Object result = invocation.proceed(); + transaction.setStatus(SpanStatus.OK); + return result; + } catch (Throwable e) { + transaction.setStatus(SpanStatus.INTERNAL_ERROR); + transaction.setThrowable(e); + throw e; + } finally { + transaction.finish(); + hub.popScope(); } } } @@ -107,8 +104,8 @@ public Object invoke(final @NotNull MethodInvocation invocation) throws Throwabl } } - private boolean isTransactionActive(final @NotNull IScopes scopes) { - return scopes.getSpan() != null; + private boolean isTransactionActive() { + return hub.getSpan() != null; } private static class TransactionNameAndSource { diff --git a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryRequestResolver.java b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryRequestResolver.java index 76e50985e5..a710438c46 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryRequestResolver.java +++ b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryRequestResolver.java @@ -1,7 +1,7 @@ package io.sentry.spring.webflux; import com.jakewharton.nopen.annotation.Open; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.protocol.Request; import io.sentry.util.HttpUtils; import io.sentry.util.Objects; @@ -20,10 +20,10 @@ @Open @ApiStatus.Experimental public class SentryRequestResolver { - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; - public SentryRequestResolver(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + public SentryRequestResolver(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "options is required"); } public @NotNull Request resolveSentryRequest(final @NotNull ServerHttpRequest httpRequest) { @@ -36,7 +36,7 @@ public SentryRequestResolver(final @NotNull IScopes scopes) { urlDetails.applyToRequest(sentryRequest); sentryRequest.setHeaders(resolveHeadersMap(httpRequest.getHeaders())); - if (scopes.getOptions().isSendDefaultPii()) { + if (hub.getOptions().isSendDefaultPii()) { String headerName = HttpUtils.COOKIE_HEADER_NAME; sentryRequest.setCookies( toString( @@ -52,8 +52,7 @@ Map resolveHeadersMap(final HttpHeaders request) { for (Map.Entry> entry : request.entrySet()) { // do not copy personal information identifiable headers String headerName = entry.getKey(); - if (scopes.getOptions().isSendDefaultPii() - || !HttpUtils.containsSensitiveHeader(headerName)) { + if (hub.getOptions().isSendDefaultPii() || !HttpUtils.containsSensitiveHeader(headerName)) { headersMap.put( headerName, toString( diff --git a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryScheduleHook.java b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryScheduleHook.java index 50839e653e..7775d4e482 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryScheduleHook.java +++ b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryScheduleHook.java @@ -1,7 +1,6 @@ package io.sentry.spring.webflux; -import io.sentry.IScopes; -import io.sentry.ISentryLifecycleToken; +import io.sentry.IHub; import io.sentry.Sentry; import java.util.function.Function; import org.jetbrains.annotations.ApiStatus; @@ -9,17 +8,21 @@ /** * Hook meant to used with {@link reactor.core.scheduler.Schedulers#onScheduleHook(String, - * Function)} to configure Reactor to copy correct scopes into the operating thread. + * Function)} to configure Reactor to copy correct hub into the operating thread. */ @ApiStatus.Experimental public final class SentryScheduleHook implements Function { @Override public Runnable apply(final @NotNull Runnable runnable) { - final IScopes newScopes = Sentry.getCurrentScopes().forkedCurrentScope("spring.scheduleHook"); + final IHub newHub = Sentry.getCurrentHub().clone(); return () -> { - try (final @NotNull ISentryLifecycleToken ignored = newScopes.makeCurrent()) { + final IHub oldState = Sentry.getCurrentHub(); + Sentry.setCurrentHub(newHub); + try { runnable.run(); + } finally { + Sentry.setCurrentHub(oldState); } }; } diff --git a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebExceptionHandler.java b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebExceptionHandler.java index 042d1d48ec..98d3855a04 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebExceptionHandler.java +++ b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebExceptionHandler.java @@ -5,7 +5,7 @@ import static io.sentry.TypeCheckHint.WEBFLUX_EXCEPTION_HANDLER_RESPONSE; import io.sentry.Hint; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.SentryEvent; import io.sentry.SentryLevel; import io.sentry.exception.ExceptionMechanismException; @@ -26,10 +26,10 @@ @ApiStatus.Experimental public final class SentryWebExceptionHandler implements WebExceptionHandler { public static final String MECHANISM_TYPE = "Spring5WebFluxExceptionResolver"; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; - public SentryWebExceptionHandler(final @NotNull IScopes scopes) { - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + public SentryWebExceptionHandler(final @NotNull IHub hub) { + this.hub = Objects.requireNonNull(hub, "hub is required"); } @Override @@ -50,7 +50,7 @@ public SentryWebExceptionHandler(final @NotNull IScopes scopes) { hint.set(WEBFLUX_EXCEPTION_HANDLER_RESPONSE, serverWebExchange.getResponse()); hint.set(WEBFLUX_EXCEPTION_HANDLER_EXCHANGE, serverWebExchange); - scopes.captureEvent(event, hint); + hub.captureEvent(event, hint); } return Mono.error(ex); } diff --git a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java index ee5a5a7094..f68a87ae0f 100644 --- a/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java +++ b/sentry-spring/src/main/java/io/sentry/spring/webflux/SentryWebFilter.java @@ -7,10 +7,10 @@ import io.sentry.Breadcrumb; import io.sentry.CustomSamplingContext; import io.sentry.Hint; +import io.sentry.IHub; import io.sentry.IScope; -import io.sentry.IScopes; import io.sentry.ITransaction; -import io.sentry.NoOpScopes; +import io.sentry.NoOpHub; import io.sentry.Sentry; import io.sentry.SentryTraceHeader; import io.sentry.SpanStatus; @@ -34,45 +34,44 @@ /** Manages {@link IScope} in Webflux request processing. */ @ApiStatus.Experimental public final class SentryWebFilter implements WebFilter { - public static final String SENTRY_SCOPES_KEY = "sentry-scopes"; - /** - * @deprecated please use {@link SentryWebFilter#SENTRY_SCOPES_KEY} instead. - */ - @Deprecated public static final String SENTRY_HUB_KEY = SENTRY_SCOPES_KEY; - + public static final String SENTRY_HUB_KEY = "sentry-hub"; private static final String TRANSACTION_OP = "http.server"; private static final String TRACE_ORIGIN = "auto.spring.webflux"; private final @NotNull SentryRequestResolver sentryRequestResolver; - public SentryWebFilter(final @NotNull IScopes scopes) { - Objects.requireNonNull(scopes, "scopes are required"); - this.sentryRequestResolver = new SentryRequestResolver(scopes); + public SentryWebFilter(final @NotNull IHub hub) { + Objects.requireNonNull(hub, "hub is required"); + this.sentryRequestResolver = new SentryRequestResolver(hub); } @Override public Mono filter( final @NotNull ServerWebExchange serverWebExchange, final @NotNull WebFilterChain webFilterChain) { - @NotNull IScopes requestScopes = Sentry.forkedRootScopes("request.webflux"); - if (!requestScopes.isEnabled()) { + @NotNull IHub requestHub = Sentry.cloneMainHub(); + if (!requestHub.isEnabled()) { return webFilterChain.filter(serverWebExchange); } - final boolean isTracingEnabled = requestScopes.getOptions().isTracingEnabled(); + final boolean isTracingEnabled = requestHub.getOptions().isTracingEnabled(); final @NotNull ServerHttpRequest request = serverWebExchange.getRequest(); final @NotNull HttpHeaders headers = request.getHeaders(); final @Nullable String sentryTraceHeader = headers.getFirst(SentryTraceHeader.SENTRY_TRACE_HEADER); final @Nullable List baggageHeaders = headers.get(BaggageHeader.BAGGAGE_HEADER); final @Nullable TransactionContext transactionContext = - requestScopes.continueTrace(sentryTraceHeader, baggageHeaders); + requestHub.continueTrace(sentryTraceHeader, baggageHeaders); final @Nullable ITransaction transaction = - isTracingEnabled && shouldTraceRequest(requestScopes, request) - ? startTransaction(requestScopes, request, transactionContext) + isTracingEnabled && shouldTraceRequest(requestHub, request) + ? startTransaction(requestHub, request, transactionContext) : null; + if (transaction != null) { + transaction.getSpanContext().setOrigin(TRACE_ORIGIN); + } + return webFilterChain .filter(serverWebExchange) .doFinally( @@ -80,7 +79,8 @@ isTracingEnabled && shouldTraceRequest(requestScopes, request) if (transaction != null) { finishTransaction(serverWebExchange, transaction); } - Sentry.setCurrentScopes(NoOpScopes.getInstance()); + requestHub.popScope(); + Sentry.setCurrentHub(NoOpHub.getInstance()); }) .doOnError( e -> { @@ -91,8 +91,9 @@ isTracingEnabled && shouldTraceRequest(requestScopes, request) }) .doFirst( () -> { - serverWebExchange.getAttributes().put(SENTRY_SCOPES_KEY, requestScopes); - Sentry.setCurrentScopes(requestScopes); + serverWebExchange.getAttributes().put(SENTRY_HUB_KEY, requestHub); + Sentry.setCurrentHub(requestHub); + requestHub.pushScope(); final ServerHttpResponse response = serverWebExchange.getResponse(); final Hint hint = new Hint(); @@ -100,21 +101,21 @@ isTracingEnabled && shouldTraceRequest(requestScopes, request) hint.set(WEBFLUX_FILTER_RESPONSE, response); final String methodName = request.getMethod() != null ? request.getMethod().name() : "unknown"; - requestScopes.addBreadcrumb( + requestHub.addBreadcrumb( Breadcrumb.http(request.getURI().toString(), methodName), hint); - requestScopes.configureScope( + requestHub.configureScope( scope -> scope.setRequest(sentryRequestResolver.resolveSentryRequest(request))); }); } private boolean shouldTraceRequest( - final @NotNull IScopes scopes, final @NotNull ServerHttpRequest request) { - return scopes.getOptions().isTraceOptionsRequests() + final @NotNull IHub hub, final @NotNull ServerHttpRequest request) { + return hub.getOptions().isTraceOptionsRequests() || !HttpMethod.OPTIONS.equals(request.getMethod()); } private @NotNull ITransaction startTransaction( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull ServerHttpRequest request, final @Nullable TransactionContext transactionContext) { final @NotNull String name = request.getMethod() + " " + request.getURI().getPath(); @@ -124,17 +125,16 @@ private boolean shouldTraceRequest( final TransactionOptions transactionOptions = new TransactionOptions(); transactionOptions.setCustomSamplingContext(customSamplingContext); transactionOptions.setBindToScope(true); - transactionOptions.setOrigin(TRACE_ORIGIN); if (transactionContext != null) { transactionContext.setName(name); transactionContext.setTransactionNameSource(TransactionNameSource.URL); transactionContext.setOperation(TRANSACTION_OP); - return scopes.startTransaction(transactionContext, transactionOptions); + return hub.startTransaction(transactionContext, transactionOptions); } - return scopes.startTransaction( + return hub.startTransaction( new TransactionContext(name, TransactionNameSource.URL, TRANSACTION_OP), transactionOptions); } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/EnableSentryTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/EnableSentryTest.kt index 5182309416..5a8fec3053 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/EnableSentryTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/EnableSentryTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring import io.sentry.EventProcessor -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ITransportFactory import io.sentry.Integration import io.sentry.Sentry @@ -65,9 +65,9 @@ class EnableSentryTest { } @Test - fun `creates Sentry Scopes`() { + fun `creates Sentry Hub`() { contextRunner.run { - assertThat(it).hasSingleBean(IScopes::class.java) + assertThat(it).hasSingleBean(IHub::class.java) } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryCheckInAdviceTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryCheckInAdviceTest.kt index 6e18ef64f9..4f94fd7ce0 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryCheckInAdviceTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryCheckInAdviceTest.kt @@ -2,8 +2,7 @@ package io.sentry.spring import io.sentry.CheckIn import io.sentry.CheckInStatus -import io.sentry.IScopes -import io.sentry.ISentryLifecycleToken +import io.sentry.IHub import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.protocol.SentryId @@ -56,23 +55,19 @@ class SentryCheckInAdviceTest { lateinit var sampleServiceSpringProperties: SampleServiceSpringProperties @Autowired - lateinit var scopes: IScopes - - val lifecycleToken = mock() + lateinit var hub: IHub @BeforeTest fun setup() { - reset(scopes) - whenever(scopes.options).thenReturn(SentryOptions()) - whenever(scopes.forkedScopes(any())).thenReturn(scopes) - whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) + reset(hub) + whenever(hub.options).thenReturn(SentryOptions()) } @Test fun `when method is annotated with @SentryCheckIn, every method call creates two check-ins`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleService.hello() assertEquals(1, result) assertEquals(2, checkInCaptor.allValues.size) @@ -85,18 +80,17 @@ class SentryCheckInAdviceTest { assertEquals("monitor_slug_1", doneCheckIn.monitorSlug) assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) - val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).forkedScopes(any()) - order.verify(scopes).makeCurrent() - order.verify(scopes, times(2)).captureCheckIn(any()) - order.verify(lifecycleToken).close() + val order = inOrder(hub) + order.verify(hub).pushScope() + order.verify(hub, times(2)).captureCheckIn(any()) + order.verify(hub).popScope() } @Test fun `when method is annotated with @SentryCheckIn, every method call creates two check-ins error`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) assertThrows { sampleService.oops() } @@ -110,18 +104,17 @@ class SentryCheckInAdviceTest { assertEquals("monitor_slug_1e", doneCheckIn.monitorSlug) assertEquals(CheckInStatus.ERROR.apiName(), doneCheckIn.status) - val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).forkedScopes(any()) - order.verify(scopes).makeCurrent() - order.verify(scopes, times(2)).captureCheckIn(any()) - order.verify(lifecycleToken).close() + val order = inOrder(hub) + order.verify(hub).pushScope() + order.verify(hub, times(2)).captureCheckIn(any()) + order.verify(hub).popScope() } @Test fun `when method is annotated with @SentryCheckIn and heartbeat only, every method call creates only one check-in at the end`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceHeartbeat.hello() assertEquals(1, result) assertEquals(1, checkInCaptor.allValues.size) @@ -131,18 +124,17 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).forkedScopes(any()) - order.verify(scopes).makeCurrent() - order.verify(scopes).captureCheckIn(any()) - order.verify(lifecycleToken).close() + val order = inOrder(hub) + order.verify(hub).pushScope() + order.verify(hub).captureCheckIn(any()) + order.verify(hub).popScope() } @Test fun `when method is annotated with @SentryCheckIn and heartbeat only, every method call creates only one check-in at the end with error`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) assertThrows { sampleServiceHeartbeat.oops() } @@ -153,33 +145,31 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.ERROR.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).forkedScopes(any()) - order.verify(scopes).makeCurrent() - order.verify(scopes).captureCheckIn(any()) - order.verify(lifecycleToken).close() + val order = inOrder(hub) + order.verify(hub).pushScope() + order.verify(hub).captureCheckIn(any()) + order.verify(hub).popScope() } @Test fun `when method is annotated with @SentryCheckIn but slug is missing, does not create check-in`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceNoSlug.hello() assertEquals(1, result) assertEquals(0, checkInCaptor.allValues.size) - verify(scopes, never()).forkedScopes(any()) - verify(scopes, never()).makeCurrent() - verify(scopes, never()).captureCheckIn(any()) - verify(lifecycleToken, never()).close() + verify(hub, never()).pushScope() + verify(hub, never()).captureCheckIn(any()) + verify(hub, never()).popScope() } @Test fun `when @SentryCheckIn is passed a spring property it is resolved correctly`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceSpringProperties.hello() assertEquals(1, result) assertEquals(1, checkInCaptor.allValues.size) @@ -189,18 +179,17 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).forkedScopes(any()) - order.verify(scopes).makeCurrent() - order.verify(scopes).captureCheckIn(any()) - order.verify(lifecycleToken).close() + val order = inOrder(hub) + order.verify(hub).pushScope() + order.verify(hub).captureCheckIn(any()) + order.verify(hub).popScope() } @Test fun `when @SentryCheckIn is passed a spring property that does not exist, raw value is used`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceSpringProperties.helloUnresolvedProperty() assertEquals(1, result) assertEquals(1, checkInCaptor.allValues.size) @@ -210,18 +199,17 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).forkedScopes(any()) - order.verify(scopes).makeCurrent() - order.verify(scopes).captureCheckIn(any()) - order.verify(lifecycleToken).close() + val order = inOrder(hub) + order.verify(hub).pushScope() + order.verify(hub).captureCheckIn(any()) + order.verify(hub).popScope() } @Test fun `when @SentryCheckIn is passed a spring property that causes an exception, raw value is used`() { val checkInId = SentryId() val checkInCaptor = argumentCaptor() - whenever(scopes.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) + whenever(hub.captureCheckIn(checkInCaptor.capture())).thenReturn(checkInId) val result = sampleServiceSpringProperties.helloExceptionProperty() assertEquals(1, result) assertEquals(1, checkInCaptor.allValues.size) @@ -231,11 +219,10 @@ class SentryCheckInAdviceTest { assertEquals(CheckInStatus.OK.apiName(), doneCheckIn.status) assertNotNull(doneCheckIn.duration) - val order = inOrder(scopes, lifecycleToken) - order.verify(scopes).forkedScopes(any()) - order.verify(scopes).makeCurrent() - order.verify(scopes).captureCheckIn(any()) - order.verify(lifecycleToken).close() + val order = inOrder(hub) + order.verify(hub).pushScope() + order.verify(hub).captureCheckIn(any()) + order.verify(hub).popScope() } @Configuration @@ -256,10 +243,10 @@ class SentryCheckInAdviceTest { open fun sampleServiceSpringProperties() = SampleServiceSpringProperties() @Bean - open fun scopes(): IScopes { - val scopes = mock() - Sentry.setCurrentScopes(scopes) - return scopes + open fun hub(): IHub { + val hub = mock() + Sentry.setCurrentHub(hub) + return hub } companion object { diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryExceptionResolverTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryExceptionResolverTest.kt index 048166107b..f3e4c32fb0 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryExceptionResolverTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryExceptionResolverTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryEvent import io.sentry.SentryLevel import io.sentry.exception.ExceptionMechanismException @@ -17,7 +17,7 @@ import javax.servlet.http.HttpServletResponse import kotlin.test.Test class SentryExceptionResolverTest { - private val scopes = mock() + private val hub = mock() private val transactionNameProvider = mock() private val request = mock() @@ -26,10 +26,10 @@ class SentryExceptionResolverTest { @Test fun `when handles exception, sets wrapped exception for event`() { val eventCaptor = argumentCaptor() - whenever(scopes.captureEvent(eventCaptor.capture(), any())).thenReturn(null) + whenever(hub.captureEvent(eventCaptor.capture(), any())).thenReturn(null) val expectedCause = RuntimeException("test") - SentryExceptionResolver(scopes, transactionNameProvider, 1) + SentryExceptionResolver(hub, transactionNameProvider, 1) .resolveException(request, response, null, expectedCause) assertThat(eventCaptor.firstValue.throwable).isEqualTo(expectedCause) @@ -46,9 +46,9 @@ class SentryExceptionResolverTest { @Test fun `when handles exception, sets fatal level for event`() { val eventCaptor = argumentCaptor() - whenever(scopes.captureEvent(eventCaptor.capture(), any())).thenReturn(null) + whenever(hub.captureEvent(eventCaptor.capture(), any())).thenReturn(null) - SentryExceptionResolver(scopes, transactionNameProvider, 1) + SentryExceptionResolver(hub, transactionNameProvider, 1) .resolveException(request, response, null, RuntimeException("test")) assertThat(eventCaptor.firstValue.level).isEqualTo(SentryLevel.FATAL) @@ -59,9 +59,9 @@ class SentryExceptionResolverTest { val expectedTransactionName = "test-transaction" whenever(transactionNameProvider.provideTransactionName(any())).thenReturn(expectedTransactionName) val eventCaptor = argumentCaptor() - whenever(scopes.captureEvent(eventCaptor.capture(), any())).thenReturn(null) + whenever(hub.captureEvent(eventCaptor.capture(), any())).thenReturn(null) - SentryExceptionResolver(scopes, transactionNameProvider, 1) + SentryExceptionResolver(hub, transactionNameProvider, 1) .resolveException(request, response, null, RuntimeException("test")) assertThat(eventCaptor.firstValue.transaction).isEqualTo(expectedTransactionName) @@ -71,9 +71,9 @@ class SentryExceptionResolverTest { @Test fun `when handles exception, provides spring resolver hint`() { val hintCaptor = argumentCaptor() - whenever(scopes.captureEvent(any(), hintCaptor.capture())).thenReturn(null) + whenever(hub.captureEvent(any(), hintCaptor.capture())).thenReturn(null) - SentryExceptionResolver(scopes, transactionNameProvider, 1) + SentryExceptionResolver(hub, transactionNameProvider, 1) .resolveException(request, response, null, RuntimeException("test")) with(hintCaptor.firstValue) { @@ -86,8 +86,8 @@ class SentryExceptionResolverTest { fun `when custom create event method provided, uses it to capture event`() { val expectedEvent = SentryEvent() val eventCaptor = argumentCaptor() - whenever(scopes.captureEvent(eventCaptor.capture(), any())).thenReturn(null) - val resolver = object : SentryExceptionResolver(scopes, transactionNameProvider, 1) { + whenever(hub.captureEvent(eventCaptor.capture(), any())).thenReturn(null) + val resolver = object : SentryExceptionResolver(hub, transactionNameProvider, 1) { override fun createEvent(request: HttpServletRequest, ex: Exception) = expectedEvent } @@ -100,8 +100,8 @@ class SentryExceptionResolverTest { fun `when custom create hint method provided, uses it to capture event`() { val expectedHint = Hint() val hintCaptor = argumentCaptor() - whenever(scopes.captureEvent(any(), hintCaptor.capture())).thenReturn(null) - val resolver = object : SentryExceptionResolver(scopes, transactionNameProvider, 1) { + whenever(hub.captureEvent(any(), hintCaptor.capture())).thenReturn(null) + val resolver = object : SentryExceptionResolver(hub, transactionNameProvider, 1) { override fun createHint(request: HttpServletRequest, response: HttpServletResponse) = expectedHint } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryInitBeanPostProcessorTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryInitBeanPostProcessorTest.kt index dc4a14e656..78ebf961c9 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryInitBeanPostProcessorTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryInitBeanPostProcessorTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring -import io.sentry.IScopes +import io.sentry.IHub import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.springframework.context.annotation.AnnotationConfigApplicationContext @@ -13,18 +13,18 @@ class SentryInitBeanPostProcessorTest { @Test fun closesSentryOnApplicationContextDestroy() { val ctx = AnnotationConfigApplicationContext(TestConfig::class.java) - val scopes = ctx.getBean(IScopes::class.java) + val hub = ctx.getBean(IHub::class.java) ctx.close() - verify(scopes).close() + verify(hub).close() } @Configuration open class TestConfig { @Bean(destroyMethod = "") - open fun scopes() = mock() + open fun hub() = mock() @Bean - open fun sentryInitBeanPostProcessor() = SentryInitBeanPostProcessor(scopes()) + open fun sentryInitBeanPostProcessor() = SentryInitBeanPostProcessor(hub()) } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryRequestHttpServletRequestProcessorTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryRequestHttpServletRequestProcessorTest.kt index c7277a2240..3fe9b60743 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryRequestHttpServletRequestProcessorTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryRequestHttpServletRequestProcessorTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryEvent import io.sentry.SentryOptions import io.sentry.spring.tracing.SpringMvcTransactionNameProvider @@ -19,10 +19,10 @@ import kotlin.test.assertNotNull class SentryRequestHttpServletRequestProcessorTest { private class Fixture { - val scopes = mock() + val hub = mock() fun getSut(request: HttpServletRequest, options: SentryOptions = SentryOptions()): SentryRequestHttpServletRequestProcessor { - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) return SentryRequestHttpServletRequestProcessor(SpringMvcTransactionNameProvider(), request) } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringFilterTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringFilterTest.kt index f4dce4cad0..ce83c4b9b7 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringFilterTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentrySpringFilterTest.kt @@ -1,9 +1,8 @@ package io.sentry.spring import io.sentry.Breadcrumb +import io.sentry.IHub import io.sentry.IScope -import io.sentry.IScopes -import io.sentry.ISentryLifecycleToken import io.sentry.Scope import io.sentry.ScopeCallback import io.sentry.SentryOptions @@ -38,29 +37,23 @@ import kotlin.test.fail class SentrySpringFilterTest { private class Fixture { - val scopes = mock() - val scopesBeforeForking = mock() + val hub = mock() val response = MockHttpServletResponse() - val lifecycleToken = mock() val chain = mock() lateinit var scope: IScope lateinit var request: HttpServletRequest fun getSut(request: HttpServletRequest? = null, options: SentryOptions = SentryOptions()): SentrySpringFilter { scope = Scope(options) - whenever(scopesBeforeForking.options).thenReturn(options) - whenever(scopesBeforeForking.isEnabled).thenReturn(true) - whenever(scopes.options).thenReturn(options) - whenever(scopes.isEnabled).thenReturn(true) - whenever(scopesBeforeForking.forkedScopes(any())).thenReturn(scopes) - whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) + whenever(hub.options).thenReturn(options) + whenever(hub.isEnabled).thenReturn(true) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) this.request = request ?: MockHttpServletRequest().apply { this.requestURI = "http://localhost:8080/some-uri" this.method = "post" } - return SentrySpringFilter(scopesBeforeForking) + return SentrySpringFilter(hub) } } @@ -71,8 +64,7 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopesBeforeForking).forkedScopes(any()) - verify(fixture.scopes).makeCurrent() + verify(fixture.hub).pushScope() } @Test @@ -80,7 +72,7 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).addBreadcrumb( + verify(fixture.hub).addBreadcrumb( check { it: Breadcrumb -> Assertions.assertThat(it.getData("url")).isEqualTo("http://localhost:8080/some-uri") Assertions.assertThat(it.getData("method")).isEqualTo("POST") @@ -95,7 +87,7 @@ class SentrySpringFilterTest { val listener = fixture.getSut() listener.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.lifecycleToken).close() + verify(fixture.hub).popScope() } @Test @@ -107,7 +99,7 @@ class SentrySpringFilterTest { listener.doFilter(fixture.request, fixture.response, fixture.chain) fail() } catch (e: Exception) { - verify(fixture.lifecycleToken).close() + verify(fixture.hub).popScope() } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryTaskDecoratorTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryTaskDecoratorTest.kt index 4bbce919eb..e202deca86 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryTaskDecoratorTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryTaskDecoratorTest.kt @@ -25,35 +25,35 @@ class SentryTaskDecoratorTest { } @Test - fun `scopes is reset to its state within the thread after decoration is done`() { + fun `hub is reset to its state within the thread after decoration is done`() { Sentry.init { it.dsn = dsn } val sut = SentryTaskDecorator() - val mainScopes = Sentry.getCurrentScopes() - val threadedScopes = Sentry.getCurrentScopes().forkedCurrentScope("test") + val mainHub = Sentry.getCurrentHub() + val threadedHub = Sentry.getCurrentHub().clone() executor.submit { - Sentry.setCurrentScopes(threadedScopes) + Sentry.setCurrentHub(threadedHub) }.get() - assertEquals(mainScopes, Sentry.getCurrentScopes()) + assertEquals(mainHub, Sentry.getCurrentHub()) val callableFuture = executor.submit( sut.decorate { - assertNotEquals(mainScopes, Sentry.getCurrentScopes()) - assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) + assertNotEquals(mainHub, Sentry.getCurrentHub()) + assertNotEquals(threadedHub, Sentry.getCurrentHub()) } ) callableFuture.get() executor.submit { - assertNotEquals(mainScopes, Sentry.getCurrentScopes()) - assertEquals(threadedScopes, Sentry.getCurrentScopes()) + assertNotEquals(mainHub, Sentry.getCurrentHub()) + assertEquals(threadedHub, Sentry.getCurrentHub()) }.get() } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryUserFilterTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryUserFilterTest.kt index 7f9be1ba8b..2dac9a57ed 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/SentryUserFilterTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/SentryUserFilterTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryOptions import io.sentry.protocol.User import org.mockito.kotlin.check @@ -16,7 +16,7 @@ import kotlin.test.assertNull class SentryUserFilterTest { class Fixture { - val scopes = mock() + val hub = mock() val request = MockHttpServletRequest() val response = MockHttpServletResponse() val chain = mock() @@ -25,8 +25,8 @@ class SentryUserFilterTest { val options = SentryOptions().apply { this.isSendDefaultPii = isSendDefaultPii } - whenever(scopes.options).thenReturn(options) - return SentryUserFilter(scopes, userProviders) + whenever(hub.options).thenReturn(options) + return SentryUserFilter(hub, userProviders) } } @@ -52,7 +52,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).setUser( + verify(fixture.hub).setUser( check { assertEquals(sampleUser, it) } @@ -72,7 +72,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).setUser( + verify(fixture.hub).setUser( check { assertEquals(sampleUser, it) } @@ -92,7 +92,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).setUser( + verify(fixture.hub).setUser( check { assertEquals(sampleUser, it) } @@ -118,7 +118,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).setUser( + verify(fixture.hub).setUser( check { assertEquals(mapOf("key" to "value", "new-key" to "new-value"), it.others) } @@ -140,7 +140,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).setUser( + verify(fixture.hub).setUser( check { assertEquals("192.168.0.1", it.ipAddress) } @@ -162,7 +162,7 @@ class SentryUserFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).setUser( + verify(fixture.hub).setUser( check { assertNull(it.ipAddress) } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/exception/SentryCaptureExceptionParameterAdviceTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/exception/SentryCaptureExceptionParameterAdviceTest.kt index dca1c4c592..39bc66fabb 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/exception/SentryCaptureExceptionParameterAdviceTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/exception/SentryCaptureExceptionParameterAdviceTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring.exception import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.Sentry import io.sentry.exception.ExceptionMechanismException import org.junit.runner.RunWith @@ -30,18 +30,18 @@ class SentryCaptureExceptionParameterAdviceTest { lateinit var sampleService: SampleService @Autowired - lateinit var scopes: IScopes + lateinit var hub: IHub @BeforeTest fun setup() { - reset(scopes) + reset(hub) } @Test fun `captures exception passed to method annotated with @SentryCaptureException`() { val exception = RuntimeException("test exception") sampleService.methodTakingAnException(exception) - verify(scopes).captureException( + verify(hub).captureException( check { assertTrue(it is ExceptionMechanismException) assertEquals(exception, it.throwable) @@ -60,10 +60,10 @@ class SentryCaptureExceptionParameterAdviceTest { open fun sampleService() = SampleService() @Bean - open fun scopes(): IScopes { - val scopes = mock() - Sentry.setCurrentScopes(scopes) - return scopes + open fun hub(): IHub { + val hub = mock() + Sentry.setCurrentHub(hub) + return hub } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/graphql/SentrySpringSubscriptionHandlerTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/graphql/SentrySpringSubscriptionHandlerTest.kt index 1aa97cd262..df2df5b3ef 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/graphql/SentrySpringSubscriptionHandlerTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/graphql/SentrySpringSubscriptionHandlerTest.kt @@ -4,7 +4,7 @@ import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchPar import graphql.language.Document import graphql.language.OperationDefinition import graphql.schema.DataFetchingEnvironment -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.graphql.ExceptionReporter import org.junit.jupiter.api.assertThrows import org.mockito.kotlin.anyOrNull @@ -23,7 +23,7 @@ class SentrySpringSubscriptionHandlerTest { @Test fun `reports exception`() { val exception = IllegalStateException("some exception") - val scopes = mock() + val hub = mock() val exceptionReporter = mock() val parameters = mock() val dataFetchingEnvironment = mock() @@ -32,7 +32,7 @@ class SentrySpringSubscriptionHandlerTest { .build() whenever(dataFetchingEnvironment.document).thenReturn(document) whenever(parameters.environment).thenReturn(dataFetchingEnvironment) - val resultObject = SentrySpringSubscriptionHandler().onSubscriptionResult(Flux.error(exception), scopes, exceptionReporter, parameters) + val resultObject = SentrySpringSubscriptionHandler().onSubscriptionResult(Flux.error(exception), hub, exceptionReporter, parameters) assertThrows { (resultObject as Flux).blockFirst() } @@ -41,7 +41,7 @@ class SentrySpringSubscriptionHandlerTest { same(exception), org.mockito.kotlin.check { assertEquals(true, it.isSubscription) - assertSame(scopes, it.scopes) + assertSame(hub, it.hub) assertEquals("query testQuery\n", it.query) }, anyOrNull() @@ -52,7 +52,7 @@ class SentrySpringSubscriptionHandlerTest { fun `unwraps SubscriptionPublisherException and reports cause`() { val exception = IllegalStateException("some exception") val wrappedException = SubscriptionPublisherException(emptyList(), exception) - val scopes = mock() + val hub = mock() val exceptionReporter = mock() val parameters = mock() val dataFetchingEnvironment = mock() @@ -61,7 +61,7 @@ class SentrySpringSubscriptionHandlerTest { .build() whenever(dataFetchingEnvironment.document).thenReturn(document) whenever(parameters.environment).thenReturn(dataFetchingEnvironment) - val resultObject = SentrySpringSubscriptionHandler().onSubscriptionResult(Flux.error(wrappedException), scopes, exceptionReporter, parameters) + val resultObject = SentrySpringSubscriptionHandler().onSubscriptionResult(Flux.error(wrappedException), hub, exceptionReporter, parameters) assertThrows { (resultObject as Flux).blockFirst() } @@ -70,7 +70,7 @@ class SentrySpringSubscriptionHandlerTest { same(exception), org.mockito.kotlin.check { assertEquals(true, it.isSubscription) - assertSame(scopes, it.scopes) + assertSame(hub, it.hub) assertEquals("query testQuery\n", it.query) }, anyOrNull() diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/mvc/SentrySpringIntegrationTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/mvc/SentrySpringIntegrationTest.kt index ad9734def6..998b27c7e8 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/mvc/SentrySpringIntegrationTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/mvc/SentrySpringIntegrationTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.mvc -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ITransportFactory import io.sentry.Sentry import io.sentry.SentryOptions @@ -104,7 +104,7 @@ class SentrySpringIntegrationTest { lateinit var anotherService: AnotherService @Autowired - lateinit var scopes: IScopes + lateinit var hub: IHub @LocalServerPort var port: Int? = null @@ -260,7 +260,7 @@ class SentrySpringIntegrationTest { try { someService.aMethodThrowing() } catch (e: Exception) { - scopes.captureException(e) + hub.captureException(e) } verify(transport).send( checkEvent { @@ -276,7 +276,7 @@ class SentrySpringIntegrationTest { try { someService.aMethodWithInnerSpanThrowing() } catch (e: Exception) { - scopes.captureException(e) + hub.captureException(e) } verify(transport).send( checkEvent { @@ -370,20 +370,20 @@ open class App { open fun springSecuritySentryUserProvider(sentryOptions: SentryOptions) = SpringSecuritySentryUserProvider(sentryOptions) @Bean - open fun sentryUserFilter(scopes: IScopes, @Lazy sentryUserProviders: List) = FilterRegistrationBean().apply { - this.filter = SentryUserFilter(scopes, sentryUserProviders) + open fun sentryUserFilter(hub: IHub, @Lazy sentryUserProviders: List) = FilterRegistrationBean().apply { + this.filter = SentryUserFilter(hub, sentryUserProviders) this.order = Ordered.LOWEST_PRECEDENCE } @Bean - open fun sentrySpringFilter(scopes: IScopes) = FilterRegistrationBean().apply { - this.filter = SentrySpringFilter(scopes) + open fun sentrySpringFilter(hub: IHub) = FilterRegistrationBean().apply { + this.filter = SentrySpringFilter(hub) this.order = Ordered.HIGHEST_PRECEDENCE } @Bean - open fun sentryTracingFilter(scopes: IScopes) = FilterRegistrationBean().apply { - this.filter = SentryTracingFilter(scopes) + open fun sentryTracingFilter(hub: IHub) = FilterRegistrationBean().apply { + this.filter = SentryTracingFilter(hub) this.order = Ordered.HIGHEST_PRECEDENCE + 1 // must run after SentrySpringFilter } @@ -391,13 +391,13 @@ open class App { open fun sentryTaskDecorator() = SentryTaskDecorator() @Bean - open fun webClient(scopes: IScopes): WebClient { + open fun webClient(hub: IHub): WebClient { return WebClient.builder() .filter( ExchangeFilterFunctions .basicAuthentication("user", "password") ) - .filter(SentrySpanClientWebRequestFilter(scopes)).build() + .filter(SentrySpanClientWebRequestFilter(hub)).build() } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentrySpanAdviceTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentrySpanAdviceTest.kt index a6f59971c9..dc6f00eb46 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentrySpanAdviceTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentrySpanAdviceTest.kt @@ -1,6 +1,6 @@ package io.sentry.spring.tracing -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.Scope import io.sentry.Sentry import io.sentry.SentryOptions @@ -37,20 +37,20 @@ class SentrySpanAdviceTest { lateinit var classAnnotatedWithOperationSampleService: ClassAnnotatedWithOperationSampleService @Autowired - lateinit var scopes: IScopes + lateinit var hub: IHub @BeforeTest fun setup() { - whenever(scopes.options).thenReturn(SentryOptions()) + whenever(hub.options).thenReturn(SentryOptions()) } @Test fun `when class is annotated with @SentrySpan, every method call attaches span to existing transaction`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) scope.setTransaction(tx) - whenever(scopes.span).thenReturn(tx) + whenever(hub.span).thenReturn(tx) val result = classAnnotatedSampleService.hello() assertEquals(1, result) assertEquals(1, tx.spans.size) @@ -62,10 +62,10 @@ class SentrySpanAdviceTest { @Test fun `when class is annotated with @SentrySpan with operation set, every method call attaches span to existing transaction`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) scope.setTransaction(tx) - whenever(scopes.span).thenReturn(tx) + whenever(hub.span).thenReturn(tx) val result = classAnnotatedWithOperationSampleService.hello() assertEquals(1, result) assertEquals(1, tx.spans.size) @@ -76,10 +76,10 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan with properties set, attaches span to existing transaction`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) scope.setTransaction(tx) - whenever(scopes.span).thenReturn(tx) + whenever(hub.span).thenReturn(tx) val result = sampleService.methodWithSpanDescriptionSet() assertEquals(1, result) assertEquals(1, tx.spans.size) @@ -90,10 +90,10 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan without properties set, attaches span to existing transaction and sets Span description as className dot methodName`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) scope.setTransaction(tx) - whenever(scopes.span).thenReturn(tx) + whenever(hub.span).thenReturn(tx) val result = sampleService.methodWithoutSpanDescriptionSet() assertEquals(2, result) assertEquals(1, tx.spans.size) @@ -104,10 +104,10 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan and returns, attached span has status OK`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) scope.setTransaction(tx) - whenever(scopes.span).thenReturn(tx) + whenever(hub.span).thenReturn(tx) sampleService.methodWithSpanDescriptionSet() assertEquals(SpanStatus.OK, tx.spans.first().status) } @@ -115,10 +115,10 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan and throws exception, attached span has throwable set and INTERNAL_ERROR status`() { val scope = Scope(SentryOptions()) - val tx = SentryTracer(TransactionContext("aTransaction", "op"), scopes) + val tx = SentryTracer(TransactionContext("aTransaction", "op"), hub) scope.setTransaction(tx) - whenever(scopes.span).thenReturn(tx) + whenever(hub.span).thenReturn(tx) var throwable: Throwable? = null try { sampleService.methodThrowingException() @@ -131,7 +131,7 @@ class SentrySpanAdviceTest { @Test fun `when method is annotated with @SentrySpan and there is no active transaction, span is not created and method is executed`() { - whenever(scopes.span).thenReturn(null) + whenever(hub.span).thenReturn(null) val result = sampleService.methodWithSpanDescriptionSet() assertEquals(1, result) } @@ -151,10 +151,10 @@ class SentrySpanAdviceTest { open fun classAnnotatedWithOperationSampleService() = ClassAnnotatedWithOperationSampleService() @Bean - open fun scopes(): IScopes { - val scopes = mock() - Sentry.setCurrentScopes(scopes) - return scopes + open fun hub(): IHub { + val hub = mock() + Sentry.setCurrentHub(hub) + return hub } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTracingFilterTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTracingFilterTest.kt index ca89c09a12..6c4c06ebff 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTracingFilterTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTracingFilterTest.kt @@ -1,7 +1,7 @@ package io.sentry.spring.tracing +import io.sentry.IHub import io.sentry.ILogger -import io.sentry.IScopes import io.sentry.PropagationContext import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -38,7 +38,7 @@ import kotlin.test.fail class SentryTracingFilterTest { private class Fixture { - val scopes = mock() + val hub = mock() val request = MockHttpServletRequest() val response = MockHttpServletResponse() val chain = mock() @@ -50,7 +50,7 @@ class SentryTracingFilterTest { val logger = mock() init { - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) } fun getSut(isEnabled: Boolean = true, status: Int = 200, sentryTraceHeader: String? = null, baggageHeaders: List? = null): SentryTracingFilter { @@ -61,16 +61,16 @@ class SentryTracingFilterTest { whenever(transactionNameProvider.provideTransactionSource()).thenReturn(TransactionNameSource.CUSTOM) if (sentryTraceHeader != null) { request.addHeader("sentry-trace", sentryTraceHeader) - whenever(scopes.startTransaction(any(), check { it.isBindToScope })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } + whenever(hub.startTransaction(any(), check { it.isBindToScope })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } } if (baggageHeaders != null) { request.addHeader("baggage", baggageHeaders) } response.status = status - whenever(scopes.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } - whenever(scopes.isEnabled).thenReturn(isEnabled) - whenever(scopes.continueTrace(any(), any())).thenAnswer { TransactionContext.fromPropagationContext(PropagationContext.fromHeaders(logger, it.arguments[0] as String?, it.arguments[1] as List?)) } - return SentryTracingFilter(scopes, transactionNameProvider) + whenever(hub.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } + whenever(hub.isEnabled).thenReturn(isEnabled) + whenever(hub.continueTrace(any(), any())).thenAnswer { TransactionContext.fromPropagationContext(PropagationContext.fromHeaders(logger, it.arguments[0] as String?, it.arguments[1] as List?)) } + return SentryTracingFilter(hub, transactionNameProvider) } } @@ -82,7 +82,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("POST /product/12", it.name) assertEquals(TransactionNameSource.URL, it.transactionNameSource) @@ -92,15 +92,15 @@ class SentryTracingFilterTest { assertNotNull(it.customSamplingContext?.get("request")) assertTrue(it.customSamplingContext?.get("request") is HttpServletRequest) assertTrue(it.isBindToScope) - assertThat(it.origin).isEqualTo("auto.http.spring.webmvc") } ) verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.transaction).isEqualTo("POST /product/{id}") assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.OK) assertThat(it.contexts.trace!!.operation).isEqualTo("http.server") + assertThat(it.contexts.trace!!.origin).isEqualTo("auto.http.spring.webmvc") }, anyOrNull(), anyOrNull(), @@ -114,7 +114,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.INTERNAL_ERROR) }, @@ -130,7 +130,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.status).isNull() }, @@ -146,7 +146,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -163,7 +163,7 @@ class SentryTracingFilterTest { filter.doFilter(fixture.request, fixture.response, fixture.chain) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isEqualTo(parentSpanId) }, @@ -174,15 +174,15 @@ class SentryTracingFilterTest { } @Test - fun `when scopes is disabled, components are not invoked`() { + fun `when hub is disabled, components are not invoked`() { val filter = fixture.getSut(isEnabled = false) filter.doFilter(fixture.request, fixture.response, fixture.chain) verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.scopes).isEnabled - verifyNoMoreInteractions(fixture.scopes) + verify(fixture.hub).isEnabled + verifyNoMoreInteractions(fixture.hub) verify(fixture.transactionNameProvider, never()).provideTransactionName(any()) } @@ -196,7 +196,7 @@ class SentryTracingFilterTest { fail("filter is expected to rethrow exception") } catch (_: Exception) { } - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.status).isEqualTo(SpanStatus.INTERNAL_ERROR) }, @@ -216,10 +216,10 @@ class SentryTracingFilterTest { verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.scopes).isEnabled - verify(fixture.scopes, times(2)).options - verify(fixture.scopes).continueTrace(anyOrNull(), anyOrNull()) - verifyNoMoreInteractions(fixture.scopes) + verify(fixture.hub).isEnabled + verify(fixture.hub, times(2)).options + verify(fixture.hub).continueTrace(anyOrNull(), anyOrNull()) + verifyNoMoreInteractions(fixture.hub) verify(fixture.transactionNameProvider, never()).provideTransactionName(any()) } @@ -233,7 +233,7 @@ class SentryTracingFilterTest { verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -253,7 +253,7 @@ class SentryTracingFilterTest { verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -275,9 +275,9 @@ class SentryTracingFilterTest { verify(fixture.chain).doFilter(fixture.request, fixture.response) - verify(fixture.scopes).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) + verify(fixture.hub).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) - verify(fixture.scopes, never()).captureTransaction( + verify(fixture.hub, never()).captureTransaction( anyOrNull(), anyOrNull(), anyOrNull(), diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt index dd1dabf572..0fa9abbb29 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/tracing/SentryTransactionAdviceTest.kt @@ -1,7 +1,6 @@ package io.sentry.spring.tracing -import io.sentry.IScopes -import io.sentry.ISentryLifecycleToken +import io.sentry.IHub import io.sentry.Sentry import io.sentry.SentryOptions import io.sentry.SentryTracer @@ -45,39 +44,28 @@ class SentryTransactionAdviceTest { lateinit var classAnnotatedWithOperationSampleService: ClassAnnotatedWithOperationSampleService @Autowired - lateinit var scopes: IScopes - - val lifecycleToken = mock() + lateinit var hub: IHub @BeforeTest fun setup() { - reset(scopes) - whenever( - scopes.startTransaction( - any(), - check { - assertTrue(it.isBindToScope) - assertThat(it.origin).isEqualTo("auto.function.spring.advice") - } - ) - ).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } - whenever(scopes.options).thenReturn( + reset(hub) + whenever(hub.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } + whenever(hub.options).thenReturn( SentryOptions().apply { dsn = "https://key@sentry.io/proj" } ) - whenever(scopes.forkedScopes(any())).thenReturn(scopes) - whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) } @Test fun `creates transaction around method annotated with @SentryTransaction`() { sampleService.methodWithTransactionNameSet() - verify(scopes).captureTransaction( + verify(hub).captureTransaction( check { assertThat(it.transaction).isEqualTo("customName") assertThat(it.contexts.trace!!.operation).isEqualTo("bean") assertThat(it.status).isEqualTo(SpanStatus.OK) + assertThat(it.contexts.trace!!.origin).isEqualTo("auto.function.spring.advice") }, anyOrNull(), anyOrNull(), @@ -88,7 +76,7 @@ class SentryTransactionAdviceTest { @Test fun `when method annotated with @SentryTransaction throws exception, sets error status on transaction`() { assertThrows { sampleService.methodThrowingException() } - verify(scopes).captureTransaction( + verify(hub).captureTransaction( check { assertThat(it.status).isEqualTo(SpanStatus.INTERNAL_ERROR) }, @@ -101,7 +89,7 @@ class SentryTransactionAdviceTest { @Test fun `when @SentryTransaction has no name set, sets transaction name as className dot methodName`() { sampleService.methodWithoutTransactionNameSet() - verify(scopes).captureTransaction( + verify(hub).captureTransaction( check { assertThat(it.transaction).isEqualTo("SampleService.methodWithoutTransactionNameSet") assertThat(it.contexts.trace!!.operation).isEqualTo("op") @@ -114,18 +102,18 @@ class SentryTransactionAdviceTest { @Test fun `when transaction is already active, does not start new transaction`() { - whenever(scopes.options).thenReturn(SentryOptions()) - whenever(scopes.span).then { SentryTracer(TransactionContext("aTransaction", "op"), scopes) } + whenever(hub.options).thenReturn(SentryOptions()) + whenever(hub.span).then { SentryTracer(TransactionContext("aTransaction", "op"), hub) } sampleService.methodWithTransactionNameSet() - verify(scopes, times(0)).captureTransaction(any(), any()) + verify(hub, times(0)).captureTransaction(any(), any()) } @Test fun `creates transaction around method in class annotated with @SentryTransaction`() { classAnnotatedSampleService.hello() - verify(scopes).captureTransaction( + verify(hub).captureTransaction( check { assertThat(it.transaction).isEqualTo("ClassAnnotatedSampleService.hello") assertThat(it.contexts.trace!!.operation).isEqualTo("op") @@ -139,7 +127,7 @@ class SentryTransactionAdviceTest { @Test fun `creates transaction with operation set around method in class annotated with @SentryTransaction`() { classAnnotatedWithOperationSampleService.hello() - verify(scopes).captureTransaction( + verify(hub).captureTransaction( check { assertThat(it.transaction).isEqualTo("ClassAnnotatedWithOperationSampleService.hello") assertThat(it.contexts.trace!!.operation).isEqualTo("my-op") @@ -153,14 +141,13 @@ class SentryTransactionAdviceTest { @Test fun `pushes the scope when advice starts`() { classAnnotatedSampleService.hello() - verify(scopes).forkedScopes(any()) - verify(scopes).makeCurrent() + verify(hub).pushScope() } @Test fun `pops the scope when advice finishes`() { classAnnotatedSampleService.hello() - verify(lifecycleToken).close() + verify(hub).popScope() } @Configuration @@ -178,10 +165,10 @@ class SentryTransactionAdviceTest { open fun classAnnotatedWithOperationSampleService() = ClassAnnotatedWithOperationSampleService() @Bean - open fun scopes(): IScopes { - val scopes = mock() - Sentry.setCurrentScopes(scopes) - return scopes + open fun hub(): IHub { + val hub = mock() + Sentry.setCurrentHub(hub) + return hub } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryScheduleHookTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryScheduleHookTest.kt index 88c33c3695..b09011d544 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryScheduleHookTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryScheduleHookTest.kt @@ -26,35 +26,35 @@ class SentryScheduleHookTest { } @Test - fun `scopes is reset to its state within the thread after hook is done`() { + fun `hub is reset to its state within the thread after hook is done`() { Sentry.init { it.dsn = dsn } val sut = SentryScheduleHook() - val mainScopes = Sentry.getCurrentScopes() - val threadedScopes = Sentry.getCurrentScopes().forkedCurrentScope("test") + val mainHub = Sentry.getCurrentHub() + val threadedHub = Sentry.getCurrentHub().clone() executor.submit { - Sentry.setCurrentScopes(threadedScopes) + Sentry.setCurrentHub(threadedHub) }.get() - assertEquals(mainScopes, Sentry.getCurrentScopes()) + assertEquals(mainHub, Sentry.getCurrentHub()) val callableFuture = executor.submit( sut.apply { - assertNotEquals(mainScopes, Sentry.getCurrentScopes()) - assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) + assertNotEquals(mainHub, Sentry.getCurrentHub()) + assertNotEquals(threadedHub, Sentry.getCurrentHub()) } ) callableFuture.get() executor.submit { - assertNotEquals(mainScopes, Sentry.getCurrentScopes()) - assertEquals(threadedScopes, Sentry.getCurrentScopes()) + assertNotEquals(mainHub, Sentry.getCurrentHub()) + assertEquals(threadedHub, Sentry.getCurrentHub()) }.get() } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt index 0b0d7e6496..be56a8391d 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebFluxTracingFilterTest.kt @@ -2,8 +2,8 @@ package io.sentry.spring.webflux import io.sentry.Breadcrumb import io.sentry.Hint +import io.sentry.IHub import io.sentry.ILogger -import io.sentry.IScopes import io.sentry.PropagationContext import io.sentry.ScopeCallback import io.sentry.Sentry @@ -17,7 +17,7 @@ import io.sentry.TransactionOptions import io.sentry.protocol.SentryId import io.sentry.protocol.SentryTransaction import io.sentry.protocol.TransactionNameSource -import io.sentry.spring.webflux.SentryWebFilter.SENTRY_SCOPES_KEY +import io.sentry.spring.webflux.SentryWebFilter.SENTRY_HUB_KEY import org.assertj.core.api.Assertions.assertThat import org.mockito.Mockito import org.mockito.kotlin.any @@ -47,7 +47,7 @@ import kotlin.test.fail class SentryWebFluxTracingFilterTest { private class Fixture { - val scopes = mock() + val hub = mock() lateinit var request: MockServerHttpRequest lateinit var exchange: MockServerWebExchange val chain = mock() @@ -58,36 +58,35 @@ class SentryWebFluxTracingFilterTest { val logger = mock() init { - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) } fun getSut(isEnabled: Boolean = true, status: HttpStatus = HttpStatus.OK, sentryTraceHeader: String? = null, baggageHeaders: List? = null, method: HttpMethod = HttpMethod.POST): SentryWebFilter { var requestBuilder = MockServerHttpRequest.method(method, "/product/{id}", 12) if (sentryTraceHeader != null) { requestBuilder = requestBuilder.header("sentry-trace", sentryTraceHeader) - whenever(scopes.startTransaction(any(), check { it.isBindToScope })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } + whenever(hub.startTransaction(any(), check { it.isBindToScope })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } } if (baggageHeaders != null) { requestBuilder = requestBuilder.header("baggage", *baggageHeaders.toTypedArray()) } request = requestBuilder.build() exchange = MockServerWebExchange.builder(request).build() - exchange.attributes.put(SENTRY_SCOPES_KEY, scopes) + exchange.attributes.put(SENTRY_HUB_KEY, hub) exchange.attributes.put(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, PathPatternParser().parse("/product/{id}")) exchange.response.statusCode = status - whenever(scopes.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, scopes) } - whenever(scopes.isEnabled).thenReturn(isEnabled) + whenever(hub.startTransaction(any(), check { assertTrue(it.isBindToScope) })).thenAnswer { SentryTracer(it.arguments[0] as TransactionContext, hub) } + whenever(hub.isEnabled).thenReturn(isEnabled) whenever(chain.filter(any())).thenReturn(Mono.create { s -> s.success() }) - whenever(scopes.continueTrace(anyOrNull(), anyOrNull())).thenAnswer { TransactionContext.fromPropagationContext(PropagationContext.fromHeaders(logger, it.arguments[0] as String?, it.arguments[1] as List?)) } - return SentryWebFilter(scopes) + whenever(hub.continueTrace(anyOrNull(), anyOrNull())).thenAnswer { TransactionContext.fromPropagationContext(PropagationContext.fromHeaders(logger, it.arguments[0] as String?, it.arguments[1] as List?)) } + return SentryWebFilter(hub) } } private val fixture = Fixture() - fun withMockScopes(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { - it.`when` { Sentry.getCurrentScopes() }.thenReturn(fixture.scopes) - it.`when` { Sentry.forkedRootScopes(any()) }.thenReturn(fixture.scopes) + fun withMockHub(closure: () -> Unit) = Mockito.mockStatic(Sentry::class.java).use { + it.`when` { Sentry.cloneMainHub() }.thenReturn(fixture.hub) closure.invoke() } @@ -95,10 +94,10 @@ class SentryWebFluxTracingFilterTest { fun `creates transaction around the request`() { val filter = fixture.getSut() - withMockScopes { + withMockHub { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.scopes).startTransaction( + verify(fixture.hub).startTransaction( check { assertEquals("POST /product/12", it.name) assertEquals(TransactionNameSource.URL, it.transactionNameSource) @@ -108,15 +107,15 @@ class SentryWebFluxTracingFilterTest { assertNotNull(it.customSamplingContext?.get("request")) assertTrue(it.customSamplingContext?.get("request") is ServerHttpRequest) assertTrue(it.isBindToScope) - assertThat(it.origin).isEqualTo("auto.spring.webflux") } ) verify(fixture.chain).filter(fixture.exchange) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.transaction).isEqualTo("POST /product/{id}") assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.OK) assertThat(it.contexts.trace!!.operation).isEqualTo("http.server") + assertThat(it.contexts.trace!!.origin).isEqualTo("auto.spring.webflux") assertThat(it.contexts.response!!.statusCode).isEqualTo(200) }, anyOrNull(), @@ -130,10 +129,10 @@ class SentryWebFluxTracingFilterTest { fun `sets correct span status based on the response status`() { val filter = fixture.getSut(status = HttpStatus.INTERNAL_SERVER_ERROR) - withMockScopes { + withMockHub { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.status).isEqualTo(SpanStatus.INTERNAL_ERROR) assertThat(it.contexts.response!!.statusCode).isEqualTo(500) @@ -149,10 +148,10 @@ class SentryWebFluxTracingFilterTest { fun `does not set span status for response status that dont match predefined span statuses`() { val filter = fixture.getSut(status = HttpStatus.FOUND) - withMockScopes { + withMockHub { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.status).isNull() }, @@ -167,10 +166,10 @@ class SentryWebFluxTracingFilterTest { fun `when sentry trace is not present, transaction does not have parentSpanId set`() { val filter = fixture.getSut() - withMockScopes { + withMockHub { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -186,10 +185,10 @@ class SentryWebFluxTracingFilterTest { val parentSpanId = SpanId() val filter = fixture.getSut(sentryTraceHeader = "${SentryId()}-$parentSpanId-1") - withMockScopes { + withMockHub { filter.filter(fixture.exchange, fixture.chain).block() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isEqualTo(parentSpanId) }, @@ -201,16 +200,16 @@ class SentryWebFluxTracingFilterTest { } @Test - fun `when scopes is disabled, components are not invoked`() { + fun `when hub is disabled, components are not invoked`() { val filter = fixture.getSut(isEnabled = false) - withMockScopes { + withMockHub { filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.scopes).isEnabled - verifyNoMoreInteractions(fixture.scopes) + verify(fixture.hub).isEnabled + verifyNoMoreInteractions(fixture.hub) } } @@ -218,7 +217,7 @@ class SentryWebFluxTracingFilterTest { fun `sets status to internal server error when chain throws exception`() { val filter = fixture.getSut() - withMockScopes { + withMockHub { whenever(fixture.chain.filter(any())).thenReturn(Mono.error(RuntimeException("error"))) try { @@ -226,7 +225,7 @@ class SentryWebFluxTracingFilterTest { fail("filter is expected to rethrow exception") } catch (_: Exception) { } - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.status).isEqualTo(SpanStatus.INTERNAL_ERROR) }, @@ -241,19 +240,21 @@ class SentryWebFluxTracingFilterTest { fun `does not track OPTIONS request with traceOptionsRequests=false`() { val filter = fixture.getSut(method = HttpMethod.OPTIONS) - withMockScopes { + withMockHub { fixture.options.isTraceOptionsRequests = false filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.scopes).isEnabled - verify(fixture.scopes, times(2)).options - verify(fixture.scopes).continueTrace(anyOrNull(), anyOrNull()) - verify(fixture.scopes).addBreadcrumb(any(), any()) - verify(fixture.scopes).configureScope(any()) - verifyNoMoreInteractions(fixture.scopes) + verify(fixture.hub).isEnabled + verify(fixture.hub, times(2)).options + verify(fixture.hub).continueTrace(anyOrNull(), anyOrNull()) + verify(fixture.hub).pushScope() + verify(fixture.hub).addBreadcrumb(any(), any()) + verify(fixture.hub).configureScope(any()) + verify(fixture.hub).popScope() + verifyNoMoreInteractions(fixture.hub) } } @@ -261,14 +262,14 @@ class SentryWebFluxTracingFilterTest { fun `tracks OPTIONS request with traceOptionsRequests=true`() { val filter = fixture.getSut(method = HttpMethod.OPTIONS) - withMockScopes { + withMockHub { fixture.options.isTraceOptionsRequests = true filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -283,14 +284,14 @@ class SentryWebFluxTracingFilterTest { fun `tracks POST request with traceOptionsRequests=false`() { val filter = fixture.getSut(method = HttpMethod.POST) - withMockScopes { + withMockHub { fixture.options.isTraceOptionsRequests = false filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertThat(it.contexts.trace!!.parentSpanId).isNull() }, @@ -309,19 +310,19 @@ class SentryWebFluxTracingFilterTest { fixture.options.enableTracing = false val filter = fixture.getSut(sentryTraceHeader = sentryTraceHeaderString, baggageHeaders = baggageHeaderStrings) - withMockScopes { + withMockHub { filter.filter(fixture.exchange, fixture.chain).block() verify(fixture.chain).filter(fixture.exchange) - verify(fixture.scopes, never()).captureTransaction( + verify(fixture.hub, never()).captureTransaction( anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull() ) - verify(fixture.scopes).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) + verify(fixture.hub).continueTrace(eq(sentryTraceHeaderString), eq(baggageHeaderStrings)) } } } diff --git a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebfluxIntegrationTest.kt b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebfluxIntegrationTest.kt index 316aaf8738..0d4346c5ae 100644 --- a/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebfluxIntegrationTest.kt +++ b/sentry-spring/src/test/kotlin/io/sentry/spring/webflux/SentryWebfluxIntegrationTest.kt @@ -1,8 +1,8 @@ package io.sentry.spring.webflux -import io.sentry.IScopes +import io.sentry.HubAdapter +import io.sentry.IHub import io.sentry.ITransportFactory -import io.sentry.ScopesAdapter import io.sentry.Sentry import io.sentry.checkEvent import io.sentry.checkTransaction @@ -95,7 +95,7 @@ class SentryWebfluxIntegrationTest { checkEvent { event -> assertEquals("GET /throws", event.transaction) assertNotNull(event.exceptions) { - val ex = it.last() + val ex = it.first() assertEquals("something went wrong", ex.value) assertNotNull(ex.mechanism) { assertThat(it.isHandled).isFalse() @@ -160,13 +160,13 @@ open class App { open fun mockTransport() = transport @Bean - open fun scopes() = ScopesAdapter.getInstance() + open fun hub() = HubAdapter.getInstance() @Bean - open fun sentryFilter(scopes: IScopes) = SentryWebFilter(scopes) + open fun sentryFilter(hub: IHub) = SentryWebFilter(hub) @Bean - open fun sentryWebExceptionHandler(scopes: IScopes) = SentryWebExceptionHandler(scopes) + open fun sentryWebExceptionHandler(hub: IHub) = SentryWebExceptionHandler(hub) @Bean open fun sentryScheduleHookRegistrar() = ApplicationRunner { diff --git a/sentry-test-support/api/sentry-test-support.api b/sentry-test-support/api/sentry-test-support.api index 27d7034690..ffce23a516 100644 --- a/sentry-test-support/api/sentry-test-support.api +++ b/sentry-test-support/api/sentry-test-support.api @@ -31,15 +31,7 @@ public final class io/sentry/test/ImmediateExecutorService : io/sentry/ISentryEx public fun submit (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Future; } -public final class io/sentry/test/MocksKt { - public static final fun createSentryClientMock (Z)Lio/sentry/ISentryClient; - public static synthetic fun createSentryClientMock$default (ZILjava/lang/Object;)Lio/sentry/ISentryClient; - public static final fun createTestScopes (Lio/sentry/SentryOptions;ZLio/sentry/IScope;Lio/sentry/IScope;Lio/sentry/IScope;)Lio/sentry/Scopes; - public static synthetic fun createTestScopes$default (Lio/sentry/SentryOptions;ZLio/sentry/IScope;Lio/sentry/IScope;Lio/sentry/IScope;ILjava/lang/Object;)Lio/sentry/Scopes; -} - public final class io/sentry/test/ReflectionKt { - public static final fun collectInterfaceHierarchy (Ljava/lang/Class;)Ljava/util/List; public static final fun containsMethod (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Z public static final fun containsMethod (Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Z public static final fun getCtor (Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor; diff --git a/sentry-test-support/src/main/kotlin/io/sentry/test/Mocks.kt b/sentry-test-support/src/main/kotlin/io/sentry/test/Mocks.kt index 26f0066beb..168f3e6372 100644 --- a/sentry-test-support/src/main/kotlin/io/sentry/test/Mocks.kt +++ b/sentry-test-support/src/main/kotlin/io/sentry/test/Mocks.kt @@ -1,19 +1,11 @@ // ktlint-disable filename package io.sentry.test -import io.sentry.IScope -import io.sentry.ISentryClient import io.sentry.ISentryExecutorService -import io.sentry.Scope -import io.sentry.Scopes -import io.sentry.SentryOptions import io.sentry.backpressure.IBackpressureMonitor -import org.mockito.kotlin.any import org.mockito.kotlin.mock -import org.mockito.kotlin.whenever import java.util.concurrent.Callable import java.util.concurrent.Future -import java.util.concurrent.atomic.AtomicBoolean class ImmediateExecutorService : ISentryExecutorService { override fun submit(runnable: Runnable): Future<*> { @@ -73,22 +65,3 @@ class DeferredExecutorService : ISentryExecutorService { fun hasScheduledRunnables(): Boolean = scheduledRunnables.isNotEmpty() } - -fun createSentryClientMock(enabled: Boolean = true) = mock().also { - val isEnabled = AtomicBoolean(enabled) - whenever(it.isEnabled).then { isEnabled.get() } - whenever(it.close()).then { isEnabled.set(false) } - whenever(it.close(any())).then { isEnabled.set(false) } -} - -fun createTestScopes(options: SentryOptions? = null, enabled: Boolean = true, scope: IScope? = null, isolationScope: IScope? = null, globalScope: IScope? = null): Scopes { - val optionsToUse = options ?: SentryOptions().also { it.dsn = "https://key@sentry.io/proj" } - val scopeToUse = scope ?: Scope(optionsToUse) - val isolationScopeToUse = isolationScope ?: Scope(optionsToUse) - val globalScopeToUse = globalScope ?: Scope(optionsToUse) - return Scopes(scopeToUse, isolationScopeToUse, globalScopeToUse, "test").also { - if (enabled) { - it.bindClient(createSentryClientMock()) - } - } -} diff --git a/sentry-test-support/src/main/kotlin/io/sentry/test/Reflection.kt b/sentry-test-support/src/main/kotlin/io/sentry/test/Reflection.kt index 19d11676e1..690dcc8725 100644 --- a/sentry-test-support/src/main/kotlin/io/sentry/test/Reflection.kt +++ b/sentry-test-support/src/main/kotlin/io/sentry/test/Reflection.kt @@ -12,23 +12,16 @@ inline fun T.callMethod(name: String, parameterTypes: Class<*> val declaredMethod = try { T::class.java.getDeclaredMethod(name, parameterTypes) } catch (e: NoSuchMethodException) { - collectInterfaceHierarchy(T::class.java).first { it.containsMethod(name, parameterTypes) }.getDeclaredMethod(name, parameterTypes) + T::class.java.interfaces.first { it.containsMethod(name, parameterTypes) }.getDeclaredMethod(name, parameterTypes) } return declaredMethod.invoke(this, value) } -fun collectInterfaceHierarchy(clazz: Class<*>): List> { - if (clazz.interfaces.isEmpty()) { - return listOf(clazz) - } - return clazz.interfaces.flatMap { iface -> collectInterfaceHierarchy(iface) }.also { it.toMutableList().add(clazz) } -} - inline fun T.callMethod(name: String, parameterTypes: Array>, vararg value: Any?): Any? { val declaredMethod = try { T::class.java.getDeclaredMethod(name, *parameterTypes) } catch (e: NoSuchMethodException) { - collectInterfaceHierarchy(T::class.java).first { it.containsMethod(name, parameterTypes) }.getDeclaredMethod(name, *parameterTypes) + T::class.java.interfaces.first { it.containsMethod(name, parameterTypes) }.getDeclaredMethod(name, *parameterTypes) } return declaredMethod.invoke(this, *value) } diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 52d6c2f329..d29b04dd7d 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -66,7 +66,7 @@ public final class io/sentry/Baggage { public fun setUserId (Ljava/lang/String;)V public fun setUserSegment (Ljava/lang/String;)V public fun setValuesFromScope (Lio/sentry/IScope;Lio/sentry/SentryOptions;)V - public fun setValuesFromTransaction (Lio/sentry/protocol/SentryId;Lio/sentry/protocol/User;Lio/sentry/SentryOptions;Lio/sentry/TracesSamplingDecision;Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;)V + public fun setValuesFromTransaction (Lio/sentry/ITransaction;Lio/sentry/protocol/User;Lio/sentry/SentryOptions;Lio/sentry/TracesSamplingDecision;)V public fun toHeaderString (Ljava/lang/String;)Ljava/lang/String; public fun toTraceContext ()Lio/sentry/TraceContext; } @@ -93,12 +93,10 @@ public final class io/sentry/BaggageHeader { public fun getValue ()Ljava/lang/String; } -public final class io/sentry/Breadcrumb : io/sentry/JsonSerializable, io/sentry/JsonUnknown, java/lang/Comparable { +public final class io/sentry/Breadcrumb : io/sentry/JsonSerializable, io/sentry/JsonUnknown { public fun ()V public fun (Ljava/lang/String;)V public fun (Ljava/util/Date;)V - public fun compareTo (Lio/sentry/Breadcrumb;)I - public synthetic fun compareTo (Ljava/lang/Object;)I public static fun debug (Ljava/lang/String;)Lio/sentry/Breadcrumb; public fun equals (Ljava/lang/Object;)Z public static fun error (Ljava/lang/String;)Lio/sentry/Breadcrumb; @@ -208,102 +206,6 @@ public final class io/sentry/CheckInStatus : java/lang/Enum { public static fun values ()[Lio/sentry/CheckInStatus; } -public final class io/sentry/CombinedContextsView : io/sentry/protocol/Contexts { - public fun (Lio/sentry/protocol/Contexts;Lio/sentry/protocol/Contexts;Lio/sentry/protocol/Contexts;Lio/sentry/ScopeType;)V - public fun containsKey (Ljava/lang/Object;)Z - public fun entrySet ()Ljava/util/Set; - public fun get (Ljava/lang/Object;)Ljava/lang/Object; - public fun getApp ()Lio/sentry/protocol/App; - public fun getBrowser ()Lio/sentry/protocol/Browser; - public fun getDevice ()Lio/sentry/protocol/Device; - public fun getGpu ()Lio/sentry/protocol/Gpu; - public fun getOperatingSystem ()Lio/sentry/protocol/OperatingSystem; - public fun getResponse ()Lio/sentry/protocol/Response; - public fun getRuntime ()Lio/sentry/protocol/SentryRuntime; - public fun getSize ()I - public fun getTrace ()Lio/sentry/SpanContext; - public fun isEmpty ()Z - public fun keys ()Ljava/util/Enumeration; - public fun put (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; - public fun remove (Ljava/lang/Object;)Ljava/lang/Object; - public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V - public fun setApp (Lio/sentry/protocol/App;)V - public fun setBrowser (Lio/sentry/protocol/Browser;)V - public fun setDevice (Lio/sentry/protocol/Device;)V - public fun setGpu (Lio/sentry/protocol/Gpu;)V - public fun setOperatingSystem (Lio/sentry/protocol/OperatingSystem;)V - public fun setResponse (Lio/sentry/protocol/Response;)V - public fun setRuntime (Lio/sentry/protocol/SentryRuntime;)V - public fun setTrace (Lio/sentry/SpanContext;)V - public fun size ()I - public fun withResponse (Lio/sentry/util/HintUtils$SentryConsumer;)V -} - -public final class io/sentry/CombinedScopeView : io/sentry/IScope { - public fun (Lio/sentry/IScope;Lio/sentry/IScope;Lio/sentry/IScope;)V - public fun addAttachment (Lio/sentry/Attachment;)V - public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V - public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V - public fun addEventProcessor (Lio/sentry/EventProcessor;)V - public fun assignTraceContext (Lio/sentry/SentryEvent;)V - public fun bindClient (Lio/sentry/ISentryClient;)V - public fun clear ()V - public fun clearAttachments ()V - public fun clearBreadcrumbs ()V - public fun clearSession ()V - public fun clearTransaction ()V - public fun clone ()Lio/sentry/IScope; - public synthetic fun clone ()Ljava/lang/Object; - public fun endSession ()Lio/sentry/Session; - public fun getAttachments ()Ljava/util/List; - public fun getBreadcrumbs ()Ljava/util/Queue; - public fun getClient ()Lio/sentry/ISentryClient; - public fun getContexts ()Lio/sentry/protocol/Contexts; - public fun getEventProcessors ()Ljava/util/List; - public fun getEventProcessorsWithOrder ()Ljava/util/List; - public fun getExtras ()Ljava/util/Map; - public fun getFingerprint ()Ljava/util/List; - public fun getLastEventId ()Lio/sentry/protocol/SentryId; - public fun getLevel ()Lio/sentry/SentryLevel; - public fun getOptions ()Lio/sentry/SentryOptions; - public fun getPropagationContext ()Lio/sentry/PropagationContext; - public fun getRequest ()Lio/sentry/protocol/Request; - public fun getScreen ()Ljava/lang/String; - public fun getSession ()Lio/sentry/Session; - public fun getSpan ()Lio/sentry/ISpan; - public fun getTags ()Ljava/util/Map; - public fun getTransaction ()Lio/sentry/ITransaction; - public fun getTransactionName ()Ljava/lang/String; - public fun getUser ()Lio/sentry/protocol/User; - public fun removeContexts (Ljava/lang/String;)V - public fun removeExtra (Ljava/lang/String;)V - public fun removeTag (Ljava/lang/String;)V - public fun replaceOptions (Lio/sentry/SentryOptions;)V - public fun setContexts (Ljava/lang/String;Ljava/lang/Boolean;)V - public fun setContexts (Ljava/lang/String;Ljava/lang/Character;)V - public fun setContexts (Ljava/lang/String;Ljava/lang/Number;)V - public fun setContexts (Ljava/lang/String;Ljava/lang/Object;)V - public fun setContexts (Ljava/lang/String;Ljava/lang/String;)V - public fun setContexts (Ljava/lang/String;Ljava/util/Collection;)V - public fun setContexts (Ljava/lang/String;[Ljava/lang/Object;)V - public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V - public fun setFingerprint (Ljava/util/List;)V - public fun setLastEventId (Lio/sentry/protocol/SentryId;)V - public fun setLevel (Lio/sentry/SentryLevel;)V - public fun setPropagationContext (Lio/sentry/PropagationContext;)V - public fun setRequest (Lio/sentry/protocol/Request;)V - public fun setScreen (Ljava/lang/String;)V - public fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V - public fun setTag (Ljava/lang/String;Ljava/lang/String;)V - public fun setTransaction (Lio/sentry/ITransaction;)V - public fun setTransaction (Ljava/lang/String;)V - public fun setUser (Lio/sentry/protocol/User;)V - public fun startSession ()Lio/sentry/Scope$SessionPair; - public fun withPropagationContext (Lio/sentry/Scope$IWithPropagationContext;)Lio/sentry/PropagationContext; - public fun withSession (Lio/sentry/Scope$IWithSession;)Lio/sentry/Session; - public fun withTransaction (Lio/sentry/Scope$IWithTransaction;)V -} - public final class io/sentry/CpuCollectionData { public fun (JD)V public fun getCpuUsagePercentage ()D @@ -327,7 +229,6 @@ public final class io/sentry/DataCategory : java/lang/Enum { public static final field Profile Lio/sentry/DataCategory; public static final field Security Lio/sentry/DataCategory; public static final field Session Lio/sentry/DataCategory; - public static final field Span Lio/sentry/DataCategory; public static final field Transaction Lio/sentry/DataCategory; public static final field Unknown Lio/sentry/DataCategory; public static final field UserReport Lio/sentry/DataCategory; @@ -356,25 +257,9 @@ public final class io/sentry/DateUtils { public final class io/sentry/DeduplicateMultithreadedEventProcessor : io/sentry/EventProcessor { public fun (Lio/sentry/SentryOptions;)V - public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } -public final class io/sentry/DefaultScopesStorage : io/sentry/IScopesStorage { - public fun ()V - public fun close ()V - public fun get ()Lio/sentry/IScopes; - public fun set (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; -} - -public final class io/sentry/DefaultSpanFactory : io/sentry/ISpanFactory { - public fun ()V - public fun createSpan (Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/SpanContext;Lio/sentry/ISpan;)Lio/sentry/ISpan; - public fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; - public fun retrieveCurrentSpan (Lio/sentry/IScope;)Lio/sentry/ISpan; - public fun retrieveCurrentSpan (Lio/sentry/IScopes;)Lio/sentry/ISpan; -} - public final class io/sentry/DefaultTransactionPerformanceCollector : io/sentry/TransactionPerformanceCollector { public fun (Lio/sentry/SentryOptions;)V public fun close ()V @@ -400,7 +285,6 @@ public final class io/sentry/DsnUtil { public final class io/sentry/DuplicateEventDetectionEventProcessor : io/sentry/EventProcessor { public fun (Lio/sentry/SentryOptions;)V - public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } @@ -410,13 +294,12 @@ public final class io/sentry/EnvelopeReader : io/sentry/IEnvelopeReader { } public final class io/sentry/EnvelopeSender : io/sentry/IEnvelopeSender { - public fun (Lio/sentry/IScopes;Lio/sentry/ISerializer;Lio/sentry/ILogger;JI)V + public fun (Lio/sentry/IHub;Lio/sentry/ISerializer;Lio/sentry/ILogger;JI)V public synthetic fun processDirectory (Ljava/io/File;)V public fun processEnvelopeFile (Ljava/lang/String;Lio/sentry/Hint;)V } public abstract interface class io/sentry/EventProcessor { - public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; public fun process (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryTransaction; } @@ -461,7 +344,6 @@ public final class io/sentry/ExternalOptions { public fun isEnableBackpressureHandling ()Ljava/lang/Boolean; public fun isEnablePrettySerializationOutput ()Ljava/lang/Boolean; public fun isEnabled ()Ljava/lang/Boolean; - public fun isSendDefaultPii ()Ljava/lang/Boolean; public fun isSendModules ()Ljava/lang/Boolean; public fun setCron (Lio/sentry/SentryOptions$Cron;)V public fun setDebug (Ljava/lang/Boolean;)V @@ -483,7 +365,6 @@ public final class io/sentry/ExternalOptions { public fun setProxy (Lio/sentry/SentryOptions$Proxy;)V public fun setRelease (Ljava/lang/String;)V public fun setSendClientReports (Ljava/lang/Boolean;)V - public fun setSendDefaultPii (Ljava/lang/Boolean;)V public fun setSendModules (Ljava/lang/Boolean;)V public fun setServerName (Ljava/lang/String;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V @@ -530,7 +411,8 @@ public final class io/sentry/HttpStatusCodeRange { public fun isInRange (I)Z } -public final class io/sentry/HubAdapter : io/sentry/IHub { +public final class io/sentry/Hub : io/sentry/IHub, io/sentry/metrics/MetricsApi$IMetricsInterface { + public fun (Lio/sentry/SentryOptions;)V public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V public fun bindClient (Lio/sentry/ISentryClient;)V @@ -549,34 +431,26 @@ public final class io/sentry/HubAdapter : io/sentry/IHub { public synthetic fun clone ()Ljava/lang/Object; public fun close ()V public fun close (Z)V - public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V + public fun configureScope (Lio/sentry/ScopeCallback;)V public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public fun endSession ()V public fun flush (J)V - public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; - public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; - public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun getBaggage ()Lio/sentry/BaggageHeader; - public fun getGlobalScope ()Lio/sentry/IScope; - public static fun getInstance ()Lio/sentry/HubAdapter; - public fun getIsolationScope ()Lio/sentry/IScope; + public fun getDefaultTagsForMetrics ()Ljava/util/Map; public fun getLastEventId ()Lio/sentry/protocol/SentryId; + public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; + public fun getMetricsAggregator ()Lio/sentry/IMetricsAggregator; public fun getOptions ()Lio/sentry/SentryOptions; - public fun getParentScopes ()Lio/sentry/IScopes; public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; - public fun getScope ()Lio/sentry/IScope; public fun getSpan ()Lio/sentry/ISpan; public fun getTraceparent ()Lio/sentry/SentryTraceHeader; public fun getTransaction ()Lio/sentry/ITransaction; - public fun isAncestorOf (Lio/sentry/IScopes;)Z public fun isCrashedLastRun ()Ljava/lang/Boolean; public fun isEnabled ()Z public fun isHealthy ()Z - public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; public fun metrics ()Lio/sentry/metrics/MetricsApi; public fun popScope ()V - public fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; - public fun pushScope ()Lio/sentry/ISentryLifecycleToken; + public fun pushScope ()V public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V public fun reportFullyDisplayed ()V @@ -588,14 +462,13 @@ public final class io/sentry/HubAdapter : io/sentry/IHub { public fun setTransaction (Ljava/lang/String;)V public fun setUser (Lio/sentry/protocol/User;)V public fun startSession ()V + public fun startSpanForMetric (Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ISpan; public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; public fun traceHeaders ()Lio/sentry/SentryTraceHeader; - public fun withIsolationScope (Lio/sentry/ScopeCallback;)V public fun withScope (Lio/sentry/ScopeCallback;)V } -public final class io/sentry/HubScopesWrapper : io/sentry/IHub { - public fun (Lio/sentry/IScopes;)V +public final class io/sentry/HubAdapter : io/sentry/IHub { public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V public fun bindClient (Lio/sentry/ISentryClient;)V @@ -614,33 +487,24 @@ public final class io/sentry/HubScopesWrapper : io/sentry/IHub { public synthetic fun clone ()Ljava/lang/Object; public fun close ()V public fun close (Z)V - public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V + public fun configureScope (Lio/sentry/ScopeCallback;)V public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public fun endSession ()V public fun flush (J)V - public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; - public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; - public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun getBaggage ()Lio/sentry/BaggageHeader; - public fun getGlobalScope ()Lio/sentry/IScope; - public fun getIsolationScope ()Lio/sentry/IScope; + public static fun getInstance ()Lio/sentry/HubAdapter; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getOptions ()Lio/sentry/SentryOptions; - public fun getParentScopes ()Lio/sentry/IScopes; public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; - public fun getScope ()Lio/sentry/IScope; public fun getSpan ()Lio/sentry/ISpan; public fun getTraceparent ()Lio/sentry/SentryTraceHeader; public fun getTransaction ()Lio/sentry/ITransaction; - public fun isAncestorOf (Lio/sentry/IScopes;)Z public fun isCrashedLastRun ()Ljava/lang/Boolean; public fun isEnabled ()Z public fun isHealthy ()Z - public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; public fun metrics ()Lio/sentry/metrics/MetricsApi; public fun popScope ()V - public fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; - public fun pushScope ()Lio/sentry/ISentryLifecycleToken; + public fun pushScope ()V public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V public fun reportFullyDisplayed ()V @@ -654,7 +518,6 @@ public final class io/sentry/HubScopesWrapper : io/sentry/IHub { public fun startSession ()V public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; public fun traceHeaders ()Lio/sentry/SentryTraceHeader; - public fun withIsolationScope (Lio/sentry/ScopeCallback;)V public fun withScope (Lio/sentry/ScopeCallback;)V } @@ -686,7 +549,71 @@ public abstract interface class io/sentry/IEnvelopeSender { public abstract fun processEnvelopeFile (Ljava/lang/String;Lio/sentry/Hint;)V } -public abstract interface class io/sentry/IHub : io/sentry/IScopes { +public abstract interface class io/sentry/IHub { + public abstract fun addBreadcrumb (Lio/sentry/Breadcrumb;)V + public abstract fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V + public fun addBreadcrumb (Ljava/lang/String;)V + public fun addBreadcrumb (Ljava/lang/String;Ljava/lang/String;)V + public abstract fun bindClient (Lio/sentry/ISentryClient;)V + public abstract fun captureCheckIn (Lio/sentry/CheckIn;)Lio/sentry/protocol/SentryId; + public fun captureEnvelope (Lio/sentry/SentryEnvelope;)Lio/sentry/protocol/SentryId; + public abstract fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureEvent (Lio/sentry/SentryEvent;)Lio/sentry/protocol/SentryId; + public abstract fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public abstract fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureException (Ljava/lang/Throwable;)Lio/sentry/protocol/SentryId; + public abstract fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public abstract fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureException (Ljava/lang/Throwable;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureMessage (Ljava/lang/String;)Lio/sentry/protocol/SentryId; + public fun captureMessage (Ljava/lang/String;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public abstract fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; + public abstract fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;)Lio/sentry/protocol/SentryId; + public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public abstract fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; + public abstract fun captureUserFeedback (Lio/sentry/UserFeedback;)V + public abstract fun clearBreadcrumbs ()V + public abstract fun clone ()Lio/sentry/IHub; + public abstract fun close ()V + public abstract fun close (Z)V + public abstract fun configureScope (Lio/sentry/ScopeCallback;)V + public abstract fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; + public abstract fun endSession ()V + public abstract fun flush (J)V + public abstract fun getBaggage ()Lio/sentry/BaggageHeader; + public abstract fun getLastEventId ()Lio/sentry/protocol/SentryId; + public abstract fun getOptions ()Lio/sentry/SentryOptions; + public abstract fun getRateLimiter ()Lio/sentry/transport/RateLimiter; + public abstract fun getSpan ()Lio/sentry/ISpan; + public abstract fun getTraceparent ()Lio/sentry/SentryTraceHeader; + public abstract fun getTransaction ()Lio/sentry/ITransaction; + public abstract fun isCrashedLastRun ()Ljava/lang/Boolean; + public abstract fun isEnabled ()Z + public abstract fun isHealthy ()Z + public abstract fun metrics ()Lio/sentry/metrics/MetricsApi; + public abstract fun popScope ()V + public abstract fun pushScope ()V + public abstract fun removeExtra (Ljava/lang/String;)V + public abstract fun removeTag (Ljava/lang/String;)V + public fun reportFullDisplayed ()V + public abstract fun reportFullyDisplayed ()V + public abstract fun setExtra (Ljava/lang/String;Ljava/lang/String;)V + public abstract fun setFingerprint (Ljava/util/List;)V + public abstract fun setLevel (Lio/sentry/SentryLevel;)V + public abstract fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V + public abstract fun setTag (Ljava/lang/String;Ljava/lang/String;)V + public abstract fun setTransaction (Ljava/lang/String;)V + public abstract fun setUser (Lio/sentry/protocol/User;)V + public abstract fun startSession ()V + public fun startTransaction (Lio/sentry/TransactionContext;)Lio/sentry/ITransaction; + public abstract fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; + public fun startTransaction (Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ITransaction; + public fun startTransaction (Ljava/lang/String;Ljava/lang/String;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; + public abstract fun traceHeaders ()Lio/sentry/SentryTraceHeader; + public abstract fun withScope (Lio/sentry/ScopeCallback;)V } public abstract interface class io/sentry/ILogger { @@ -737,24 +664,18 @@ public abstract interface class io/sentry/IScope { public abstract fun addBreadcrumb (Lio/sentry/Breadcrumb;)V public abstract fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V public abstract fun addEventProcessor (Lio/sentry/EventProcessor;)V - public abstract fun assignTraceContext (Lio/sentry/SentryEvent;)V - public abstract fun bindClient (Lio/sentry/ISentryClient;)V public abstract fun clear ()V public abstract fun clearAttachments ()V public abstract fun clearBreadcrumbs ()V - public abstract fun clearSession ()V public abstract fun clearTransaction ()V public abstract fun clone ()Lio/sentry/IScope; public abstract fun endSession ()Lio/sentry/Session; public abstract fun getAttachments ()Ljava/util/List; public abstract fun getBreadcrumbs ()Ljava/util/Queue; - public abstract fun getClient ()Lio/sentry/ISentryClient; public abstract fun getContexts ()Lio/sentry/protocol/Contexts; public abstract fun getEventProcessors ()Ljava/util/List; - public abstract fun getEventProcessorsWithOrder ()Ljava/util/List; public abstract fun getExtras ()Ljava/util/Map; public abstract fun getFingerprint ()Ljava/util/List; - public abstract fun getLastEventId ()Lio/sentry/protocol/SentryId; public abstract fun getLevel ()Lio/sentry/SentryLevel; public abstract fun getOptions ()Lio/sentry/SentryOptions; public abstract fun getPropagationContext ()Lio/sentry/PropagationContext; @@ -769,7 +690,6 @@ public abstract interface class io/sentry/IScope { public abstract fun removeContexts (Ljava/lang/String;)V public abstract fun removeExtra (Ljava/lang/String;)V public abstract fun removeTag (Ljava/lang/String;)V - public abstract fun replaceOptions (Lio/sentry/SentryOptions;)V public abstract fun setContexts (Ljava/lang/String;Ljava/lang/Boolean;)V public abstract fun setContexts (Ljava/lang/String;Ljava/lang/Character;)V public abstract fun setContexts (Ljava/lang/String;Ljava/lang/Number;)V @@ -779,12 +699,10 @@ public abstract interface class io/sentry/IScope { public abstract fun setContexts (Ljava/lang/String;[Ljava/lang/Object;)V public abstract fun setExtra (Ljava/lang/String;Ljava/lang/String;)V public abstract fun setFingerprint (Ljava/util/List;)V - public abstract fun setLastEventId (Lio/sentry/protocol/SentryId;)V public abstract fun setLevel (Lio/sentry/SentryLevel;)V public abstract fun setPropagationContext (Lio/sentry/PropagationContext;)V public abstract fun setRequest (Lio/sentry/protocol/Request;)V public abstract fun setScreen (Ljava/lang/String;)V - public abstract fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V public abstract fun setTag (Ljava/lang/String;Ljava/lang/String;)V public abstract fun setTransaction (Lio/sentry/ITransaction;)V public abstract fun setTransaction (Ljava/lang/String;)V @@ -813,92 +731,6 @@ public abstract interface class io/sentry/IScopeObserver { public abstract fun setUser (Lio/sentry/protocol/User;)V } -public abstract interface class io/sentry/IScopes { - public abstract fun addBreadcrumb (Lio/sentry/Breadcrumb;)V - public abstract fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V - public fun addBreadcrumb (Ljava/lang/String;)V - public fun addBreadcrumb (Ljava/lang/String;Ljava/lang/String;)V - public abstract fun bindClient (Lio/sentry/ISentryClient;)V - public abstract fun captureCheckIn (Lio/sentry/CheckIn;)Lio/sentry/protocol/SentryId; - public fun captureEnvelope (Lio/sentry/SentryEnvelope;)Lio/sentry/protocol/SentryId; - public abstract fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureEvent (Lio/sentry/SentryEvent;)Lio/sentry/protocol/SentryId; - public abstract fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public abstract fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureException (Ljava/lang/Throwable;)Lio/sentry/protocol/SentryId; - public abstract fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public abstract fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureException (Ljava/lang/Throwable;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureMessage (Ljava/lang/String;)Lio/sentry/protocol/SentryId; - public fun captureMessage (Ljava/lang/String;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public abstract fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; - public abstract fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;)Lio/sentry/protocol/SentryId; - public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public abstract fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; - public abstract fun captureUserFeedback (Lio/sentry/UserFeedback;)V - public abstract fun clearBreadcrumbs ()V - public abstract fun clone ()Lio/sentry/IHub; - public abstract fun close ()V - public abstract fun close (Z)V - public fun configureScope (Lio/sentry/ScopeCallback;)V - public abstract fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V - public abstract fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; - public abstract fun endSession ()V - public abstract fun flush (J)V - public abstract fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; - public abstract fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; - public abstract fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; - public abstract fun getBaggage ()Lio/sentry/BaggageHeader; - public abstract fun getGlobalScope ()Lio/sentry/IScope; - public abstract fun getIsolationScope ()Lio/sentry/IScope; - public abstract fun getLastEventId ()Lio/sentry/protocol/SentryId; - public abstract fun getOptions ()Lio/sentry/SentryOptions; - public abstract fun getParentScopes ()Lio/sentry/IScopes; - public abstract fun getRateLimiter ()Lio/sentry/transport/RateLimiter; - public abstract fun getScope ()Lio/sentry/IScope; - public abstract fun getSpan ()Lio/sentry/ISpan; - public abstract fun getTraceparent ()Lio/sentry/SentryTraceHeader; - public abstract fun getTransaction ()Lio/sentry/ITransaction; - public abstract fun isAncestorOf (Lio/sentry/IScopes;)Z - public abstract fun isCrashedLastRun ()Ljava/lang/Boolean; - public abstract fun isEnabled ()Z - public abstract fun isHealthy ()Z - public fun isNoOp ()Z - public abstract fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; - public abstract fun metrics ()Lio/sentry/metrics/MetricsApi; - public abstract fun popScope ()V - public abstract fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; - public abstract fun pushScope ()Lio/sentry/ISentryLifecycleToken; - public abstract fun removeExtra (Ljava/lang/String;)V - public abstract fun removeTag (Ljava/lang/String;)V - public fun reportFullDisplayed ()V - public abstract fun reportFullyDisplayed ()V - public abstract fun setExtra (Ljava/lang/String;Ljava/lang/String;)V - public abstract fun setFingerprint (Ljava/util/List;)V - public abstract fun setLevel (Lio/sentry/SentryLevel;)V - public abstract fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V - public abstract fun setTag (Ljava/lang/String;Ljava/lang/String;)V - public abstract fun setTransaction (Ljava/lang/String;)V - public abstract fun setUser (Lio/sentry/protocol/User;)V - public abstract fun startSession ()V - public fun startTransaction (Lio/sentry/TransactionContext;)Lio/sentry/ITransaction; - public abstract fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; - public fun startTransaction (Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ITransaction; - public fun startTransaction (Ljava/lang/String;Ljava/lang/String;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; - public abstract fun traceHeaders ()Lio/sentry/SentryTraceHeader; - public abstract fun withIsolationScope (Lio/sentry/ScopeCallback;)V - public abstract fun withScope (Lio/sentry/ScopeCallback;)V -} - -public abstract interface class io/sentry/IScopesStorage { - public abstract fun close ()V - public abstract fun get ()Lio/sentry/IScopes; - public abstract fun set (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; -} - public abstract interface class io/sentry/ISentryClient { public abstract fun captureCheckIn (Lio/sentry/CheckIn;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureEnvelope (Lio/sentry/SentryEnvelope;)Lio/sentry/protocol/SentryId; @@ -938,10 +770,6 @@ public abstract interface class io/sentry/ISentryExecutorService { public abstract fun submit (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Future; } -public abstract interface class io/sentry/ISentryLifecycleToken : java/lang/AutoCloseable { - public abstract fun close ()V -} - public abstract interface class io/sentry/ISerializer { public abstract fun deserialize (Ljava/io/Reader;Ljava/lang/Class;)Ljava/lang/Object; public abstract fun deserializeCollection (Ljava/io/Reader;Ljava/lang/Class;Lio/sentry/JsonDeserializer;)Ljava/lang/Object; @@ -955,13 +783,11 @@ public abstract interface class io/sentry/ISpan { public abstract fun finish ()V public abstract fun finish (Lio/sentry/SpanStatus;)V public abstract fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V - public abstract fun getContexts ()Lio/sentry/protocol/Contexts; public abstract fun getData (Ljava/lang/String;)Ljava/lang/Object; public abstract fun getDescription ()Ljava/lang/String; public abstract fun getFinishDate ()Lio/sentry/SentryDate; public abstract fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; public abstract fun getOperation ()Ljava/lang/String; - public abstract fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision; public abstract fun getSpanContext ()Lio/sentry/SpanContext; public abstract fun getStartDate ()Lio/sentry/SentryDate; public abstract fun getStatus ()Lio/sentry/SpanStatus; @@ -969,9 +795,6 @@ public abstract interface class io/sentry/ISpan { public abstract fun getThrowable ()Ljava/lang/Throwable; public abstract fun isFinished ()Z public abstract fun isNoOp ()Z - public abstract fun isSampled ()Ljava/lang/Boolean; - public abstract fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; - public abstract fun setContext (Ljava/lang/String;Ljava/lang/Object;)V public abstract fun setData (Ljava/lang/String;Ljava/lang/Object;)V public abstract fun setDescription (Ljava/lang/String;)V public abstract fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;)V @@ -980,7 +803,6 @@ public abstract interface class io/sentry/ISpan { public abstract fun setStatus (Lio/sentry/SpanStatus;)V public abstract fun setTag (Ljava/lang/String;Ljava/lang/String;)V public abstract fun setThrowable (Ljava/lang/Throwable;)V - public abstract fun startChild (Lio/sentry/SpanContext;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; public abstract fun startChild (Ljava/lang/String;)Lio/sentry/ISpan; public abstract fun startChild (Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ISpan; public abstract fun startChild (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryDate;Lio/sentry/Instrumenter;)Lio/sentry/ISpan; @@ -992,23 +814,20 @@ public abstract interface class io/sentry/ISpan { public abstract fun updateEndDate (Lio/sentry/SentryDate;)Z } -public abstract interface class io/sentry/ISpanFactory { - public abstract fun createSpan (Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/SpanContext;Lio/sentry/ISpan;)Lio/sentry/ISpan; - public abstract fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; - public abstract fun retrieveCurrentSpan (Lio/sentry/IScope;)Lio/sentry/ISpan; - public abstract fun retrieveCurrentSpan (Lio/sentry/IScopes;)Lio/sentry/ISpan; -} - public abstract interface class io/sentry/ITransaction : io/sentry/ISpan { public abstract fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;ZLio/sentry/Hint;)V public abstract fun forceFinish (Lio/sentry/SpanStatus;ZLio/sentry/Hint;)V + public abstract fun getContexts ()Lio/sentry/protocol/Contexts; public abstract fun getEventId ()Lio/sentry/protocol/SentryId; - public abstract fun getLatestActiveSpan ()Lio/sentry/ISpan; + public abstract fun getLatestActiveSpan ()Lio/sentry/Span; public abstract fun getName ()Ljava/lang/String; + public abstract fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision; public abstract fun getSpans ()Ljava/util/List; public abstract fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; public abstract fun isProfileSampled ()Ljava/lang/Boolean; + public abstract fun isSampled ()Ljava/lang/Boolean; public abstract fun scheduleFinish ()V + 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; @@ -1034,7 +853,7 @@ public final class io/sentry/Instrumenter : java/lang/Enum { } public abstract interface class io/sentry/Integration { - public abstract fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public abstract fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/IpAddressUtils { @@ -1142,7 +961,6 @@ public abstract interface class io/sentry/JsonUnknown { public final class io/sentry/MainEventProcessor : io/sentry/EventProcessor, java/io/Closeable { public fun (Lio/sentry/SentryOptions;)V public fun close ()V - public fun getOrder ()Ljava/lang/Long; public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; public fun process (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryTransaction; } @@ -1353,35 +1171,24 @@ public final class io/sentry/NoOpHub : io/sentry/IHub { public synthetic fun clone ()Ljava/lang/Object; public fun close ()V public fun close (Z)V - public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V + public fun configureScope (Lio/sentry/ScopeCallback;)V public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public fun endSession ()V public fun flush (J)V - public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; - public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; - public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public fun getBaggage ()Lio/sentry/BaggageHeader; - public fun getGlobalScope ()Lio/sentry/IScope; public static fun getInstance ()Lio/sentry/NoOpHub; - public fun getIsolationScope ()Lio/sentry/IScope; public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getOptions ()Lio/sentry/SentryOptions; - public fun getParentScopes ()Lio/sentry/IScopes; public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; - public fun getScope ()Lio/sentry/IScope; public fun getSpan ()Lio/sentry/ISpan; public fun getTraceparent ()Lio/sentry/SentryTraceHeader; public fun getTransaction ()Lio/sentry/ITransaction; - public fun isAncestorOf (Lio/sentry/IScopes;)Z public fun isCrashedLastRun ()Ljava/lang/Boolean; public fun isEnabled ()Z public fun isHealthy ()Z - public fun isNoOp ()Z - public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; public fun metrics ()Lio/sentry/metrics/MetricsApi; public fun popScope ()V - public fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; - public fun pushScope ()Lio/sentry/ISentryLifecycleToken; + public fun pushScope ()V public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V public fun reportFullyDisplayed ()V @@ -1395,7 +1202,6 @@ public final class io/sentry/NoOpHub : io/sentry/IHub { public fun startSession ()V public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; public fun traceHeaders ()Lio/sentry/SentryTraceHeader; - public fun withIsolationScope (Lio/sentry/ScopeCallback;)V public fun withScope (Lio/sentry/ScopeCallback;)V } @@ -1412,26 +1218,20 @@ public final class io/sentry/NoOpScope : io/sentry/IScope { public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V public fun addEventProcessor (Lio/sentry/EventProcessor;)V - public fun assignTraceContext (Lio/sentry/SentryEvent;)V - public fun bindClient (Lio/sentry/ISentryClient;)V public fun clear ()V public fun clearAttachments ()V public fun clearBreadcrumbs ()V - public fun clearSession ()V public fun clearTransaction ()V public fun clone ()Lio/sentry/IScope; public synthetic fun clone ()Ljava/lang/Object; public fun endSession ()Lio/sentry/Session; public fun getAttachments ()Ljava/util/List; public fun getBreadcrumbs ()Ljava/util/Queue; - public fun getClient ()Lio/sentry/ISentryClient; public fun getContexts ()Lio/sentry/protocol/Contexts; public fun getEventProcessors ()Ljava/util/List; - public fun getEventProcessorsWithOrder ()Ljava/util/List; public fun getExtras ()Ljava/util/Map; public fun getFingerprint ()Ljava/util/List; public static fun getInstance ()Lio/sentry/NoOpScope; - public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getLevel ()Lio/sentry/SentryLevel; public fun getOptions ()Lio/sentry/SentryOptions; public fun getPropagationContext ()Lio/sentry/PropagationContext; @@ -1446,7 +1246,6 @@ public final class io/sentry/NoOpScope : io/sentry/IScope { public fun removeContexts (Ljava/lang/String;)V public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V - public fun replaceOptions (Lio/sentry/SentryOptions;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Boolean;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Character;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Number;)V @@ -1456,12 +1255,10 @@ public final class io/sentry/NoOpScope : io/sentry/IScope { public fun setContexts (Ljava/lang/String;[Ljava/lang/Object;)V public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V public fun setFingerprint (Ljava/util/List;)V - public fun setLastEventId (Lio/sentry/protocol/SentryId;)V public fun setLevel (Lio/sentry/SentryLevel;)V public fun setPropagationContext (Lio/sentry/PropagationContext;)V public fun setRequest (Lio/sentry/protocol/Request;)V public fun setScreen (Ljava/lang/String;)V - public fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V public fun setTransaction (Lio/sentry/ITransaction;)V public fun setTransaction (Ljava/lang/String;)V @@ -1472,95 +1269,16 @@ public final class io/sentry/NoOpScope : io/sentry/IScope { public fun withTransaction (Lio/sentry/Scope$IWithTransaction;)V } -public final class io/sentry/NoOpScopes : io/sentry/IScopes { - public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V - public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V - public fun bindClient (Lio/sentry/ISentryClient;)V - public fun captureCheckIn (Lio/sentry/CheckIn;)Lio/sentry/protocol/SentryId; - public fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; - public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; - public fun captureUserFeedback (Lio/sentry/UserFeedback;)V - public fun clearBreadcrumbs ()V - public fun clone ()Lio/sentry/IHub; - public synthetic fun clone ()Ljava/lang/Object; - public fun close ()V - public fun close (Z)V - public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V - public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; - public fun endSession ()V - public fun flush (J)V - public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; - public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; - public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; - public fun getBaggage ()Lio/sentry/BaggageHeader; - public fun getGlobalScope ()Lio/sentry/IScope; - public static fun getInstance ()Lio/sentry/NoOpScopes; - public fun getIsolationScope ()Lio/sentry/IScope; - public fun getLastEventId ()Lio/sentry/protocol/SentryId; - public fun getOptions ()Lio/sentry/SentryOptions; - public fun getParentScopes ()Lio/sentry/IScopes; - public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; - public fun getScope ()Lio/sentry/IScope; - public fun getSpan ()Lio/sentry/ISpan; - public fun getTraceparent ()Lio/sentry/SentryTraceHeader; - public fun getTransaction ()Lio/sentry/ITransaction; - public fun isAncestorOf (Lio/sentry/IScopes;)Z - public fun isCrashedLastRun ()Ljava/lang/Boolean; - public fun isEnabled ()Z - public fun isHealthy ()Z - public fun isNoOp ()Z - public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; - public fun metrics ()Lio/sentry/metrics/MetricsApi; - public fun popScope ()V - public fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; - public fun pushScope ()Lio/sentry/ISentryLifecycleToken; - public fun removeExtra (Ljava/lang/String;)V - public fun removeTag (Ljava/lang/String;)V - public fun reportFullyDisplayed ()V - public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V - public fun setFingerprint (Ljava/util/List;)V - public fun setLevel (Lio/sentry/SentryLevel;)V - public fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V - public fun setTag (Ljava/lang/String;Ljava/lang/String;)V - public fun setTransaction (Ljava/lang/String;)V - public fun setUser (Lio/sentry/protocol/User;)V - public fun startSession ()V - public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; - public fun traceHeaders ()Lio/sentry/SentryTraceHeader; - public fun withIsolationScope (Lio/sentry/ScopeCallback;)V - public fun withScope (Lio/sentry/ScopeCallback;)V -} - -public final class io/sentry/NoOpScopesLifecycleToken : io/sentry/ISentryLifecycleToken { - public fun close ()V - public static fun getInstance ()Lio/sentry/NoOpScopesLifecycleToken; -} - -public final class io/sentry/NoOpScopesStorage : io/sentry/IScopesStorage { - public fun close ()V - public fun get ()Lio/sentry/IScopes; - public static fun getInstance ()Lio/sentry/NoOpScopesStorage; - public fun set (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; -} - public final class io/sentry/NoOpSpan : io/sentry/ISpan { public fun finish ()V public fun finish (Lio/sentry/SpanStatus;)V public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V - public fun getContexts ()Lio/sentry/protocol/Contexts; public fun getData (Ljava/lang/String;)Ljava/lang/Object; public fun getDescription ()Ljava/lang/String; public fun getFinishDate ()Lio/sentry/SentryDate; public static fun getInstance ()Lio/sentry/NoOpSpan; public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; public fun getOperation ()Ljava/lang/String; - public fun getSamplingDecision ()Lio/sentry/TracesSamplingDecision; public fun getSpanContext ()Lio/sentry/SpanContext; public fun getStartDate ()Lio/sentry/SentryDate; public fun getStatus ()Lio/sentry/SpanStatus; @@ -1568,9 +1286,6 @@ public final class io/sentry/NoOpSpan : io/sentry/ISpan { public fun getThrowable ()Ljava/lang/Throwable; public fun isFinished ()Z public fun isNoOp ()Z - public fun isSampled ()Ljava/lang/Boolean; - public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; - public fun setContext (Ljava/lang/String;Ljava/lang/Object;)V public fun setData (Ljava/lang/String;Ljava/lang/Object;)V public fun setDescription (Ljava/lang/String;)V public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;)V @@ -1579,7 +1294,6 @@ public final class io/sentry/NoOpSpan : io/sentry/ISpan { public fun setStatus (Lio/sentry/SpanStatus;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V public fun setThrowable (Ljava/lang/Throwable;)V - public fun startChild (Lio/sentry/SpanContext;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; 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/Instrumenter;)Lio/sentry/ISpan; @@ -1591,14 +1305,6 @@ public final class io/sentry/NoOpSpan : io/sentry/ISpan { public fun updateEndDate (Lio/sentry/SentryDate;)Z } -public final class io/sentry/NoOpSpanFactory : io/sentry/ISpanFactory { - public fun createSpan (Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/SpanContext;Lio/sentry/ISpan;)Lio/sentry/ISpan; - public fun createTransaction (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;Lio/sentry/TransactionPerformanceCollector;)Lio/sentry/ITransaction; - public static fun getInstance ()Lio/sentry/NoOpSpanFactory; - public fun retrieveCurrentSpan (Lio/sentry/IScope;)Lio/sentry/ISpan; - public fun retrieveCurrentSpan (Lio/sentry/IScopes;)Lio/sentry/ISpan; -} - public final class io/sentry/NoOpTransaction : io/sentry/ITransaction { public fun finish ()V public fun finish (Lio/sentry/SpanStatus;)V @@ -1611,7 +1317,7 @@ public final class io/sentry/NoOpTransaction : io/sentry/ITransaction { public fun getEventId ()Lio/sentry/protocol/SentryId; public fun getFinishDate ()Lio/sentry/SentryDate; public static fun getInstance ()Lio/sentry/NoOpTransaction; - public fun getLatestActiveSpan ()Lio/sentry/ISpan; + public fun getLatestActiveSpan ()Lio/sentry/Span; public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; public fun getName ()Ljava/lang/String; public fun getOperation ()Ljava/lang/String; @@ -1627,7 +1333,6 @@ public final class io/sentry/NoOpTransaction : io/sentry/ITransaction { public fun isNoOp ()Z public fun isProfileSampled ()Ljava/lang/Boolean; public fun isSampled ()Ljava/lang/Boolean; - public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; public fun scheduleFinish ()V public fun setContext (Ljava/lang/String;Ljava/lang/Object;)V public fun setData (Ljava/lang/String;Ljava/lang/Object;)V @@ -1640,7 +1345,6 @@ public final class io/sentry/NoOpTransaction : io/sentry/ITransaction { public fun setStatus (Lio/sentry/SpanStatus;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V public fun setThrowable (Ljava/lang/Throwable;)V - public fun startChild (Lio/sentry/SpanContext;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; 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; @@ -1698,7 +1402,7 @@ public final class io/sentry/OptionsContainer { } public final class io/sentry/OutboxSender : io/sentry/IEnvelopeSender { - public fun (Lio/sentry/IScopes;Lio/sentry/IEnvelopeReader;Lio/sentry/ISerializer;Lio/sentry/ILogger;JI)V + public fun (Lio/sentry/IHub;Lio/sentry/IEnvelopeReader;Lio/sentry/ISerializer;Lio/sentry/ILogger;JI)V public synthetic fun processDirectory (Ljava/io/File;)V public fun processEnvelopeFile (Ljava/lang/String;Lio/sentry/Hint;)V } @@ -1885,25 +1589,19 @@ public final class io/sentry/Scope : io/sentry/IScope { public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V public fun addEventProcessor (Lio/sentry/EventProcessor;)V - public fun assignTraceContext (Lio/sentry/SentryEvent;)V - public fun bindClient (Lio/sentry/ISentryClient;)V public fun clear ()V public fun clearAttachments ()V public fun clearBreadcrumbs ()V - public fun clearSession ()V public fun clearTransaction ()V public fun clone ()Lio/sentry/IScope; public synthetic fun clone ()Ljava/lang/Object; public fun endSession ()Lio/sentry/Session; public fun getAttachments ()Ljava/util/List; public fun getBreadcrumbs ()Ljava/util/Queue; - public fun getClient ()Lio/sentry/ISentryClient; public fun getContexts ()Lio/sentry/protocol/Contexts; public fun getEventProcessors ()Ljava/util/List; - public fun getEventProcessorsWithOrder ()Ljava/util/List; public fun getExtras ()Ljava/util/Map; public fun getFingerprint ()Ljava/util/List; - public fun getLastEventId ()Lio/sentry/protocol/SentryId; public fun getLevel ()Lio/sentry/SentryLevel; public fun getOptions ()Lio/sentry/SentryOptions; public fun getPropagationContext ()Lio/sentry/PropagationContext; @@ -1918,7 +1616,6 @@ public final class io/sentry/Scope : io/sentry/IScope { public fun removeContexts (Ljava/lang/String;)V public fun removeExtra (Ljava/lang/String;)V public fun removeTag (Ljava/lang/String;)V - public fun replaceOptions (Lio/sentry/SentryOptions;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Boolean;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Character;)V public fun setContexts (Ljava/lang/String;Ljava/lang/Number;)V @@ -1928,12 +1625,10 @@ public final class io/sentry/Scope : io/sentry/IScope { public fun setContexts (Ljava/lang/String;[Ljava/lang/Object;)V public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V public fun setFingerprint (Ljava/util/List;)V - public fun setLastEventId (Lio/sentry/protocol/SentryId;)V public fun setLevel (Lio/sentry/SentryLevel;)V public fun setPropagationContext (Lio/sentry/PropagationContext;)V public fun setRequest (Lio/sentry/protocol/Request;)V public fun setScreen (Ljava/lang/String;)V - public fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V public fun setTransaction (Lio/sentry/ITransaction;)V public fun setTransaction (Ljava/lang/String;)V @@ -1975,158 +1670,11 @@ public abstract class io/sentry/ScopeObserverAdapter : io/sentry/IScopeObserver public fun setUser (Lio/sentry/protocol/User;)V } -public final class io/sentry/ScopeType : java/lang/Enum { - public static final field COMBINED Lio/sentry/ScopeType; - public static final field CURRENT Lio/sentry/ScopeType; - public static final field GLOBAL Lio/sentry/ScopeType; - public static final field ISOLATION Lio/sentry/ScopeType; - public static fun valueOf (Ljava/lang/String;)Lio/sentry/ScopeType; - public static fun values ()[Lio/sentry/ScopeType; -} - -public final class io/sentry/Scopes : io/sentry/IScopes, io/sentry/metrics/MetricsApi$IMetricsInterface { - public fun (Lio/sentry/IScope;Lio/sentry/IScope;Lio/sentry/IScope;Ljava/lang/String;)V - public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V - public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V - public fun bindClient (Lio/sentry/ISentryClient;)V - public fun captureCheckIn (Lio/sentry/CheckIn;)Lio/sentry/protocol/SentryId; - public fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; - public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; - public fun captureUserFeedback (Lio/sentry/UserFeedback;)V - public fun clearBreadcrumbs ()V - public fun clone ()Lio/sentry/IHub; - public synthetic fun clone ()Ljava/lang/Object; - public fun close ()V - public fun close (Z)V - public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V - public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; - public fun endSession ()V - public fun flush (J)V - public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; - public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; - public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; - public fun getBaggage ()Lio/sentry/BaggageHeader; - public fun getCreator ()Ljava/lang/String; - public fun getDefaultTagsForMetrics ()Ljava/util/Map; - public fun getGlobalScope ()Lio/sentry/IScope; - public fun getIsolationScope ()Lio/sentry/IScope; - public fun getLastEventId ()Lio/sentry/protocol/SentryId; - public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; - public fun getMetricsAggregator ()Lio/sentry/IMetricsAggregator; - public fun getOptions ()Lio/sentry/SentryOptions; - public fun getParentScopes ()Lio/sentry/IScopes; - public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; - public fun getScope ()Lio/sentry/IScope; - public fun getSpan ()Lio/sentry/ISpan; - public fun getTraceparent ()Lio/sentry/SentryTraceHeader; - public fun getTransaction ()Lio/sentry/ITransaction; - public fun isAncestorOf (Lio/sentry/IScopes;)Z - public fun isCrashedLastRun ()Ljava/lang/Boolean; - public fun isEnabled ()Z - public fun isHealthy ()Z - public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; - public fun metrics ()Lio/sentry/metrics/MetricsApi; - public fun popScope ()V - public fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; - public fun pushScope ()Lio/sentry/ISentryLifecycleToken; - public fun removeExtra (Ljava/lang/String;)V - public fun removeTag (Ljava/lang/String;)V - public fun reportFullyDisplayed ()V - public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V - public fun setFingerprint (Ljava/util/List;)V - public fun setLevel (Lio/sentry/SentryLevel;)V - public fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V - public fun setTag (Ljava/lang/String;Ljava/lang/String;)V - public fun setTransaction (Ljava/lang/String;)V - public fun setUser (Lio/sentry/protocol/User;)V - public fun startSession ()V - public fun startSpanForMetric (Ljava/lang/String;Ljava/lang/String;)Lio/sentry/ISpan; - public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; - public fun traceHeaders ()Lio/sentry/SentryTraceHeader; - public fun withIsolationScope (Lio/sentry/ScopeCallback;)V - public fun withScope (Lio/sentry/ScopeCallback;)V -} - -public final class io/sentry/ScopesAdapter : io/sentry/IScopes { - public fun addBreadcrumb (Lio/sentry/Breadcrumb;)V - public fun addBreadcrumb (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)V - public fun bindClient (Lio/sentry/ISentryClient;)V - public fun captureCheckIn (Lio/sentry/CheckIn;)Lio/sentry/protocol/SentryId; - public fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; - public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; - public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; - public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; - public fun captureUserFeedback (Lio/sentry/UserFeedback;)V - public fun clearBreadcrumbs ()V - public fun clone ()Lio/sentry/IHub; - public synthetic fun clone ()Ljava/lang/Object; - public fun close ()V - public fun close (Z)V - public fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V - public fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; - public fun endSession ()V - public fun flush (J)V - public fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; - public fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; - public fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; - public fun getBaggage ()Lio/sentry/BaggageHeader; - public fun getGlobalScope ()Lio/sentry/IScope; - public static fun getInstance ()Lio/sentry/ScopesAdapter; - public fun getIsolationScope ()Lio/sentry/IScope; - public fun getLastEventId ()Lio/sentry/protocol/SentryId; - public fun getOptions ()Lio/sentry/SentryOptions; - public fun getParentScopes ()Lio/sentry/IScopes; - public fun getRateLimiter ()Lio/sentry/transport/RateLimiter; - public fun getScope ()Lio/sentry/IScope; - public fun getSpan ()Lio/sentry/ISpan; - public fun getTraceparent ()Lio/sentry/SentryTraceHeader; - public fun getTransaction ()Lio/sentry/ITransaction; - public fun isAncestorOf (Lio/sentry/IScopes;)Z - public fun isCrashedLastRun ()Ljava/lang/Boolean; - public fun isEnabled ()Z - public fun isHealthy ()Z - public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; - public fun metrics ()Lio/sentry/metrics/MetricsApi; - public fun popScope ()V - public fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; - public fun pushScope ()Lio/sentry/ISentryLifecycleToken; - public fun removeExtra (Ljava/lang/String;)V - public fun removeTag (Ljava/lang/String;)V - public fun reportFullyDisplayed ()V - public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V - public fun setFingerprint (Ljava/util/List;)V - public fun setLevel (Lio/sentry/SentryLevel;)V - public fun setSpanContext (Ljava/lang/Throwable;Lio/sentry/ISpan;Ljava/lang/String;)V - public fun setTag (Ljava/lang/String;Ljava/lang/String;)V - public fun setTransaction (Ljava/lang/String;)V - public fun setUser (Lio/sentry/protocol/User;)V - public fun startSession ()V - public fun startTransaction (Lio/sentry/TransactionContext;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; - public fun traceHeaders ()Lio/sentry/SentryTraceHeader; - public fun withIsolationScope (Lio/sentry/ScopeCallback;)V - public fun withScope (Lio/sentry/ScopeCallback;)V -} - -public final class io/sentry/ScopesStorageFactory { - public fun ()V - public static fun create (Lio/sentry/util/LoadClass;Lio/sentry/ILogger;)Lio/sentry/IScopesStorage; -} - public final class io/sentry/SendCachedEnvelopeFireAndForgetIntegration : io/sentry/IConnectionStatusProvider$IConnectionStatusObserver, io/sentry/Integration, java/io/Closeable { public fun (Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForgetFactory;)V public fun close ()V public fun onConnectionStatusChanged (Lio/sentry/IConnectionStatusProvider$ConnectionStatus;)V - public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public abstract interface class io/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForget { @@ -2138,19 +1686,19 @@ public abstract interface class io/sentry/SendCachedEnvelopeFireAndForgetIntegra } public abstract interface class io/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForgetFactory { - public abstract fun create (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForget; + public abstract fun create (Lio/sentry/IHub;Lio/sentry/SentryOptions;)Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForget; public fun hasValidPath (Ljava/lang/String;Lio/sentry/ILogger;)Z public fun processDir (Lio/sentry/DirectoryProcessor;Ljava/lang/String;Lio/sentry/ILogger;)Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForget; } public final class io/sentry/SendFireAndForgetEnvelopeSender : io/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForgetFactory { public fun (Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForgetDirPath;)V - public fun create (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForget; + public fun create (Lio/sentry/IHub;Lio/sentry/SentryOptions;)Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForget; } public final class io/sentry/SendFireAndForgetOutboxSender : io/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForgetFactory { public fun (Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForgetDirPath;)V - public fun create (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForget; + public fun create (Lio/sentry/IHub;Lio/sentry/SentryOptions;)Lio/sentry/SendCachedEnvelopeFireAndForgetIntegration$SendFireAndForget; } public final class io/sentry/Sentry { @@ -2175,19 +1723,14 @@ public final class io/sentry/Sentry { public static fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; public static fun captureUserFeedback (Lio/sentry/UserFeedback;)V public static fun clearBreadcrumbs ()V + public static fun cloneMainHub ()Lio/sentry/IHub; public static fun close ()V public static fun configureScope (Lio/sentry/ScopeCallback;)V - public static fun configureScope (Lio/sentry/ScopeType;Lio/sentry/ScopeCallback;)V public static fun continueTrace (Ljava/lang/String;Ljava/util/List;)Lio/sentry/TransactionContext; public static fun endSession ()V public static fun flush (J)V - public static fun forkedCurrentScope (Ljava/lang/String;)Lio/sentry/IScopes; - public static fun forkedRootScopes (Ljava/lang/String;)Lio/sentry/IScopes; - public static fun forkedScopes (Ljava/lang/String;)Lio/sentry/IScopes; public static fun getBaggage ()Lio/sentry/BaggageHeader; public static fun getCurrentHub ()Lio/sentry/IHub; - public static fun getCurrentScopes ()Lio/sentry/IScopes; - public static fun getGlobalScope ()Lio/sentry/IScope; public static fun getLastEventId ()Lio/sentry/protocol/SentryId; public static fun getSpan ()Lio/sentry/ISpan; public static fun getTraceparent ()Lio/sentry/SentryTraceHeader; @@ -2203,14 +1746,12 @@ public final class io/sentry/Sentry { public static fun isHealthy ()Z public static fun metrics ()Lio/sentry/metrics/MetricsApi; public static fun popScope ()V - public static fun pushIsolationScope ()Lio/sentry/ISentryLifecycleToken; - public static fun pushScope ()Lio/sentry/ISentryLifecycleToken; + public static fun pushScope ()V public static fun removeExtra (Ljava/lang/String;)V public static fun removeTag (Ljava/lang/String;)V public static fun reportFullDisplayed ()V public static fun reportFullyDisplayed ()V - public static fun setCurrentHub (Lio/sentry/IHub;)Lio/sentry/ISentryLifecycleToken; - public static fun setCurrentScopes (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken; + public static fun setCurrentHub (Lio/sentry/IHub;)V public static fun setExtra (Ljava/lang/String;Ljava/lang/String;)V public static fun setFingerprint (Ljava/util/List;)V public static fun setLevel (Lio/sentry/SentryLevel;)V @@ -2224,7 +1765,6 @@ public final class io/sentry/Sentry { public static fun startTransaction (Ljava/lang/String;Ljava/lang/String;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; public static fun startTransaction (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/sentry/TransactionOptions;)Lio/sentry/ITransaction; public static fun traceHeaders ()Lio/sentry/SentryTraceHeader; - public static fun withIsolationScope (Lio/sentry/ScopeCallback;)V public static fun withScope (Lio/sentry/ScopeCallback;)V } @@ -2678,7 +2218,6 @@ public class io/sentry/SentryOptions { public fun getCron ()Lio/sentry/SentryOptions$Cron; public fun getDateProvider ()Lio/sentry/SentryDateProvider; public fun getDebugMetaLoader ()Lio/sentry/internal/debugmeta/IDebugMetaLoader; - public fun getDefaultScopeType ()Lio/sentry/ScopeType; public fun getDiagnosticLevel ()Lio/sentry/SentryLevel; public fun getDist ()Ljava/lang/String; public fun getDistinctId ()Ljava/lang/String; @@ -2695,12 +2234,10 @@ public class io/sentry/SentryOptions { public fun getIdleTimeout ()Ljava/lang/Long; public fun getIgnoredCheckIns ()Ljava/util/List; public fun getIgnoredExceptionsForType ()Ljava/util/Set; - public fun getIgnoredSpanOrigins ()Ljava/util/List; public fun getInAppExcludes ()Ljava/util/List; public fun getInAppIncludes ()Ljava/util/List; public fun getInstrumenter ()Lio/sentry/Instrumenter; public fun getIntegrations ()Ljava/util/List; - public fun getInternalTracesSampler ()Lio/sentry/TracesSampler; public fun getLogger ()Lio/sentry/ILogger; public fun getMainThreadChecker ()Lio/sentry/util/thread/IMainThreadChecker; public fun getMaxAttachmentSize ()J @@ -2733,7 +2270,6 @@ public class io/sentry/SentryOptions { public fun getSessionTrackingIntervalMillis ()J public fun getShutdownTimeout ()J public fun getShutdownTimeoutMillis ()J - public fun getSpanFactory ()Lio/sentry/ISpanFactory; public fun getSpotlightConnectionUrl ()Ljava/lang/String; public fun getSslSocketFactory ()Ljavax/net/ssl/SSLSocketFactory; public fun getTags ()Ljava/util/Map; @@ -2792,7 +2328,6 @@ public class io/sentry/SentryOptions { public fun setDateProvider (Lio/sentry/SentryDateProvider;)V public fun setDebug (Z)V public fun setDebugMetaLoader (Lio/sentry/internal/debugmeta/IDebugMetaLoader;)V - public fun setDefaultScopeType (Lio/sentry/ScopeType;)V public fun setDiagnosticLevel (Lio/sentry/SentryLevel;)V public fun setDist (Ljava/lang/String;)V public fun setDistinctId (Ljava/lang/String;)V @@ -2823,7 +2358,6 @@ public class io/sentry/SentryOptions { public fun setGestureTargetLocators (Ljava/util/List;)V public fun setIdleTimeout (Ljava/lang/Long;)V public fun setIgnoredCheckIns (Ljava/util/List;)V - public fun setIgnoredSpanOrigins (Ljava/util/List;)V public fun setInstrumenter (Lio/sentry/Instrumenter;)V public fun setLogger (Lio/sentry/ILogger;)V public fun setMainThreadChecker (Lio/sentry/util/thread/IMainThreadChecker;)V @@ -2857,7 +2391,6 @@ public class io/sentry/SentryOptions { public fun setSessionTrackingIntervalMillis (J)V public fun setShutdownTimeout (J)V public fun setShutdownTimeoutMillis (J)V - public fun setSpanFactory (Lio/sentry/ISpanFactory;)V public fun setSpotlightConnectionUrl (Ljava/lang/String;)V public fun setSslSocketFactory (Ljavax/net/ssl/SSLSocketFactory;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V @@ -2943,12 +2476,6 @@ public abstract interface class io/sentry/SentryOptions$TracesSamplerCallback { public abstract fun sample (Lio/sentry/SamplingContext;)Ljava/lang/Double; } -public final class io/sentry/SentrySpanFactoryHolder { - public fun ()V - public static fun getSpanFactory ()Lio/sentry/ISpanFactory; - public static fun setSpanFactory (Lio/sentry/ISpanFactory;)V -} - public final class io/sentry/SentrySpanStorage { public fun get (Ljava/lang/String;)Lio/sentry/ISpan; public static fun getInstance ()Lio/sentry/SentrySpanStorage; @@ -2979,8 +2506,8 @@ public final class io/sentry/SentryTraceHeader { } public final class io/sentry/SentryTracer : io/sentry/ITransaction { - public fun (Lio/sentry/TransactionContext;Lio/sentry/IScopes;)V - public fun (Lio/sentry/TransactionContext;Lio/sentry/IScopes;Lio/sentry/TransactionOptions;)V + public fun (Lio/sentry/TransactionContext;Lio/sentry/IHub;)V + public fun (Lio/sentry/TransactionContext;Lio/sentry/IHub;Lio/sentry/TransactionOptions;)V public fun finish ()V public fun finish (Lio/sentry/SpanStatus;)V public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V @@ -2993,7 +2520,7 @@ public final class io/sentry/SentryTracer : io/sentry/ITransaction { public fun getDescription ()Ljava/lang/String; public fun getEventId ()Lio/sentry/protocol/SentryId; public fun getFinishDate ()Lio/sentry/SentryDate; - public fun getLatestActiveSpan ()Lio/sentry/ISpan; + public fun getLatestActiveSpan ()Lio/sentry/Span; public fun getLocalMetricsAggregator ()Lio/sentry/metrics/LocalMetricsAggregator; public fun getName ()Ljava/lang/String; public fun getOperation ()Ljava/lang/String; @@ -3009,7 +2536,6 @@ public final class io/sentry/SentryTracer : io/sentry/ITransaction { public fun isNoOp ()Z public fun isProfileSampled ()Ljava/lang/Boolean; public fun isSampled ()Ljava/lang/Boolean; - public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; public fun scheduleFinish ()V public fun setContext (Ljava/lang/String;Ljava/lang/Object;)V public fun setData (Ljava/lang/String;Ljava/lang/Object;)V @@ -3024,7 +2550,6 @@ public final class io/sentry/SentryTracer : io/sentry/ITransaction { public fun setStatus (Lio/sentry/SpanStatus;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V public fun setThrowable (Ljava/lang/Throwable;)V - public fun startChild (Lio/sentry/SpanContext;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; 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; @@ -3111,15 +2636,14 @@ public final class io/sentry/ShutdownHookIntegration : io/sentry/Integration, ja public fun ()V public fun (Ljava/lang/Runtime;)V public fun close ()V - public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/Span : io/sentry/ISpan { - public fun (Lio/sentry/TransactionContext;Lio/sentry/SentryTracer;Lio/sentry/IScopes;Lio/sentry/SpanOptions;)V + public fun (Lio/sentry/TransactionContext;Lio/sentry/SentryTracer;Lio/sentry/IHub;Lio/sentry/SentryDate;Lio/sentry/SpanOptions;)V public fun finish ()V public fun finish (Lio/sentry/SpanStatus;)V public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V - public fun getContexts ()Lio/sentry/protocol/Contexts; public fun getData ()Ljava/util/Map; public fun getData (Ljava/lang/String;)Ljava/lang/Object; public fun getDescription ()Ljava/lang/String; @@ -3141,8 +2665,6 @@ public final class io/sentry/Span : io/sentry/ISpan { public fun isNoOp ()Z public fun isProfileSampled ()Ljava/lang/Boolean; public fun isSampled ()Ljava/lang/Boolean; - public fun makeCurrent ()Lio/sentry/ISentryLifecycleToken; - public fun setContext (Ljava/lang/String;Ljava/lang/Object;)V public fun setData (Ljava/lang/String;Ljava/lang/Object;)V public fun setDescription (Ljava/lang/String;)V public fun setMeasurement (Ljava/lang/String;Ljava/lang/Number;)V @@ -3151,7 +2673,6 @@ public final class io/sentry/Span : io/sentry/ISpan { public fun setStatus (Lio/sentry/SpanStatus;)V public fun setTag (Ljava/lang/String;Ljava/lang/String;)V public fun setThrowable (Ljava/lang/Throwable;)V - public fun startChild (Lio/sentry/SpanContext;Lio/sentry/SpanOptions;)Lio/sentry/ISpan; 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/Instrumenter;)Lio/sentry/ISpan; @@ -3164,9 +2685,7 @@ public final class io/sentry/Span : io/sentry/ISpan { } public class io/sentry/SpanContext : io/sentry/JsonSerializable, io/sentry/JsonUnknown { - public static final field DEFAULT_ORIGIN Ljava/lang/String; public static final field TYPE Ljava/lang/String; - protected field baggage Lio/sentry/Baggage; protected field description Ljava/lang/String; protected field op Ljava/lang/String; protected field origin Ljava/lang/String; @@ -3177,11 +2696,8 @@ public class io/sentry/SpanContext : io/sentry/JsonSerializable, io/sentry/JsonU public fun (Lio/sentry/protocol/SentryId;Lio/sentry/SpanId;Ljava/lang/String;Lio/sentry/SpanId;Lio/sentry/TracesSamplingDecision;)V public fun (Ljava/lang/String;)V public fun (Ljava/lang/String;Lio/sentry/TracesSamplingDecision;)V - public fun copyForChild (Ljava/lang/String;Lio/sentry/SpanId;Lio/sentry/SpanId;)Lio/sentry/SpanContext; public fun equals (Ljava/lang/Object;)Z - public fun getBaggage ()Lio/sentry/Baggage; public fun getDescription ()Ljava/lang/String; - public fun getInstrumenter ()Lio/sentry/Instrumenter; public fun getOperation ()Ljava/lang/String; public fun getOrigin ()Ljava/lang/String; public fun getParentSpanId ()Lio/sentry/SpanId; @@ -3196,7 +2712,6 @@ public class io/sentry/SpanContext : io/sentry/JsonSerializable, io/sentry/JsonU public fun hashCode ()I public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V public fun setDescription (Ljava/lang/String;)V - public fun setInstrumenter (Lio/sentry/Instrumenter;)V public fun setOperation (Ljava/lang/String;)V public fun setOrigin (Ljava/lang/String;)V public fun setSampled (Ljava/lang/Boolean;)V @@ -3245,10 +2760,6 @@ public abstract interface class io/sentry/SpanDataConvention { public static final field THREAD_NAME Ljava/lang/String; } -public abstract interface class io/sentry/SpanFinishedCallback { - public abstract fun execute (Lio/sentry/Span;)V -} - public final class io/sentry/SpanId : io/sentry/JsonSerializable { public static final field EMPTY_ID Lio/sentry/SpanId; public fun ()V @@ -3266,16 +2777,11 @@ public final class io/sentry/SpanId$Deserializer : io/sentry/JsonDeserializer { } public class io/sentry/SpanOptions { - protected field origin Ljava/lang/String; public fun ()V - public fun getOrigin ()Ljava/lang/String; - public fun getStartTimestamp ()Lio/sentry/SentryDate; public fun isIdle ()Z public fun isTrimEnd ()Z public fun isTrimStart ()Z public fun setIdle (Z)V - public fun setOrigin (Ljava/lang/String;)V - public fun setStartTimestamp (Lio/sentry/SentryDate;)V public fun setTrimEnd (Z)V public fun setTrimStart (Z)V } @@ -3299,8 +2805,6 @@ public final class io/sentry/SpanStatus : java/lang/Enum, io/sentry/JsonSerializ public static final field UNIMPLEMENTED Lio/sentry/SpanStatus; public static final field UNKNOWN Lio/sentry/SpanStatus; public static final field UNKNOWN_ERROR Lio/sentry/SpanStatus; - public fun apiName ()Ljava/lang/String; - public static fun fromApiNameSafely (Ljava/lang/String;)Lio/sentry/SpanStatus; public static fun fromHttpStatusCode (I)Lio/sentry/SpanStatus; public static fun fromHttpStatusCode (Ljava/lang/Integer;Lio/sentry/SpanStatus;)Lio/sentry/SpanStatus; public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V @@ -3319,7 +2823,7 @@ public final class io/sentry/SpotlightIntegration : io/sentry/Integration, io/se public fun close ()V public fun execute (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)V public fun getSpotlightConnectionUrl ()Ljava/lang/String; - public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } public final class io/sentry/SystemOutLogger : io/sentry/ILogger { @@ -3365,11 +2869,6 @@ public final class io/sentry/TraceContext$JsonKeys { public fun ()V } -public final class io/sentry/TracesSampler { - public fun (Lio/sentry/SentryOptions;)V - public fun sample (Lio/sentry/SamplingContext;)Lio/sentry/TracesSamplingDecision; -} - public final class io/sentry/TracesSamplingDecision { public fun (Ljava/lang/Boolean;)V public fun (Ljava/lang/Boolean;Ljava/lang/Double;)V @@ -3381,7 +2880,6 @@ public final class io/sentry/TracesSamplingDecision { } public final class io/sentry/TransactionContext : io/sentry/SpanContext { - public static final field DEFAULT_TRANSACTION_NAME Ljava/lang/String; public fun (Lio/sentry/protocol/SentryId;Lio/sentry/SpanId;Lio/sentry/SpanId;Lio/sentry/TracesSamplingDecision;Lio/sentry/Baggage;)V public fun (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;Ljava/lang/String;)V public fun (Ljava/lang/String;Lio/sentry/protocol/TransactionNameSource;Ljava/lang/String;Lio/sentry/TracesSamplingDecision;)V @@ -3389,12 +2887,15 @@ public final class io/sentry/TransactionContext : io/sentry/SpanContext { public fun (Ljava/lang/String;Ljava/lang/String;Lio/sentry/TracesSamplingDecision;)V public static fun fromPropagationContext (Lio/sentry/PropagationContext;)Lio/sentry/TransactionContext; public static fun fromSentryTrace (Ljava/lang/String;Ljava/lang/String;Lio/sentry/SentryTraceHeader;)Lio/sentry/TransactionContext; + public fun getBaggage ()Lio/sentry/Baggage; + public fun getInstrumenter ()Lio/sentry/Instrumenter; public fun getName ()Ljava/lang/String; public fun getParentSampled ()Ljava/lang/Boolean; public fun getParentSamplingDecision ()Lio/sentry/TracesSamplingDecision; public fun getTransactionNameSource ()Lio/sentry/protocol/TransactionNameSource; public fun isForNextAppStart ()Z public fun setForNextAppStart (Z)V + public fun setInstrumenter (Lio/sentry/Instrumenter;)V public fun setName (Ljava/lang/String;)V public fun setParentSampled (Ljava/lang/Boolean;)V public fun setParentSampled (Ljava/lang/Boolean;Ljava/lang/Boolean;)V @@ -3411,7 +2912,7 @@ public final class io/sentry/TransactionOptions : io/sentry/SpanOptions { public fun getCustomSamplingContext ()Lio/sentry/CustomSamplingContext; public fun getDeadlineTimeout ()Ljava/lang/Long; public fun getIdleTimeout ()Ljava/lang/Long; - public fun getSpanFactory ()Lio/sentry/ISpanFactory; + public fun getStartTimestamp ()Lio/sentry/SentryDate; public fun getTransactionFinishedCallback ()Lio/sentry/TransactionFinishedCallback; public fun isAppStartTransaction ()Z public fun isBindToScope ()Z @@ -3421,7 +2922,7 @@ public final class io/sentry/TransactionOptions : io/sentry/SpanOptions { public fun setCustomSamplingContext (Lio/sentry/CustomSamplingContext;)V public fun setDeadlineTimeout (Ljava/lang/Long;)V public fun setIdleTimeout (Ljava/lang/Long;)V - public fun setSpanFactory (Lio/sentry/ISpanFactory;)V + public fun setStartTimestamp (Lio/sentry/SentryDate;)V public fun setTransactionFinishedCallback (Lio/sentry/TransactionFinishedCallback;)V public fun setWaitForChildren (Z)V } @@ -3446,7 +2947,6 @@ public final class io/sentry/TypeCheckHint { public static final field ANDROID_VIEW Ljava/lang/String; public static final field APOLLO_REQUEST Ljava/lang/String; public static final field APOLLO_RESPONSE Ljava/lang/String; - public static final field GRAPHQL_DATA_FETCHING_ENVIRONMENT Ljava/lang/String; public static final field GRAPHQL_HANDLER_PARAMETERS Ljava/lang/String; public static final field JUL_LOG_RECORD Ljava/lang/String; public static final field LOG4J_LOG_EVENT Ljava/lang/String; @@ -3483,7 +2983,7 @@ public final class io/sentry/TypeCheckHint { public final class io/sentry/UncaughtExceptionHandlerIntegration : io/sentry/Integration, java/io/Closeable, java/lang/Thread$UncaughtExceptionHandler { public fun ()V public fun close ()V - public final fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V + public final fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V public fun uncaughtException (Ljava/lang/Thread;Ljava/lang/Throwable;)V } @@ -3524,7 +3024,7 @@ public final class io/sentry/UserFeedback$JsonKeys { } public final class io/sentry/backpressure/BackpressureMonitor : io/sentry/backpressure/IBackpressureMonitor, java/lang/Runnable { - public fun (Lio/sentry/SentryOptions;Lio/sentry/IScopes;)V + public fun (Lio/sentry/SentryOptions;Lio/sentry/IHub;)V public fun getDownsampleFactor ()I public fun run ()V public fun start ()V @@ -3639,7 +3139,6 @@ public final class io/sentry/clientreport/ClientReportRecorder : io/sentry/clien public fun recordLostEnvelope (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelope;)V public fun recordLostEnvelopeItem (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelopeItem;)V public fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;)V - public fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;J)V } public final class io/sentry/clientreport/DiscardReason : java/lang/Enum { @@ -3685,7 +3184,6 @@ public abstract interface class io/sentry/clientreport/IClientReportRecorder { public abstract fun recordLostEnvelope (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelope;)V public abstract fun recordLostEnvelopeItem (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelopeItem;)V public abstract fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;)V - public abstract fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;J)V } public abstract interface class io/sentry/clientreport/IClientReportStorage { @@ -3699,7 +3197,6 @@ public final class io/sentry/clientreport/NoOpClientReportRecorder : io/sentry/c public fun recordLostEnvelope (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelope;)V public fun recordLostEnvelopeItem (Lio/sentry/clientreport/DiscardReason;Lio/sentry/SentryEnvelopeItem;)V public fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;)V - public fun recordLostEvent (Lio/sentry/clientreport/DiscardReason;Lio/sentry/DataCategory;J)V } public abstract interface class io/sentry/config/PropertiesProvider { @@ -3880,14 +3377,6 @@ public final class io/sentry/internal/debugmeta/ResourcesDebugMetaLoader : io/se public fun loadDebugMeta ()Ljava/util/List; } -public final class io/sentry/internal/eventprocessor/EventProcessorAndOrder : java/lang/Comparable { - public fun (Lio/sentry/EventProcessor;Ljava/lang/Long;)V - public fun compareTo (Lio/sentry/internal/eventprocessor/EventProcessorAndOrder;)I - public synthetic fun compareTo (Ljava/lang/Object;)I - public fun getEventProcessor ()Lio/sentry/EventProcessor; - public fun getOrder ()Ljava/lang/Long; -} - public abstract interface class io/sentry/internal/gestures/GestureTargetLocator { public abstract fun locate (Ljava/lang/Object;FFLio/sentry/internal/gestures/UiElement$Type;)Lio/sentry/internal/gestures/UiElement; } @@ -4224,13 +3713,9 @@ public final class io/sentry/protocol/Browser$JsonKeys { public fun ()V } -public class io/sentry/protocol/Contexts : io/sentry/JsonSerializable { +public final class io/sentry/protocol/Contexts : java/util/concurrent/ConcurrentHashMap, io/sentry/JsonSerializable { public fun ()V public fun (Lio/sentry/protocol/Contexts;)V - public fun containsKey (Ljava/lang/Object;)Z - public fun entrySet ()Ljava/util/Set; - public fun equals (Ljava/lang/Object;)Z - public fun get (Ljava/lang/Object;)Ljava/lang/Object; public fun getApp ()Lio/sentry/protocol/App; public fun getBrowser ()Lio/sentry/protocol/Browser; public fun getDevice ()Lio/sentry/protocol/Device; @@ -4238,17 +3723,8 @@ public class io/sentry/protocol/Contexts : io/sentry/JsonSerializable { public fun getOperatingSystem ()Lio/sentry/protocol/OperatingSystem; public fun getResponse ()Lio/sentry/protocol/Response; public fun getRuntime ()Lio/sentry/protocol/SentryRuntime; - public fun getSize ()I public fun getTrace ()Lio/sentry/SpanContext; - public fun hashCode ()I - public fun isEmpty ()Z - public fun keys ()Ljava/util/Enumeration; - public fun put (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; - public fun putAll (Lio/sentry/protocol/Contexts;)V - public fun putAll (Ljava/util/Map;)V - public fun remove (Ljava/lang/Object;)Ljava/lang/Object; public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V - public fun set (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; public fun setApp (Lio/sentry/protocol/App;)V public fun setBrowser (Lio/sentry/protocol/Browser;)V public fun setDevice (Lio/sentry/protocol/Device;)V @@ -4257,7 +3733,6 @@ public class io/sentry/protocol/Contexts : io/sentry/JsonSerializable { public fun setResponse (Lio/sentry/protocol/Response;)V public fun setRuntime (Lio/sentry/protocol/SentryRuntime;)V public fun setTrace (Lio/sentry/SpanContext;)V - public fun size ()I public fun withResponse (Lio/sentry/util/HintUtils$SentryConsumer;)V } @@ -4583,24 +4058,18 @@ public final class io/sentry/protocol/Mechanism : io/sentry/JsonSerializable, io public fun (Ljava/lang/Thread;)V public fun getData ()Ljava/util/Map; public fun getDescription ()Ljava/lang/String; - public fun getExceptionId ()Ljava/lang/Integer; public fun getHelpLink ()Ljava/lang/String; public fun getMeta ()Ljava/util/Map; - public fun getParentId ()Ljava/lang/Integer; public fun getSynthetic ()Ljava/lang/Boolean; public fun getType ()Ljava/lang/String; public fun getUnknown ()Ljava/util/Map; - public fun isExceptionGroup ()Ljava/lang/Boolean; public fun isHandled ()Ljava/lang/Boolean; public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V public fun setData (Ljava/util/Map;)V public fun setDescription (Ljava/lang/String;)V - public fun setExceptionGroup (Ljava/lang/Boolean;)V - public fun setExceptionId (Ljava/lang/Integer;)V public fun setHandled (Ljava/lang/Boolean;)V public fun setHelpLink (Ljava/lang/String;)V public fun setMeta (Ljava/util/Map;)V - public fun setParentId (Ljava/lang/Integer;)V public fun setSynthetic (Ljava/lang/Boolean;)V public fun setType (Ljava/lang/String;)V public fun setUnknown (Ljava/util/Map;)V @@ -4615,12 +4084,9 @@ public final class io/sentry/protocol/Mechanism$Deserializer : io/sentry/JsonDes public final class io/sentry/protocol/Mechanism$JsonKeys { public static final field DATA Ljava/lang/String; public static final field DESCRIPTION Ljava/lang/String; - public static final field EXCEPTION_ID Ljava/lang/String; public static final field HANDLED Ljava/lang/String; public static final field HELP_LINK Ljava/lang/String; - public static final field IS_EXCEPTION_GROUP Ljava/lang/String; public static final field META Ljava/lang/String; - public static final field PARENT_ID Ljava/lang/String; public static final field SYNTHETIC Ljava/lang/String; public static final field TYPE Ljava/lang/String; public fun ()V @@ -5490,11 +4956,6 @@ public final class io/sentry/util/DebugMetaPropertiesApplier { public static fun getProguardUuid (Ljava/util/Properties;)Ljava/lang/String; } -public final class io/sentry/util/EventProcessorUtils { - public fun ()V - public static fun unwrap (Ljava/util/List;)Ljava/util/List; -} - public final class io/sentry/util/ExceptionUtils { public fun ()V public static fun findRootCause (Ljava/lang/Throwable;)Ljava/lang/Throwable; @@ -5567,18 +5028,6 @@ public abstract interface class io/sentry/util/LazyEvaluator$Evaluator { public abstract fun evaluate ()Ljava/lang/Object; } -public final class io/sentry/util/LifecycleHelper { - public fun ()V - public static fun close (Ljava/lang/Object;)V -} - -public class io/sentry/util/LoadClass { - public fun ()V - public fun isClassAvailable (Ljava/lang/String;Lio/sentry/ILogger;)Z - public fun isClassAvailable (Ljava/lang/String;Lio/sentry/SentryOptions;)Z - public fun loadClass (Ljava/lang/String;Lio/sentry/ILogger;)Ljava/lang/Class; -} - public final class io/sentry/util/LogUtils { public fun ()V public static fun logNotInstanceOf (Ljava/lang/Class;Ljava/lang/Object;Lio/sentry/ILogger;)V @@ -5647,12 +5096,6 @@ public final class io/sentry/util/SampleRateUtils { public static fun isValidTracesSampleRate (Ljava/lang/Double;Z)Z } -public final class io/sentry/util/SpanUtils { - public fun ()V - public static fun ignoredSpanOriginsForOpenTelemetry ()Ljava/util/List; - public static fun isIgnored (Ljava/util/List;Ljava/lang/String;)Z -} - public final class io/sentry/util/StringUtils { public static fun byteCountToString (J)Ljava/lang/String; public static fun calculateStringHash (Ljava/lang/String;Lio/sentry/ILogger;)Ljava/lang/String; @@ -5671,9 +5114,9 @@ public final class io/sentry/util/StringUtils { public final class io/sentry/util/TracingUtils { public fun ()V public static fun maybeUpdateBaggage (Lio/sentry/IScope;Lio/sentry/SentryOptions;)Lio/sentry/PropagationContext; - public static fun startNewTrace (Lio/sentry/IScopes;)V - public static fun trace (Lio/sentry/IScopes;Ljava/util/List;Lio/sentry/ISpan;)Lio/sentry/util/TracingUtils$TracingHeaders; - public static fun traceIfAllowed (Lio/sentry/IScopes;Ljava/lang/String;Ljava/util/List;Lio/sentry/ISpan;)Lio/sentry/util/TracingUtils$TracingHeaders; + public static fun startNewTrace (Lio/sentry/IHub;)V + public static fun trace (Lio/sentry/IHub;Ljava/util/List;Lio/sentry/ISpan;)Lio/sentry/util/TracingUtils$TracingHeaders; + public static fun traceIfAllowed (Lio/sentry/IHub;Ljava/lang/String;Ljava/util/List;Lio/sentry/ISpan;)Lio/sentry/util/TracingUtils$TracingHeaders; } public final class io/sentry/util/TracingUtils$TracingHeaders { diff --git a/sentry/src/main/java/io/sentry/Baggage.java b/sentry/src/main/java/io/sentry/Baggage.java index dce417cd95..8e19fceaf8 100644 --- a/sentry/src/main/java/io/sentry/Baggage.java +++ b/sentry/src/main/java/io/sentry/Baggage.java @@ -39,13 +39,13 @@ public final class Baggage { @NotNull public static Baggage fromHeader(final @Nullable String headerValue) { return Baggage.fromHeader( - headerValue, false, ScopesAdapter.getInstance().getOptions().getLogger()); + headerValue, false, HubAdapter.getInstance().getOptions().getLogger()); } @NotNull public static Baggage fromHeader(final @Nullable List headerValues) { return Baggage.fromHeader( - headerValues, false, ScopesAdapter.getInstance().getOptions().getLogger()); + headerValues, false, HubAdapter.getInstance().getOptions().getLogger()); } @ApiStatus.Internal @@ -305,21 +305,11 @@ public void setUserId(final @Nullable String userId) { set(DSCKeys.USER_ID, userId); } - /** - * @deprecated has no effect and will be removed in the next major update. - */ - @Deprecated - @SuppressWarnings("InlineMeSuggester") @ApiStatus.Internal public @Nullable String getUserSegment() { return get(DSCKeys.USER_SEGMENT); } - /** - * @deprecated has no effect and will be removed in the next major update. - */ - @Deprecated - @SuppressWarnings("InlineMeSuggester") @ApiStatus.Internal public void setUserSegment(final @Nullable String userSegment) { set(DSCKeys.USER_SEGMENT, userSegment); @@ -381,18 +371,19 @@ public void set(final @NotNull String key, final @Nullable String value) { @ApiStatus.Internal public void setValuesFromTransaction( - final @NotNull SentryId traceId, + final @NotNull ITransaction transaction, final @Nullable User user, final @NotNull SentryOptions sentryOptions, - final @Nullable TracesSamplingDecision samplingDecision, - final @Nullable String transactionName, - final @Nullable TransactionNameSource transactionNameSource) { - setTraceId(traceId.toString()); + final @Nullable TracesSamplingDecision samplingDecision) { + setTraceId(transaction.getSpanContext().getTraceId().toString()); setPublicKey(new Dsn(sentryOptions.getDsn()).getPublicKey()); setRelease(sentryOptions.getRelease()); setEnvironment(sentryOptions.getEnvironment()); setUserSegment(user != null ? getSegment(user) : null); - setTransaction(isHighQualityTransactionName(transactionNameSource) ? transactionName : null); + setTransaction( + isHighQualityTransactionName(transaction.getTransactionNameSource()) + ? transaction.getName() + : null); setSampleRate(sampleRateToString(sampleRate(samplingDecision))); setSampled(StringUtils.toString(sampled(samplingDecision))); } @@ -412,10 +403,6 @@ public void setValuesFromScope( setSampled(null); } - /** - * @deprecated has no effect and will be removed in the next major update. - */ - @Deprecated private static @Nullable String getSegment(final @NotNull User user) { if (user.getSegment() != null) { return user.getSegment(); @@ -484,7 +471,6 @@ public TraceContext toTraceContext() { final String publicKey = getPublicKey(); if (traceIdString != null && publicKey != null) { - @SuppressWarnings("deprecation") final @NotNull TraceContext traceContext = new TraceContext( new SentryId(traceIdString), diff --git a/sentry/src/main/java/io/sentry/Breadcrumb.java b/sentry/src/main/java/io/sentry/Breadcrumb.java index b4e2fadd71..fe2055c336 100644 --- a/sentry/src/main/java/io/sentry/Breadcrumb.java +++ b/sentry/src/main/java/io/sentry/Breadcrumb.java @@ -17,13 +17,11 @@ import org.jetbrains.annotations.Nullable; /** Series of application events */ -public final class Breadcrumb implements JsonUnknown, JsonSerializable, Comparable { +public final class Breadcrumb implements JsonUnknown, JsonSerializable { /** A timestamp representing when the breadcrumb occurred. */ private final @NotNull Date timestamp; - private final @NotNull Long nanos; - /** If a message is provided, its rendered as text and the whitespace is preserved. */ private @Nullable String message; @@ -48,12 +46,10 @@ public final class Breadcrumb implements JsonUnknown, JsonSerializable, Comparab * @param timestamp the timestamp */ public Breadcrumb(final @NotNull Date timestamp) { - this.nanos = System.nanoTime(); this.timestamp = timestamp; } Breadcrumb(final @NotNull Breadcrumb breadcrumb) { - this.nanos = System.nanoTime(); this.timestamp = breadcrumb.timestamp; this.message = breadcrumb.message; this.type = breadcrumb.type; @@ -664,12 +660,6 @@ public void setUnknown(@Nullable Map unknown) { this.unknown = unknown; } - @Override - @SuppressWarnings("JavaUtilDate") - public int compareTo(@NotNull Breadcrumb o) { - return nanos.compareTo(o.nanos); - } - public static final class JsonKeys { public static final String TIMESTAMP = "timestamp"; public static final String MESSAGE = "message"; diff --git a/sentry/src/main/java/io/sentry/CombinedContextsView.java b/sentry/src/main/java/io/sentry/CombinedContextsView.java deleted file mode 100644 index 3720fa5b93..0000000000 --- a/sentry/src/main/java/io/sentry/CombinedContextsView.java +++ /dev/null @@ -1,276 +0,0 @@ -package io.sentry; - -import io.sentry.protocol.App; -import io.sentry.protocol.Browser; -import io.sentry.protocol.Contexts; -import io.sentry.protocol.Device; -import io.sentry.protocol.Gpu; -import io.sentry.protocol.OperatingSystem; -import io.sentry.protocol.Response; -import io.sentry.protocol.SentryRuntime; -import io.sentry.util.HintUtils; -import java.io.IOException; -import java.util.Enumeration; -import java.util.Map; -import java.util.Set; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public final class CombinedContextsView extends Contexts { - - private static final long serialVersionUID = 3585992094653318439L; - private final @NotNull Contexts globalContexts; - private final @NotNull Contexts isolationContexts; - private final @NotNull Contexts currentContexts; - - private final @NotNull ScopeType defaultScopeType; - - public CombinedContextsView( - final @NotNull Contexts globalContexts, - final @NotNull Contexts isolationContexts, - final @NotNull Contexts currentContexts, - final @NotNull ScopeType defaultScopeType) { - this.globalContexts = globalContexts; - this.isolationContexts = isolationContexts; - this.currentContexts = currentContexts; - this.defaultScopeType = defaultScopeType; - } - - @Override - public @Nullable SpanContext getTrace() { - final @Nullable SpanContext current = currentContexts.getTrace(); - if (current != null) { - return current; - } - final @Nullable SpanContext isolation = isolationContexts.getTrace(); - if (isolation != null) { - return isolation; - } - return globalContexts.getTrace(); - } - - @Override - public void setTrace(@Nullable SpanContext traceContext) { - getDefaultContexts().setTrace(traceContext); - } - - private @NotNull Contexts getDefaultContexts() { - switch (defaultScopeType) { - case CURRENT: - return currentContexts; - case ISOLATION: - return isolationContexts; - case GLOBAL: - return globalContexts; - default: - return currentContexts; - } - } - - @Override - public @Nullable App getApp() { - final @Nullable App current = currentContexts.getApp(); - if (current != null) { - return current; - } - final @Nullable App isolation = isolationContexts.getApp(); - if (isolation != null) { - return isolation; - } - return globalContexts.getApp(); - } - - @Override - public void setApp(@NotNull App app) { - getDefaultContexts().setApp(app); - } - - @Override - public @Nullable Browser getBrowser() { - final @Nullable Browser current = currentContexts.getBrowser(); - if (current != null) { - return current; - } - final @Nullable Browser isolation = isolationContexts.getBrowser(); - if (isolation != null) { - return isolation; - } - return globalContexts.getBrowser(); - } - - @Override - public void setBrowser(@NotNull Browser browser) { - getDefaultContexts().setBrowser(browser); - } - - @Override - public @Nullable Device getDevice() { - final @Nullable Device current = currentContexts.getDevice(); - if (current != null) { - return current; - } - final @Nullable Device isolation = isolationContexts.getDevice(); - if (isolation != null) { - return isolation; - } - return globalContexts.getDevice(); - } - - @Override - public void setDevice(@NotNull Device device) { - getDefaultContexts().setDevice(device); - } - - @Override - public @Nullable OperatingSystem getOperatingSystem() { - final @Nullable OperatingSystem current = currentContexts.getOperatingSystem(); - if (current != null) { - return current; - } - final @Nullable OperatingSystem isolation = isolationContexts.getOperatingSystem(); - if (isolation != null) { - return isolation; - } - return globalContexts.getOperatingSystem(); - } - - @Override - public void setOperatingSystem(@NotNull OperatingSystem operatingSystem) { - getDefaultContexts().setOperatingSystem(operatingSystem); - } - - @Override - public @Nullable SentryRuntime getRuntime() { - final @Nullable SentryRuntime current = currentContexts.getRuntime(); - if (current != null) { - return current; - } - final @Nullable SentryRuntime isolation = isolationContexts.getRuntime(); - if (isolation != null) { - return isolation; - } - return globalContexts.getRuntime(); - } - - @Override - public void setRuntime(@NotNull SentryRuntime runtime) { - getDefaultContexts().setRuntime(runtime); - } - - @Override - public @Nullable Gpu getGpu() { - final @Nullable Gpu current = currentContexts.getGpu(); - if (current != null) { - return current; - } - final @Nullable Gpu isolation = isolationContexts.getGpu(); - if (isolation != null) { - return isolation; - } - return globalContexts.getGpu(); - } - - @Override - public void setGpu(@NotNull Gpu gpu) { - getDefaultContexts().setGpu(gpu); - } - - @Override - public @Nullable Response getResponse() { - final @Nullable Response current = currentContexts.getResponse(); - if (current != null) { - return current; - } - final @Nullable Response isolation = isolationContexts.getResponse(); - if (isolation != null) { - return isolation; - } - return globalContexts.getResponse(); - } - - @Override - public void withResponse(HintUtils.SentryConsumer callback) { - if (currentContexts.getResponse() != null) { - currentContexts.withResponse(callback); - } else if (isolationContexts.getResponse() != null) { - isolationContexts.withResponse(callback); - } else if (globalContexts.getResponse() != null) { - globalContexts.withResponse(callback); - } else { - getDefaultContexts().withResponse(callback); - } - } - - @Override - public void setResponse(@NotNull Response response) { - getDefaultContexts().setResponse(response); - } - - @Override - public int size() { - return mergeContexts().size(); - } - - @Override - public int getSize() { - return size(); - } - - @Override - public boolean isEmpty() { - return globalContexts.isEmpty() && isolationContexts.isEmpty() && currentContexts.isEmpty(); - } - - @Override - public boolean containsKey(final @NotNull Object key) { - return globalContexts.containsKey(key) - || isolationContexts.containsKey(key) - || currentContexts.containsKey(key); - } - - @Override - public @Nullable Object get(final @NotNull Object key) { - final @Nullable Object current = currentContexts.get(key); - if (current != null) { - return current; - } - final @Nullable Object isolation = isolationContexts.get(key); - if (isolation != null) { - return isolation; - } - return globalContexts.get(key); - } - - @Override - public @Nullable Object put(final @NotNull String key, final @Nullable Object value) { - return getDefaultContexts().put(key, value); - } - - @Override - public @Nullable Object remove(final @NotNull Object key) { - return getDefaultContexts().remove(key); - } - - @Override - public @NotNull Enumeration keys() { - return mergeContexts().keys(); - } - - @Override - public @NotNull Set> entrySet() { - return mergeContexts().entrySet(); - } - - @Override - public void serialize(@NotNull ObjectWriter writer, @NotNull ILogger logger) throws IOException { - mergeContexts().serialize(writer, logger); - } - - private @NotNull Contexts mergeContexts() { - final @NotNull Contexts allContexts = new Contexts(); - allContexts.putAll(globalContexts); - allContexts.putAll(isolationContexts); - allContexts.putAll(currentContexts); - return allContexts; - } -} diff --git a/sentry/src/main/java/io/sentry/CombinedScopeView.java b/sentry/src/main/java/io/sentry/CombinedScopeView.java deleted file mode 100644 index c22fd060bb..0000000000 --- a/sentry/src/main/java/io/sentry/CombinedScopeView.java +++ /dev/null @@ -1,487 +0,0 @@ -package io.sentry; - -import static io.sentry.Scope.createBreadcrumbsList; - -import io.sentry.internal.eventprocessor.EventProcessorAndOrder; -import io.sentry.protocol.Contexts; -import io.sentry.protocol.Request; -import io.sentry.protocol.SentryId; -import io.sentry.protocol.User; -import io.sentry.util.EventProcessorUtils; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public final class CombinedScopeView implements IScope { - - private final IScope globalScope; - private final IScope isolationScope; - private final IScope scope; - - public CombinedScopeView( - final @NotNull IScope globalScope, - final @NotNull IScope isolationScope, - final @NotNull IScope scope) { - this.globalScope = globalScope; - this.isolationScope = isolationScope; - this.scope = scope; - } - - @Override - public @Nullable SentryLevel getLevel() { - final @Nullable SentryLevel current = scope.getLevel(); - if (current != null) { - return current; - } - final @Nullable SentryLevel isolation = isolationScope.getLevel(); - if (isolation != null) { - return isolation; - } - return globalScope.getLevel(); - } - - @Override - public void setLevel(@Nullable SentryLevel level) { - getDefaultWriteScope().setLevel(level); - } - - @Override - public @Nullable String getTransactionName() { - final @Nullable String current = scope.getTransactionName(); - if (current != null) { - return current; - } - final @Nullable String isolation = isolationScope.getTransactionName(); - if (isolation != null) { - return isolation; - } - return globalScope.getTransactionName(); - } - - @Override - public void setTransaction(@NotNull String transaction) { - getDefaultWriteScope().setTransaction(transaction); - } - - @Override - public @Nullable ISpan getSpan() { - final @Nullable ISpan current = scope.getSpan(); - if (current != null) { - return current; - } - final @Nullable ISpan isolation = isolationScope.getSpan(); - if (isolation != null) { - return isolation; - } - return globalScope.getSpan(); - } - - @Override - public void setTransaction(@Nullable ITransaction transaction) { - getDefaultWriteScope().setTransaction(transaction); - } - - @Override - public @Nullable User getUser() { - final @Nullable User current = scope.getUser(); - if (current != null) { - return current; - } - final @Nullable User isolation = isolationScope.getUser(); - if (isolation != null) { - return isolation; - } - return globalScope.getUser(); - } - - @Override - public void setUser(@Nullable User user) { - getDefaultWriteScope().setUser(user); - } - - @Override - public @Nullable String getScreen() { - final @Nullable String current = scope.getScreen(); - if (current != null) { - return current; - } - final @Nullable String isolation = isolationScope.getScreen(); - if (isolation != null) { - return isolation; - } - return globalScope.getScreen(); - } - - @Override - public void setScreen(@Nullable String screen) { - getDefaultWriteScope().setScreen(screen); - } - - @Override - public @Nullable Request getRequest() { - final @Nullable Request current = scope.getRequest(); - if (current != null) { - return current; - } - final @Nullable Request isolation = isolationScope.getRequest(); - if (isolation != null) { - return isolation; - } - return globalScope.getRequest(); - } - - @Override - public void setRequest(@Nullable Request request) { - getDefaultWriteScope().setRequest(request); - } - - @Override - public @NotNull List getFingerprint() { - // TODO [HSM] should these be merged? - final @Nullable List current = scope.getFingerprint(); - if (!current.isEmpty()) { - return current; - } - final @Nullable List isolation = isolationScope.getFingerprint(); - if (!isolation.isEmpty()) { - return isolation; - } - return globalScope.getFingerprint(); - } - - @Override - public void setFingerprint(@NotNull List fingerprint) { - getDefaultWriteScope().setFingerprint(fingerprint); - } - - @Override - public @NotNull Queue getBreadcrumbs() { - final @NotNull List allBreadcrumbs = new ArrayList<>(); - allBreadcrumbs.addAll(globalScope.getBreadcrumbs()); - allBreadcrumbs.addAll(isolationScope.getBreadcrumbs()); - allBreadcrumbs.addAll(scope.getBreadcrumbs()); - Collections.sort(allBreadcrumbs); - - final @NotNull Queue breadcrumbs = - createBreadcrumbsList(scope.getOptions().getMaxBreadcrumbs()); - breadcrumbs.addAll(allBreadcrumbs); - - return breadcrumbs; - } - - @Override - public void addBreadcrumb(@NotNull Breadcrumb breadcrumb, @Nullable Hint hint) { - getDefaultWriteScope().addBreadcrumb(breadcrumb, hint); - } - - @Override - public void addBreadcrumb(@NotNull Breadcrumb breadcrumb) { - getDefaultWriteScope().addBreadcrumb(breadcrumb); - } - - @Override - public void clearBreadcrumbs() { - getDefaultWriteScope().clearBreadcrumbs(); - } - - @Override - public void clearTransaction() { - getDefaultWriteScope().clearTransaction(); - } - - @Override - public @Nullable ITransaction getTransaction() { - final @Nullable ITransaction current = scope.getTransaction(); - if (current != null) { - return current; - } - final @Nullable ITransaction isolation = isolationScope.getTransaction(); - if (isolation != null) { - return isolation; - } - return globalScope.getTransaction(); - } - - @Override - public void clear() { - getDefaultWriteScope().clear(); - } - - @Override - public @NotNull Map getTags() { - final @NotNull Map allTags = new ConcurrentHashMap<>(); - allTags.putAll(globalScope.getTags()); - allTags.putAll(isolationScope.getTags()); - allTags.putAll(scope.getTags()); - return allTags; - } - - @Override - public void setTag(@NotNull String key, @NotNull String value) { - getDefaultWriteScope().setTag(key, value); - } - - @Override - public void removeTag(@NotNull String key) { - getDefaultWriteScope().removeTag(key); - } - - @Override - public @NotNull Map getExtras() { - final @NotNull Map allTags = new ConcurrentHashMap<>(); - allTags.putAll(globalScope.getExtras()); - allTags.putAll(isolationScope.getExtras()); - allTags.putAll(scope.getExtras()); - return allTags; - } - - @Override - public void setExtra(@NotNull String key, @NotNull String value) { - getDefaultWriteScope().setExtra(key, value); - } - - @Override - public void removeExtra(@NotNull String key) { - getDefaultWriteScope().removeExtra(key); - } - - @Override - public @NotNull Contexts getContexts() { - return new CombinedContextsView( - globalScope.getContexts(), - isolationScope.getContexts(), - scope.getContexts(), - getOptions().getDefaultScopeType()); - } - - @Override - public void setContexts(@NotNull String key, @NotNull Object value) { - getDefaultWriteScope().setContexts(key, value); - } - - @Override - public void setContexts(@NotNull String key, @NotNull Boolean value) { - getDefaultWriteScope().setContexts(key, value); - } - - @Override - public void setContexts(@NotNull String key, @NotNull String value) { - getDefaultWriteScope().setContexts(key, value); - } - - @Override - public void setContexts(@NotNull String key, @NotNull Number value) { - getDefaultWriteScope().setContexts(key, value); - } - - @Override - public void setContexts(@NotNull String key, @NotNull Collection value) { - getDefaultWriteScope().setContexts(key, value); - } - - @Override - public void setContexts(@NotNull String key, @NotNull Object[] value) { - getDefaultWriteScope().setContexts(key, value); - } - - @Override - public void setContexts(@NotNull String key, @NotNull Character value) { - getDefaultWriteScope().setContexts(key, value); - } - - @Override - public void removeContexts(@NotNull String key) { - getDefaultWriteScope().removeContexts(key); - } - - private @NotNull IScope getDefaultWriteScope() { - return getSpecificScope(null); - } - - IScope getSpecificScope(final @Nullable ScopeType scopeType) { - if (scopeType != null) { - switch (scopeType) { - case CURRENT: - return scope; - case ISOLATION: - return isolationScope; - case GLOBAL: - return globalScope; - case COMBINED: - return this; - default: - break; - } - } - - switch (getOptions().getDefaultScopeType()) { - case CURRENT: - return scope; - case ISOLATION: - return isolationScope; - case GLOBAL: - return globalScope; - default: - // calm the compiler - return scope; - } - } - - @Override - public @NotNull List getAttachments() { - final @NotNull List allAttachments = new CopyOnWriteArrayList<>(); - allAttachments.addAll(globalScope.getAttachments()); - allAttachments.addAll(isolationScope.getAttachments()); - allAttachments.addAll(scope.getAttachments()); - return allAttachments; - } - - @Override - public void addAttachment(@NotNull Attachment attachment) { - getDefaultWriteScope().addAttachment(attachment); - } - - @Override - public void clearAttachments() { - getDefaultWriteScope().clearAttachments(); - } - - @Override - public @NotNull List getEventProcessorsWithOrder() { - final @NotNull List allEventProcessors = new CopyOnWriteArrayList<>(); - allEventProcessors.addAll(globalScope.getEventProcessorsWithOrder()); - allEventProcessors.addAll(isolationScope.getEventProcessorsWithOrder()); - allEventProcessors.addAll(scope.getEventProcessorsWithOrder()); - Collections.sort(allEventProcessors); - return allEventProcessors; - } - - @Override - public @NotNull List getEventProcessors() { - return EventProcessorUtils.unwrap(getEventProcessorsWithOrder()); - } - - @Override - public void addEventProcessor(@NotNull EventProcessor eventProcessor) { - getDefaultWriteScope().addEventProcessor(eventProcessor); - } - - @Override - public @Nullable Session withSession(Scope.@NotNull IWithSession sessionCallback) { - return getDefaultWriteScope().withSession(sessionCallback); - } - - @Override - public @Nullable Scope.SessionPair startSession() { - return getDefaultWriteScope().startSession(); - } - - @Override - public @Nullable Session endSession() { - return getDefaultWriteScope().endSession(); - } - - @Override - public void withTransaction(Scope.@NotNull IWithTransaction callback) { - getDefaultWriteScope().withTransaction(callback); - } - - @Override - public @NotNull SentryOptions getOptions() { - return globalScope.getOptions(); - } - - @Override - public @Nullable Session getSession() { - final @Nullable Session current = scope.getSession(); - if (current != null) { - return current; - } - final @Nullable Session isolation = isolationScope.getSession(); - if (isolation != null) { - return isolation; - } - return globalScope.getSession(); - } - - @Override - public void clearSession() { - getDefaultWriteScope().clearSession(); - } - - @Override - public void setPropagationContext(@NotNull PropagationContext propagationContext) { - getDefaultWriteScope().setPropagationContext(propagationContext); - } - - @Override - public @NotNull PropagationContext getPropagationContext() { - return getDefaultWriteScope().getPropagationContext(); - } - - @Override - public @NotNull PropagationContext withPropagationContext( - Scope.@NotNull IWithPropagationContext callback) { - return getDefaultWriteScope().withPropagationContext(callback); - } - - @Override - public @NotNull IScope clone() { - return new CombinedScopeView(globalScope, isolationScope.clone(), scope.clone()); - } - - @Override - public void setLastEventId(@NotNull SentryId lastEventId) { - globalScope.setLastEventId(lastEventId); - isolationScope.setLastEventId(lastEventId); - scope.setLastEventId(lastEventId); - } - - @Override - public @NotNull SentryId getLastEventId() { - return globalScope.getLastEventId(); - } - - @Override - public void bindClient(@NotNull ISentryClient client) { - getDefaultWriteScope().bindClient(client); - } - - @Override - public @NotNull ISentryClient getClient() { - final @Nullable ISentryClient current = scope.getClient(); - if (!(current instanceof NoOpSentryClient)) { - return current; - } - final @Nullable ISentryClient isolation = isolationScope.getClient(); - if (!(isolation instanceof NoOpSentryClient)) { - return isolation; - } - return globalScope.getClient(); - } - - @Override - public void assignTraceContext(@NotNull SentryEvent event) { - globalScope.assignTraceContext(event); - } - - @Override - public void setSpanContext( - @NotNull Throwable throwable, @NotNull ISpan span, @NotNull String transactionName) { - globalScope.setSpanContext(throwable, span, transactionName); - } - - @ApiStatus.Internal - @Override - public void replaceOptions(@NotNull SentryOptions options) { - globalScope.replaceOptions(options); - } -} diff --git a/sentry/src/main/java/io/sentry/DataCategory.java b/sentry/src/main/java/io/sentry/DataCategory.java index a4eafc2bb5..c3d6520987 100644 --- a/sentry/src/main/java/io/sentry/DataCategory.java +++ b/sentry/src/main/java/io/sentry/DataCategory.java @@ -14,7 +14,6 @@ public enum DataCategory { Profile("profile"), MetricBucket("metric_bucket"), Transaction("transaction"), - Span("span"), Security("security"), UserReport("user_report"), Unknown("unknown"); diff --git a/sentry/src/main/java/io/sentry/DeduplicateMultithreadedEventProcessor.java b/sentry/src/main/java/io/sentry/DeduplicateMultithreadedEventProcessor.java index b5869a6379..924b253db8 100644 --- a/sentry/src/main/java/io/sentry/DeduplicateMultithreadedEventProcessor.java +++ b/sentry/src/main/java/io/sentry/DeduplicateMultithreadedEventProcessor.java @@ -62,9 +62,4 @@ public DeduplicateMultithreadedEventProcessor(final @NotNull SentryOptions optio processedEvents.put(type, currentEventTid); return event; } - - @Override - public @Nullable Long getOrder() { - return 7000L; - } } diff --git a/sentry/src/main/java/io/sentry/DefaultScopesStorage.java b/sentry/src/main/java/io/sentry/DefaultScopesStorage.java deleted file mode 100644 index 6884370c9f..0000000000 --- a/sentry/src/main/java/io/sentry/DefaultScopesStorage.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.sentry; - -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@ApiStatus.Internal -public final class DefaultScopesStorage implements IScopesStorage { - - private static final @NotNull ThreadLocal currentScopes = new ThreadLocal<>(); - - @Override - public ISentryLifecycleToken set(@Nullable IScopes scopes) { - final @Nullable IScopes oldScopes = get(); - currentScopes.set(scopes); - return new DefaultScopesLifecycleToken(oldScopes); - } - - @Override - public @Nullable IScopes get() { - return currentScopes.get(); - } - - @Override - public void close() { - currentScopes.remove(); - } - - static final class DefaultScopesLifecycleToken implements ISentryLifecycleToken { - - private final @Nullable IScopes oldValue; - - DefaultScopesLifecycleToken(final @Nullable IScopes scopes) { - this.oldValue = scopes; - } - - @Override - public void close() { - currentScopes.set(oldValue); - } - } -} diff --git a/sentry/src/main/java/io/sentry/DefaultSpanFactory.java b/sentry/src/main/java/io/sentry/DefaultSpanFactory.java deleted file mode 100644 index 3282d32949..0000000000 --- a/sentry/src/main/java/io/sentry/DefaultSpanFactory.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.sentry; - -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@ApiStatus.Internal -public final class DefaultSpanFactory implements ISpanFactory { - @Override - public @NotNull ITransaction createTransaction( - final @NotNull TransactionContext context, - final @NotNull IScopes scopes, - final @NotNull TransactionOptions transactionOptions, - final @Nullable TransactionPerformanceCollector transactionPerformanceCollector) { - return new SentryTracer(context, scopes, transactionOptions, transactionPerformanceCollector); - } - - @Override - public @NotNull ISpan createSpan( - final @NotNull IScopes scopes, - final @NotNull SpanOptions spanOptions, - final @NotNull SpanContext spanContext, - @Nullable ISpan parentSpan) { - if (parentSpan == null) { - // TODO [POTEL] We could create a transaction here - return NoOpSpan.getInstance(); - } - return parentSpan.startChild(spanContext, spanOptions); - } - - @Override - public @Nullable ISpan retrieveCurrentSpan(final IScopes scopes) { - return scopes.getSpan(); - } - - @Override - public @Nullable ISpan retrieveCurrentSpan(final IScope scope) { - return scope.getSpan(); - } -} diff --git a/sentry/src/main/java/io/sentry/DirectoryProcessor.java b/sentry/src/main/java/io/sentry/DirectoryProcessor.java index a6bb258f30..5d60feba60 100644 --- a/sentry/src/main/java/io/sentry/DirectoryProcessor.java +++ b/sentry/src/main/java/io/sentry/DirectoryProcessor.java @@ -19,17 +19,17 @@ abstract class DirectoryProcessor { private static final long ENVELOPE_PROCESSING_DELAY = 100L; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private final @NotNull ILogger logger; private final long flushTimeoutMillis; private final Queue processedEnvelopes; DirectoryProcessor( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull ILogger logger, final long flushTimeoutMillis, final int maxQueueSize) { - this.scopes = scopes; + this.hub = hub; this.logger = logger; this.flushTimeoutMillis = flushTimeoutMillis; this.processedEnvelopes = @@ -86,7 +86,7 @@ public void processDirectory(final @NotNull File directory) { } // in case there's rate limiting active, skip processing - final @Nullable RateLimiter rateLimiter = scopes.getRateLimiter(); + final @Nullable RateLimiter rateLimiter = hub.getRateLimiter(); if (rateLimiter != null && rateLimiter.isActiveForCategory(DataCategory.All)) { logger.log(SentryLevel.INFO, "DirectoryProcessor, rate limiting active."); return; diff --git a/sentry/src/main/java/io/sentry/DuplicateEventDetectionEventProcessor.java b/sentry/src/main/java/io/sentry/DuplicateEventDetectionEventProcessor.java index 5004e6514e..e9b77a8860 100644 --- a/sentry/src/main/java/io/sentry/DuplicateEventDetectionEventProcessor.java +++ b/sentry/src/main/java/io/sentry/DuplicateEventDetectionEventProcessor.java @@ -62,9 +62,4 @@ private static boolean containsAnyKey( } return causes; } - - @Override - public @Nullable Long getOrder() { - return 1000L; - } } diff --git a/sentry/src/main/java/io/sentry/EnvelopeSender.java b/sentry/src/main/java/io/sentry/EnvelopeSender.java index 9f630fc260..598caad280 100644 --- a/sentry/src/main/java/io/sentry/EnvelopeSender.java +++ b/sentry/src/main/java/io/sentry/EnvelopeSender.java @@ -17,18 +17,18 @@ @ApiStatus.Internal public final class EnvelopeSender extends DirectoryProcessor implements IEnvelopeSender { - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private final @NotNull ISerializer serializer; private final @NotNull ILogger logger; public EnvelopeSender( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull ISerializer serializer, final @NotNull ILogger logger, final long flushTimeoutMillis, final int maxQueueSize) { - super(scopes, logger, flushTimeoutMillis, maxQueueSize); - this.scopes = Objects.requireNonNull(scopes, "Scopes are required."); + super(hub, logger, flushTimeoutMillis, maxQueueSize); + this.hub = Objects.requireNonNull(hub, "Hub is required."); this.serializer = Objects.requireNonNull(serializer, "Serializer is required."); this.logger = Objects.requireNonNull(logger, "Logger is required."); } @@ -60,7 +60,7 @@ protected void processFile(final @NotNull File file, final @NotNull Hint hint) { logger.log( SentryLevel.ERROR, "Failed to deserialize cached envelope %s", file.getAbsolutePath()); } else { - scopes.captureEnvelope(envelope, hint); + hub.captureEnvelope(envelope, hint); } HintUtils.runIfHasTypeLogIfNot( diff --git a/sentry/src/main/java/io/sentry/EventProcessor.java b/sentry/src/main/java/io/sentry/EventProcessor.java index 6a8f3c7057..ba67508614 100644 --- a/sentry/src/main/java/io/sentry/EventProcessor.java +++ b/sentry/src/main/java/io/sentry/EventProcessor.java @@ -32,15 +32,4 @@ default SentryEvent process(@NotNull SentryEvent event, @NotNull Hint hint) { default SentryTransaction process(@NotNull SentryTransaction transaction, @NotNull Hint hint) { return transaction; } - - /** - * Controls when this EventProcessor is invoked. - * - * @return order higher number = later, lower number = earlier (negative values may also be - * passed), null = latest (note: multiple event processors using null may lead to random - * ordering) - */ - default @Nullable Long getOrder() { - return null; - } } diff --git a/sentry/src/main/java/io/sentry/ExternalOptions.java b/sentry/src/main/java/io/sentry/ExternalOptions.java index aa5aa43937..99eaacdd24 100644 --- a/sentry/src/main/java/io/sentry/ExternalOptions.java +++ b/sentry/src/main/java/io/sentry/ExternalOptions.java @@ -49,7 +49,6 @@ public final class ExternalOptions { private @Nullable List ignoredCheckIns; private @Nullable Boolean sendModules; - private @Nullable Boolean sendDefaultPii; private @Nullable Boolean enableBackpressureHandling; private @Nullable SentryOptions.Cron cron; @@ -132,7 +131,6 @@ public final class ExternalOptions { propertiesProvider.getBooleanProperty("enable-pretty-serialization-output")); options.setSendModules(propertiesProvider.getBooleanProperty("send-modules")); - options.setSendDefaultPii(propertiesProvider.getBooleanProperty("send-default-pii")); options.setIgnoredCheckIns(propertiesProvider.getList("ignored-checkins")); @@ -423,14 +421,6 @@ public void setSendModules(final @Nullable Boolean sendModules) { this.sendModules = sendModules; } - public @Nullable Boolean isSendDefaultPii() { - return sendDefaultPii; - } - - public void setSendDefaultPii(final @Nullable Boolean sendDefaultPii) { - this.sendDefaultPii = sendDefaultPii; - } - @ApiStatus.Experimental public void setIgnoredCheckIns(final @Nullable List ignoredCheckIns) { this.ignoredCheckIns = ignoredCheckIns; diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Hub.java similarity index 57% rename from sentry/src/main/java/io/sentry/Scopes.java rename to sentry/src/main/java/io/sentry/Hub.java index 1e26eefeba..6b6da88f09 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Hub.java @@ -1,5 +1,6 @@ package io.sentry; +import io.sentry.Stack.StackItem; import io.sentry.clientreport.DiscardReason; import io.sentry.hints.SessionEndHint; import io.sentry.hints.SessionStartHint; @@ -9,134 +10,91 @@ import io.sentry.protocol.SentryTransaction; import io.sentry.protocol.User; import io.sentry.transport.RateLimiter; +import io.sentry.util.ExceptionUtils; import io.sentry.util.HintUtils; import io.sentry.util.Objects; -import io.sentry.util.SpanUtils; +import io.sentry.util.Pair; import io.sentry.util.TracingUtils; import java.io.Closeable; +import java.io.IOException; +import java.lang.ref.WeakReference; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.WeakHashMap; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public final class Scopes implements IScopes, MetricsApi.IMetricsInterface { +public final class Hub implements IHub, MetricsApi.IMetricsInterface { - private final @NotNull IScope scope; - private final @NotNull IScope isolationScope; - private final @NotNull IScope globalScope; - - private final @Nullable Scopes parentScopes; - - private final @NotNull String creator; + private volatile @NotNull SentryId lastEventId; + private final @NotNull SentryOptions options; + private volatile boolean isEnabled; + private final @NotNull Stack stack; + private final @NotNull TracesSampler tracesSampler; + private final @NotNull Map, String>> throwableToSpan = + Collections.synchronizedMap(new WeakHashMap<>()); private final @NotNull TransactionPerformanceCollector transactionPerformanceCollector; private final @NotNull MetricsApi metricsApi; - private final @NotNull CombinedScopeView combinedScope; - - public Scopes( - final @NotNull IScope scope, - final @NotNull IScope isolationScope, - final @NotNull IScope globalScope, - final @NotNull String creator) { - this(scope, isolationScope, globalScope, null, creator); - } - - private Scopes( - final @NotNull IScope scope, - final @NotNull IScope isolationScope, - final @NotNull IScope globalScope, - final @Nullable Scopes parentScopes, - final @NotNull String creator) { - this.combinedScope = new CombinedScopeView(globalScope, isolationScope, scope); - this.scope = scope; - this.isolationScope = isolationScope; - this.globalScope = globalScope; - this.parentScopes = parentScopes; - this.creator = creator; - - final @NotNull SentryOptions options = getOptions(); - validateOptions(options); - this.transactionPerformanceCollector = options.getTransactionPerformanceCollector(); - this.metricsApi = new MetricsApi(this); + public Hub(final @NotNull SentryOptions options) { + this(options, createRootStackItem(options)); + // Integrations are no longer registered on Hub ctor, but on Sentry.init } - public @NotNull String getCreator() { - return creator; - } + private Hub(final @NotNull SentryOptions options, final @NotNull Stack stack) { + validateOptions(options); - @Override - @ApiStatus.Internal - public @NotNull IScope getScope() { - return scope; - } + this.options = options; + this.tracesSampler = new TracesSampler(options); + this.stack = stack; + this.lastEventId = SentryId.EMPTY_ID; + this.transactionPerformanceCollector = options.getTransactionPerformanceCollector(); - @Override - @ApiStatus.Internal - public @NotNull IScope getIsolationScope() { - return isolationScope; - } + // Integrations will use this Hub instance once registered. + // Make sure Hub ready to be used then. + this.isEnabled = true; - @Override - @ApiStatus.Internal - public @NotNull IScope getGlobalScope() { - return globalScope; + this.metricsApi = new MetricsApi(this); } - @Override - @ApiStatus.Internal - public @Nullable IScopes getParentScopes() { - return parentScopes; + private Hub(final @NotNull SentryOptions options, final @NotNull StackItem rootStackItem) { + this(options, new Stack(options.getLogger(), rootStackItem)); } - @Override - @ApiStatus.Internal - public boolean isAncestorOf(final @Nullable IScopes otherScopes) { - if (otherScopes == null) { - return false; - } - - if (this == otherScopes) { - return true; - } - - if (otherScopes.getParentScopes() != null) { - return isAncestorOf(otherScopes.getParentScopes()); + private static void validateOptions(final @NotNull SentryOptions options) { + Objects.requireNonNull(options, "SentryOptions is required."); + if (options.getDsn() == null || options.getDsn().isEmpty()) { + throw new IllegalArgumentException( + "Hub requires a DSN to be instantiated. Considering using the NoOpHub if no DSN is available."); } - - return false; } - @Override - public @NotNull IScopes forkedScopes(final @NotNull String creator) { - return new Scopes(scope.clone(), isolationScope.clone(), globalScope, this, creator); - } - - @Override - public @NotNull IScopes forkedCurrentScope(final @NotNull String creator) { - return new Scopes(scope.clone(), isolationScope, globalScope, this, creator); - } - - @Override - public @NotNull IScopes forkedRootScopes(final @NotNull String creator) { - return Sentry.forkedRootScopes(creator); + private static StackItem createRootStackItem(final @NotNull SentryOptions options) { + validateOptions(options); + final IScope scope = new Scope(options); + final ISentryClient client = new SentryClient(options); + return new StackItem(options, client, scope); } @Override public boolean isEnabled() { - return getClient().isEnabled(); + return isEnabled; } @Override - public @NotNull SentryId captureEvent(@NotNull SentryEvent event, @Nullable Hint hint) { + public @NotNull SentryId captureEvent( + final @NotNull SentryEvent event, final @Nullable Hint hint) { return captureEventInternal(event, hint, null); } @Override public @NotNull SentryId captureEvent( - @NotNull SentryEvent event, @Nullable Hint hint, @NotNull ScopeCallback callback) { + final @NotNull SentryEvent event, + final @Nullable Hint hint, + final @NotNull ScopeCallback callback) { return captureEventInternal(event, hint, callback); } @@ -146,21 +104,23 @@ public boolean isEnabled() { final @Nullable ScopeCallback scopeCallback) { SentryId sentryId = SentryId.EMPTY_ID; if (!isEnabled()) { - getOptions() + options .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'captureEvent' call is a no-op."); } else if (event == null) { - getOptions().getLogger().log(SentryLevel.WARNING, "captureEvent called with null parameter."); + options.getLogger().log(SentryLevel.WARNING, "captureEvent called with null parameter."); } else { try { assignTraceContext(event); - final IScope localScope = buildLocalScope(getCombinedScopeView(), scopeCallback); + final StackItem item = stack.peek(); + + final IScope scope = buildLocalScope(item.getScope(), scopeCallback); - sentryId = getClient().captureEvent(event, localScope, hint); - updateLastEventId(sentryId); + sentryId = item.getClient().captureEvent(event, scope, hint); + this.lastEventId = sentryId; } catch (Throwable e) { - getOptions() + options .getLogger() .log( SentryLevel.ERROR, "Error while capturing event with id: " + event.getEventId(), e); @@ -169,30 +129,6 @@ public boolean isEnabled() { return sentryId; } - private @NotNull ISentryClient getClient() { - return getCombinedScopeView().getClient(); - } - - private void assignTraceContext(final @NotNull SentryEvent event) { - getCombinedScopeView().assignTraceContext(event); - } - - private IScope buildLocalScope( - final @NotNull IScope parentScope, final @Nullable ScopeCallback callback) { - if (callback != null) { - try { - final IScope localScope = parentScope.clone(); - callback.run(localScope); - return localScope; - } catch (Throwable t) { - getOptions() - .getLogger() - .log(SentryLevel.ERROR, "Error in the 'ScopeCallback' callback.", t); - } - } - return parentScope; - } - @Override public @NotNull SentryId captureMessage( final @NotNull String message, final @NotNull SentryLevel level) { @@ -213,27 +149,25 @@ private IScope buildLocalScope( final @Nullable ScopeCallback scopeCallback) { SentryId sentryId = SentryId.EMPTY_ID; if (!isEnabled()) { - getOptions() + options .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'captureMessage' call is a no-op."); } else if (message == null) { - getOptions() - .getLogger() - .log(SentryLevel.WARNING, "captureMessage called with null parameter."); + options.getLogger().log(SentryLevel.WARNING, "captureMessage called with null parameter."); } else { try { - final IScope localScope = buildLocalScope(getCombinedScopeView(), scopeCallback); + final StackItem item = stack.peek(); + + final IScope scope = buildLocalScope(item.getScope(), scopeCallback); - sentryId = getClient().captureMessage(message, level, localScope); + sentryId = item.getClient().captureMessage(message, level, scope); } catch (Throwable e) { - getOptions() - .getLogger() - .log(SentryLevel.ERROR, "Error while capturing message: " + message, e); + options.getLogger().log(SentryLevel.ERROR, "Error while capturing message: " + message, e); } } - updateLastEventId(sentryId); + this.lastEventId = sentryId; return sentryId; } @@ -245,19 +179,20 @@ private IScope buildLocalScope( SentryId sentryId = SentryId.EMPTY_ID; if (!isEnabled()) { - getOptions() + options .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'captureEnvelope' call is a no-op."); } else { try { - final SentryId capturedEnvelopeId = getClient().captureEnvelope(envelope, hint); + final SentryId capturedEnvelopeId = + stack.peek().getClient().captureEnvelope(envelope, hint); if (capturedEnvelopeId != null) { sentryId = capturedEnvelopeId; } } catch (Throwable e) { - getOptions().getLogger().log(SentryLevel.ERROR, "Error while capturing envelope.", e); + options.getLogger().log(SentryLevel.ERROR, "Error while capturing envelope.", e); } } return sentryId; @@ -284,47 +219,67 @@ private IScope buildLocalScope( final @Nullable ScopeCallback scopeCallback) { SentryId sentryId = SentryId.EMPTY_ID; if (!isEnabled()) { - getOptions() + options .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'captureException' call is a no-op."); } else if (throwable == null) { - getOptions() - .getLogger() - .log(SentryLevel.WARNING, "captureException called with null parameter."); + options.getLogger().log(SentryLevel.WARNING, "captureException called with null parameter."); } else { try { + final StackItem item = stack.peek(); final SentryEvent event = new SentryEvent(throwable); assignTraceContext(event); - final IScope localScope = buildLocalScope(getCombinedScopeView(), scopeCallback); + final IScope scope = buildLocalScope(item.getScope(), scopeCallback); - sentryId = getClient().captureEvent(event, localScope, hint); + sentryId = item.getClient().captureEvent(event, scope, hint); } catch (Throwable e) { - getOptions() + options .getLogger() .log( SentryLevel.ERROR, "Error while capturing exception: " + throwable.getMessage(), e); } } - updateLastEventId(sentryId); + this.lastEventId = sentryId; return sentryId; } + private void assignTraceContext(final @NotNull SentryEvent event) { + if (options.isTracingEnabled() && event.getThrowable() != null) { + final Pair, String> pair = + throwableToSpan.get(ExceptionUtils.findRootCause(event.getThrowable())); + if (pair != null) { + final WeakReference spanWeakRef = pair.getFirst(); + if (event.getContexts().getTrace() == null && spanWeakRef != null) { + final ISpan span = spanWeakRef.get(); + if (span != null) { + event.getContexts().setTrace(span.getSpanContext()); + } + } + final String transactionName = pair.getSecond(); + if (event.getTransaction() == null && transactionName != null) { + event.setTransaction(transactionName); + } + } + } + } + @Override public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { if (!isEnabled()) { - getOptions() + options .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'captureUserFeedback' call is a no-op."); } else { try { - getClient().captureUserFeedback(userFeedback); + final StackItem item = stack.peek(); + item.getClient().captureUserFeedback(userFeedback); } catch (Throwable e) { - getOptions() + options .getLogger() .log( SentryLevel.ERROR, @@ -337,12 +292,13 @@ public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { @Override public void startSession() { if (!isEnabled()) { - getOptions() + options .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'startSession' call is a no-op."); } else { - final Scope.SessionPair pair = getCombinedScopeView().startSession(); + final StackItem item = this.stack.peek(); + final Scope.SessionPair pair = item.getScope().startSession(); if (pair != null) { // TODO: add helper overload `captureSessions` to pass a list of sessions and submit a // single envelope @@ -350,14 +306,14 @@ public void startSession() { if (pair.getPrevious() != null) { final Hint hint = HintUtils.createWithTypeCheckHint(new SessionEndHint()); - getClient().captureSession(pair.getPrevious(), hint); + item.getClient().captureSession(pair.getPrevious(), hint); } final Hint hint = HintUtils.createWithTypeCheckHint(new SessionStartHint()); - getClient().captureSession(pair.getCurrent(), hint); + item.getClient().captureSession(pair.getCurrent(), hint); } else { - getOptions().getLogger().log(SentryLevel.WARNING, "Session could not be started."); + options.getLogger().log(SentryLevel.WARNING, "Session could not be started."); } } } @@ -365,23 +321,20 @@ public void startSession() { @Override public void endSession() { if (!isEnabled()) { - getOptions() + options .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'endSession' call is a no-op."); } else { - final Session previousSession = getCombinedScopeView().endSession(); + final StackItem item = this.stack.peek(); + final Session previousSession = item.getScope().endSession(); if (previousSession != null) { final Hint hint = HintUtils.createWithTypeCheckHint(new SessionEndHint()); - getClient().captureSession(previousSession, hint); + item.getClient().captureSession(previousSession, hint); } } } - private IScope getCombinedScopeView() { - return combinedScope; - } - @Override public void close() { close(false); @@ -391,17 +344,17 @@ public void close() { @SuppressWarnings("FutureReturnValueIgnored") public void close(final boolean isRestarting) { if (!isEnabled()) { - getOptions() + options .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'close' call is a no-op."); } else { try { - for (Integration integration : getOptions().getIntegrations()) { + for (Integration integration : options.getIntegrations()) { if (integration instanceof Closeable) { try { ((Closeable) integration).close(); - } catch (Throwable e) { - getOptions() + } catch (IOException e) { + options .getLogger() .log(SentryLevel.WARNING, "Failed to close the integration {}.", integration, e); } @@ -409,41 +362,38 @@ public void close(final boolean isRestarting) { } configureScope(scope -> scope.clear()); - configureScope(ScopeType.ISOLATION, scope -> scope.clear()); - getOptions().getTransactionProfiler().close(); - getOptions().getTransactionPerformanceCollector().close(); - final @NotNull ISentryExecutorService executorService = getOptions().getExecutorService(); + options.getTransactionProfiler().close(); + options.getTransactionPerformanceCollector().close(); + final @NotNull ISentryExecutorService executorService = options.getExecutorService(); if (isRestarting) { - executorService.submit( - () -> executorService.close(getOptions().getShutdownTimeoutMillis())); + executorService.submit(() -> executorService.close(options.getShutdownTimeoutMillis())); } else { - executorService.close(getOptions().getShutdownTimeoutMillis()); + executorService.close(options.getShutdownTimeoutMillis()); } + // Close the top-most client + final StackItem item = stack.peek(); // TODO: should we end session before closing client? - configureScope(ScopeType.CURRENT, scope -> scope.getClient().close(isRestarting)); - configureScope(ScopeType.ISOLATION, scope -> scope.getClient().close(isRestarting)); - configureScope(ScopeType.GLOBAL, scope -> scope.getClient().close(isRestarting)); + item.getClient().close(isRestarting); } catch (Throwable e) { - getOptions().getLogger().log(SentryLevel.ERROR, "Error while closing the Scopes.", e); + options.getLogger().log(SentryLevel.ERROR, "Error while closing the Hub.", e); } + isEnabled = false; } } @Override public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb, final @Nullable Hint hint) { if (!isEnabled()) { - getOptions() + options .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'addBreadcrumb' call is a no-op."); } else if (breadcrumb == null) { - getOptions() - .getLogger() - .log(SentryLevel.WARNING, "addBreadcrumb called with null parameter."); + options.getLogger().log(SentryLevel.WARNING, "addBreadcrumb called with null parameter."); } else { - getCombinedScopeView().addBreadcrumb(breadcrumb, hint); + stack.peek().getScope().addBreadcrumb(breadcrumb, hint); } } @@ -455,180 +405,164 @@ public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb) { @Override public void setLevel(final @Nullable SentryLevel level) { if (!isEnabled()) { - getOptions() + options .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'setLevel' call is a no-op."); } else { - getCombinedScopeView().setLevel(level); + stack.peek().getScope().setLevel(level); } } @Override public void setTransaction(final @Nullable String transaction) { if (!isEnabled()) { - getOptions() + options .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'setTransaction' call is a no-op."); } else if (transaction != null) { - getCombinedScopeView().setTransaction(transaction); + stack.peek().getScope().setTransaction(transaction); } else { - getOptions().getLogger().log(SentryLevel.WARNING, "Transaction cannot be null"); + options.getLogger().log(SentryLevel.WARNING, "Transaction cannot be null"); } } @Override public void setUser(final @Nullable User user) { if (!isEnabled()) { - getOptions() + options .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'setUser' call is a no-op."); } else { - getCombinedScopeView().setUser(user); + stack.peek().getScope().setUser(user); } } @Override public void setFingerprint(final @NotNull List fingerprint) { if (!isEnabled()) { - getOptions() + options .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'setFingerprint' call is a no-op."); } else if (fingerprint == null) { - getOptions() - .getLogger() - .log(SentryLevel.WARNING, "setFingerprint called with null parameter."); + options.getLogger().log(SentryLevel.WARNING, "setFingerprint called with null parameter."); } else { - getCombinedScopeView().setFingerprint(fingerprint); + stack.peek().getScope().setFingerprint(fingerprint); } } @Override public void clearBreadcrumbs() { if (!isEnabled()) { - getOptions() + options .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'clearBreadcrumbs' call is a no-op."); } else { - getCombinedScopeView().clearBreadcrumbs(); + stack.peek().getScope().clearBreadcrumbs(); } } @Override public void setTag(final @NotNull String key, final @NotNull String value) { if (!isEnabled()) { - getOptions() + options .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'setTag' call is a no-op."); } else if (key == null || value == null) { - getOptions().getLogger().log(SentryLevel.WARNING, "setTag called with null parameter."); + options.getLogger().log(SentryLevel.WARNING, "setTag called with null parameter."); } else { - getCombinedScopeView().setTag(key, value); + stack.peek().getScope().setTag(key, value); } } @Override public void removeTag(final @NotNull String key) { if (!isEnabled()) { - getOptions() + options .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'removeTag' call is a no-op."); } else if (key == null) { - getOptions().getLogger().log(SentryLevel.WARNING, "removeTag called with null parameter."); + options.getLogger().log(SentryLevel.WARNING, "removeTag called with null parameter."); } else { - getCombinedScopeView().removeTag(key); + stack.peek().getScope().removeTag(key); } } @Override public void setExtra(final @NotNull String key, final @NotNull String value) { if (!isEnabled()) { - getOptions() + options .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'setExtra' call is a no-op."); } else if (key == null || value == null) { - getOptions().getLogger().log(SentryLevel.WARNING, "setExtra called with null parameter."); + options.getLogger().log(SentryLevel.WARNING, "setExtra called with null parameter."); } else { - getCombinedScopeView().setExtra(key, value); + stack.peek().getScope().setExtra(key, value); } } @Override public void removeExtra(final @NotNull String key) { if (!isEnabled()) { - getOptions() + options .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'removeExtra' call is a no-op."); } else if (key == null) { - getOptions().getLogger().log(SentryLevel.WARNING, "removeExtra called with null parameter."); + options.getLogger().log(SentryLevel.WARNING, "removeExtra called with null parameter."); } else { - getCombinedScopeView().removeExtra(key); + stack.peek().getScope().removeExtra(key); } } - private void updateLastEventId(final @NotNull SentryId lastEventId) { - getCombinedScopeView().setLastEventId(lastEventId); - } - @Override public @NotNull SentryId getLastEventId() { - return getCombinedScopeView().getLastEventId(); + return lastEventId; } @Override - public ISentryLifecycleToken pushScope() { + public void pushScope() { if (!isEnabled()) { - getOptions() + options .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'pushScope' call is a no-op."); - return NoOpScopesLifecycleToken.getInstance(); } else { - final @NotNull IScopes scopes = this.forkedCurrentScope("pushScope"); - return scopes.makeCurrent(); + final StackItem item = stack.peek(); + final StackItem newItem = new StackItem(options, item.getClient(), item.getScope().clone()); + stack.push(newItem); } } @Override - public ISentryLifecycleToken pushIsolationScope() { - if (!isEnabled()) { - getOptions() - .getLogger() - .log( - SentryLevel.WARNING, - "Instance is disabled and this 'pushIsolationScope' call is a no-op."); - return NoOpScopesLifecycleToken.getInstance(); - } else { - final @NotNull IScopes scopes = this.forkedScopes("pushIsolationScope"); - return scopes.makeCurrent(); - } + public @NotNull SentryOptions getOptions() { + return this.stack.peek().getOptions(); } @Override - public @NotNull ISentryLifecycleToken makeCurrent() { - return Sentry.setCurrentScopes(this); + public @Nullable Boolean isCrashedLastRun() { + return SentryCrashLastRunState.getInstance() + .isCrashedLastRun(options.getCacheDirPath(), !options.isEnableAutoSessionTracking()); + } + + @Override + public void reportFullyDisplayed() { + if (options.isEnableTimeToFullDisplayTracing()) { + options.getFullyDisplayedReporter().reportFullyDrawn(); + } } - /** - * @deprecated please call {@link ISentryLifecycleToken#close()} on the token returned by {@link - * IScopes#pushScope()} or {@link IScopes#pushIsolationScope()} instead. - */ @Override - @Deprecated public void popScope() { if (!isEnabled()) { - getOptions() + options .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'popScope' call is a no-op."); } else { - final @Nullable Scopes parent = parentScopes; - if (parent != null) { - parent.makeCurrent(); - } + stack.pop(); } } @@ -638,105 +572,82 @@ public void withScope(final @NotNull ScopeCallback callback) { try { callback.run(NoOpScope.getInstance()); } catch (Throwable e) { - getOptions().getLogger().log(SentryLevel.ERROR, "Error in the 'withScope' callback.", e); + options.getLogger().log(SentryLevel.ERROR, "Error in the 'withScope' callback.", e); } } else { - final @NotNull IScopes forkedScopes = forkedCurrentScope("withScope"); - try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) { - callback.run(forkedScopes.getScope()); - } catch (Throwable e) { - getOptions().getLogger().log(SentryLevel.ERROR, "Error in the 'withScope' callback.", e); - } - } - } - - @Override - public void withIsolationScope(final @NotNull ScopeCallback callback) { - if (!isEnabled()) { + pushScope(); try { - callback.run(NoOpScope.getInstance()); + callback.run(stack.peek().getScope()); } catch (Throwable e) { - getOptions() - .getLogger() - .log(SentryLevel.ERROR, "Error in the 'withIsolationScope' callback.", e); - } - - } else { - final @NotNull IScopes forkedScopes = forkedScopes("withIsolationScope"); - try (final @NotNull ISentryLifecycleToken ignored = forkedScopes.makeCurrent()) { - callback.run(forkedScopes.getIsolationScope()); - } catch (Throwable e) { - getOptions() - .getLogger() - .log(SentryLevel.ERROR, "Error in the 'withIsolationScope' callback.", e); + options.getLogger().log(SentryLevel.ERROR, "Error in the 'withScope' callback.", e); } + popScope(); } } @Override - public void configureScope( - final @Nullable ScopeType scopeType, final @NotNull ScopeCallback callback) { + public void configureScope(final @NotNull ScopeCallback callback) { if (!isEnabled()) { - getOptions() + options .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'configureScope' call is a no-op."); } else { try { - callback.run(combinedScope.getSpecificScope(scopeType)); + callback.run(stack.peek().getScope()); } catch (Throwable e) { - getOptions() - .getLogger() - .log(SentryLevel.ERROR, "Error in the 'configureScope' callback.", e); + options.getLogger().log(SentryLevel.ERROR, "Error in the 'configureScope' callback.", e); } } } @Override public void bindClient(final @NotNull ISentryClient client) { - if (client != null) { - getOptions().getLogger().log(SentryLevel.DEBUG, "New client bound to scope."); - getCombinedScopeView().bindClient(client); + if (!isEnabled()) { + options + .getLogger() + .log(SentryLevel.WARNING, "Instance is disabled and this 'bindClient' call is a no-op."); } else { - getOptions().getLogger().log(SentryLevel.DEBUG, "NoOp client bound to scope."); - getCombinedScopeView().bindClient(NoOpSentryClient.getInstance()); + final StackItem item = stack.peek(); + if (client != null) { + options.getLogger().log(SentryLevel.DEBUG, "New client bound to scope."); + item.setClient(client); + } else { + options.getLogger().log(SentryLevel.DEBUG, "NoOp client bound to scope."); + item.setClient(NoOpSentryClient.getInstance()); + } } } @Override public boolean isHealthy() { - return getClient().isHealthy(); + return stack.peek().getClient().isHealthy(); } @Override public void flush(long timeoutMillis) { if (!isEnabled()) { - getOptions() + options .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'flush' call is a no-op."); } else { try { - getClient().flush(timeoutMillis); + stack.peek().getClient().flush(timeoutMillis); } catch (Throwable e) { - getOptions().getLogger().log(SentryLevel.ERROR, "Error in the 'client.flush'.", e); + options.getLogger().log(SentryLevel.ERROR, "Error in the 'client.flush'.", e); } } } - /** - * @deprecated please use {@link IScopes#forkedScopes(String)} or {@link - * IScopes#forkedCurrentScope(String)} instead. - */ @Override - @Deprecated - @SuppressWarnings("deprecation") public @NotNull IHub clone() { if (!isEnabled()) { - getOptions().getLogger().log(SentryLevel.WARNING, "Disabled Scopes cloned."); + options.getLogger().log(SentryLevel.WARNING, "Disabled Hub cloned."); } - return new HubScopesWrapper(forkedScopes("scopes clone")); + // Clone will be invoked in parallel + return new Hub(this.options, new Stack(this.stack)); } @ApiStatus.Internal @@ -750,14 +661,14 @@ public void flush(long timeoutMillis) { SentryId sentryId = SentryId.EMPTY_ID; if (!isEnabled()) { - getOptions() + options .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'captureTransaction' call is a no-op."); } else { if (!transaction.isFinished()) { - getOptions() + options .getLogger() .log( SentryLevel.WARNING, @@ -765,45 +676,31 @@ public void flush(long timeoutMillis) { transaction.getEventId()); } else { if (!Boolean.TRUE.equals(transaction.isSampled())) { - getOptions() + options .getLogger() .log( SentryLevel.DEBUG, "Transaction %s was dropped due to sampling decision.", transaction.getEventId()); - if (getOptions().getBackpressureMonitor().getDownsampleFactor() > 0) { - getOptions() + if (options.getBackpressureMonitor().getDownsampleFactor() > 0) { + options .getClientReportRecorder() .recordLostEvent(DiscardReason.BACKPRESSURE, DataCategory.Transaction); - getOptions() - .getClientReportRecorder() - .recordLostEvent( - DiscardReason.BACKPRESSURE, - DataCategory.Span, - transaction.getSpans().size() + 1); } else { - getOptions() + options .getClientReportRecorder() .recordLostEvent(DiscardReason.SAMPLE_RATE, DataCategory.Transaction); - getOptions() - .getClientReportRecorder() - .recordLostEvent( - DiscardReason.SAMPLE_RATE, - DataCategory.Span, - transaction.getSpans().size() + 1); } } else { + StackItem item = null; try { + item = stack.peek(); sentryId = - getClient() + item.getClient() .captureTransaction( - transaction, - traceContext, - getCombinedScopeView(), - hint, - profilingTraceData); + transaction, traceContext, item.getScope(), hint, profilingTraceData); } catch (Throwable e) { - getOptions() + options .getLogger() .log( SentryLevel.ERROR, @@ -827,42 +724,26 @@ public void flush(long timeoutMillis) { final @NotNull TransactionContext transactionContext, final @NotNull TransactionOptions transactionOptions) { Objects.requireNonNull(transactionContext, "transactionContext is required"); - // TODO [POTEL] what if span is already running and someone calls startTransaction? - - transactionContext.setOrigin(transactionOptions.getOrigin()); ITransaction transaction; if (!isEnabled()) { - getOptions() + options .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'startTransaction' returns a no-op."); transaction = NoOpTransaction.getInstance(); - } else if (SpanUtils.isIgnored( - getOptions().getIgnoredSpanOrigins(), transactionContext.getOrigin())) { - // TODO [POTEL] may not have been set yet? - getOptions() + } else if (!options.getInstrumenter().equals(transactionContext.getInstrumenter())) { + options .getLogger() .log( SentryLevel.DEBUG, - "Returning no-op for span origin %s as the SDK has been configured to ignore it", - transactionContext.getOrigin()); + "Returning no-op for instrumenter %s as the SDK has been configured to use instrumenter %s", + transactionContext.getInstrumenter(), + options.getInstrumenter()); transaction = NoOpTransaction.getInstance(); - - // } else if (!getOptions().getInstrumenter().equals(transactionContext.getInstrumenter())) - // { - // getOptions() - // .getLogger() - // .log( - // SentryLevel.DEBUG, - // "Returning no-op for instrumenter %s as the SDK has been configured to use - // instrumenter %s", - // transactionContext.getInstrumenter(), - // getOptions().getInstrumenter()); - // transaction = NoOpTransaction.getInstance(); - } else if (!getOptions().isTracingEnabled()) { - getOptions() + } else if (!options.isTracingEnabled()) { + options .getLogger() .log( SentryLevel.INFO, "Tracing is disabled and this 'startTransaction' returns a no-op."); @@ -870,25 +751,17 @@ public void flush(long timeoutMillis) { } else { final SamplingContext samplingContext = new SamplingContext(transactionContext, transactionOptions.getCustomSamplingContext()); - final @NotNull TracesSampler tracesSampler = getOptions().getInternalTracesSampler(); @NotNull TracesSamplingDecision samplingDecision = tracesSampler.sample(samplingContext); transactionContext.setSamplingDecision(samplingDecision); - final @Nullable ISpanFactory maybeSpanFactory = transactionOptions.getSpanFactory(); - final @NotNull ISpanFactory spanFactory = - maybeSpanFactory == null ? getOptions().getSpanFactory() : maybeSpanFactory; - transaction = - spanFactory.createTransaction( + new SentryTracer( transactionContext, this, transactionOptions, transactionPerformanceCollector); - // new SentryTracer( - // transactionContext, this, transactionOptions, - // transactionPerformanceCollector); // The listener is called only if the transaction exists, as the transaction is needed to // stop it if (samplingDecision.getSampled() && samplingDecision.getProfileSampled()) { - final ITransactionProfiler transactionProfiler = getOptions().getTransactionProfiler(); + final ITransactionProfiler transactionProfiler = options.getTransactionProfiler(); // If the profiler is not running, we start and bind it here. if (!transactionProfiler.isRunning()) { transactionProfiler.start(); @@ -900,7 +773,7 @@ public void flush(long timeoutMillis) { } } if (transactionOptions.isBindToScope()) { - transaction.makeCurrent(); + configureScope(scope -> scope.setTransaction(transaction)); } return transaction; } @@ -912,25 +785,17 @@ public void flush(long timeoutMillis) { return getTraceparent(); } - @Override - @ApiStatus.Internal - public void setSpanContext( - final @NotNull Throwable throwable, - final @NotNull ISpan span, - final @NotNull String transactionName) { - getCombinedScopeView().setSpanContext(throwable, span, transactionName); - } - @Override public @Nullable ISpan getSpan() { + ISpan span = null; if (!isEnabled()) { - getOptions() + options .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'getSpan' call is a no-op."); } else { - return getOptions().getSpanFactory().retrieveCurrentSpan(getCombinedScopeView()); + span = stack.peek().getScope().getSpan(); } - return null; + return span; } @Override @@ -938,34 +803,63 @@ public void setSpanContext( public @Nullable ITransaction getTransaction() { ITransaction span = null; if (!isEnabled()) { - getOptions() + options .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'getTransaction' call is a no-op."); } else { - span = getCombinedScopeView().getTransaction(); + span = stack.peek().getScope().getTransaction(); } return span; } @Override - public @NotNull SentryOptions getOptions() { - return combinedScope.getOptions(); - } - - @Override - public @Nullable Boolean isCrashedLastRun() { - return SentryCrashLastRunState.getInstance() - .isCrashedLastRun( - getOptions().getCacheDirPath(), !getOptions().isEnableAutoSessionTracking()); + @ApiStatus.Internal + public void setSpanContext( + final @NotNull Throwable throwable, + final @NotNull ISpan span, + final @NotNull String transactionName) { + Objects.requireNonNull(throwable, "throwable is required"); + Objects.requireNonNull(span, "span is required"); + Objects.requireNonNull(transactionName, "transactionName is required"); + // to match any cause, span context is always attached to the root cause of the exception + final Throwable rootCause = ExceptionUtils.findRootCause(throwable); + // the most inner span should be assigned to a throwable + if (!throwableToSpan.containsKey(rootCause)) { + throwableToSpan.put(rootCause, new Pair<>(new WeakReference<>(span), transactionName)); + } + } + + @Nullable + SpanContext getSpanContext(final @NotNull Throwable throwable) { + Objects.requireNonNull(throwable, "throwable is required"); + final Throwable rootCause = ExceptionUtils.findRootCause(throwable); + final Pair, String> pair = this.throwableToSpan.get(rootCause); + if (pair != null) { + final WeakReference spanWeakRef = pair.getFirst(); + if (spanWeakRef != null) { + final ISpan span = spanWeakRef.get(); + if (span != null) { + return span.getSpanContext(); + } + } + } + return null; } - @Override - public void reportFullyDisplayed() { - if (getOptions().isEnableTimeToFullDisplayTracing()) { - getOptions().getFullyDisplayedReporter().reportFullyDrawn(); + private IScope buildLocalScope( + final @NotNull IScope scope, final @Nullable ScopeCallback callback) { + if (callback != null) { + try { + final IScope localScope = scope.clone(); + callback.run(localScope); + return localScope; + } catch (Throwable t) { + options.getLogger().log(SentryLevel.ERROR, "Error in the 'ScopeCallback' callback.", t); + } } + return scope; } @Override @@ -978,7 +872,7 @@ public void reportFullyDisplayed() { (scope) -> { scope.setPropagationContext(propagationContext); }); - if (getOptions().isTracingEnabled()) { + if (options.isTracingEnabled()) { return TransactionContext.fromPropagationContext(propagationContext); } else { return null; @@ -988,7 +882,7 @@ public void reportFullyDisplayed() { @Override public @Nullable SentryTraceHeader getTraceparent() { if (!isEnabled()) { - getOptions() + options .getLogger() .log( SentryLevel.WARNING, @@ -1007,7 +901,7 @@ public void reportFullyDisplayed() { @Override public @Nullable BaggageHeader getBaggage() { if (!isEnabled()) { - getOptions() + options .getLogger() .log(SentryLevel.WARNING, "Instance is disabled and this 'getBaggage' call is a no-op."); } else { @@ -1026,28 +920,28 @@ public void reportFullyDisplayed() { public @NotNull SentryId captureCheckIn(final @NotNull CheckIn checkIn) { SentryId sentryId = SentryId.EMPTY_ID; if (!isEnabled()) { - getOptions() + options .getLogger() .log( SentryLevel.WARNING, "Instance is disabled and this 'captureCheckIn' call is a no-op."); } else { try { - sentryId = getClient().captureCheckIn(checkIn, getCombinedScopeView(), null); + StackItem item = stack.peek(); + sentryId = item.getClient().captureCheckIn(checkIn, item.getScope(), null); } catch (Throwable e) { - getOptions() - .getLogger() - .log(SentryLevel.ERROR, "Error while capturing check-in for slug", e); + options.getLogger().log(SentryLevel.ERROR, "Error while capturing check-in for slug", e); } } - updateLastEventId(sentryId); + this.lastEventId = sentryId; return sentryId; } @ApiStatus.Internal @Override public @Nullable RateLimiter getRateLimiter() { - return getClient().getRateLimiter(); + final StackItem item = stack.peek(); + return item.getClient().getRateLimiter(); } @Override @@ -1057,27 +951,27 @@ public void reportFullyDisplayed() { @Override public @NotNull IMetricsAggregator getMetricsAggregator() { - return getClient().getMetricsAggregator(); + return stack.peek().getClient().getMetricsAggregator(); } @Override public @NotNull Map getDefaultTagsForMetrics() { - if (!getOptions().isEnableDefaultTagsForMetrics()) { + if (!options.isEnableDefaultTagsForMetrics()) { return Collections.emptyMap(); } final @NotNull Map tags = new HashMap<>(); - final @Nullable String release = getOptions().getRelease(); + final @Nullable String release = options.getRelease(); if (release != null) { tags.put("release", release); } - final @Nullable String environment = getOptions().getEnvironment(); + final @Nullable String environment = options.getEnvironment(); if (environment != null) { tags.put("environment", environment); } - final @Nullable String txnName = getCombinedScopeView().getTransactionName(); + final @Nullable String txnName = stack.peek().getScope().getTransactionName(); if (txnName != null) { tags.put("transaction", txnName); } @@ -1095,7 +989,7 @@ public void reportFullyDisplayed() { @Override public @Nullable LocalMetricsAggregator getLocalMetricsAggregator() { - if (!getOptions().isEnableSpanLocalMetricAggregation()) { + if (!options.isEnableSpanLocalMetricAggregation()) { return null; } final @Nullable ISpan span = getSpan(); @@ -1104,12 +998,4 @@ public void reportFullyDisplayed() { } return null; } - - private static void validateOptions(final @NotNull SentryOptions options) { - Objects.requireNonNull(options, "SentryOptions is required."); - if (options.getDsn() == null || options.getDsn().isEmpty()) { - throw new IllegalArgumentException( - "Scopes requires a DSN to be instantiated. Considering using the NoOpScopes if no DSN is available."); - } - } } diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index 8ba2ae9193..b31d853192 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -10,10 +10,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** - * @deprecated use {@link ScopesAdapter} instead - */ -@Deprecated public final class HubAdapter implements IHub { private static final HubAdapter INSTANCE = new HubAdapter(); @@ -54,7 +50,7 @@ public boolean isEnabled() { @ApiStatus.Internal @Override public @NotNull SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Hint hint) { - return Sentry.getCurrentScopes().captureEnvelope(envelope, hint); + return Sentry.getCurrentHub().captureEnvelope(envelope, hint); } @Override @@ -154,21 +150,11 @@ public void removeExtra(@NotNull String key) { } @Override - public @NotNull ISentryLifecycleToken pushScope() { - return Sentry.pushScope(); + public void pushScope() { + Sentry.pushScope(); } @Override - public @NotNull ISentryLifecycleToken pushIsolationScope() { - return Sentry.pushIsolationScope(); - } - - /** - * @deprecated please call {@link ISentryLifecycleToken#close()} on the token returned by {@link - * ScopesAdapter#pushScope()} or {@link ScopesAdapter#pushIsolationScope()} instead. - */ - @Override - @Deprecated public void popScope() { Sentry.popScope(); } @@ -179,13 +165,8 @@ public void withScope(@NotNull ScopeCallback callback) { } @Override - public void withIsolationScope(@NotNull ScopeCallback callback) { - Sentry.withIsolationScope(callback); - } - - @Override - public void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback) { - Sentry.configureScope(scopeType, callback); + public void configureScope(@NotNull ScopeCallback callback) { + Sentry.configureScope(callback); } @Override @@ -203,62 +184,9 @@ public void flush(long timeoutMillis) { Sentry.flush(timeoutMillis); } - /** - * @deprecated please use {@link IScopes#forkedScopes(String)} or {@link - * IScopes#forkedCurrentScope(String)} instead. - */ - @Deprecated @Override public @NotNull IHub clone() { - return Sentry.getCurrentScopes().clone(); - } - - @Override - public @NotNull IScopes forkedScopes(@NotNull String creator) { - return Sentry.forkedScopes(creator); - } - - @Override - public @NotNull IScopes forkedCurrentScope(@NotNull String creator) { - return Sentry.forkedCurrentScope(creator); - } - - @Override - public @NotNull IScopes forkedRootScopes(final @NotNull String creator) { - return Sentry.forkedRootScopes(creator); - } - - @Override - public @NotNull ISentryLifecycleToken makeCurrent() { - return NoOpScopesLifecycleToken.getInstance(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getScope() { - return Sentry.getCurrentScopes().getScope(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getIsolationScope() { - return Sentry.getCurrentScopes().getIsolationScope(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getGlobalScope() { - return Sentry.getGlobalScope(); - } - - @Override - public @Nullable IScopes getParentScopes() { - return Sentry.getCurrentScopes().getParentScopes(); - } - - @Override - public boolean isAncestorOf(final @Nullable IScopes otherScopes) { - return Sentry.getCurrentScopes().isAncestorOf(otherScopes); + return Sentry.getCurrentHub().clone(); } @Override @@ -267,7 +195,7 @@ public boolean isAncestorOf(final @Nullable IScopes otherScopes) { @Nullable TraceContext traceContext, @Nullable Hint hint, @Nullable ProfilingTraceData profilingTraceData) { - return Sentry.getCurrentScopes() + return Sentry.getCurrentHub() .captureTransaction(transaction, traceContext, hint, profilingTraceData); } @@ -289,23 +217,23 @@ public void setSpanContext( final @NotNull Throwable throwable, final @NotNull ISpan span, final @NotNull String transactionName) { - Sentry.getCurrentScopes().setSpanContext(throwable, span, transactionName); + Sentry.getCurrentHub().setSpanContext(throwable, span, transactionName); } @Override public @Nullable ISpan getSpan() { - return Sentry.getCurrentScopes().getSpan(); + return Sentry.getCurrentHub().getSpan(); } @Override @ApiStatus.Internal public @Nullable ITransaction getTransaction() { - return Sentry.getCurrentScopes().getTransaction(); + return Sentry.getCurrentHub().getTransaction(); } @Override public @NotNull SentryOptions getOptions() { - return Sentry.getCurrentScopes().getOptions(); + return Sentry.getCurrentHub().getOptions(); } @Override @@ -343,11 +271,11 @@ public void reportFullyDisplayed() { @ApiStatus.Internal @Override public @Nullable RateLimiter getRateLimiter() { - return Sentry.getCurrentScopes().getRateLimiter(); + return Sentry.getCurrentHub().getRateLimiter(); } @Override public @NotNull MetricsApi metrics() { - return Sentry.getCurrentScopes().metrics(); + return Sentry.getCurrentHub().metrics(); } } diff --git a/sentry/src/main/java/io/sentry/HubScopesWrapper.java b/sentry/src/main/java/io/sentry/HubScopesWrapper.java deleted file mode 100644 index 371321eaf2..0000000000 --- a/sentry/src/main/java/io/sentry/HubScopesWrapper.java +++ /dev/null @@ -1,347 +0,0 @@ -package io.sentry; - -import io.sentry.metrics.MetricsApi; -import io.sentry.protocol.SentryId; -import io.sentry.protocol.SentryTransaction; -import io.sentry.protocol.User; -import io.sentry.transport.RateLimiter; -import java.util.List; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@SuppressWarnings("deprecation") -@Deprecated -public final class HubScopesWrapper implements IHub { - - private final @NotNull IScopes scopes; - - public HubScopesWrapper(final @NotNull IScopes scopes) { - this.scopes = scopes; - } - - @Override - public boolean isEnabled() { - return scopes.isEnabled(); - } - - @Override - public @NotNull SentryId captureEvent(@NotNull SentryEvent event, @Nullable Hint hint) { - return scopes.captureEvent(event, hint); - } - - @Override - public @NotNull SentryId captureEvent( - @NotNull SentryEvent event, @Nullable Hint hint, @NotNull ScopeCallback callback) { - return scopes.captureEvent(event, hint, callback); - } - - @Override - public @NotNull SentryId captureMessage(@NotNull String message, @NotNull SentryLevel level) { - return scopes.captureMessage(message, level); - } - - @Override - public @NotNull SentryId captureMessage( - @NotNull String message, @NotNull SentryLevel level, @NotNull ScopeCallback callback) { - return scopes.captureMessage(message, level, callback); - } - - @Override - public @NotNull SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Hint hint) { - return scopes.captureEnvelope(envelope, hint); - } - - @Override - public @NotNull SentryId captureException(@NotNull Throwable throwable, @Nullable Hint hint) { - return scopes.captureException(throwable, hint); - } - - @Override - public @NotNull SentryId captureException( - @NotNull Throwable throwable, @Nullable Hint hint, @NotNull ScopeCallback callback) { - return scopes.captureException(throwable, hint, callback); - } - - @Override - public void captureUserFeedback(@NotNull UserFeedback userFeedback) { - scopes.captureUserFeedback(userFeedback); - } - - @Override - public void startSession() { - scopes.startSession(); - } - - @Override - public void endSession() { - scopes.endSession(); - } - - @Override - public void close() { - scopes.close(); - } - - @Override - public void close(boolean isRestarting) { - scopes.close(isRestarting); - } - - @Override - public void addBreadcrumb(@NotNull Breadcrumb breadcrumb, @Nullable Hint hint) { - scopes.addBreadcrumb(breadcrumb, hint); - } - - @Override - public void addBreadcrumb(@NotNull Breadcrumb breadcrumb) { - scopes.addBreadcrumb(breadcrumb); - } - - @Override - public void setLevel(@Nullable SentryLevel level) { - scopes.setLevel(level); - } - - @Override - public void setTransaction(@Nullable String transaction) { - scopes.setTransaction(transaction); - } - - @Override - public void setUser(@Nullable User user) { - scopes.setUser(user); - } - - @Override - public void setFingerprint(@NotNull List fingerprint) { - scopes.setFingerprint(fingerprint); - } - - @Override - public void clearBreadcrumbs() { - scopes.clearBreadcrumbs(); - } - - @Override - public void setTag(@NotNull String key, @NotNull String value) { - scopes.setTag(key, value); - } - - @Override - public void removeTag(@NotNull String key) { - scopes.removeTag(key); - } - - @Override - public void setExtra(@NotNull String key, @NotNull String value) { - scopes.setExtra(key, value); - } - - @Override - public void removeExtra(@NotNull String key) { - scopes.removeExtra(key); - } - - @Override - public @NotNull SentryId getLastEventId() { - return scopes.getLastEventId(); - } - - @Override - public @NotNull ISentryLifecycleToken pushScope() { - return scopes.pushScope(); - } - - @Override - public @NotNull ISentryLifecycleToken pushIsolationScope() { - return scopes.pushIsolationScope(); - } - - /** - * @deprecated please call {@link ISentryLifecycleToken#close()} on the token returned by {@link - * IScopes#pushScope()} or {@link IScopes#pushIsolationScope()} instead. - */ - @Override - @Deprecated - public void popScope() { - scopes.popScope(); - } - - @Override - public void withScope(@NotNull ScopeCallback callback) { - scopes.withScope(callback); - } - - @Override - public void withIsolationScope(@NotNull ScopeCallback callback) { - scopes.withIsolationScope(callback); - } - - @Override - public void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback) { - scopes.configureScope(scopeType, callback); - } - - @Override - public void bindClient(@NotNull ISentryClient client) { - scopes.bindClient(client); - } - - @Override - public boolean isHealthy() { - return scopes.isHealthy(); - } - - @Override - public void flush(long timeoutMillis) { - scopes.flush(timeoutMillis); - } - - /** - * @deprecated please use {@link IScopes#forkedScopes(String)} or {@link - * IScopes#forkedCurrentScope(String)} instead. - */ - @Override - @Deprecated - public @NotNull IHub clone() { - return scopes.clone(); - } - - @Override - public @NotNull IScopes forkedScopes(@NotNull String creator) { - return scopes.forkedScopes(creator); - } - - @Override - public @NotNull IScopes forkedCurrentScope(@NotNull String creator) { - return scopes.forkedCurrentScope(creator); - } - - @Override - public @NotNull IScopes forkedRootScopes(final @NotNull String creator) { - return Sentry.forkedRootScopes(creator); - } - - @Override - public @NotNull ISentryLifecycleToken makeCurrent() { - return scopes.makeCurrent(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getScope() { - return scopes.getScope(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getIsolationScope() { - return scopes.getIsolationScope(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getGlobalScope() { - return Sentry.getGlobalScope(); - } - - @Override - public @Nullable IScopes getParentScopes() { - return scopes.getParentScopes(); - } - - @Override - public boolean isAncestorOf(final @Nullable IScopes otherScopes) { - return scopes.isAncestorOf(otherScopes); - } - - @ApiStatus.Internal - @Override - public @NotNull SentryId captureTransaction( - @NotNull SentryTransaction transaction, - @Nullable TraceContext traceContext, - @Nullable Hint hint, - @Nullable ProfilingTraceData profilingTraceData) { - return scopes.captureTransaction(transaction, traceContext, hint, profilingTraceData); - } - - @Override - public @NotNull ITransaction startTransaction( - @NotNull TransactionContext transactionContext, - @NotNull TransactionOptions transactionOptions) { - return scopes.startTransaction(transactionContext, transactionOptions); - } - - @Override - public @Nullable SentryTraceHeader traceHeaders() { - return scopes.traceHeaders(); - } - - @ApiStatus.Internal - @Override - public void setSpanContext( - @NotNull Throwable throwable, @NotNull ISpan span, @NotNull String transactionName) { - scopes.setSpanContext(throwable, span, transactionName); - } - - @Override - public @Nullable ISpan getSpan() { - return scopes.getSpan(); - } - - @ApiStatus.Internal - @Override - public @Nullable ITransaction getTransaction() { - return scopes.getTransaction(); - } - - @Override - public @NotNull SentryOptions getOptions() { - return scopes.getOptions(); - } - - @Override - public @Nullable Boolean isCrashedLastRun() { - return scopes.isCrashedLastRun(); - } - - @Override - public void reportFullyDisplayed() { - scopes.reportFullyDisplayed(); - } - - @Override - public @Nullable TransactionContext continueTrace( - @Nullable String sentryTrace, @Nullable List baggageHeaders) { - return scopes.continueTrace(sentryTrace, baggageHeaders); - } - - @Override - public @Nullable SentryTraceHeader getTraceparent() { - return scopes.getTraceparent(); - } - - @Override - public @Nullable BaggageHeader getBaggage() { - return scopes.getBaggage(); - } - - @ApiStatus.Experimental - @Override - public @NotNull SentryId captureCheckIn(@NotNull CheckIn checkIn) { - return scopes.captureCheckIn(checkIn); - } - - @ApiStatus.Internal - @Override - public @Nullable RateLimiter getRateLimiter() { - return scopes.getRateLimiter(); - } - - @ApiStatus.Experimental - @Override - public @NotNull MetricsApi metrics() { - return scopes.metrics(); - } -} diff --git a/sentry/src/main/java/io/sentry/IHub.java b/sentry/src/main/java/io/sentry/IHub.java index 23a7bed7de..684d8ec528 100644 --- a/sentry/src/main/java/io/sentry/IHub.java +++ b/sentry/src/main/java/io/sentry/IHub.java @@ -1,9 +1,590 @@ package io.sentry; -/** - * SDK API contract which combines a client and scope management - * - * @deprecated please use {@link IScopes} instead - */ -@Deprecated -public interface IHub extends IScopes {} +import io.sentry.metrics.MetricsApi; +import io.sentry.protocol.SentryId; +import io.sentry.protocol.SentryTransaction; +import io.sentry.protocol.User; +import io.sentry.transport.RateLimiter; +import java.util.List; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** SDK API contract which combines a client and scope management */ +public interface IHub { + + /** + * Check if the Hub is enabled/active. + * + * @return true if its enabled or false otherwise. + */ + boolean isEnabled(); + + /** + * Captures the event. + * + * @param event the event + * @param hint SDK specific but provides high level information about the origin of the event + * @return The Id (SentryId object) of the event + */ + @NotNull + SentryId captureEvent(@NotNull SentryEvent event, @Nullable Hint hint); + + /** + * Captures the event. + * + * @param event the event + * @return The Id (SentryId object) of the event + */ + default @NotNull SentryId captureEvent(@NotNull SentryEvent event) { + return captureEvent(event, new Hint()); + } + + /** + * Captures the event. + * + * @param event the event + * @param callback The callback to configure the scope for a single invocation. + * @return The Id (SentryId object) of the event + */ + default @NotNull SentryId captureEvent( + @NotNull SentryEvent event, final @NotNull ScopeCallback callback) { + return captureEvent(event, new Hint(), callback); + } + + /** + * Captures the event. + * + * @param event the event + * @param hint SDK specific but provides high level information about the origin of the event + * @param callback The callback to configure the scope for a single invocation. + * @return The Id (SentryId object) of the event + */ + @NotNull + SentryId captureEvent( + final @NotNull SentryEvent event, + final @Nullable Hint hint, + final @NotNull ScopeCallback callback); + + /** + * Captures the message. + * + * @param message The message to send. + * @return The Id (SentryId object) of the event + */ + default @NotNull SentryId captureMessage(@NotNull String message) { + return captureMessage(message, SentryLevel.INFO); + } + + /** + * Captures the message. + * + * @param message The message to send. + * @param level The message level. + * @return The Id (SentryId object) of the event + */ + @NotNull + SentryId captureMessage(@NotNull String message, @NotNull SentryLevel level); + + /** + * Captures the message. + * + * @param message The message to send. + * @param level The message level. + * @param callback The callback to configure the scope for a single invocation. + * @return The Id (SentryId object) of the event + */ + @NotNull + SentryId captureMessage( + @NotNull String message, @NotNull SentryLevel level, @NotNull ScopeCallback callback); + + /** + * Captures the message. + * + * @param message The message to send. + * @param callback The callback to configure the scope for a single invocation. + * @return The Id (SentryId object) of the event + */ + default @NotNull SentryId captureMessage( + @NotNull String message, @NotNull ScopeCallback callback) { + return captureMessage(message, SentryLevel.INFO, callback); + } + + /** + * Captures an envelope. + * + * @param envelope the SentryEnvelope to send. + * @param hint SDK specific but provides high level information about the origin of the event + * @return The Id (SentryId object) of the event + */ + @NotNull + SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Hint hint); + + /** + * Captures an envelope. + * + * @param envelope the SentryEnvelope to send. + * @return The Id (SentryId object) of the event + */ + default @NotNull SentryId captureEnvelope(@NotNull SentryEnvelope envelope) { + return captureEnvelope(envelope, new Hint()); + } + + /** + * Captures the exception. + * + * @param throwable The exception. + * @param hint SDK specific but provides high level information about the origin of the event + * @return The Id (SentryId object) of the event + */ + @NotNull + SentryId captureException(@NotNull Throwable throwable, @Nullable Hint hint); + + /** + * Captures the exception. + * + * @param throwable The exception. + * @return The Id (SentryId object) of the event + */ + default @NotNull SentryId captureException(@NotNull Throwable throwable) { + return captureException(throwable, new Hint()); + } + + /** + * Captures the exception. + * + * @param throwable The exception. + * @param callback The callback to configure the scope for a single invocation. + * @return The Id (SentryId object) of the event + */ + default @NotNull SentryId captureException( + @NotNull Throwable throwable, final @NotNull ScopeCallback callback) { + return captureException(throwable, new Hint(), callback); + } + + /** + * Captures the exception. + * + * @param throwable The exception. + * @param hint SDK specific but provides high level information about the origin of the event + * @param callback The callback to configure the scope for a single invocation. + * @return The Id (SentryId object) of the event + */ + @NotNull + SentryId captureException( + final @NotNull Throwable throwable, + final @Nullable Hint hint, + final @NotNull ScopeCallback callback); + + /** + * Captures a manually created user feedback and sends it to Sentry. + * + * @param userFeedback The user feedback to send to Sentry. + */ + void captureUserFeedback(@NotNull UserFeedback userFeedback); + + /** Starts a new session. If there's a running session, it ends it before starting the new one. */ + void startSession(); + + /** Ends the current session */ + void endSession(); + + /** Flushes out the queue for up to timeout seconds and disable the Hub. */ + void close(); + + /** + * Flushes out the queue for up to timeout seconds and disable the Hub. + * + * @param isRestarting if true, avoids locking the main thread when finishing the queue. + */ + void close(boolean isRestarting); + + /** + * Adds a breadcrumb to the current Scope + * + * @param breadcrumb the breadcrumb + * @param hint SDK specific but provides high level information about the origin of the event + */ + void addBreadcrumb(@NotNull Breadcrumb breadcrumb, @Nullable Hint hint); + + /** + * Adds a breadcrumb to the current Scope + * + * @param breadcrumb the breadcrumb + */ + void addBreadcrumb(@NotNull Breadcrumb breadcrumb); + + /** + * Adds a breadcrumb to the current Scope + * + * @param message rendered as text and the whitespace is preserved. + */ + default void addBreadcrumb(@NotNull String message) { + addBreadcrumb(new Breadcrumb(message)); + } + + /** + * Adds a breadcrumb to the current Scope + * + * @param message rendered as text and the whitespace is preserved. + * @param category Categories are dotted strings that indicate what the crumb is or where it comes + * from. + */ + default void addBreadcrumb(@NotNull String message, @NotNull String category) { + Breadcrumb breadcrumb = new Breadcrumb(message); + breadcrumb.setCategory(category); + addBreadcrumb(breadcrumb); + } + + /** + * Sets the level of all events sent within current Scope + * + * @param level the Sentry level + */ + void setLevel(@Nullable SentryLevel level); + + /** + * Sets the name of the current transaction to the current Scope. + * + * @param transaction the transaction + */ + void setTransaction(@Nullable String transaction); + + /** + * Shallow merges user configuration (email, username, etc) to the current Scope. + * + * @param user the user + */ + void setUser(@Nullable User user); + + /** + * Sets the fingerprint to group specific events together to the current Scope. + * + * @param fingerprint the fingerprints + */ + void setFingerprint(@NotNull List fingerprint); + + /** Deletes current breadcrumbs from the current scope. */ + void clearBreadcrumbs(); + + /** + * Sets the tag to a string value to the current Scope, overwriting a potential previous value + * + * @param key the key + * @param value the value + */ + void setTag(@NotNull String key, @NotNull String value); + + /** + * Removes the tag to a string value to the current Scope + * + * @param key the key + */ + void removeTag(@NotNull String key); + + /** + * Sets the extra key to an arbitrary value to the current Scope, overwriting a potential previous + * value + * + * @param key the key + * @param value the value + */ + void setExtra(@NotNull String key, @NotNull String value); + + /** + * Removes the extra key to an arbitrary value to the current Scope + * + * @param key the key + */ + void removeExtra(@NotNull String key); + + /** + * Last event id recorded in the current scope + * + * @return last SentryId + */ + @NotNull + SentryId getLastEventId(); + + /** Pushes a new scope while inheriting the current scope's data. */ + void pushScope(); + + /** Removes the first scope */ + void popScope(); + + /** + * Runs the callback with a new scope which gets dropped at the end. If you're using the Sentry + * SDK in globalHubMode (defaults to true on Android) {@link + * Sentry#init(Sentry.OptionsConfiguration, boolean)} calling withScope is discouraged, as scope + * changes may be dropped when executed in parallel. Use {@link + * IHub#configureScope(ScopeCallback)} instead. + * + * @param callback the callback + */ + void withScope(@NotNull ScopeCallback callback); + + /** + * Configures the scope through the callback. + * + * @param callback The configure scope callback. + */ + void configureScope(@NotNull ScopeCallback callback); + + /** + * Binds a different client to the hub + * + * @param client the client. + */ + void bindClient(@NotNull ISentryClient client); + + /** + * Whether the transport is healthy. + * + * @return true if the transport is healthy + */ + boolean isHealthy(); + + /** + * Flushes events queued up, but keeps the Hub enabled. Not implemented yet. + * + * @param timeoutMillis time in milliseconds + */ + void flush(long timeoutMillis); + + /** + * Clones the Hub + * + * @return the cloned Hub + */ + @NotNull + IHub clone(); + + /** + * Captures the transaction and enqueues it for sending to Sentry server. + * + * @param transaction the transaction + * @param traceContext the trace context + * @param hint the hints + * @param profilingTraceData the profiling trace data + * @return transaction's id + */ + @ApiStatus.Internal + @NotNull + SentryId captureTransaction( + @NotNull SentryTransaction transaction, + @Nullable TraceContext traceContext, + @Nullable Hint hint, + @Nullable ProfilingTraceData profilingTraceData); + + /** + * Captures the transaction and enqueues it for sending to Sentry server. + * + * @param transaction the transaction + * @param traceContext the trace context + * @param hint the hints + * @return transaction's id + */ + @ApiStatus.Internal + @NotNull + default SentryId captureTransaction( + @NotNull SentryTransaction transaction, + @Nullable TraceContext traceContext, + @Nullable Hint hint) { + return captureTransaction(transaction, traceContext, hint, null); + } + + @ApiStatus.Internal + @NotNull + default SentryId captureTransaction(@NotNull SentryTransaction transaction, @Nullable Hint hint) { + return captureTransaction(transaction, null, hint); + } + + /** + * Captures the transaction and enqueues it for sending to Sentry server. + * + * @param transaction the transaction + * @param traceContext the trace context + * @return transaction's id + */ + @ApiStatus.Internal + default @NotNull SentryId captureTransaction( + @NotNull SentryTransaction transaction, @Nullable TraceContext traceContext) { + return captureTransaction(transaction, traceContext, null); + } + + /** + * Creates a Transaction and returns the instance. + * + * @param transactionContexts the transaction contexts + * @return created transaction + */ + default @NotNull ITransaction startTransaction(@NotNull TransactionContext transactionContexts) { + return startTransaction(transactionContexts, new TransactionOptions()); + } + + /** + * Creates a Transaction and returns the instance. Based on the {@link + * SentryOptions#getTracesSampleRate()} the decision if transaction is sampled will be taken by + * {@link TracesSampler}. + * + * @param name the transaction name + * @param operation the operation + * @return created transaction + */ + default @NotNull ITransaction startTransaction( + final @NotNull String name, final @NotNull String operation) { + return startTransaction(name, operation, new TransactionOptions()); + } + + /** + * Creates a Transaction and returns the instance. Based on the {@link + * SentryOptions#getTracesSampleRate()} the decision if transaction is sampled will be taken by + * {@link TracesSampler}. + * + * @param name the transaction name + * @param operation the operation + * @param transactionOptions the transaction options + * @return created transaction + */ + default @NotNull ITransaction startTransaction( + final @NotNull String name, + final @NotNull String operation, + final @NotNull TransactionOptions transactionOptions) { + return startTransaction(new TransactionContext(name, operation), transactionOptions); + } + + /** + * 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, + final @NotNull TransactionOptions transactionOptions); + + /** + * Returns the "sentry-trace" header that allows tracing across services. Can also be used in + * <meta> HTML tags. Also see {@link IHub#getBaggage()}. + * + * @deprecated please use {@link IHub#getTraceparent()} instead. + * @return sentry trace header or null + */ + @Deprecated + @Nullable + SentryTraceHeader traceHeaders(); + + /** + * Associates {@link ISpan} and the transaction name with the {@link Throwable}. Used to determine + * in which trace the exception has been thrown in framework integrations. + * + * @param throwable the throwable + * @param span the span context + * @param transactionName the transaction name + */ + @ApiStatus.Internal + void setSpanContext( + @NotNull Throwable throwable, @NotNull ISpan span, @NotNull String transactionName); + + /** + * Gets the current active transaction or span. + * + * @return the active span or null when no active transaction is running + */ + @Nullable + ISpan getSpan(); + + /** + * Returns the transaction. + * + * @return the transaction or null when no active transaction is running. + */ + @ApiStatus.Internal + @Nullable + ITransaction getTransaction(); + + /** + * Gets the {@link SentryOptions} attached to current scope. + * + * @return the options attached to current scope. + */ + @NotNull + SentryOptions getOptions(); + + /** + * Returns if the App has crashed (Process has terminated) during the last run. It only returns + * true or false if offline caching {{@link SentryOptions#getCacheDirPath()} } is set with a valid + * dir. + * + *

    If the call to this method is early in the App lifecycle and the SDK could not check if the + * App has crashed in the background, the check is gonna do IO in the calling thread. + * + * @return true if App has crashed, false otherwise, and null if not evaluated yet + */ + @Nullable + Boolean isCrashedLastRun(); + + /** + * Report a screen has been fully loaded. That means all data needed by the UI was loaded. If + * time-to-full-display tracing {{@link SentryOptions#isEnableTimeToFullDisplayTracing()} } is + * disabled this call is ignored. + * + *

    This method is safe to be called multiple times. If the time-to-full-display span is already + * finished, this call will be ignored. + */ + void reportFullyDisplayed(); + + /** + * @deprecated See {@link IHub#reportFullyDisplayed()}. + */ + @Deprecated + default void reportFullDisplayed() { + reportFullyDisplayed(); + } + + /** + * Continue a trace based on HTTP header values. If no "sentry-trace" header is provided a random + * trace ID and span ID is created. + * + * @param sentryTrace "sentry-trace" header + * @param baggageHeaders "baggage" headers + * @return a transaction context for starting a transaction or null if performance is disabled + */ + @Nullable + TransactionContext continueTrace( + final @Nullable String sentryTrace, final @Nullable List baggageHeaders); + + /** + * Returns the "sentry-trace" header that allows tracing across services. Can also be used in + * <meta> HTML tags. Also see {@link IHub#getBaggage()}. + * + * @return sentry trace header or null + */ + @Nullable + SentryTraceHeader getTraceparent(); + + /** + * Returns the "baggage" header that allows tracing across services. Can also be used in + * <meta> HTML tags. Also see {@link IHub#getTraceparent()}. + * + * @return baggage header or null + */ + @Nullable + BaggageHeader getBaggage(); + + @ApiStatus.Experimental + @NotNull + SentryId captureCheckIn(final @NotNull CheckIn checkIn); + + @ApiStatus.Internal + @Nullable + RateLimiter getRateLimiter(); + + @ApiStatus.Experimental + @NotNull + MetricsApi metrics(); +} diff --git a/sentry/src/main/java/io/sentry/IScope.java b/sentry/src/main/java/io/sentry/IScope.java index 6d6ddfb4ce..3842fb2c3a 100644 --- a/sentry/src/main/java/io/sentry/IScope.java +++ b/sentry/src/main/java/io/sentry/IScope.java @@ -1,9 +1,7 @@ package io.sentry; -import io.sentry.internal.eventprocessor.EventProcessorAndOrder; import io.sentry.protocol.Contexts; import io.sentry.protocol.Request; -import io.sentry.protocol.SentryId; import io.sentry.protocol.User; import java.util.Collection; import java.util.List; @@ -304,14 +302,9 @@ public interface IScope { * * @return the event processors list */ - @ApiStatus.Internal @NotNull List getEventProcessors(); - @ApiStatus.Internal - @NotNull - List getEventProcessorsWithOrder(); - /** * Adds an event processor to the Scope's event processors list * @@ -359,9 +352,6 @@ public interface IScope { @Nullable Session getSession(); - @ApiStatus.Internal - void clearSession(); - @ApiStatus.Internal void setPropagationContext(final @NotNull PropagationContext propagationContext); @@ -380,26 +370,4 @@ public interface IScope { */ @NotNull IScope clone(); - - void setLastEventId(final @NotNull SentryId lastEventId); - - @NotNull - SentryId getLastEventId(); - - void bindClient(final @NotNull ISentryClient client); - - @NotNull - ISentryClient getClient(); - - @ApiStatus.Internal - void assignTraceContext(final @NotNull SentryEvent event); - - @ApiStatus.Internal - void setSpanContext( - final @NotNull Throwable throwable, - final @NotNull ISpan span, - final @NotNull String transactionName); - - @ApiStatus.Internal - void replaceOptions(final @NotNull SentryOptions options); } diff --git a/sentry/src/main/java/io/sentry/IScopes.java b/sentry/src/main/java/io/sentry/IScopes.java deleted file mode 100644 index 400f08e457..0000000000 --- a/sentry/src/main/java/io/sentry/IScopes.java +++ /dev/null @@ -1,710 +0,0 @@ -package io.sentry; - -import io.sentry.metrics.MetricsApi; -import io.sentry.protocol.SentryId; -import io.sentry.protocol.SentryTransaction; -import io.sentry.protocol.User; -import io.sentry.transport.RateLimiter; -import java.util.List; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public interface IScopes { - - /** - * Check if Sentry is enabled/active. - * - * @return true if its enabled or false otherwise. - */ - boolean isEnabled(); - - /** - * Captures the event. - * - * @param event the event - * @param hint SDK specific but provides high level information about the origin of the event - * @return The Id (SentryId object) of the event - */ - @NotNull - SentryId captureEvent(@NotNull SentryEvent event, @Nullable Hint hint); - - /** - * Captures the event. - * - * @param event the event - * @return The Id (SentryId object) of the event - */ - default @NotNull SentryId captureEvent(@NotNull SentryEvent event) { - return captureEvent(event, new Hint()); - } - - /** - * Captures the event. - * - * @param event the event - * @param callback The callback to configure the scope for a single invocation. - * @return The Id (SentryId object) of the event - */ - default @NotNull SentryId captureEvent( - @NotNull SentryEvent event, final @NotNull ScopeCallback callback) { - return captureEvent(event, new Hint(), callback); - } - - /** - * Captures the event. - * - * @param event the event - * @param hint SDK specific but provides high level information about the origin of the event - * @param callback The callback to configure the scope for a single invocation. - * @return The Id (SentryId object) of the event - */ - @NotNull - SentryId captureEvent( - final @NotNull SentryEvent event, - final @Nullable Hint hint, - final @NotNull ScopeCallback callback); - - /** - * Captures the message. - * - * @param message The message to send. - * @return The Id (SentryId object) of the event - */ - default @NotNull SentryId captureMessage(@NotNull String message) { - return captureMessage(message, SentryLevel.INFO); - } - - /** - * Captures the message. - * - * @param message The message to send. - * @param level The message level. - * @return The Id (SentryId object) of the event - */ - @NotNull - SentryId captureMessage(@NotNull String message, @NotNull SentryLevel level); - - /** - * Captures the message. - * - * @param message The message to send. - * @param level The message level. - * @param callback The callback to configure the scope for a single invocation. - * @return The Id (SentryId object) of the event - */ - @NotNull - SentryId captureMessage( - @NotNull String message, @NotNull SentryLevel level, @NotNull ScopeCallback callback); - - /** - * Captures the message. - * - * @param message The message to send. - * @param callback The callback to configure the scope for a single invocation. - * @return The Id (SentryId object) of the event - */ - default @NotNull SentryId captureMessage( - @NotNull String message, @NotNull ScopeCallback callback) { - return captureMessage(message, SentryLevel.INFO, callback); - } - - /** - * Captures an envelope. - * - * @param envelope the SentryEnvelope to send. - * @param hint SDK specific but provides high level information about the origin of the event - * @return The Id (SentryId object) of the event - */ - @NotNull - SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Hint hint); - - /** - * Captures an envelope. - * - * @param envelope the SentryEnvelope to send. - * @return The Id (SentryId object) of the event - */ - default @NotNull SentryId captureEnvelope(@NotNull SentryEnvelope envelope) { - return captureEnvelope(envelope, new Hint()); - } - - /** - * Captures the exception. - * - * @param throwable The exception. - * @param hint SDK specific but provides high level information about the origin of the event - * @return The Id (SentryId object) of the event - */ - @NotNull - SentryId captureException(@NotNull Throwable throwable, @Nullable Hint hint); - - /** - * Captures the exception. - * - * @param throwable The exception. - * @return The Id (SentryId object) of the event - */ - default @NotNull SentryId captureException(@NotNull Throwable throwable) { - return captureException(throwable, new Hint()); - } - - /** - * Captures the exception. - * - * @param throwable The exception. - * @param callback The callback to configure the scope for a single invocation. - * @return The Id (SentryId object) of the event - */ - default @NotNull SentryId captureException( - @NotNull Throwable throwable, final @NotNull ScopeCallback callback) { - return captureException(throwable, new Hint(), callback); - } - - /** - * Captures the exception. - * - * @param throwable The exception. - * @param hint SDK specific but provides high level information about the origin of the event - * @param callback The callback to configure the scope for a single invocation. - * @return The Id (SentryId object) of the event - */ - @NotNull - SentryId captureException( - final @NotNull Throwable throwable, - final @Nullable Hint hint, - final @NotNull ScopeCallback callback); - - /** - * Captures a manually created user feedback and sends it to Sentry. - * - * @param userFeedback The user feedback to send to Sentry. - */ - void captureUserFeedback(@NotNull UserFeedback userFeedback); - - /** Starts a new session. If there's a running session, it ends it before starting the new one. */ - void startSession(); - - /** Ends the current session */ - void endSession(); - - /** Flushes out the queue for up to timeout seconds and disable the Scopes. */ - void close(); - - /** - * Flushes out the queue for up to timeout seconds and disable the Scopes. - * - * @param isRestarting if true, avoids locking the main thread when finishing the queue. - */ - void close(boolean isRestarting); - - /** - * Adds a breadcrumb to the current Scope - * - * @param breadcrumb the breadcrumb - * @param hint SDK specific but provides high level information about the origin of the event - */ - void addBreadcrumb(@NotNull Breadcrumb breadcrumb, @Nullable Hint hint); - - /** - * Adds a breadcrumb to the current Scope - * - * @param breadcrumb the breadcrumb - */ - void addBreadcrumb(@NotNull Breadcrumb breadcrumb); - - /** - * Adds a breadcrumb to the current Scope - * - * @param message rendered as text and the whitespace is preserved. - */ - default void addBreadcrumb(@NotNull String message) { - addBreadcrumb(new Breadcrumb(message)); - } - - /** - * Adds a breadcrumb to the current Scope - * - * @param message rendered as text and the whitespace is preserved. - * @param category Categories are dotted strings that indicate what the crumb is or where it comes - * from. - */ - default void addBreadcrumb(@NotNull String message, @NotNull String category) { - Breadcrumb breadcrumb = new Breadcrumb(message); - breadcrumb.setCategory(category); - addBreadcrumb(breadcrumb); - } - - /** - * Sets the level of all events sent within current Scope - * - * @param level the Sentry level - */ - void setLevel(@Nullable SentryLevel level); - - /** - * Sets the name of the current transaction to the current Scope. - * - * @param transaction the transaction - */ - void setTransaction(@Nullable String transaction); - - /** - * Shallow merges user configuration (email, username, etc) to the current Scope. - * - * @param user the user - */ - void setUser(@Nullable User user); - - /** - * Sets the fingerprint to group specific events together to the current Scope. - * - * @param fingerprint the fingerprints - */ - void setFingerprint(@NotNull List fingerprint); - - /** Deletes current breadcrumbs from the current scope. */ - void clearBreadcrumbs(); - - /** - * Sets the tag to a string value to the current Scope, overwriting a potential previous value - * - * @param key the key - * @param value the value - */ - void setTag(@NotNull String key, @NotNull String value); - - /** - * Removes the tag to a string value to the current Scope - * - * @param key the key - */ - void removeTag(@NotNull String key); - - /** - * Sets the extra key to an arbitrary value to the current Scope, overwriting a potential previous - * value - * - * @param key the key - * @param value the value - */ - void setExtra(@NotNull String key, @NotNull String value); - - /** - * Removes the extra key to an arbitrary value to the current Scope - * - * @param key the key - */ - void removeExtra(@NotNull String key); - - /** - * Last event id recorded in the current scope - * - * @return last SentryId - */ - @NotNull - SentryId getLastEventId(); - - /** Pushes a new scope while inheriting the current scope's data. */ - @NotNull - ISentryLifecycleToken pushScope(); - - @NotNull - ISentryLifecycleToken pushIsolationScope(); - - /** - * Removes the first scope and restores its parent. - * - * @deprecated please call {@link ISentryLifecycleToken#close()} on the token returned by {@link - * IScopes#pushScope()} or {@link IScopes#pushIsolationScope()} instead. - */ - @Deprecated - void popScope(); - - /** - * Runs the callback with a new current scope which gets dropped at the end. - * - *

    If you're using the Sentry SDK in globalHubMode (defaults to true on Android) {@link - * Sentry#init(Sentry.OptionsConfiguration, boolean)} calling withScope is discouraged, as scope - * changes may be dropped when executed in parallel. Use {@link - * IScopes#configureScope(ScopeCallback)} instead. - * - * @param callback the callback - */ - void withScope(@NotNull ScopeCallback callback); - - /** - * Runs the callback with a new isolation scope which gets dropped at the end. Current scope is - * also forked. - * - *

    If you're using the Sentry SDK in globalHubMode (defaults to true on Android) {@link - * Sentry#init(Sentry.OptionsConfiguration, boolean)} calling withScope is discouraged, as scope - * changes may be dropped when executed in parallel. Use {@link IScopes#configureScope(ScopeType, - * ScopeCallback)} instead. - * - * @param callback the callback - */ - void withIsolationScope(@NotNull ScopeCallback callback); - - /** - * Configures the scope through the callback. - * - * @param callback The configure scope callback. - */ - default void configureScope(@NotNull ScopeCallback callback) { - configureScope(null, callback); - } - - /** - * Configures the scope through the callback. - * - * @param callback The configure scope callback. - */ - void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback); - - /** - * Binds a different client to the scopes - * - * @param client the client. - */ - void bindClient(@NotNull ISentryClient client); - - /** - * Whether the transport is healthy. - * - * @return true if the transport is healthy - */ - boolean isHealthy(); - - /** - * Flushes events queued up, but keeps the scopes enabled. Not implemented yet. - * - * @param timeoutMillis time in milliseconds - */ - void flush(long timeoutMillis); - - /** - * Clones the Hub - * - * @deprecated please use {@link IScopes#forkedScopes(String)} or {@link - * IScopes#forkedCurrentScope(String)} instead. - * @return the cloned Hub - */ - @NotNull - @Deprecated - IHub clone(); - - /** - * Creates a fork of both current and isolation scope from current scopes. - * - * @param creator debug information to see why scopes where forked - * @return forked Scopes - */ - @NotNull - IScopes forkedScopes(final @NotNull String creator); - - /** - * Creates a fork of current scope without forking isolation scope. - * - * @param creator debug information to see why scopes where forked - * @return forked Scopes - */ - @NotNull - IScopes forkedCurrentScope(final @NotNull String creator); - - /** - * Creates a fork of both current and isolation scope from root scopes. - * - * @param creator debug information to see why scopes where forked - * @return forked Scopes - */ - @NotNull - IScopes forkedRootScopes(final @NotNull String creator); - - /** - * Stores this Scopes in store, making it the current one that is used by static API. - * - * @return a token you should call .close() on when you're done. - */ - @NotNull - ISentryLifecycleToken makeCurrent(); - - /** - * Returns the current scope of this Scopes. - * - * @return scope - */ - @ApiStatus.Internal - @NotNull - IScope getScope(); - - /** - * Returns the isolation scope of this Scopes. - * - * @return isolation scope - */ - @ApiStatus.Internal - @NotNull - IScope getIsolationScope(); - - /** - * Returns the global scope. - * - * @return global scope - */ - @ApiStatus.Internal - @NotNull - IScope getGlobalScope(); - - /** - * Returns the parent of this Scopes instance or null, if it does not have a parent. The parent is - * the Scopes instance this instance was forked from. - * - * @return parent Scopes or null - */ - @ApiStatus.Internal - @Nullable - IScopes getParentScopes(); - - /** - * Checks whether this Scopes instance is direct or indirect parent of the other Scopes instance. - * - * @param otherScopes Scopes instance that could be a direct or indirect child. - * @return true if this Scopes instance is a direct or indirect parent of the other Scopes. - */ - @ApiStatus.Internal - boolean isAncestorOf(final @Nullable IScopes otherScopes); - - /** - * Captures the transaction and enqueues it for sending to Sentry server. - * - * @param transaction the transaction - * @param traceContext the trace context - * @param hint the hints - * @param profilingTraceData the profiling trace data - * @return transaction's id - */ - @ApiStatus.Internal - @NotNull - SentryId captureTransaction( - @NotNull SentryTransaction transaction, - @Nullable TraceContext traceContext, - @Nullable Hint hint, - @Nullable ProfilingTraceData profilingTraceData); - - /** - * Captures the transaction and enqueues it for sending to Sentry server. - * - * @param transaction the transaction - * @param traceContext the trace context - * @param hint the hints - * @return transaction's id - */ - @ApiStatus.Internal - @NotNull - default SentryId captureTransaction( - @NotNull SentryTransaction transaction, - @Nullable TraceContext traceContext, - @Nullable Hint hint) { - return captureTransaction(transaction, traceContext, hint, null); - } - - @ApiStatus.Internal - @NotNull - default SentryId captureTransaction(@NotNull SentryTransaction transaction, @Nullable Hint hint) { - return captureTransaction(transaction, null, hint); - } - - /** - * Captures the transaction and enqueues it for sending to Sentry server. - * - * @param transaction the transaction - * @param traceContext the trace context - * @return transaction's id - */ - @ApiStatus.Internal - default @NotNull SentryId captureTransaction( - @NotNull SentryTransaction transaction, @Nullable TraceContext traceContext) { - return captureTransaction(transaction, traceContext, null); - } - - /** - * Creates a Transaction and returns the instance. - * - * @param transactionContexts the transaction contexts - * @return created transaction - */ - default @NotNull ITransaction startTransaction(@NotNull TransactionContext transactionContexts) { - return startTransaction(transactionContexts, new TransactionOptions()); - } - - /** - * Creates a Transaction and returns the instance. Based on the {@link - * SentryOptions#getTracesSampleRate()} the decision if transaction is sampled will be taken by - * {@link TracesSampler}. - * - * @param name the transaction name - * @param operation the operation - * @return created transaction - */ - default @NotNull ITransaction startTransaction( - final @NotNull String name, final @NotNull String operation) { - return startTransaction(name, operation, new TransactionOptions()); - } - - /** - * Creates a Transaction and returns the instance. Based on the {@link - * SentryOptions#getTracesSampleRate()} the decision if transaction is sampled will be taken by - * {@link TracesSampler}. - * - * @param name the transaction name - * @param operation the operation - * @param transactionOptions the transaction options - * @return created transaction - */ - default @NotNull ITransaction startTransaction( - final @NotNull String name, - final @NotNull String operation, - final @NotNull TransactionOptions transactionOptions) { - return startTransaction(new TransactionContext(name, operation), transactionOptions); - } - - /** - * 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, - final @NotNull TransactionOptions transactionOptions); - - /** - * Returns the "sentry-trace" header that allows tracing across services. Can also be used in - * <meta> HTML tags. Also see {@link IScopes#getBaggage()}. - * - * @deprecated please use {@link IScopes#getTraceparent()} instead. - * @return sentry trace header or null - */ - @Deprecated - @Nullable - SentryTraceHeader traceHeaders(); - - /** - * Associates {@link ISpan} and the transaction name with the {@link Throwable}. Used to determine - * in which trace the exception has been thrown in framework integrations. - * - * @param throwable the throwable - * @param span the span context - * @param transactionName the transaction name - */ - @ApiStatus.Internal - void setSpanContext( - @NotNull Throwable throwable, @NotNull ISpan span, @NotNull String transactionName); - - /** - * Gets the current active transaction or span. - * - * @return the active span or null when no active transaction is running - */ - @Nullable - ISpan getSpan(); - - /** - * Returns the transaction. - * - * @return the transaction or null when no active transaction is running. - */ - @ApiStatus.Internal - @Nullable - ITransaction getTransaction(); - - /** - * Gets the {@link SentryOptions} attached to current scope. - * - * @return the options attached to current scope. - */ - @NotNull - SentryOptions getOptions(); - - /** - * Returns if the App has crashed (Process has terminated) during the last run. It only returns - * true or false if offline caching {{@link SentryOptions#getCacheDirPath()} } is set with a valid - * dir. - * - *

    If the call to this method is early in the App lifecycle and the SDK could not check if the - * App has crashed in the background, the check is gonna do IO in the calling thread. - * - * @return true if App has crashed, false otherwise, and null if not evaluated yet - */ - @Nullable - Boolean isCrashedLastRun(); - - /** - * Report a screen has been fully loaded. That means all data needed by the UI was loaded. If - * time-to-full-display tracing {{@link SentryOptions#isEnableTimeToFullDisplayTracing()} } is - * disabled this call is ignored. - * - *

    This method is safe to be called multiple times. If the time-to-full-display span is already - * finished, this call will be ignored. - */ - void reportFullyDisplayed(); - - /** - * @deprecated See {@link IScopes#reportFullyDisplayed()}. - */ - @Deprecated - default void reportFullDisplayed() { - reportFullyDisplayed(); - } - - /** - * Continue a trace based on HTTP header values. If no "sentry-trace" header is provided a random - * trace ID and span ID is created. - * - * @param sentryTrace "sentry-trace" header - * @param baggageHeaders "baggage" headers - * @return a transaction context for starting a transaction or null if performance is disabled - */ - @Nullable - TransactionContext continueTrace( - final @Nullable String sentryTrace, final @Nullable List baggageHeaders); - - /** - * Returns the "sentry-trace" header that allows tracing across services. Can also be used in - * <meta> HTML tags. Also see {@link IScopes#getBaggage()}. - * - * @return sentry trace header or null - */ - @Nullable - SentryTraceHeader getTraceparent(); - - /** - * Returns the "baggage" header that allows tracing across services. Can also be used in - * <meta> HTML tags. Also see {@link IScopes#getTraceparent()}. - * - * @return baggage header or null - */ - @Nullable - BaggageHeader getBaggage(); - - @ApiStatus.Experimental - @NotNull - SentryId captureCheckIn(final @NotNull CheckIn checkIn); - - @ApiStatus.Internal - @Nullable - RateLimiter getRateLimiter(); - - @ApiStatus.Experimental - @NotNull - MetricsApi metrics(); - - default boolean isNoOp() { - return false; - } -} diff --git a/sentry/src/main/java/io/sentry/IScopesStorage.java b/sentry/src/main/java/io/sentry/IScopesStorage.java deleted file mode 100644 index 394510a528..0000000000 --- a/sentry/src/main/java/io/sentry/IScopesStorage.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.sentry; - -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@ApiStatus.Internal -public interface IScopesStorage { - - @NotNull - ISentryLifecycleToken set(final @Nullable IScopes scopes); - - @Nullable - IScopes get(); - - void close(); -} diff --git a/sentry/src/main/java/io/sentry/ISentryLifecycleToken.java b/sentry/src/main/java/io/sentry/ISentryLifecycleToken.java deleted file mode 100644 index 2d0ad180f7..0000000000 --- a/sentry/src/main/java/io/sentry/ISentryLifecycleToken.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.sentry; - -public interface ISentryLifecycleToken extends AutoCloseable { - - // overridden to not have a checked exception on the method. - @Override - void close(); -} diff --git a/sentry/src/main/java/io/sentry/ISpan.java b/sentry/src/main/java/io/sentry/ISpan.java index 0b8f931033..5b251930ae 100644 --- a/sentry/src/main/java/io/sentry/ISpan.java +++ b/sentry/src/main/java/io/sentry/ISpan.java @@ -1,7 +1,6 @@ package io.sentry; import io.sentry.metrics.LocalMetricsAggregator; -import io.sentry.protocol.Contexts; import java.util.List; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -23,10 +22,6 @@ public interface ISpan { ISpan startChild( @NotNull String operation, @Nullable String description, @NotNull SpanOptions spanOptions); - @ApiStatus.Internal - @NotNull - ISpan startChild(@NotNull SpanContext spanContext, @NotNull SpanOptions spanOptions); - @ApiStatus.Internal @NotNull ISpan startChild( @@ -268,19 +263,4 @@ ISpan startChild( */ @Nullable LocalMetricsAggregator getLocalMetricsAggregator(); - - void setContext(@NotNull String key, @NotNull Object context); - - @NotNull - Contexts getContexts(); - - @Nullable - Boolean isSampled(); - - @Nullable - TracesSamplingDecision getSamplingDecision(); - - @ApiStatus.Internal - @NotNull - ISentryLifecycleToken makeCurrent(); } diff --git a/sentry/src/main/java/io/sentry/ISpanFactory.java b/sentry/src/main/java/io/sentry/ISpanFactory.java deleted file mode 100644 index 53bffbb57b..0000000000 --- a/sentry/src/main/java/io/sentry/ISpanFactory.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.sentry; - -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@ApiStatus.Internal -public interface ISpanFactory { - @NotNull - ITransaction createTransaction( - @NotNull TransactionContext context, - @NotNull IScopes scopes, - @NotNull TransactionOptions transactionOptions, - @Nullable TransactionPerformanceCollector transactionPerformanceCollector); - - @NotNull - ISpan createSpan( - @NotNull IScopes scopes, - @NotNull SpanOptions spanOptions, - @NotNull SpanContext spanContext, - @Nullable ISpan parentSpan); - - @Nullable - ISpan retrieveCurrentSpan(IScopes scopes); - - @Nullable - ISpan retrieveCurrentSpan(IScope scope); -} diff --git a/sentry/src/main/java/io/sentry/ITransaction.java b/sentry/src/main/java/io/sentry/ITransaction.java index 403b21187b..a34e491802 100644 --- a/sentry/src/main/java/io/sentry/ITransaction.java +++ b/sentry/src/main/java/io/sentry/ITransaction.java @@ -1,5 +1,6 @@ package io.sentry; +import io.sentry.protocol.Contexts; import io.sentry.protocol.SentryId; import io.sentry.protocol.TransactionNameSource; import java.util.List; @@ -52,6 +53,14 @@ public interface ITransaction extends ISpan { ISpan startChild( @NotNull String operation, @Nullable String description, @Nullable SentryDate timestamp); + /** + * Returns if transaction is sampled. + * + * @return is sampled + */ + @Nullable + Boolean isSampled(); + /** * Returns if the profile of a transaction is sampled. * @@ -60,13 +69,24 @@ ISpan startChild( @Nullable Boolean isProfileSampled(); + @Nullable + TracesSamplingDecision getSamplingDecision(); + /** * Returns the latest span that is not finished. * * @return span or null if not found. */ @Nullable - ISpan getLatestActiveSpan(); + Span getLatestActiveSpan(); + + /** + * Returns transaction's event id. + * + * @return the event id + */ + @NotNull + SentryId getEventId(); /** Schedules when transaction should be automatically finished. */ void scheduleFinish(); @@ -91,6 +111,10 @@ void finish( boolean dropIfNoChildren, @Nullable Hint hint); + @ApiStatus.Internal + void setContext(@NotNull String key, @NotNull Object context); + + @ApiStatus.Internal @NotNull - SentryId getEventId(); + Contexts getContexts(); } diff --git a/sentry/src/main/java/io/sentry/Integration.java b/sentry/src/main/java/io/sentry/Integration.java index 1b1a520473..54b17e4d51 100644 --- a/sentry/src/main/java/io/sentry/Integration.java +++ b/sentry/src/main/java/io/sentry/Integration.java @@ -10,8 +10,8 @@ public interface Integration { /** * Registers an integration * - * @param scopes the Scopes + * @param hub the Hub * @param options the options */ - void register(@NotNull IScopes scopes, @NotNull SentryOptions options); + void register(@NotNull IHub hub, @NotNull SentryOptions options); } diff --git a/sentry/src/main/java/io/sentry/MainEventProcessor.java b/sentry/src/main/java/io/sentry/MainEventProcessor.java index 23a14eb3a6..abbf21c84e 100644 --- a/sentry/src/main/java/io/sentry/MainEventProcessor.java +++ b/sentry/src/main/java/io/sentry/MainEventProcessor.java @@ -307,9 +307,4 @@ boolean isClosed() { HostnameCache getHostnameCache() { return hostnameCache; } - - @Override - public @Nullable Long getOrder() { - return 0L; - } } diff --git a/sentry/src/main/java/io/sentry/MonitorConfig.java b/sentry/src/main/java/io/sentry/MonitorConfig.java index 763e3b65a4..d954a50466 100644 --- a/sentry/src/main/java/io/sentry/MonitorConfig.java +++ b/sentry/src/main/java/io/sentry/MonitorConfig.java @@ -21,7 +21,7 @@ public final class MonitorConfig implements JsonUnknown, JsonSerializable { public MonitorConfig(final @NotNull MonitorSchedule schedule) { this.schedule = schedule; - final SentryOptions.Cron defaultCron = ScopesAdapter.getInstance().getOptions().getCron(); + final SentryOptions.Cron defaultCron = HubAdapter.getInstance().getOptions().getCron(); if (defaultCron != null) { this.checkinMargin = defaultCron.getDefaultCheckinMargin(); this.maxRuntime = defaultCron.getDefaultMaxRuntime(); diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index 55893746d8..e51cea8d2d 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -11,10 +11,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -@Deprecated -/** - * @deprecated use {@link NoOpScopes} instead. - */ public final class NoOpHub implements IHub { private static final NoOpHub instance = new NoOpHub(); @@ -25,7 +21,6 @@ public final class NoOpHub implements IHub { private NoOpHub() {} - @Deprecated public static NoOpHub getInstance() { return instance; } @@ -127,21 +122,9 @@ public void removeExtra(@NotNull String key) {} } @Override - public @NotNull ISentryLifecycleToken pushScope() { - return NoOpScopesLifecycleToken.getInstance(); - } - - @Override - public @NotNull ISentryLifecycleToken pushIsolationScope() { - return NoOpScopesLifecycleToken.getInstance(); - } + public void pushScope() {} - /** - * @deprecated please call {@link ISentryLifecycleToken#close()} on the token returned by {@link - * IScopes#pushScope()} or {@link IScopes#pushIsolationScope()} instead. - */ @Override - @Deprecated public void popScope() {} @Override @@ -150,12 +133,7 @@ public void withScope(@NotNull ScopeCallback callback) { } @Override - public void withIsolationScope(@NotNull ScopeCallback callback) { - callback.run(NoOpScope.getInstance()); - } - - @Override - public void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback) {} + public void configureScope(@NotNull ScopeCallback callback) {} @Override public void bindClient(@NotNull ISentryClient client) {} @@ -168,64 +146,11 @@ public boolean isHealthy() { @Override public void flush(long timeoutMillis) {} - /** - * @deprecated please use {@link IScopes#forkedScopes(String)} or {@link - * IScopes#forkedCurrentScope(String)} instead. - */ - @Deprecated @Override public @NotNull IHub clone() { return instance; } - @Override - public @NotNull IScopes forkedScopes(@NotNull String creator) { - return NoOpScopes.getInstance(); - } - - @Override - public @NotNull IScopes forkedCurrentScope(@NotNull String creator) { - return NoOpScopes.getInstance(); - } - - @Override - public @NotNull ISentryLifecycleToken makeCurrent() { - return NoOpScopesLifecycleToken.getInstance(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getScope() { - return NoOpScope.getInstance(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getIsolationScope() { - return NoOpScope.getInstance(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getGlobalScope() { - return NoOpScope.getInstance(); - } - - @Override - public @Nullable IScopes getParentScopes() { - return null; - } - - @Override - public boolean isAncestorOf(@Nullable IScopes otherScopes) { - return false; - } - - @Override - public @NotNull IScopes forkedRootScopes(final @NotNull String creator) { - return NoOpScopes.getInstance(); - } - @Override public @NotNull SentryId captureTransaction( final @NotNull SentryTransaction transaction, @@ -309,9 +234,4 @@ public void reportFullyDisplayed() {} public @NotNull MetricsApi metrics() { return metricsApi; } - - @Override - public boolean isNoOp() { - return true; - } } diff --git a/sentry/src/main/java/io/sentry/NoOpScope.java b/sentry/src/main/java/io/sentry/NoOpScope.java index fd258fd7c6..c756fb49a3 100644 --- a/sentry/src/main/java/io/sentry/NoOpScope.java +++ b/sentry/src/main/java/io/sentry/NoOpScope.java @@ -1,9 +1,7 @@ package io.sentry; -import io.sentry.internal.eventprocessor.EventProcessorAndOrder; import io.sentry.protocol.Contexts; import io.sentry.protocol.Request; -import io.sentry.protocol.SentryId; import io.sentry.protocol.User; import java.util.ArrayDeque; import java.util.ArrayList; @@ -184,12 +182,6 @@ public void clearAttachments() {} return new ArrayList<>(); } - @ApiStatus.Internal - @Override - public @NotNull List getEventProcessorsWithOrder() { - return new ArrayList<>(); - } - @Override public void addEventProcessor(@NotNull EventProcessor eventProcessor) {} @@ -227,10 +219,6 @@ public void withTransaction(Scope.@NotNull IWithTransaction callback) {} return null; } - @ApiStatus.Internal - @Override - public void clearSession() {} - @ApiStatus.Internal @Override public void setPropagationContext(@NotNull PropagationContext propagationContext) {} @@ -248,9 +236,6 @@ public void setPropagationContext(@NotNull PropagationContext propagationContext return new PropagationContext(); } - @Override - public void setLastEventId(@NotNull SentryId lastEventId) {} - /** * Clones the Scope * @@ -260,27 +245,4 @@ public void setLastEventId(@NotNull SentryId lastEventId) {} public @NotNull IScope clone() { return NoOpScope.getInstance(); } - - @Override - public @NotNull SentryId getLastEventId() { - return SentryId.EMPTY_ID; - } - - @Override - public void bindClient(@NotNull ISentryClient client) {} - - @Override - public @NotNull ISentryClient getClient() { - return NoOpSentryClient.getInstance(); - } - - @Override - public void assignTraceContext(@NotNull SentryEvent event) {} - - @Override - public void setSpanContext( - @NotNull Throwable throwable, @NotNull ISpan span, @NotNull String transactionName) {} - - @Override - public void replaceOptions(@NotNull SentryOptions options) {} } diff --git a/sentry/src/main/java/io/sentry/NoOpScopes.java b/sentry/src/main/java/io/sentry/NoOpScopes.java deleted file mode 100644 index 58c1207809..0000000000 --- a/sentry/src/main/java/io/sentry/NoOpScopes.java +++ /dev/null @@ -1,312 +0,0 @@ -package io.sentry; - -import io.sentry.metrics.MetricsApi; -import io.sentry.metrics.NoopMetricsAggregator; -import io.sentry.protocol.SentryId; -import io.sentry.protocol.SentryTransaction; -import io.sentry.protocol.User; -import io.sentry.transport.RateLimiter; -import java.util.List; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public final class NoOpScopes implements IScopes { - - private static final NoOpScopes instance = new NoOpScopes(); - - private final @NotNull SentryOptions emptyOptions = SentryOptions.empty(); - private final @NotNull MetricsApi metricsApi = - new MetricsApi(NoopMetricsAggregator.getInstance()); - - private NoOpScopes() {} - - public static NoOpScopes getInstance() { - return instance; - } - - @Override - public boolean isEnabled() { - return false; - } - - @Override - public @NotNull SentryId captureEvent(@NotNull SentryEvent event, @Nullable Hint hint) { - return SentryId.EMPTY_ID; - } - - @Override - public @NotNull SentryId captureEvent( - @NotNull SentryEvent event, @Nullable Hint hint, @NotNull ScopeCallback callback) { - return SentryId.EMPTY_ID; - } - - @Override - public @NotNull SentryId captureMessage(@NotNull String message, @NotNull SentryLevel level) { - return SentryId.EMPTY_ID; - } - - @Override - public @NotNull SentryId captureMessage( - @NotNull String message, @NotNull SentryLevel level, @NotNull ScopeCallback callback) { - return SentryId.EMPTY_ID; - } - - @Override - public @NotNull SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Hint hint) { - return SentryId.EMPTY_ID; - } - - @Override - public @NotNull SentryId captureException(@NotNull Throwable throwable, @Nullable Hint hint) { - return SentryId.EMPTY_ID; - } - - @Override - public @NotNull SentryId captureException( - @NotNull Throwable throwable, @Nullable Hint hint, @NotNull ScopeCallback callback) { - return SentryId.EMPTY_ID; - } - - @Override - public void captureUserFeedback(@NotNull UserFeedback userFeedback) {} - - @Override - public void startSession() {} - - @Override - public void endSession() {} - - @Override - public void close() {} - - @Override - public void close(final boolean isRestarting) {} - - @Override - public void addBreadcrumb(@NotNull Breadcrumb breadcrumb, @Nullable Hint hint) {} - - @Override - public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb) {} - - @Override - public void setLevel(@Nullable SentryLevel level) {} - - @Override - public void setTransaction(@Nullable String transaction) {} - - @Override - public void setUser(@Nullable User user) {} - - @Override - public void setFingerprint(@NotNull List fingerprint) {} - - @Override - public void clearBreadcrumbs() {} - - @Override - public void setTag(@NotNull String key, @NotNull String value) {} - - @Override - public void removeTag(@NotNull String key) {} - - @Override - public void setExtra(@NotNull String key, @NotNull String value) {} - - @Override - public void removeExtra(@NotNull String key) {} - - @Override - public @NotNull SentryId getLastEventId() { - return SentryId.EMPTY_ID; - } - - @Override - public @NotNull ISentryLifecycleToken pushScope() { - return NoOpScopesLifecycleToken.getInstance(); - } - - @Override - public @NotNull ISentryLifecycleToken pushIsolationScope() { - return NoOpScopesLifecycleToken.getInstance(); - } - - /** - * @deprecated please call {@link ISentryLifecycleToken#close()} on the token returned by {@link - * IScopes#pushScope()} or {@link IScopes#pushIsolationScope()} instead. - */ - @Override - @Deprecated - public void popScope() {} - - @Override - public void withScope(@NotNull ScopeCallback callback) { - callback.run(NoOpScope.getInstance()); - } - - @Override - public void withIsolationScope(@NotNull ScopeCallback callback) { - callback.run(NoOpScope.getInstance()); - } - - @Override - public void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback) {} - - @Override - public void bindClient(@NotNull ISentryClient client) {} - - @Override - public boolean isHealthy() { - return true; - } - - @Override - public void flush(long timeoutMillis) {} - - /** - * @deprecated please use {@link IScopes#forkedScopes(String)} or {@link - * IScopes#forkedCurrentScope(String)} instead. - */ - @Deprecated - @Override - public @NotNull IHub clone() { - return NoOpHub.getInstance(); - } - - @Override - public @NotNull IScopes forkedScopes(@NotNull String creator) { - return NoOpScopes.getInstance(); - } - - @Override - public @NotNull IScopes forkedCurrentScope(@NotNull String creator) { - return NoOpScopes.getInstance(); - } - - @Override - public @NotNull IScopes forkedRootScopes(final @NotNull String creator) { - return NoOpScopes.getInstance(); - } - - @Override - public @NotNull ISentryLifecycleToken makeCurrent() { - return NoOpScopesLifecycleToken.getInstance(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getScope() { - return NoOpScope.getInstance(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getIsolationScope() { - return NoOpScope.getInstance(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getGlobalScope() { - return NoOpScope.getInstance(); - } - - @Override - public @Nullable IScopes getParentScopes() { - return null; - } - - @Override - public boolean isAncestorOf(@Nullable IScopes otherScopes) { - return false; - } - - @Override - public @NotNull SentryId captureTransaction( - final @NotNull SentryTransaction transaction, - final @Nullable TraceContext traceContext, - final @Nullable Hint hint, - final @Nullable ProfilingTraceData profilingTraceData) { - return SentryId.EMPTY_ID; - } - - @Override - public @NotNull ITransaction startTransaction( - @NotNull TransactionContext transactionContext, - @NotNull TransactionOptions transactionOptions) { - return NoOpTransaction.getInstance(); - } - - @Override - @Deprecated - @SuppressWarnings("InlineMeSuggester") - public @NotNull SentryTraceHeader traceHeaders() { - return new SentryTraceHeader(SentryId.EMPTY_ID, SpanId.EMPTY_ID, true); - } - - @Override - public void setSpanContext( - final @NotNull Throwable throwable, - final @NotNull ISpan spanContext, - final @NotNull String transactionName) {} - - @Override - public @Nullable ISpan getSpan() { - return null; - } - - @Override - public @Nullable ITransaction getTransaction() { - return null; - } - - @Override - public @NotNull SentryOptions getOptions() { - return emptyOptions; - } - - @Override - public @Nullable Boolean isCrashedLastRun() { - return null; - } - - @Override - public void reportFullyDisplayed() {} - - @Override - public @Nullable TransactionContext continueTrace( - final @Nullable String sentryTrace, final @Nullable List baggageHeaders) { - return null; - } - - @Override - public @Nullable SentryTraceHeader getTraceparent() { - return null; - } - - @Override - public @Nullable BaggageHeader getBaggage() { - return null; - } - - @Override - @ApiStatus.Experimental - public @NotNull SentryId captureCheckIn(final @NotNull CheckIn checkIn) { - return SentryId.EMPTY_ID; - } - - @Override - public @Nullable RateLimiter getRateLimiter() { - return null; - } - - @Override - public @NotNull MetricsApi metrics() { - return metricsApi; - } - - @Override - public boolean isNoOp() { - return true; - } -} diff --git a/sentry/src/main/java/io/sentry/NoOpScopesLifecycleToken.java b/sentry/src/main/java/io/sentry/NoOpScopesLifecycleToken.java deleted file mode 100644 index 4fc589d5b3..0000000000 --- a/sentry/src/main/java/io/sentry/NoOpScopesLifecycleToken.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.sentry; - -public final class NoOpScopesLifecycleToken implements ISentryLifecycleToken { - - private static final NoOpScopesLifecycleToken instance = new NoOpScopesLifecycleToken(); - - private NoOpScopesLifecycleToken() {} - - public static NoOpScopesLifecycleToken getInstance() { - return instance; - } - - @Override - public void close() {} -} diff --git a/sentry/src/main/java/io/sentry/NoOpScopesStorage.java b/sentry/src/main/java/io/sentry/NoOpScopesStorage.java deleted file mode 100644 index 9e2d7f82d6..0000000000 --- a/sentry/src/main/java/io/sentry/NoOpScopesStorage.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.sentry; - -import org.jetbrains.annotations.Nullable; - -public final class NoOpScopesStorage implements IScopesStorage { - private static final NoOpScopesStorage instance = new NoOpScopesStorage(); - - private NoOpScopesStorage() {} - - public static NoOpScopesStorage getInstance() { - return instance; - } - - @Override - public ISentryLifecycleToken set(@Nullable IScopes scopes) { - return NoOpScopesLifecycleToken.getInstance(); - } - - @Override - public @Nullable IScopes get() { - return NoOpScopes.getInstance(); - } - - @Override - public void close() {} -} diff --git a/sentry/src/main/java/io/sentry/NoOpSpan.java b/sentry/src/main/java/io/sentry/NoOpSpan.java index 6658308d76..2d11f016fb 100644 --- a/sentry/src/main/java/io/sentry/NoOpSpan.java +++ b/sentry/src/main/java/io/sentry/NoOpSpan.java @@ -1,7 +1,6 @@ package io.sentry; import io.sentry.metrics.LocalMetricsAggregator; -import io.sentry.protocol.Contexts; import io.sentry.protocol.SentryId; import java.util.List; import org.jetbrains.annotations.NotNull; @@ -28,12 +27,6 @@ public static NoOpSpan getInstance() { return NoOpSpan.getInstance(); } - @Override - public @NotNull ISpan startChild( - @NotNull SpanContext spanContext, @NotNull SpanOptions spanOptions) { - return NoOpSpan.getInstance(); - } - @Override public @NotNull ISpan startChild( @NotNull String operation, @@ -172,27 +165,4 @@ public boolean isNoOp() { public @Nullable LocalMetricsAggregator getLocalMetricsAggregator() { return null; } - - @Override - public void setContext(@NotNull String key, @NotNull Object context) {} - - @Override - public @NotNull Contexts getContexts() { - return new Contexts(); - } - - @Override - public @Nullable Boolean isSampled() { - return null; - } - - @Override - public @Nullable TracesSamplingDecision getSamplingDecision() { - return null; - } - - @Override - public @NotNull ISentryLifecycleToken makeCurrent() { - return NoOpScopesLifecycleToken.getInstance(); - } } diff --git a/sentry/src/main/java/io/sentry/NoOpSpanFactory.java b/sentry/src/main/java/io/sentry/NoOpSpanFactory.java deleted file mode 100644 index 282f2a5ab2..0000000000 --- a/sentry/src/main/java/io/sentry/NoOpSpanFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.sentry; - -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@ApiStatus.Internal -public final class NoOpSpanFactory implements ISpanFactory { - - private static final NoOpSpanFactory instance = new NoOpSpanFactory(); - - private NoOpSpanFactory() {} - - public static NoOpSpanFactory getInstance() { - return instance; - } - - @Override - public @NotNull ITransaction createTransaction( - @NotNull TransactionContext context, - @NotNull IScopes scopes, - @NotNull TransactionOptions transactionOptions, - @Nullable TransactionPerformanceCollector transactionPerformanceCollector) { - return NoOpTransaction.getInstance(); - } - - @Override - public @NotNull ISpan createSpan( - @NotNull IScopes scopes, - @NotNull SpanOptions spanOptions, - @NotNull SpanContext spanContext, - @Nullable ISpan parentSpan) { - return NoOpSpan.getInstance(); - } - - @Override - public @Nullable ISpan retrieveCurrentSpan(IScopes scopes) { - return NoOpSpan.getInstance(); - } - - @Override - public @Nullable ISpan retrieveCurrentSpan(IScope scope) { - return NoOpSpan.getInstance(); - } -} diff --git a/sentry/src/main/java/io/sentry/NoOpTransaction.java b/sentry/src/main/java/io/sentry/NoOpTransaction.java index 9cbb706151..fedbbb0667 100644 --- a/sentry/src/main/java/io/sentry/NoOpTransaction.java +++ b/sentry/src/main/java/io/sentry/NoOpTransaction.java @@ -48,12 +48,6 @@ public void setName(@NotNull String name, @NotNull TransactionNameSource transac return NoOpSpan.getInstance(); } - @Override - public @NotNull ISpan startChild( - @NotNull SpanContext spanContext, @NotNull SpanOptions spanOptions) { - return NoOpSpan.getInstance(); - } - @Override public @NotNull ISpan startChild( @NotNull String operation, @@ -96,7 +90,7 @@ public void setName(@NotNull String name, @NotNull TransactionNameSource transac } @Override - public @Nullable ISpan getLatestActiveSpan() { + public @Nullable Span getLatestActiveSpan() { return null; } @@ -105,11 +99,6 @@ public void setName(@NotNull String name, @NotNull TransactionNameSource transac return SentryId.EMPTY_ID; } - @Override - public @NotNull ISentryLifecycleToken makeCurrent() { - return NoOpScopesLifecycleToken.getInstance(); - } - @Override public void scheduleFinish() {} diff --git a/sentry/src/main/java/io/sentry/OutboxSender.java b/sentry/src/main/java/io/sentry/OutboxSender.java index 4e223da03d..709cbb8580 100644 --- a/sentry/src/main/java/io/sentry/OutboxSender.java +++ b/sentry/src/main/java/io/sentry/OutboxSender.java @@ -36,20 +36,20 @@ public final class OutboxSender extends DirectoryProcessor implements IEnvelopeS @SuppressWarnings("CharsetObjectCanBeUsed") private static final Charset UTF_8 = Charset.forName("UTF-8"); - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private final @NotNull IEnvelopeReader envelopeReader; private final @NotNull ISerializer serializer; private final @NotNull ILogger logger; public OutboxSender( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull IEnvelopeReader envelopeReader, final @NotNull ISerializer serializer, final @NotNull ILogger logger, final long flushTimeoutMillis, final int maxQueueSize) { - super(scopes, logger, flushTimeoutMillis, maxQueueSize); - this.scopes = Objects.requireNonNull(scopes, "Scopes are required."); + super(hub, logger, flushTimeoutMillis, maxQueueSize); + this.hub = Objects.requireNonNull(hub, "Hub is required."); this.envelopeReader = Objects.requireNonNull(envelopeReader, "Envelope reader is required."); this.serializer = Objects.requireNonNull(serializer, "Serializer is required."); this.logger = Objects.requireNonNull(logger, "Logger is required."); @@ -144,7 +144,7 @@ private void processEnvelope(final @NotNull SentryEnvelope envelope, final @NotN logUnexpectedEventId(envelope, event.getEventId(), currentItem); continue; } - scopes.captureEvent(event, hint); + hub.captureEvent(event, hint); logItemCaptured(currentItem); if (!waitFlush(hint)) { @@ -181,7 +181,7 @@ private void processEnvelope(final @NotNull SentryEnvelope envelope, final @NotN .getTrace() .setSamplingDecision(extractSamplingDecision(traceContext)); } - scopes.captureTransaction(transaction, traceContext, hint); + hub.captureTransaction(transaction, traceContext, hint); logItemCaptured(currentItem); if (!waitFlush(hint)) { @@ -197,7 +197,7 @@ private void processEnvelope(final @NotNull SentryEnvelope envelope, final @NotN final SentryEnvelope newEnvelope = new SentryEnvelope( envelope.getHeader().getEventId(), envelope.getHeader().getSdkVersion(), item); - scopes.captureEnvelope(newEnvelope, hint); + hub.captureEnvelope(newEnvelope, hint); logger.log( SentryLevel.DEBUG, "%s item %d is being captured.", diff --git a/sentry/src/main/java/io/sentry/PreviousSessionFinalizer.java b/sentry/src/main/java/io/sentry/PreviousSessionFinalizer.java index bda2a14477..458c6532ba 100644 --- a/sentry/src/main/java/io/sentry/PreviousSessionFinalizer.java +++ b/sentry/src/main/java/io/sentry/PreviousSessionFinalizer.java @@ -33,11 +33,11 @@ final class PreviousSessionFinalizer implements Runnable { private final @NotNull SentryOptions options; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; - PreviousSessionFinalizer(final @NotNull SentryOptions options, final @NotNull IScopes scopes) { + PreviousSessionFinalizer(final @NotNull SentryOptions options, final @NotNull IHub hub) { this.options = options; - this.scopes = scopes; + this.hub = hub; } @Override @@ -116,7 +116,7 @@ public void run() { // SdkVersion will be outdated. final SentryEnvelope fromSession = SentryEnvelope.from(serializer, session, options.getSdkVersion()); - scopes.captureEnvelope(fromSession); + hub.captureEnvelope(fromSession); } } catch (Throwable e) { options.getLogger().log(SentryLevel.ERROR, "Error processing previous session.", e); diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index 8b47bddf38..91c9fcd8cf 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -1,26 +1,18 @@ package io.sentry; -import io.sentry.internal.eventprocessor.EventProcessorAndOrder; import io.sentry.protocol.App; import io.sentry.protocol.Contexts; import io.sentry.protocol.Request; -import io.sentry.protocol.SentryId; import io.sentry.protocol.TransactionNameSource; import io.sentry.protocol.User; import io.sentry.util.CollectionUtils; -import io.sentry.util.EventProcessorUtils; -import io.sentry.util.ExceptionUtils; import io.sentry.util.Objects; -import io.sentry.util.Pair; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Queue; -import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import org.jetbrains.annotations.ApiStatus; @@ -30,8 +22,6 @@ /** Scope data to be sent with the event */ public final class Scope implements IScope { - private volatile @NotNull SentryId lastEventId; - /** Scope's SentryLevel */ private @Nullable SentryLevel level; @@ -54,7 +44,7 @@ public final class Scope implements IScope { private @NotNull List fingerprint = new ArrayList<>(); /** Scope's breadcrumb queue */ - private volatile @NotNull Queue breadcrumbs; + private final @NotNull Queue breadcrumbs; /** Scope's tags */ private @NotNull Map tags = new ConcurrentHashMap<>(); @@ -63,10 +53,10 @@ public final class Scope implements IScope { private @NotNull Map extra = new ConcurrentHashMap<>(); /** Scope's event processor list */ - private @NotNull List eventProcessors = new CopyOnWriteArrayList<>(); + private @NotNull List eventProcessors = new CopyOnWriteArrayList<>(); /** Scope's SentryOptions */ - private volatile @NotNull SentryOptions options; + private final @NotNull SentryOptions options; // TODO Consider: Scope clone doesn't clone sessions @@ -90,11 +80,6 @@ public final class Scope implements IScope { private @NotNull PropagationContext propagationContext; - private @NotNull ISentryClient client = NoOpSentryClient.getInstance(); - - private final @NotNull Map, String>> throwableToSpan = - Collections.synchronizedMap(new WeakHashMap<>()); - /** * Scope's ctor * @@ -104,7 +89,6 @@ public Scope(final @NotNull SentryOptions options) { this.options = Objects.requireNonNull(options, "SentryOptions is required."); this.breadcrumbs = createBreadcrumbsList(this.options.getMaxBreadcrumbs()); this.propagationContext = new PropagationContext(); - this.lastEventId = SentryId.EMPTY_ID; } private Scope(final @NotNull Scope scope) { @@ -113,8 +97,6 @@ private Scope(final @NotNull Scope scope) { this.session = scope.session; this.options = scope.options; this.level = scope.level; - this.client = scope.client; - this.lastEventId = scope.getLastEventId(); final User userRef = scope.user; this.user = userRef != null ? new User(userRef) : null; @@ -234,7 +216,7 @@ public void setTransaction(final @NotNull String transaction) { public ISpan getSpan() { final ITransaction tx = transaction; if (tx != null) { - final ISpan span = tx.getLatestActiveSpan(); + final Span span = tx.getLatestActiveSpan(); if (span != null) { return span; @@ -752,7 +734,7 @@ public void clearAttachments() { * @param maxBreadcrumb the max number of breadcrumbs * @return the breadcrumbs queue */ - static @NotNull Queue createBreadcrumbsList(final int maxBreadcrumb) { + private @NotNull Queue createBreadcrumbsList(final int maxBreadcrumb) { return SynchronizedQueue.synchronizedQueue(new CircularFifoQueue<>(maxBreadcrumb)); } @@ -765,18 +747,6 @@ public void clearAttachments() { @NotNull @Override public List getEventProcessors() { - return EventProcessorUtils.unwrap(eventProcessors); - } - - /** - * Returns the Scope's event processors including their order - * - * @return the event processors list and their order - */ - @ApiStatus.Internal - @NotNull - @Override - public List getEventProcessorsWithOrder() { return eventProcessors; } @@ -787,7 +757,7 @@ public List getEventProcessorsWithOrder() { */ @Override public void addEventProcessor(final @NotNull EventProcessor eventProcessor) { - eventProcessors.add(new EventProcessorAndOrder(eventProcessor, eventProcessor.getOrder())); + eventProcessors.add(eventProcessor); } /** @@ -835,7 +805,7 @@ public SessionPair startSession() { SessionPair pair = null; synchronized (sessionLock) { if (session != null) { - // Assumes session will NOT flush itself (Not passing any scopes to it) + // Assumes session will NOT flush itself (Not passing any hub to it) session.end(); } previousSession = session; @@ -943,12 +913,6 @@ public SentryOptions getOptions() { return session; } - @ApiStatus.Internal - @Override - public void clearSession() { - session = null; - } - @ApiStatus.Internal @Override public void setPropagationContext(final @NotNull PropagationContext propagationContext) { @@ -981,82 +945,6 @@ public void setPropagationContext(final @NotNull PropagationContext propagationC return new Scope(this); } - @Override - public void setLastEventId(@NotNull SentryId lastEventId) { - this.lastEventId = lastEventId; - } - - @Override - public @NotNull SentryId getLastEventId() { - return lastEventId; - } - - @Override - public void bindClient(@NotNull ISentryClient client) { - this.client = client; - } - - @Override - public @NotNull ISentryClient getClient() { - return client; - } - - @Override - @ApiStatus.Internal - public void assignTraceContext(final @NotNull SentryEvent event) { - if (options.isTracingEnabled() && event.getThrowable() != null) { - final Pair, String> pair = - throwableToSpan.get(ExceptionUtils.findRootCause(event.getThrowable())); - if (pair != null) { - final WeakReference spanWeakRef = pair.getFirst(); - if (event.getContexts().getTrace() == null && spanWeakRef != null) { - final ISpan span = spanWeakRef.get(); - if (span != null) { - event.getContexts().setTrace(span.getSpanContext()); - } - } - final String transactionName = pair.getSecond(); - if (event.getTransaction() == null && transactionName != null) { - event.setTransaction(transactionName); - } - } - } - } - - @Override - @ApiStatus.Internal - public void setSpanContext( - final @NotNull Throwable throwable, - final @NotNull ISpan span, - final @NotNull String transactionName) { - Objects.requireNonNull(throwable, "throwable is required"); - Objects.requireNonNull(span, "span is required"); - Objects.requireNonNull(transactionName, "transactionName is required"); - // to match any cause, span context is always attached to the root cause of the exception - final Throwable rootCause = ExceptionUtils.findRootCause(throwable); - // the most inner span should be assigned to a throwable - if (!throwableToSpan.containsKey(rootCause)) { - throwableToSpan.put(rootCause, new Pair<>(new WeakReference<>(span), transactionName)); - } - } - - @ApiStatus.Internal - @Override - public void replaceOptions(final @NotNull SentryOptions options) { - if (!getClient().isEnabled()) { - this.options = options; - final Queue oldBreadcrumbs = breadcrumbs; - breadcrumbs = createBreadcrumbsList(options.getMaxBreadcrumbs()); - for (Breadcrumb breadcrumb : oldBreadcrumbs) { - /* - this should trigger beforeBreadcrumb - and notify observers for breadcrumbs added before options where customized in Sentry.init - */ - addBreadcrumb(breadcrumb); - } - } - } - /** The IWithTransaction callback */ @ApiStatus.Internal public interface IWithTransaction { diff --git a/sentry/src/main/java/io/sentry/ScopeType.java b/sentry/src/main/java/io/sentry/ScopeType.java deleted file mode 100644 index 513b92115d..0000000000 --- a/sentry/src/main/java/io/sentry/ScopeType.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.sentry; - -public enum ScopeType { - CURRENT, - ISOLATION, - GLOBAL, - COMBINED; -} diff --git a/sentry/src/main/java/io/sentry/ScopesAdapter.java b/sentry/src/main/java/io/sentry/ScopesAdapter.java deleted file mode 100644 index 92387dc602..0000000000 --- a/sentry/src/main/java/io/sentry/ScopesAdapter.java +++ /dev/null @@ -1,354 +0,0 @@ -package io.sentry; - -import io.sentry.metrics.MetricsApi; -import io.sentry.protocol.SentryId; -import io.sentry.protocol.SentryTransaction; -import io.sentry.protocol.User; -import io.sentry.transport.RateLimiter; -import java.util.List; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public final class ScopesAdapter implements IScopes { - - private static final ScopesAdapter INSTANCE = new ScopesAdapter(); - - private ScopesAdapter() {} - - public static ScopesAdapter getInstance() { - return INSTANCE; - } - - @Override - public boolean isEnabled() { - return Sentry.isEnabled(); - } - - @Override - public @NotNull SentryId captureEvent(@NotNull SentryEvent event, @Nullable Hint hint) { - return Sentry.captureEvent(event, hint); - } - - @Override - public @NotNull SentryId captureEvent( - @NotNull SentryEvent event, @Nullable Hint hint, @NotNull ScopeCallback callback) { - return Sentry.captureEvent(event, hint, callback); - } - - @Override - public @NotNull SentryId captureMessage(@NotNull String message, @NotNull SentryLevel level) { - return Sentry.captureMessage(message, level); - } - - @Override - public @NotNull SentryId captureMessage( - @NotNull String message, @NotNull SentryLevel level, @NotNull ScopeCallback callback) { - return Sentry.captureMessage(message, level, callback); - } - - @ApiStatus.Internal - @Override - public @NotNull SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Hint hint) { - return Sentry.getCurrentScopes().captureEnvelope(envelope, hint); - } - - @Override - public @NotNull SentryId captureException(@NotNull Throwable throwable, @Nullable Hint hint) { - return Sentry.captureException(throwable, hint); - } - - @Override - public @NotNull SentryId captureException( - @NotNull Throwable throwable, @Nullable Hint hint, @NotNull ScopeCallback callback) { - return Sentry.captureException(throwable, hint, callback); - } - - @Override - public void captureUserFeedback(@NotNull UserFeedback userFeedback) { - Sentry.captureUserFeedback(userFeedback); - } - - @Override - public void startSession() { - Sentry.startSession(); - } - - @Override - public void endSession() { - Sentry.endSession(); - } - - @Override - public void close(final boolean isRestarting) { - Sentry.close(); - } - - @Override - public void close() { - Sentry.close(); - } - - @Override - public void addBreadcrumb(@NotNull Breadcrumb breadcrumb, @Nullable Hint hint) { - Sentry.addBreadcrumb(breadcrumb, hint); - } - - @Override - public void addBreadcrumb(final @NotNull Breadcrumb breadcrumb) { - addBreadcrumb(breadcrumb, new Hint()); - } - - @Override - public void setLevel(@Nullable SentryLevel level) { - Sentry.setLevel(level); - } - - @Override - public void setTransaction(@Nullable String transaction) { - Sentry.setTransaction(transaction); - } - - @Override - public void setUser(@Nullable User user) { - Sentry.setUser(user); - } - - @Override - public void setFingerprint(@NotNull List fingerprint) { - Sentry.setFingerprint(fingerprint); - } - - @Override - public void clearBreadcrumbs() { - Sentry.clearBreadcrumbs(); - } - - @Override - public void setTag(@NotNull String key, @NotNull String value) { - Sentry.setTag(key, value); - } - - @Override - public void removeTag(@NotNull String key) { - Sentry.removeTag(key); - } - - @Override - public void setExtra(@NotNull String key, @NotNull String value) { - Sentry.setExtra(key, value); - } - - @Override - public void removeExtra(@NotNull String key) { - Sentry.removeExtra(key); - } - - @Override - public @NotNull SentryId getLastEventId() { - return Sentry.getLastEventId(); - } - - @Override - public @NotNull ISentryLifecycleToken pushScope() { - return Sentry.pushScope(); - } - - @Override - public @NotNull ISentryLifecycleToken pushIsolationScope() { - return Sentry.pushIsolationScope(); - } - - /** - * @deprecated please call {@link ISentryLifecycleToken#close()} on the token returned by {@link - * IScopes#pushScope()} or {@link IScopes#pushIsolationScope()} instead. - */ - @Override - @Deprecated - public void popScope() { - Sentry.popScope(); - } - - @Override - public void withScope(@NotNull ScopeCallback callback) { - Sentry.withScope(callback); - } - - @Override - public void withIsolationScope(@NotNull ScopeCallback callback) { - Sentry.withIsolationScope(callback); - } - - @Override - public void configureScope(@Nullable ScopeType scopeType, @NotNull ScopeCallback callback) { - Sentry.configureScope(scopeType, callback); - } - - @Override - public void bindClient(@NotNull ISentryClient client) { - Sentry.bindClient(client); - } - - @Override - public boolean isHealthy() { - return Sentry.isHealthy(); - } - - @Override - public void flush(long timeoutMillis) { - Sentry.flush(timeoutMillis); - } - - /** - * @deprecated please use {@link IScopes#forkedScopes(String)} or {@link - * IScopes#forkedCurrentScope(String)} instead. - */ - @Deprecated - @Override - @SuppressWarnings("deprecation") - public @NotNull IHub clone() { - return Sentry.getCurrentScopes().clone(); - } - - @Override - public @NotNull IScopes forkedScopes(@NotNull String creator) { - return Sentry.forkedScopes(creator); - } - - @Override - public @NotNull IScopes forkedCurrentScope(@NotNull String creator) { - return Sentry.forkedCurrentScope(creator); - } - - @Override - public @NotNull IScopes forkedRootScopes(final @NotNull String creator) { - return Sentry.forkedRootScopes(creator); - } - - @Override - public @NotNull ISentryLifecycleToken makeCurrent() { - return NoOpScopesLifecycleToken.getInstance(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getScope() { - return Sentry.getCurrentScopes().getScope(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getIsolationScope() { - return Sentry.getCurrentScopes().getIsolationScope(); - } - - @Override - @ApiStatus.Internal - public @NotNull IScope getGlobalScope() { - return Sentry.getGlobalScope(); - } - - @Override - public @Nullable IScopes getParentScopes() { - return Sentry.getCurrentScopes().getParentScopes(); - } - - @Override - public boolean isAncestorOf(final @Nullable IScopes otherScopes) { - return Sentry.getCurrentScopes().isAncestorOf(otherScopes); - } - - @ApiStatus.Internal - @Override - public @NotNull SentryId captureTransaction( - @NotNull SentryTransaction transaction, - @Nullable TraceContext traceContext, - @Nullable Hint hint, - @Nullable ProfilingTraceData profilingTraceData) { - return Sentry.getCurrentScopes() - .captureTransaction(transaction, traceContext, hint, profilingTraceData); - } - - @Override - public @NotNull ITransaction startTransaction( - @NotNull TransactionContext transactionContext, - @NotNull TransactionOptions transactionOptions) { - return Sentry.startTransaction(transactionContext, transactionOptions); - } - - @Deprecated - @Override - @SuppressWarnings("deprecation") - public @Nullable SentryTraceHeader traceHeaders() { - return Sentry.traceHeaders(); - } - - @ApiStatus.Internal - @Override - public void setSpanContext( - final @NotNull Throwable throwable, - final @NotNull ISpan span, - final @NotNull String transactionName) { - Sentry.getCurrentScopes().setSpanContext(throwable, span, transactionName); - } - - @Override - public @Nullable ISpan getSpan() { - return Sentry.getCurrentScopes().getSpan(); - } - - @Override - @ApiStatus.Internal - public @Nullable ITransaction getTransaction() { - return Sentry.getCurrentScopes().getTransaction(); - } - - @Override - public @NotNull SentryOptions getOptions() { - return Sentry.getCurrentScopes().getOptions(); - } - - @Override - public @Nullable Boolean isCrashedLastRun() { - return Sentry.isCrashedLastRun(); - } - - @Override - public void reportFullyDisplayed() { - Sentry.reportFullyDisplayed(); - } - - @Override - public @Nullable TransactionContext continueTrace( - final @Nullable String sentryTrace, final @Nullable List baggageHeaders) { - return Sentry.continueTrace(sentryTrace, baggageHeaders); - } - - @Override - public @Nullable SentryTraceHeader getTraceparent() { - return Sentry.getTraceparent(); - } - - @Override - public @Nullable BaggageHeader getBaggage() { - return Sentry.getBaggage(); - } - - @Override - @ApiStatus.Experimental - public @NotNull SentryId captureCheckIn(final @NotNull CheckIn checkIn) { - return Sentry.captureCheckIn(checkIn); - } - - @ApiStatus.Internal - @Override - public @Nullable RateLimiter getRateLimiter() { - return Sentry.getCurrentScopes().getRateLimiter(); - } - - @ApiStatus.Experimental - @Override - public @NotNull MetricsApi metrics() { - return Sentry.getCurrentScopes().metrics(); - } -} diff --git a/sentry/src/main/java/io/sentry/ScopesStorageFactory.java b/sentry/src/main/java/io/sentry/ScopesStorageFactory.java deleted file mode 100644 index ab136b2ac9..0000000000 --- a/sentry/src/main/java/io/sentry/ScopesStorageFactory.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.sentry; - -import io.sentry.util.LoadClass; -import java.lang.reflect.InvocationTargetException; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -@ApiStatus.Internal -public final class ScopesStorageFactory { - - private static final String OTEL_SCOPES_STORAGE = - "io.sentry.opentelemetry.OtelContextScopesStorage"; - - public static @NotNull IScopesStorage create( - final @NotNull LoadClass loadClass, final @NotNull ILogger logger) { - if (loadClass.isClassAvailable(OTEL_SCOPES_STORAGE, logger)) { - Class otelScopesStorageClazz = loadClass.loadClass(OTEL_SCOPES_STORAGE, logger); - if (otelScopesStorageClazz != null) { - try { - final @Nullable Object otelScopesStorage = - otelScopesStorageClazz.getDeclaredConstructor().newInstance(); - if (otelScopesStorage != null && otelScopesStorage instanceof IScopesStorage) { - return (IScopesStorage) otelScopesStorage; - } - } catch (InstantiationException e) { - // TODO log - } catch (IllegalAccessException e) { - // TODO log - } catch (InvocationTargetException e) { - // TODO log - } catch (NoSuchMethodException e) { - // TODO log - } - } - } - - return new DefaultScopesStorage(); - } -} diff --git a/sentry/src/main/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegration.java b/sentry/src/main/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegration.java index 8234affa05..bc813fdd1e 100644 --- a/sentry/src/main/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegration.java +++ b/sentry/src/main/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegration.java @@ -18,7 +18,7 @@ public final class SendCachedEnvelopeFireAndForgetIntegration private final @NotNull SendFireAndForgetFactory factory; private @Nullable IConnectionStatusProvider connectionStatusProvider; - private @Nullable IScopes scopes; + private @Nullable IHub hub; private @Nullable SentryOptions options; private @Nullable SendFireAndForget sender; private final AtomicBoolean isInitialized = new AtomicBoolean(false); @@ -35,7 +35,7 @@ public interface SendFireAndForgetDirPath { public interface SendFireAndForgetFactory { @Nullable - SendFireAndForget create(@NotNull IScopes scopes, @NotNull SentryOptions options); + SendFireAndForget create(@NotNull IHub hub, @NotNull SentryOptions options); default boolean hasValidPath(final @Nullable String dirPath, final @NotNull ILogger logger) { if (dirPath == null || dirPath.isEmpty()) { @@ -66,8 +66,8 @@ public SendCachedEnvelopeFireAndForgetIntegration( } @Override - public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { - this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); + public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { + this.hub = Objects.requireNonNull(hub, "Hub is required"); this.options = Objects.requireNonNull(options, "SentryOptions is required"); final String cachedDir = options.getCacheDirPath(); @@ -81,7 +81,7 @@ public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions .log(SentryLevel.DEBUG, "SendCachedEventFireAndForgetIntegration installed."); addIntegrationToSdkVersion(getClass()); - sendCachedEnvelopes(scopes, options); + sendCachedEnvelopes(hub, options); } @Override @@ -95,14 +95,14 @@ public void close() throws IOException { @Override public void onConnectionStatusChanged( final @NotNull IConnectionStatusProvider.ConnectionStatus status) { - if (scopes != null && options != null) { - sendCachedEnvelopes(scopes, options); + if (hub != null && options != null) { + sendCachedEnvelopes(hub, options); } } @SuppressWarnings({"FutureReturnValueIgnored", "NullAway"}) private synchronized void sendCachedEnvelopes( - final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + final @NotNull IHub hub, final @NotNull SentryOptions options) { try { options .getExecutorService() @@ -122,7 +122,7 @@ private synchronized void sendCachedEnvelopes( connectionStatusProvider = options.getConnectionStatusProvider(); connectionStatusProvider.addConnectionStatusObserver(this); - sender = factory.create(scopes, options); + sender = factory.create(hub, options); } // skip run only if we're certainly disconnected @@ -138,7 +138,7 @@ private synchronized void sendCachedEnvelopes( } // in case there's rate limiting active, skip processing - final @Nullable RateLimiter rateLimiter = scopes.getRateLimiter(); + final @Nullable RateLimiter rateLimiter = hub.getRateLimiter(); if (rateLimiter != null && rateLimiter.isActiveForCategory(DataCategory.All)) { options .getLogger() diff --git a/sentry/src/main/java/io/sentry/SendFireAndForgetEnvelopeSender.java b/sentry/src/main/java/io/sentry/SendFireAndForgetEnvelopeSender.java index 11c254458a..e44d18a8d6 100644 --- a/sentry/src/main/java/io/sentry/SendFireAndForgetEnvelopeSender.java +++ b/sentry/src/main/java/io/sentry/SendFireAndForgetEnvelopeSender.java @@ -21,8 +21,8 @@ public SendFireAndForgetEnvelopeSender( @Override public @Nullable SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget create( - final @NotNull IScopes scopes, final @NotNull SentryOptions options) { - Objects.requireNonNull(scopes, "Scopes are required"); + final @NotNull IHub hub, final @NotNull SentryOptions options) { + Objects.requireNonNull(hub, "Hub is required"); Objects.requireNonNull(options, "SentryOptions is required"); final String dirPath = sendFireAndForgetDirPath.getDirPath(); @@ -33,7 +33,7 @@ public SendFireAndForgetEnvelopeSender( final EnvelopeSender envelopeSender = new EnvelopeSender( - scopes, + hub, options.getSerializer(), options.getLogger(), options.getFlushTimeoutMillis(), diff --git a/sentry/src/main/java/io/sentry/SendFireAndForgetOutboxSender.java b/sentry/src/main/java/io/sentry/SendFireAndForgetOutboxSender.java index b14b3b6921..fda41610fd 100644 --- a/sentry/src/main/java/io/sentry/SendFireAndForgetOutboxSender.java +++ b/sentry/src/main/java/io/sentry/SendFireAndForgetOutboxSender.java @@ -21,8 +21,8 @@ public SendFireAndForgetOutboxSender( @Override public @Nullable SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget create( - final @NotNull IScopes scopes, final @NotNull SentryOptions options) { - Objects.requireNonNull(scopes, "Scopes are required"); + final @NotNull IHub hub, final @NotNull SentryOptions options) { + Objects.requireNonNull(hub, "Hub is required"); Objects.requireNonNull(options, "SentryOptions is required"); final String dirPath = sendFireAndForgetDirPath.getDirPath(); @@ -33,7 +33,7 @@ public SendFireAndForgetOutboxSender( final OutboxSender outboxSender = new OutboxSender( - scopes, + hub, options.getEnvelopeReader(), options.getSerializer(), options.getLogger(), diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 7206df9b37..f157256ce5 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -17,7 +17,6 @@ import io.sentry.transport.NoOpEnvelopeCache; import io.sentry.util.DebugMetaPropertiesApplier; import io.sentry.util.FileUtils; -import io.sentry.util.LoadClass; import io.sentry.util.Platform; import io.sentry.util.thread.IMainThreadChecker; import io.sentry.util.thread.MainThreadChecker; @@ -44,25 +43,16 @@ public final class Sentry { private Sentry() {} - // TODO logger? - private static volatile @NotNull IScopesStorage scopesStorage = - ScopesStorageFactory.create(new LoadClass(), NoOpLogger.getInstance()); + /** Holds Hubs per thread or only mainHub if globalHubMode is enabled. */ + private static final @NotNull ThreadLocal currentHub = new ThreadLocal<>(); - /** The root Scopes or NoOp if Sentry is disabled. */ - private static volatile @NotNull IScopes rootScopes = NoOpScopes.getInstance(); - /** - * This initializes global scope with default options. Options will later be replaced on - * Sentry.init - * - *

    For Android options will also be (temporarily) replaced by SentryAndroid static block. - */ - // TODO https://github.com/getsentry/sentry-java/issues/2541 - private static final @NotNull IScope globalScope = new Scope(SentryOptions.empty()); + /** The Main Hub or NoOp if Sentry is disabled. */ + private static volatile @NotNull IHub mainHub = NoOpHub.getInstance(); /** Default value for globalHubMode is false */ private static final boolean GLOBAL_HUB_DEFAULT_MODE = false; - /** whether to use a single (global) Scopes as opposed to one per thread. */ + /** whether to use a single (global) Hub as opposed to one per thread. */ private static volatile boolean globalHubMode = GLOBAL_HUB_DEFAULT_MODE; @ApiStatus.Internal @@ -76,83 +66,49 @@ private Sentry() {} private static final long classCreationTimestamp = System.currentTimeMillis(); /** - * Returns the current (threads) hub, if none, clones the rootScopes and returns it. + * Returns the current (threads) hub, if none, clones the mainHub and returns it. * - * @deprecated please use {@link Sentry#getCurrentScopes()} instead * @return the hub */ @ApiStatus.Internal // exposed for the coroutines integration in SentryContext - @SuppressWarnings("deprecation") - @Deprecated public static @NotNull IHub getCurrentHub() { - return new HubScopesWrapper(getCurrentScopes()); - } - - @ApiStatus.Internal // exposed for the coroutines integration in SentryContext - @SuppressWarnings("deprecation") - public static @NotNull IScopes getCurrentScopes() { if (globalHubMode) { - return rootScopes; + return mainHub; } - @Nullable IScopes scopes = getScopesStorage().get(); - if (scopes == null || scopes.isNoOp()) { - scopes = rootScopes.forkedScopes("getCurrentScopes"); - getScopesStorage().set(scopes); + IHub hub = currentHub.get(); + if (hub == null || hub instanceof NoOpHub) { + hub = mainHub.clone(); + currentHub.set(hub); } - return scopes; - } - - private static @NotNull IScopesStorage getScopesStorage() { - return scopesStorage; + return hub; } /** - * Returns a new Scopes which is cloned from the rootScopes. + * Returns a new hub which is cloned from the mainHub. * - * @return the forked scopes + * @return the hub */ @ApiStatus.Internal - public static @NotNull IScopes forkedRootScopes(final @NotNull String creator) { + @ApiStatus.Experimental + public static @NotNull IHub cloneMainHub() { if (globalHubMode) { - return rootScopes; + return mainHub; } - return rootScopes.forkedScopes(creator); + return mainHub.clone(); } - public static @NotNull IScopes forkedScopes(final @NotNull String creator) { - return getCurrentScopes().forkedScopes(creator); - } - - public static @NotNull IScopes forkedCurrentScope(final @NotNull String creator) { - return getCurrentScopes().forkedCurrentScope(creator); - } - - /** - * @deprecated please use {@link Sentry#setCurrentScopes} instead. - */ @ApiStatus.Internal // exposed for the coroutines integration in SentryContext - @Deprecated - @SuppressWarnings({"deprecation", "InlineMeSuggester"}) - public static @NotNull ISentryLifecycleToken setCurrentHub(final @NotNull IHub hub) { - return setCurrentScopes(hub); - } - - @ApiStatus.Internal // exposed for the coroutines integration in SentryContext - public static @NotNull ISentryLifecycleToken setCurrentScopes(final @NotNull IScopes scopes) { - return getScopesStorage().set(scopes); - } - - public static @NotNull IScope getGlobalScope() { - return globalScope; + public static void setCurrentHub(final @NotNull IHub hub) { + currentHub.set(hub); } /** - * Check if Sentry is enabled/active. + * Check if the current Hub is enabled/active. * * @return true if its enabled or false otherwise. */ public static boolean isEnabled() { - return getCurrentScopes().isEnabled(); + return getCurrentHub().isEnabled(); } /** Initializes the SDK */ @@ -261,7 +217,6 @@ public static void init(final @NotNull SentryOptions options) { * @param options options the SentryOptions * @param globalHubMode the globalHubMode */ - @SuppressWarnings("deprecation") private static synchronized void init( final @NotNull SentryOptions options, final boolean globalHubMode) { if (isEnabled()) { @@ -278,17 +233,13 @@ private static synchronized void init( options.getLogger().log(SentryLevel.INFO, "GlobalHubMode: '%s'", String.valueOf(globalHubMode)); Sentry.globalHubMode = globalHubMode; - globalScope.replaceOptions(options); - final IScopes scopes = getCurrentScopes(); - final IScope rootScope = new Scope(options); - final IScope rootIsolationScope = new Scope(options); - rootScopes = new Scopes(rootScope, rootIsolationScope, globalScope, "Sentry.init"); + final IHub hub = getCurrentHub(); + mainHub = new Hub(options); - getScopesStorage().set(rootScopes); + currentHub.set(mainHub); - scopes.close(true); - globalScope.bindClient(new SentryClient(rootScopes.getOptions())); + hub.close(true); // If the executorService passed in the init is the same that was previously closed, we have to // set a new one @@ -296,17 +247,17 @@ private static synchronized void init( options.setExecutorService(new SentryExecutorService()); } - // when integrations are registered on Scopes ctor and async integrations are fired, + // when integrations are registered on Hub ctor and async integrations are fired, // it might and actually happened that integrations called captureSomething - // and Scopes was still NoOp. - // Registering integrations here make sure that Scopes is already created. + // and hub was still NoOp. + // Registering integrations here make sure that Hub is already created. for (final Integration integration : options.getIntegrations()) { - integration.register(ScopesAdapter.getInstance(), options); + integration.register(HubAdapter.getInstance(), options); } notifyOptionsObservers(options); - finalizePreviousSession(options, ScopesAdapter.getInstance()); + finalizePreviousSession(options, HubAdapter.getInstance()); handleAppStartProfilingConfig(options, options.getExecutorService()); } @@ -371,17 +322,17 @@ private static void handleAppStartProfilingConfig( TransactionContext appStartTransactionContext = new TransactionContext("app.launch", "profile"); appStartTransactionContext.setForNextAppStart(true); SamplingContext appStartSamplingContext = new SamplingContext(appStartTransactionContext, null); - return options.getInternalTracesSampler().sample(appStartSamplingContext); + return new TracesSampler(options).sample(appStartSamplingContext); } @SuppressWarnings("FutureReturnValueIgnored") private static void finalizePreviousSession( - final @NotNull SentryOptions options, final @NotNull IScopes scopes) { + final @NotNull SentryOptions options, final @NotNull IHub hub) { // enqueue a task to finalize previous session. Since the executor // is single-threaded, this task will be enqueued sequentially after all integrations that have // to modify the previous session have done their work, even if they do that async. try { - options.getExecutorService().submit(new PreviousSessionFinalizer(options, scopes)); + options.getExecutorService().submit(new PreviousSessionFinalizer(options, hub)); } catch (Throwable e) { options.getLogger().log(SentryLevel.DEBUG, "Failed to finalize previous session.", e); } @@ -524,7 +475,7 @@ private static boolean initConfigurations(final @NotNull SentryOptions options) } if (options.isEnableBackpressureHandling() && Platform.isJvm()) { - options.setBackpressureMonitor(new BackpressureMonitor(options, ScopesAdapter.getInstance())); + options.setBackpressureMonitor(new BackpressureMonitor(options, HubAdapter.getInstance())); options.getBackpressureMonitor().start(); } @@ -533,11 +484,11 @@ private static boolean initConfigurations(final @NotNull SentryOptions options) /** Close the SDK */ public static synchronized void close() { - final IScopes scopes = getCurrentScopes(); - rootScopes = NoOpScopes.getInstance(); + final IHub hub = getCurrentHub(); + mainHub = NoOpHub.getInstance(); // remove thread local to avoid memory leak - getScopesStorage().close(); - scopes.close(false); + currentHub.remove(); + hub.close(false); } /** @@ -547,7 +498,7 @@ public static synchronized void close() { * @return The Id (SentryId object) of the event */ public static @NotNull SentryId captureEvent(final @NotNull SentryEvent event) { - return getCurrentScopes().captureEvent(event); + return getCurrentHub().captureEvent(event); } /** @@ -559,7 +510,7 @@ public static synchronized void close() { */ public static @NotNull SentryId captureEvent( final @NotNull SentryEvent event, final @NotNull ScopeCallback callback) { - return getCurrentScopes().captureEvent(event, callback); + return getCurrentHub().captureEvent(event, callback); } /** @@ -571,7 +522,7 @@ public static synchronized void close() { */ public static @NotNull SentryId captureEvent( final @NotNull SentryEvent event, final @Nullable Hint hint) { - return getCurrentScopes().captureEvent(event, hint); + return getCurrentHub().captureEvent(event, hint); } /** @@ -586,7 +537,7 @@ public static synchronized void close() { final @NotNull SentryEvent event, final @Nullable Hint hint, final @NotNull ScopeCallback callback) { - return getCurrentScopes().captureEvent(event, hint, callback); + return getCurrentHub().captureEvent(event, hint, callback); } /** @@ -596,7 +547,7 @@ public static synchronized void close() { * @return The Id (SentryId object) of the event */ public static @NotNull SentryId captureMessage(final @NotNull String message) { - return getCurrentScopes().captureMessage(message); + return getCurrentHub().captureMessage(message); } /** @@ -608,7 +559,7 @@ public static synchronized void close() { */ public static @NotNull SentryId captureMessage( final @NotNull String message, final @NotNull ScopeCallback callback) { - return getCurrentScopes().captureMessage(message, callback); + return getCurrentHub().captureMessage(message, callback); } /** @@ -620,7 +571,7 @@ public static synchronized void close() { */ public static @NotNull SentryId captureMessage( final @NotNull String message, final @NotNull SentryLevel level) { - return getCurrentScopes().captureMessage(message, level); + return getCurrentHub().captureMessage(message, level); } /** @@ -635,7 +586,7 @@ public static synchronized void close() { final @NotNull String message, final @NotNull SentryLevel level, final @NotNull ScopeCallback callback) { - return getCurrentScopes().captureMessage(message, level, callback); + return getCurrentHub().captureMessage(message, level, callback); } /** @@ -645,7 +596,7 @@ public static synchronized void close() { * @return The Id (SentryId object) of the event */ public static @NotNull SentryId captureException(final @NotNull Throwable throwable) { - return getCurrentScopes().captureException(throwable); + return getCurrentHub().captureException(throwable); } /** @@ -657,7 +608,7 @@ public static synchronized void close() { */ public static @NotNull SentryId captureException( final @NotNull Throwable throwable, final @NotNull ScopeCallback callback) { - return getCurrentScopes().captureException(throwable, callback); + return getCurrentHub().captureException(throwable, callback); } /** @@ -669,7 +620,7 @@ public static synchronized void close() { */ public static @NotNull SentryId captureException( final @NotNull Throwable throwable, final @Nullable Hint hint) { - return getCurrentScopes().captureException(throwable, hint); + return getCurrentHub().captureException(throwable, hint); } /** @@ -684,7 +635,7 @@ public static synchronized void close() { final @NotNull Throwable throwable, final @Nullable Hint hint, final @NotNull ScopeCallback callback) { - return getCurrentScopes().captureException(throwable, hint, callback); + return getCurrentHub().captureException(throwable, hint, callback); } /** @@ -693,7 +644,7 @@ public static synchronized void close() { * @param userFeedback The user feedback to send to Sentry. */ public static void captureUserFeedback(final @NotNull UserFeedback userFeedback) { - getCurrentScopes().captureUserFeedback(userFeedback); + getCurrentHub().captureUserFeedback(userFeedback); } /** @@ -704,7 +655,7 @@ public static void captureUserFeedback(final @NotNull UserFeedback userFeedback) */ public static void addBreadcrumb( final @NotNull Breadcrumb breadcrumb, final @Nullable Hint hint) { - getCurrentScopes().addBreadcrumb(breadcrumb, hint); + getCurrentHub().addBreadcrumb(breadcrumb, hint); } /** @@ -713,7 +664,7 @@ public static void addBreadcrumb( * @param breadcrumb the breadcrumb */ public static void addBreadcrumb(final @NotNull Breadcrumb breadcrumb) { - getCurrentScopes().addBreadcrumb(breadcrumb); + getCurrentHub().addBreadcrumb(breadcrumb); } /** @@ -722,7 +673,7 @@ public static void addBreadcrumb(final @NotNull Breadcrumb breadcrumb) { * @param message rendered as text and the whitespace is preserved. */ public static void addBreadcrumb(final @NotNull String message) { - getCurrentScopes().addBreadcrumb(message); + getCurrentHub().addBreadcrumb(message); } /** @@ -733,7 +684,7 @@ public static void addBreadcrumb(final @NotNull String message) { * from. */ public static void addBreadcrumb(final @NotNull String message, final @NotNull String category) { - getCurrentScopes().addBreadcrumb(message, category); + getCurrentHub().addBreadcrumb(message, category); } /** @@ -742,7 +693,7 @@ public static void addBreadcrumb(final @NotNull String message, final @NotNull S * @param level the Sentry level */ public static void setLevel(final @Nullable SentryLevel level) { - getCurrentScopes().setLevel(level); + getCurrentHub().setLevel(level); } /** @@ -751,7 +702,7 @@ public static void setLevel(final @Nullable SentryLevel level) { * @param transaction the transaction */ public static void setTransaction(final @Nullable String transaction) { - getCurrentScopes().setTransaction(transaction); + getCurrentHub().setTransaction(transaction); } /** @@ -760,7 +711,7 @@ public static void setTransaction(final @Nullable String transaction) { * @param user the user */ public static void setUser(final @Nullable User user) { - getCurrentScopes().setUser(user); + getCurrentHub().setUser(user); } /** @@ -769,12 +720,12 @@ public static void setUser(final @Nullable User user) { * @param fingerprint the fingerprints */ public static void setFingerprint(final @NotNull List fingerprint) { - getCurrentScopes().setFingerprint(fingerprint); + getCurrentHub().setFingerprint(fingerprint); } /** Deletes current breadcrumbs from the current scope. */ public static void clearBreadcrumbs() { - getCurrentScopes().clearBreadcrumbs(); + getCurrentHub().clearBreadcrumbs(); } /** @@ -784,7 +735,7 @@ public static void clearBreadcrumbs() { * @param value the value */ public static void setTag(final @NotNull String key, final @NotNull String value) { - getCurrentScopes().setTag(key, value); + getCurrentHub().setTag(key, value); } /** @@ -793,7 +744,7 @@ public static void setTag(final @NotNull String key, final @NotNull String value * @param key the key */ public static void removeTag(final @NotNull String key) { - getCurrentScopes().removeTag(key); + getCurrentHub().removeTag(key); } /** @@ -804,7 +755,7 @@ public static void removeTag(final @NotNull String key) { * @param value the value */ public static void setExtra(final @NotNull String key, final @NotNull String value) { - getCurrentScopes().setExtra(key, value); + getCurrentHub().setExtra(key, value); } /** @@ -813,7 +764,7 @@ public static void setExtra(final @NotNull String key, final @NotNull String val * @param key the key */ public static void removeExtra(final @NotNull String key) { - getCurrentScopes().removeExtra(key); + getCurrentHub().removeExtra(key); } /** @@ -822,58 +773,32 @@ public static void removeExtra(final @NotNull String key) { * @return last SentryId */ public static @NotNull SentryId getLastEventId() { - return getCurrentScopes().getLastEventId(); + return getCurrentHub().getLastEventId(); } /** Pushes a new scope while inheriting the current scope's data. */ - public static @NotNull ISentryLifecycleToken pushScope() { + public static void pushScope() { // pushScope is no-op in global hub mode if (!globalHubMode) { - return getCurrentScopes().pushScope(); + getCurrentHub().pushScope(); } - return NoOpScopesLifecycleToken.getInstance(); } - /** Pushes a new isolation and current scope while inheriting the current scope's data. */ - public static @NotNull ISentryLifecycleToken pushIsolationScope() { - // pushScope is no-op in global hub mode - if (!globalHubMode) { - return getCurrentScopes().pushIsolationScope(); - } - return NoOpScopesLifecycleToken.getInstance(); - } - - /** - * Removes the first scope and restores its parent. - * - * @deprecated please call {@link ISentryLifecycleToken#close()} on the token returned by {@link - * Sentry#pushScope()} or {@link Sentry#pushIsolationScope()} instead. - */ - @Deprecated + /** Removes the first scope */ public static void popScope() { // popScope is no-op in global hub mode if (!globalHubMode) { - getCurrentScopes().popScope(); + getCurrentHub().popScope(); } } /** - * Runs the callback with a new current scope which gets dropped at the end + * Runs the callback with a new scope which gets dropped at the end * * @param callback the callback */ public static void withScope(final @NotNull ScopeCallback callback) { - getCurrentScopes().withScope(callback); - } - - /** - * Runs the callback with a new isolation scope which gets dropped at the end. Current scope is - * also forked. - * - * @param callback the callback - */ - public static void withIsolationScope(final @NotNull ScopeCallback callback) { - getCurrentScopes().withIsolationScope(callback); + getCurrentHub().withScope(callback); } /** @@ -882,49 +807,39 @@ public static void withIsolationScope(final @NotNull ScopeCallback callback) { * @param callback The configure scope callback. */ public static void configureScope(final @NotNull ScopeCallback callback) { - configureScope(null, callback); - } - - /** - * Configures the scope through the callback. - * - * @param callback The configure scope callback. - */ - public static void configureScope( - final @Nullable ScopeType scopeType, final @NotNull ScopeCallback callback) { - getCurrentScopes().configureScope(scopeType, callback); + getCurrentHub().configureScope(callback); } /** - * Binds a different client to the current Scopes + * Binds a different client to the current hub * * @param client the client. */ public static void bindClient(final @NotNull ISentryClient client) { - getCurrentScopes().bindClient(client); + getCurrentHub().bindClient(client); } public static boolean isHealthy() { - return getCurrentScopes().isHealthy(); + return getCurrentHub().isHealthy(); } /** - * Flushes events queued up to the current Scopes. Not implemented yet. + * Flushes events queued up to the current hub. Not implemented yet. * * @param timeoutMillis time in milliseconds */ public static void flush(final long timeoutMillis) { - getCurrentScopes().flush(timeoutMillis); + getCurrentHub().flush(timeoutMillis); } /** Starts a new session. If there's a running session, it ends it before starting the new one. */ public static void startSession() { - getCurrentScopes().startSession(); + getCurrentHub().startSession(); } /** Ends the current session */ public static void endSession() { - getCurrentScopes().endSession(); + getCurrentHub().endSession(); } /** @@ -936,7 +851,7 @@ public static void endSession() { */ public static @NotNull ITransaction startTransaction( final @NotNull String name, final @NotNull String operation) { - return getCurrentScopes().startTransaction(name, operation); + return getCurrentHub().startTransaction(name, operation); } /** @@ -951,7 +866,7 @@ public static void endSession() { final @NotNull String name, final @NotNull String operation, final @NotNull TransactionOptions transactionOptions) { - return getCurrentScopes().startTransaction(name, operation, transactionOptions); + return getCurrentHub().startTransaction(name, operation, transactionOptions); } /** @@ -969,7 +884,7 @@ public static void endSession() { final @Nullable String description, final @NotNull TransactionOptions transactionOptions) { final ITransaction transaction = - getCurrentScopes().startTransaction(name, operation, transactionOptions); + getCurrentHub().startTransaction(name, operation, transactionOptions); transaction.setDescription(description); return transaction; } @@ -982,7 +897,7 @@ public static void endSession() { */ public static @NotNull ITransaction startTransaction( final @NotNull TransactionContext transactionContexts) { - return getCurrentScopes().startTransaction(transactionContexts); + return getCurrentHub().startTransaction(transactionContexts); } /** @@ -995,7 +910,7 @@ public static void endSession() { public static @NotNull ITransaction startTransaction( final @NotNull TransactionContext transactionContext, final @NotNull TransactionOptions transactionOptions) { - return getCurrentScopes().startTransaction(transactionContext, transactionOptions); + return getCurrentHub().startTransaction(transactionContext, transactionOptions); } /** @@ -1008,7 +923,7 @@ public static void endSession() { @Deprecated @SuppressWarnings("InlineMeSuggester") public static @Nullable SentryTraceHeader traceHeaders() { - return getCurrentScopes().traceHeaders(); + return getCurrentHub().traceHeaders(); } /** @@ -1020,12 +935,9 @@ public static void endSession() { */ public static @Nullable ISpan getSpan() { if (globalHubMode && Platform.isAndroid()) { - return getCurrentScopes().getTransaction(); + return getCurrentHub().getTransaction(); } else { - return getCurrentScopes() - .getOptions() - .getSpanFactory() - .retrieveCurrentSpan(getCurrentScopes()); + return getCurrentHub().getSpan(); } } @@ -1040,7 +952,7 @@ public static void endSession() { * @return true if App has crashed, false otherwise, and null if not evaluated yet */ public static @Nullable Boolean isCrashedLastRun() { - return getCurrentScopes().isCrashedLastRun(); + return getCurrentHub().isCrashedLastRun(); } /** @@ -1052,7 +964,7 @@ public static void endSession() { * finished, this call will be ignored. */ public static void reportFullyDisplayed() { - getCurrentScopes().reportFullyDisplayed(); + getCurrentHub().reportFullyDisplayed(); } /** @@ -1064,11 +976,11 @@ public static void reportFullDisplayed() { reportFullyDisplayed(); } - /** the metrics API for the current Scopes */ + /** the metrics API for the current hub */ @NotNull @ApiStatus.Experimental public static MetricsApi metrics() { - return getCurrentScopes().metrics(); + return getCurrentHub().metrics(); } /** @@ -1097,7 +1009,7 @@ public interface OptionsConfiguration { // return TransactionContext (if performance enabled) or null (if performance disabled) public static @Nullable TransactionContext continueTrace( final @Nullable String sentryTrace, final @Nullable List baggageHeaders) { - return getCurrentScopes().continueTrace(sentryTrace, baggageHeaders); + return getCurrentHub().continueTrace(sentryTrace, baggageHeaders); } /** @@ -1107,7 +1019,7 @@ public interface OptionsConfiguration { * @return sentry trace header or null */ public static @Nullable SentryTraceHeader getTraceparent() { - return getCurrentScopes().getTraceparent(); + return getCurrentHub().getTraceparent(); } /** @@ -1117,11 +1029,11 @@ public interface OptionsConfiguration { * @return baggage header or null */ public static @Nullable BaggageHeader getBaggage() { - return getCurrentScopes().getBaggage(); + return getCurrentHub().getBaggage(); } @ApiStatus.Experimental public static @NotNull SentryId captureCheckIn(final @NotNull CheckIn checkIn) { - return getCurrentScopes().captureCheckIn(checkIn); + return getCurrentHub().captureCheckIn(checkIn); } } diff --git a/sentry/src/main/java/io/sentry/SentryClient.java b/sentry/src/main/java/io/sentry/SentryClient.java index f21914d35f..ea70371722 100644 --- a/sentry/src/main/java/io/sentry/SentryClient.java +++ b/sentry/src/main/java/io/sentry/SentryClient.java @@ -139,7 +139,6 @@ private boolean shouldApplyScopeData(final @NotNull CheckIn event, final @NotNul } } - // TODO [HSM] EventProcessors from options are always executed after those from scopes event = processEvent(event, hint, options.getEventProcessors()); if (event != null) { @@ -413,7 +412,6 @@ private SentryTransaction processTransaction( final @NotNull Hint hint, final @NotNull List eventProcessors) { for (final EventProcessor processor : eventProcessors) { - final int spanCountBeforeProcessor = transaction.getSpans().size(); try { transaction = processor.process(transaction, hint); } catch (Throwable e) { @@ -425,7 +423,6 @@ private SentryTransaction processTransaction( "An exception occurred while processing transaction by processor: %s", processor.getClass().getName()); } - final int spanCountAfterProcessor = transaction == null ? 0 : transaction.getSpans().size(); if (transaction == null) { options @@ -437,25 +434,7 @@ private SentryTransaction processTransaction( options .getClientReportRecorder() .recordLostEvent(DiscardReason.EVENT_PROCESSOR, DataCategory.Transaction); - // If we drop a transaction, we are also dropping all its spans (+1 for the root span) - options - .getClientReportRecorder() - .recordLostEvent( - DiscardReason.EVENT_PROCESSOR, DataCategory.Span, spanCountBeforeProcessor + 1); break; - } else if (spanCountAfterProcessor < spanCountBeforeProcessor) { - // If the callback removed some spans, we report it - final int droppedSpanCount = spanCountBeforeProcessor - spanCountAfterProcessor; - options - .getLogger() - .log( - SentryLevel.DEBUG, - "%d spans were dropped by a processor: %s", - droppedSpanCount, - processor.getClass().getName()); - options - .getClientReportRecorder() - .recordLostEvent(DiscardReason.EVENT_PROCESSOR, DataCategory.Span, droppedSpanCount); } } return transaction; @@ -687,9 +666,7 @@ public void captureSession(final @NotNull Session session, final @Nullable Hint return SentryId.EMPTY_ID; } - final int spanCountBeforeCallback = transaction.getSpans().size(); transaction = executeBeforeSendTransaction(transaction, hint); - final int spanCountAfterCallback = transaction == null ? 0 : transaction.getSpans().size(); if (transaction == null) { options @@ -698,24 +675,7 @@ public void captureSession(final @NotNull Session session, final @Nullable Hint options .getClientReportRecorder() .recordLostEvent(DiscardReason.BEFORE_SEND, DataCategory.Transaction); - // If we drop a transaction, we are also dropping all its spans (+1 for the root span) - options - .getClientReportRecorder() - .recordLostEvent( - DiscardReason.BEFORE_SEND, DataCategory.Span, spanCountBeforeCallback + 1); return SentryId.EMPTY_ID; - } else if (spanCountAfterCallback < spanCountBeforeCallback) { - // If the callback removed some spans, we report it - final int droppedSpanCount = spanCountBeforeCallback - spanCountAfterCallback; - options - .getLogger() - .log( - SentryLevel.DEBUG, - "%d spans were dropped by beforeSendTransaction.", - droppedSpanCount); - options - .getClientReportRecorder() - .recordLostEvent(DiscardReason.BEFORE_SEND, DataCategory.Span, droppedSpanCount); } try { diff --git a/sentry/src/main/java/io/sentry/SentryExceptionFactory.java b/sentry/src/main/java/io/sentry/SentryExceptionFactory.java index 2808df11c0..6652ebb504 100644 --- a/sentry/src/main/java/io/sentry/SentryExceptionFactory.java +++ b/sentry/src/main/java/io/sentry/SentryExceptionFactory.java @@ -12,7 +12,7 @@ import java.util.Deque; import java.util.HashSet; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.Set; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -136,20 +136,12 @@ public List getSentryExceptions(final @NotNull Throwable throwa @TestOnly @NotNull Deque extractExceptionQueue(final @NotNull Throwable throwable) { - return extractExceptionQueueInternal( - throwable, new AtomicInteger(-1), new HashSet<>(), new ArrayDeque<>()); - } - - Deque extractExceptionQueueInternal( - final @NotNull Throwable throwable, - final @NotNull AtomicInteger exceptionId, - final @NotNull HashSet circularityDetector, - final @NotNull Deque exceptions) { + final Deque exceptions = new ArrayDeque<>(); + final Set circularityDetector = new HashSet<>(); Mechanism exceptionMechanism; Thread thread; Throwable currentThrowable = throwable; - int parentId = exceptionId.get(); // Stack the exceptions to send them in the reverse order while (currentThrowable != null && circularityDetector.add(currentThrowable)) { @@ -163,11 +155,12 @@ Deque extractExceptionQueueInternal( thread = exceptionMechanismThrowable.getThread(); snapshot = exceptionMechanismThrowable.isSnapshot(); } else { - exceptionMechanism = new Mechanism(); + exceptionMechanism = null; thread = Thread.currentThread(); } - final boolean includeSentryFrames = Boolean.FALSE.equals(exceptionMechanism.isHandled()); + final boolean includeSentryFrames = + exceptionMechanism != null && Boolean.FALSE.equals(exceptionMechanism.isHandled()); final List frames = sentryStackTraceFactory.getStackFrames( currentThrowable.getStackTrace(), includeSentryFrames); @@ -175,28 +168,7 @@ Deque extractExceptionQueueInternal( getSentryException( currentThrowable, exceptionMechanism, thread.getId(), frames, snapshot); exceptions.addFirst(exception); - - if (exceptionMechanism.getType() == null) { - exceptionMechanism.setType("chained"); - } - - if (exceptionId.get() >= 0) { - exceptionMechanism.setParentId(parentId); - } - - final int currentExceptionId = exceptionId.incrementAndGet(); - exceptionMechanism.setExceptionId(currentExceptionId); - - Throwable[] suppressed = currentThrowable.getSuppressed(); - if (suppressed != null && suppressed.length > 0) { - exceptionMechanism.setExceptionGroup(true); - for (Throwable suppressedThrowable : suppressed) { - extractExceptionQueueInternal( - suppressedThrowable, exceptionId, circularityDetector, exceptions); - } - } currentThrowable = currentThrowable.getCause(); - parentId = currentExceptionId; } return exceptions; diff --git a/sentry/src/main/java/io/sentry/SentryNanotimeDate.java b/sentry/src/main/java/io/sentry/SentryNanotimeDate.java index 2993eeed6c..093b249cb7 100644 --- a/sentry/src/main/java/io/sentry/SentryNanotimeDate.java +++ b/sentry/src/main/java/io/sentry/SentryNanotimeDate.java @@ -5,7 +5,7 @@ import org.jetbrains.annotations.Nullable; /** - * Uses {@link Date} in combination with System.nanoTime(). + * Uses {@link Date} in cominbation with System.nanoTime(). * *

    A single date only offers millisecond precision but diff can be calculated with up to * nanosecond precision. This increased precision can also be used to calculate a new end date for a diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 7e24e81dce..fe0dad0144 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -203,8 +203,6 @@ public class SentryOptions { */ private @Nullable TracesSamplerCallback tracesSampler; - private volatile @Nullable TracesSampler internalTracesSampler; - /** * A list of string prefixes of module names that do not belong to the app, but rather third-party * packages. Modules considered not to be part of the app will be hidden from stack traces by @@ -318,7 +316,7 @@ public class SentryOptions { /** Maximum number of spans that can be atteched to single transaction. */ private int maxSpans = 1000; - /** Registers hook that flushes {@link Scopes} when main thread shuts down. */ + /** Registers hook that flushes {@link Hub} when main thread shuts down. */ private boolean enableShutdownHook = true; /** @@ -412,7 +410,7 @@ public class SentryOptions { private @NotNull IMainThreadChecker mainThreadChecker = NoOpMainThreadChecker.getInstance(); - // TODO [MAJOR] this should default to false on the next major + // TODO this should default to false on the next major /** Whether OPTIONS requests should be traced. */ private boolean traceOptionsRequests = true; @@ -457,10 +455,6 @@ public class SentryOptions { /** Contains a list of monitor slugs for which check-ins should not be sent. */ @ApiStatus.Experimental private @Nullable List ignoredCheckIns = null; - /** Contains a list of span origins for which spans / transactions should not be created. */ - @ApiStatus.Experimental private @Nullable List ignoredSpanOrigins = null; - - @ApiStatus.Experimental private @NotNull IBackpressureMonitor backpressureMonitor = NoOpBackpressureMonitor.getInstance(); private boolean enableBackpressureHandling = true; @@ -476,8 +470,6 @@ public class SentryOptions { private @Nullable BeforeEmitMetricCallback beforeEmitMetricCallback = null; - private @NotNull ISpanFactory spanFactory = NoOpSpanFactory.getInstance(); - /** * Profiling traces rate. 101 hz means 101 traces in 1 second. Defaults to 101 to avoid possible * lockstep sampling. More on @@ -487,8 +479,6 @@ public class SentryOptions { @ApiStatus.Experimental private @Nullable Cron cron = null; - private @NotNull ScopeType defaultScopeType = ScopeType.ISOLATION; - /** * Adds an event processor * @@ -964,18 +954,6 @@ public void setTracesSampler(final @Nullable TracesSamplerCallback tracesSampler this.tracesSampler = tracesSampler; } - @ApiStatus.Internal - public @NotNull TracesSampler getInternalTracesSampler() { - if (internalTracesSampler == null) { - synchronized (this) { - if (internalTracesSampler == null) { - internalTracesSampler = new TracesSampler(this); - } - } - } - return internalTracesSampler; - } - /** * the list of inApp excludes * @@ -1954,11 +1932,7 @@ public void setEnableUserInteractionBreadcrumbs(boolean enableUserInteractionBre * startTransaction(...), nor will it create child spans if you call startChild(...) * * @param instrumenter - the instrumenter to use - * @deprecated this should no longer be needed with our current OpenTelmetry integration. Use - * {@link SentryOptions#setIgnoredSpanOrigins(List)} instead if you need fine grained control - * over what integrations can create spans. */ - @Deprecated public void setInstrumenter(final @NotNull Instrumenter instrumenter) { this.instrumenter = instrumenter; } @@ -2220,27 +2194,6 @@ public void setIgnoredCheckIns(final @Nullable List ignoredCheckIns) { } } - @ApiStatus.Experimental - public @Nullable List getIgnoredSpanOrigins() { - return ignoredSpanOrigins; - } - - @ApiStatus.Experimental - public void setIgnoredSpanOrigins(final @Nullable List ignoredSpanOrigins) { - if (ignoredSpanOrigins == null) { - this.ignoredSpanOrigins = null; - } else { - @NotNull final List filtered = new ArrayList<>(); - for (String origin : ignoredSpanOrigins) { - if (origin != null && !origin.isEmpty()) { - filtered.add(origin); - } - } - - this.ignoredSpanOrigins = filtered; - } - } - @ApiStatus.Experimental public @Nullable List getIgnoredCheckIns() { return ignoredCheckIns; @@ -2432,14 +2385,6 @@ public void setCron(@Nullable Cron cron) { this.cron = cron; } - public void setDefaultScopeType(final @NotNull ScopeType scopeType) { - this.defaultScopeType = scopeType; - } - - public @NotNull ScopeType getDefaultScopeType() { - return defaultScopeType; - } - /** The BeforeSend callback */ public interface BeforeSendCallback { @@ -2541,7 +2486,7 @@ public interface BeforeEmitMetricCallback { /** * Creates SentryOptions instance without initializing any of the internal parts. * - *

    Used by {@link NoOpScopes}. + *

    Used by {@link NoOpHub}. * * @return SentryOptions */ @@ -2562,7 +2507,6 @@ public SentryOptions() { */ private SentryOptions(final boolean empty) { if (!empty) { - setSpanFactory(new DefaultSpanFactory()); // SentryExecutorService should be initialized before any // SendCachedEventFireAndForgetIntegration executorService = new SentryExecutorService(); @@ -2688,12 +2632,6 @@ public void merge(final @NotNull ExternalOptions options) { if (options.isEnableBackpressureHandling() != null) { setEnableBackpressureHandling(options.isEnableBackpressureHandling()); } - if (options.getMaxRequestBodySize() != null) { - setMaxRequestBodySize(options.getMaxRequestBodySize()); - } - if (options.isSendDefaultPii() != null) { - setSendDefaultPii(options.isSendDefaultPii()); - } if (options.getCron() != null) { if (getCron() == null) { @@ -2733,17 +2671,6 @@ private void addPackageInfo() { .addPackage("maven:io.sentry:sentry", BuildConfig.VERSION_NAME); } - @ApiStatus.Internal - public @NotNull ISpanFactory getSpanFactory() { - // TODO [POTEL] use a util for checking if OTel is active or similar - return spanFactory; - } - - @ApiStatus.Internal - public void setSpanFactory(final @NotNull ISpanFactory spanFactory) { - this.spanFactory = spanFactory; - } - public static final class Proxy { private @Nullable String host; private @Nullable String port; diff --git a/sentry/src/main/java/io/sentry/SentryRuntimeEventProcessor.java b/sentry/src/main/java/io/sentry/SentryRuntimeEventProcessor.java index ca19a9ca74..9d0d6443aa 100644 --- a/sentry/src/main/java/io/sentry/SentryRuntimeEventProcessor.java +++ b/sentry/src/main/java/io/sentry/SentryRuntimeEventProcessor.java @@ -42,9 +42,4 @@ public SentryRuntimeEventProcessor() { } return event; } - - @Override - public @Nullable Long getOrder() { - return 2000L; - } } diff --git a/sentry/src/main/java/io/sentry/SentrySpanFactoryHolder.java b/sentry/src/main/java/io/sentry/SentrySpanFactoryHolder.java deleted file mode 100644 index 19f79d7e50..0000000000 --- a/sentry/src/main/java/io/sentry/SentrySpanFactoryHolder.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.sentry; - -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; - -/** - * NOTE: This just exists as a workaround for a bug. - * - *

    What bug? When using sentry-opentelemetry-agent with SENTRY_AUTO_INIT=false a global storage - * for spans does not work correctly since it's loaded multiple times. Once for bootstrap - * classloader (a.k.a null) and once for the agent classloader. Since the agent is currently loading - * these classes into the agent classloader, there should not be a noticable problem, when using the - * default of SENTRY_AUTO_INIT=true. In the future we plan to have the agent also load the classes - * into the bootstrap classloader, then this hack should no longer be necessary. - */ -@ApiStatus.Experimental -@ApiStatus.Internal -public final class SentrySpanFactoryHolder { - - private static ISpanFactory spanFactory = new DefaultSpanFactory(); - - public static ISpanFactory getSpanFactory() { - return spanFactory; - } - - @ApiStatus.Internal - public static void setSpanFactory(final @NotNull ISpanFactory factory) { - spanFactory = factory; - } -} diff --git a/sentry/src/main/java/io/sentry/SentrySpanStorage.java b/sentry/src/main/java/io/sentry/SentrySpanStorage.java index eb7379741c..b260f06e5c 100644 --- a/sentry/src/main/java/io/sentry/SentrySpanStorage.java +++ b/sentry/src/main/java/io/sentry/SentrySpanStorage.java @@ -9,10 +9,7 @@ /** * Has been moved to `sentry` gradle module to include it in the bootstrap classloader without * having to introduce yet another module for OpenTelemetry support. - * - * @deprecated please use SentryWeakSpanStorage (from sentry-opentelemetry-bootstrap) instead. */ -@Deprecated @ApiStatus.Internal public final class SentrySpanStorage { private static volatile @Nullable SentrySpanStorage INSTANCE; diff --git a/sentry/src/main/java/io/sentry/SentryThreadFactory.java b/sentry/src/main/java/io/sentry/SentryThreadFactory.java index 8af5f0aa0c..832ec8ea72 100644 --- a/sentry/src/main/java/io/sentry/SentryThreadFactory.java +++ b/sentry/src/main/java/io/sentry/SentryThreadFactory.java @@ -105,9 +105,7 @@ List getCurrentThreads( final Thread thread = item.getKey(); final boolean crashed = (thread == currentThread && !ignoreCurrentThread) - || (mechanismThreadIds != null - && mechanismThreadIds.contains(thread.getId()) - && !ignoreCurrentThread); + || (mechanismThreadIds != null && mechanismThreadIds.contains(thread.getId())); result.add(getSentryThread(crashed, item.getValue(), item.getKey())); } diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index ce739986ca..8086acd02e 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -26,7 +26,7 @@ public final class SentryTracer implements ITransaction { private final @NotNull SentryId eventId = new SentryId(); private final @NotNull Span root; private final @NotNull List children = new CopyOnWriteArrayList<>(); - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private @NotNull String name; /** @@ -52,30 +52,31 @@ public final class SentryTracer implements ITransaction { private final @Nullable TransactionPerformanceCollector transactionPerformanceCollector; private final @NotNull TransactionOptions transactionOptions; - public SentryTracer(final @NotNull TransactionContext context, final @NotNull IScopes scopes) { - this(context, scopes, new TransactionOptions(), null); + public SentryTracer(final @NotNull TransactionContext context, final @NotNull IHub hub) { + this(context, hub, new TransactionOptions(), null); } public SentryTracer( final @NotNull TransactionContext context, - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull TransactionOptions transactionOptions) { - this(context, scopes, transactionOptions, null); + this(context, hub, transactionOptions, null); } SentryTracer( final @NotNull TransactionContext context, - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull TransactionOptions transactionOptions, final @Nullable TransactionPerformanceCollector transactionPerformanceCollector) { Objects.requireNonNull(context, "context is required"); - Objects.requireNonNull(scopes, "scopes are required"); + Objects.requireNonNull(hub, "hub is required"); - this.root = new Span(context, this, scopes, transactionOptions); + this.root = + new Span(context, this, hub, transactionOptions.getStartTimestamp(), transactionOptions); this.name = context.getName(); this.instrumenter = context.getInstrumenter(); - this.scopes = scopes; + this.hub = hub; this.transactionPerformanceCollector = transactionPerformanceCollector; this.transactionNameSource = context.getTransactionNameSource(); this.transactionOptions = transactionOptions; @@ -83,7 +84,7 @@ public SentryTracer( if (context.getBaggage() != null) { this.baggage = context.getBaggage(); } else { - this.baggage = new Baggage(scopes.getOptions().getLogger()); + this.baggage = new Baggage(hub.getOptions().getLogger()); } // We are currently sending the performance data only in profiles, but we are always sending @@ -121,8 +122,7 @@ public void run() { try { timer.schedule(idleTimeoutTask, idleTimeout); } catch (Throwable e) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log(SentryLevel.WARNING, "Failed to schedule finish timer", e); // if we failed to schedule the finish timer for some reason, we finish it here right @@ -156,7 +156,7 @@ private void onDeadlineTimeoutReached() { return; } - final @NotNull SentryDate finishTimestamp = scopes.getOptions().getDateProvider().now(); + final @NotNull SentryDate finishTimestamp = hub.getOptions().getDateProvider().now(); // abort all child-spans first, this ensures the transaction can be finished, // even if waitForChildren is true @@ -186,7 +186,7 @@ public void finish( // if it's not set -> fallback to the current time if (finishTimestamp == null) { - finishTimestamp = scopes.getOptions().getDateProvider().now(); + finishTimestamp = hub.getOptions().getDateProvider().now(); } // auto-finish any idle spans first @@ -233,16 +233,15 @@ public void finish( ProfilingTraceData profilingTraceData = null; if (Boolean.TRUE.equals(isSampled()) && Boolean.TRUE.equals(isProfileSampled())) { profilingTraceData = - scopes - .getOptions() + hub.getOptions() .getTransactionProfiler() - .onTransactionFinish(this, performanceCollectionData.get(), scopes.getOptions()); + .onTransactionFinish(this, performanceCollectionData.get(), hub.getOptions()); } if (performanceCollectionData.get() != null) { performanceCollectionData.get().clear(); } - scopes.configureScope( + hub.configureScope( scope -> { scope.withTransaction( transaction -> { @@ -266,8 +265,7 @@ public void finish( if (dropIfNoChildren && children.isEmpty() && transactionOptions.getIdleTimeout() != null) { // if it's an idle transaction which has no children, we drop it to save user's quota - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -277,7 +275,7 @@ public void finish( } transaction.getMeasurements().putAll(root.getMeasurements()); - scopes.captureTransaction(transaction, traceContext(), hint, profilingTraceData); + hub.captureTransaction(transaction, traceContext(), hint, profilingTraceData); } } @@ -308,8 +306,7 @@ public void run() { try { timer.schedule(deadlineTimeoutTask, deadlineTimeOut); } catch (Throwable e) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log(SentryLevel.WARNING, "Failed to schedule finish timer", e); // if we failed to schedule the finish timer for some reason, we finish it here right @@ -386,15 +383,8 @@ ISpan startChild( final @Nullable String description, final @Nullable SentryDate timestamp, final @NotNull Instrumenter instrumenter) { - final @NotNull SpanContext spanContext = - getSpanContext().copyForChild(operation, parentSpanId, null); - spanContext.setDescription(description); - spanContext.setInstrumenter(instrumenter); - - final @NotNull SpanOptions spanOptions = new SpanOptions(); - spanOptions.setStartTimestamp(timestamp); - - return createChild(spanContext, spanOptions); + return createChild( + parentSpanId, operation, description, timestamp, instrumenter, new SpanOptions()); } @NotNull @@ -405,14 +395,7 @@ ISpan startChild( final @Nullable SentryDate timestamp, final @NotNull Instrumenter instrumenter, final @NotNull SpanOptions spanOptions) { - final @NotNull SpanContext spanContext = - getSpanContext().copyForChild(operation, parentSpanId, null); - spanContext.setDescription(description); - spanContext.setInstrumenter(instrumenter); - - spanOptions.setStartTimestamp(timestamp); - - return createChild(spanContext, spanOptions); + return createChild(parentSpanId, operation, description, timestamp, instrumenter, spanOptions); } /** @@ -430,45 +413,37 @@ private ISpan createChild( final @NotNull String operation, final @Nullable String description, final @NotNull SpanOptions options) { - final @NotNull SpanContext spanContext = - getSpanContext().copyForChild(operation, parentSpanId, null); - spanContext.setDescription(description); - spanContext.setInstrumenter(Instrumenter.SENTRY); - - return createChild(spanContext, options); + return createChild(parentSpanId, operation, description, null, Instrumenter.SENTRY, options); } @NotNull private ISpan createChild( - final @NotNull SpanContext spanContext, final @NotNull SpanOptions spanOptions) { + final @NotNull SpanId parentSpanId, + final @NotNull String operation, + final @Nullable String description, + @Nullable SentryDate timestamp, + final @NotNull Instrumenter instrumenter, + final @NotNull SpanOptions spanOptions) { if (root.isFinished()) { return NoOpSpan.getInstance(); } - if (!this.instrumenter.equals(spanContext.getInstrumenter())) { + if (!this.instrumenter.equals(instrumenter)) { return NoOpSpan.getInstance(); } - final @Nullable SpanId parentSpanId = spanContext.getParentSpanId(); - final @NotNull String operation = spanContext.getOperation(); - final @Nullable String description = spanContext.getDescription(); - - // TODO [POTEL] how should this work? return a noop? shouldn't block nested code from actually - // creating spans - // if (SpanUtils.isIgnored(scopes.getOptions().getIgnoredSpanOrigins(), - // spanOptions.getOrigin())) { - // return this; - // } - - if (children.size() < scopes.getOptions().getMaxSpans()) { + if (children.size() < hub.getOptions().getMaxSpans()) { Objects.requireNonNull(parentSpanId, "parentSpanId is required"); - // Objects.requireNonNull(operation, "operation is required"); + Objects.requireNonNull(operation, "operation is required"); cancelIdleTimer(); final Span span = new Span( + root.getTraceId(), + parentSpanId, this, - scopes, - spanContext, + operation, + this.hub, + timestamp, spanOptions, finishingSpan -> { if (transactionPerformanceCollector != null) { @@ -486,39 +461,11 @@ private ISpan createChild( finish(finishStatus.spanStatus); } }); - // TODO [POTEL] missing features - // final Span span = - // new Span( - // root.getTraceId(), - // parentSpanId, - // this, - // operation, - // this.scopes, - // timestamp, - // spanOptions, - // finishingSpan -> { - // if (transactionPerformanceCollector != null) { - // transactionPerformanceCollector.onSpanFinished(finishingSpan); - // } - // final FinishStatus finishStatus = this.finishStatus; - // if (transactionOptions.getIdleTimeout() != null) { - // // if it's an idle transaction, no matter the status, we'll reset the - // timeout here - // // so the transaction will either idle and finish itself, or a new child - // will be - // // added and we'll wait for it again - // if (!transactionOptions.isWaitForChildren() || hasAllChildrenFinished()) { - // scheduleFinish(); - // } - // } else if (finishStatus.isFinishing) { - // finish(finishStatus.spanStatus); - // } - // }); - // span.setDescription(description); + span.setDescription(description); span.setData(SpanDataConvention.THREAD_ID, String.valueOf(Thread.currentThread().getId())); span.setData( SpanDataConvention.THREAD_NAME, - scopes.getOptions().getMainThreadChecker().isMainThread() + hub.getOptions().getMainThreadChecker().isMainThread() ? "main" : Thread.currentThread().getName()); this.children.add(span); @@ -527,8 +474,7 @@ private ISpan createChild( } return span; } else { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -583,12 +529,6 @@ private ISpan createChild( return createChild(operation, description, null, Instrumenter.SENTRY, spanOptions); } - @Override - public @NotNull ISpan startChild( - @NotNull SpanContext spanContext, @NotNull SpanOptions spanOptions) { - return createChild(spanContext, spanOptions); - } - private @NotNull ISpan createChild( final @NotNull String operation, final @Nullable String description, @@ -603,11 +543,10 @@ private ISpan createChild( return NoOpSpan.getInstance(); } - if (children.size() < scopes.getOptions().getMaxSpans()) { + if (children.size() < hub.getOptions().getMaxSpans()) { return root.startChild(operation, description, timestamp, instrumenter, spanOptions); } else { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.WARNING, @@ -642,7 +581,7 @@ public void finish(@Nullable SpanStatus status, @Nullable SentryDate finishDate) @Override public @Nullable TraceContext traceContext() { - if (scopes.getOptions().isTraceSampling()) { + if (hub.getOptions().isTraceSampling()) { updateBaggageValues(); return baggage.toTraceContext(); } else { @@ -654,17 +593,12 @@ private void updateBaggageValues() { synchronized (this) { if (baggage.isMutable()) { final AtomicReference userAtomicReference = new AtomicReference<>(); - scopes.configureScope( + hub.configureScope( scope -> { userAtomicReference.set(scope.getUser()); }); baggage.setValuesFromTransaction( - getSpanContext().getTraceId(), - userAtomicReference.get(), - scopes.getOptions(), - this.getSamplingDecision(), - getName(), - getTransactionNameSource()); + this, userAtomicReference.get(), hub.getOptions(), this.getSamplingDecision()); baggage.freeze(); } } @@ -672,7 +606,7 @@ private void updateBaggageValues() { @Override public @Nullable BaggageHeader toBaggageHeader(@Nullable List thirdPartyBaggageHeaders) { - if (scopes.getOptions().isTraceSampling()) { + if (hub.getOptions().isTraceSampling()) { updateBaggageValues(); return BaggageHeader.fromBaggageAndOutgoingHeader(baggage, thirdPartyBaggageHeaders); @@ -698,8 +632,7 @@ private boolean hasAllChildrenFinished() { @Override public void setOperation(final @NotNull String operation) { if (root.isFinished()) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -719,8 +652,7 @@ public void setOperation(final @NotNull String operation) { @Override public void setDescription(final @Nullable String description) { if (root.isFinished()) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -740,8 +672,7 @@ public void setDescription(final @Nullable String description) { @Override public void setStatus(final @Nullable SpanStatus status) { if (root.isFinished()) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -761,8 +692,7 @@ public void setStatus(final @Nullable SpanStatus status) { @Override public void setThrowable(final @Nullable Throwable throwable) { if (root.isFinished()) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log(SentryLevel.DEBUG, "The transaction is already finished. Throwable cannot be set"); return; @@ -784,8 +714,7 @@ public void setThrowable(final @Nullable Throwable throwable) { @Override public void setTag(final @NotNull String key, final @NotNull String value) { if (root.isFinished()) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log(SentryLevel.DEBUG, "The transaction is already finished. Tag %s cannot be set", key); return; @@ -807,8 +736,7 @@ public boolean isFinished() { @Override public void setData(@NotNull String key, @NotNull Object value) { if (root.isFinished()) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, "The transaction is already finished. Data %s cannot be set", key); @@ -883,8 +811,7 @@ public void setName(@NotNull String name) { @Override public void setName(@NotNull String name, @NotNull TransactionNameSource transactionNameSource) { if (root.isFinished()) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -913,7 +840,7 @@ public void setName(@NotNull String name, @NotNull TransactionNameSource transac } @Override - public @Nullable ISpan getLatestActiveSpan() { + public @Nullable Span getLatestActiveSpan() { final List spans = new ArrayList<>(this.children); if (!spans.isEmpty()) { for (int i = spans.size() - 1; i >= 0; i--) { @@ -930,18 +857,6 @@ public void setName(@NotNull String name, @NotNull TransactionNameSource transac return eventId; } - @ApiStatus.Internal - @Override - public @NotNull ISentryLifecycleToken makeCurrent() { - scopes.configureScope( - (scope) -> { - scope.setTransaction(this); - }); - - // TODO [POTEL] can we return an actual token here - return NoOpScopesLifecycleToken.getInstance(); - } - @NotNull Span getRoot() { return root; diff --git a/sentry/src/main/java/io/sentry/SentryWrapper.java b/sentry/src/main/java/io/sentry/SentryWrapper.java index e4ccefed48..1a39adee99 100644 --- a/sentry/src/main/java/io/sentry/SentryWrapper.java +++ b/sentry/src/main/java/io/sentry/SentryWrapper.java @@ -12,28 +12,31 @@ *

  • {@link Supplier} * * - * that forks the current scope(s) before execution and restores previous state afterwards. Which - * scope(s) are forked, depends on the method used here. This prevents reused threads (e.g. from - * thread-pools) from getting an incorrect state. + * that clones the Hub before execution and restores it afterwards. This prevents reused threads + * (e.g. from thread-pools) from getting an incorrect state. */ public final class SentryWrapper { /** * Helper method to wrap {@link Callable} * - *

    Forks current and isolation scope before execution and restores previous state afterwards. - * This prevents reused threads (e.g. from thread-pools) from getting an incorrect state. + *

    Clones the Hub before execution and restores it afterwards. This prevents reused threads + * (e.g. from thread-pools) from getting an incorrect state. * * @param callable - the {@link Callable} to be wrapped * @return the wrapped {@link Callable} * @param - the result type of the {@link Callable} */ public static Callable wrapCallable(final @NotNull Callable callable) { - final IScopes newScopes = Sentry.getCurrentScopes().forkedScopes("SentryWrapper.wrapCallable"); + final IHub newHub = Sentry.getCurrentHub().clone(); return () -> { - try (ISentryLifecycleToken ignored = newScopes.makeCurrent()) { + final IHub oldState = Sentry.getCurrentHub(); + Sentry.setCurrentHub(newHub); + try { return callable.call(); + } finally { + Sentry.setCurrentHub(oldState); } }; } @@ -41,19 +44,24 @@ public static Callable wrapCallable(final @NotNull Callable callable) /** * Helper method to wrap {@link Supplier} * - *

    Forks current and isolation scope before execution and restores previous state afterwards. - * This prevents reused threads (e.g. from thread-pools) from getting an incorrect state. + *

    Clones the Hub before execution and restores it afterwards. This prevents reused threads + * (e.g. from thread-pools) from getting an incorrect state. * * @param supplier - the {@link Supplier} to be wrapped * @return the wrapped {@link Supplier} * @param - the result type of the {@link Supplier} */ public static Supplier wrapSupplier(final @NotNull Supplier supplier) { - final IScopes newScopes = Sentry.forkedScopes("SentryWrapper.wrapSupplier"); + + final IHub newHub = Sentry.getCurrentHub().clone(); return () -> { - try (ISentryLifecycleToken ignore = newScopes.makeCurrent()) { + final IHub oldState = Sentry.getCurrentHub(); + Sentry.setCurrentHub(newHub); + try { return supplier.get(); + } finally { + Sentry.setCurrentHub(oldState); } }; } diff --git a/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java b/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java index e553d0a9f5..4ffb47d7d1 100644 --- a/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java +++ b/sentry/src/main/java/io/sentry/ShutdownHookIntegration.java @@ -10,7 +10,7 @@ import org.jetbrains.annotations.TestOnly; import org.jetbrains.annotations.VisibleForTesting; -/** Registers hook that flushes {@link Scopes} when main thread shuts down. */ +/** Registers hook that flushes {@link Hub} when main thread shuts down. */ public final class ShutdownHookIntegration implements Integration, Closeable { private final @NotNull Runtime runtime; @@ -27,12 +27,12 @@ public ShutdownHookIntegration() { } @Override - public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { - Objects.requireNonNull(scopes, "Scopes are required"); + public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { + Objects.requireNonNull(hub, "Hub is required"); Objects.requireNonNull(options, "SentryOptions is required"); if (options.isEnableShutdownHook()) { - thread = new Thread(() -> scopes.flush(options.getFlushTimeoutMillis())); + thread = new Thread(() -> hub.flush(options.getFlushTimeoutMillis())); handleShutdownInProgress( () -> { runtime.addShutdownHook(thread); diff --git a/sentry/src/main/java/io/sentry/Span.java b/sentry/src/main/java/io/sentry/Span.java index 12fcf87c20..14cf4825bf 100644 --- a/sentry/src/main/java/io/sentry/Span.java +++ b/sentry/src/main/java/io/sentry/Span.java @@ -1,7 +1,6 @@ package io.sentry; import io.sentry.metrics.LocalMetricsAggregator; -import io.sentry.protocol.Contexts; import io.sentry.protocol.MeasurementValue; import io.sentry.protocol.SentryId; import io.sentry.util.LazyEvaluator; @@ -36,7 +35,7 @@ public final class Span implements ISpan { /** A throwable thrown during the execution of the span. */ private @Nullable Throwable throwable; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private boolean finished = false; @@ -49,47 +48,56 @@ public final class Span implements ISpan { private final @NotNull Map data = new ConcurrentHashMap<>(); private final @NotNull Map measurements = new ConcurrentHashMap<>(); - private final @NotNull Contexts contexts = new Contexts(); - @SuppressWarnings("Convert2MethodRef") // older AGP versions do not support method references private final @NotNull LazyEvaluator metricsAggregator = new LazyEvaluator<>(() -> new LocalMetricsAggregator()); Span( + final @NotNull SentryId traceId, + final @Nullable SpanId parentSpanId, + final @NotNull SentryTracer transaction, + final @NotNull String operation, + final @NotNull IHub hub) { + this(traceId, parentSpanId, transaction, operation, hub, null, new SpanOptions(), null); + } + + Span( + final @NotNull SentryId traceId, + final @Nullable SpanId parentSpanId, final @NotNull SentryTracer transaction, - final @NotNull IScopes scopes, - final @NotNull SpanContext spanContext, + final @NotNull String operation, + final @NotNull IHub hub, + final @Nullable SentryDate startTimestamp, final @NotNull SpanOptions options, final @Nullable SpanFinishedCallback spanFinishedCallback) { - this.context = spanContext; - this.context.setOrigin(options.getOrigin()); + this.context = + new SpanContext( + traceId, new SpanId(), operation, parentSpanId, transaction.getSamplingDecision()); this.transaction = Objects.requireNonNull(transaction, "transaction is required"); - this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); + this.hub = Objects.requireNonNull(hub, "hub is required"); this.options = options; this.spanFinishedCallback = spanFinishedCallback; - final @Nullable SentryDate startTimestamp = options.getStartTimestamp(); if (startTimestamp != null) { this.startTimestamp = startTimestamp; } else { - this.startTimestamp = scopes.getOptions().getDateProvider().now(); + this.startTimestamp = hub.getOptions().getDateProvider().now(); } } public Span( final @NotNull TransactionContext context, final @NotNull SentryTracer sentryTracer, - final @NotNull IScopes scopes, + final @NotNull IHub hub, + final @Nullable SentryDate startTimestamp, final @NotNull SpanOptions options) { this.context = Objects.requireNonNull(context, "context is required"); - this.context.setOrigin(options.getOrigin()); this.transaction = Objects.requireNonNull(sentryTracer, "sentryTracer is required"); - this.scopes = Objects.requireNonNull(scopes, "scopes are required"); + this.hub = Objects.requireNonNull(hub, "hub is required"); this.spanFinishedCallback = null; - final @Nullable SentryDate startTimestamp = options.getStartTimestamp(); if (startTimestamp != null) { this.startTimestamp = startTimestamp; } else { - this.startTimestamp = scopes.getOptions().getDateProvider().now(); + this.startTimestamp = hub.getOptions().getDateProvider().now(); } this.options = options; } @@ -143,12 +151,6 @@ public Span( return transaction.startChild(context.getSpanId(), operation, description, spanOptions); } - @Override - public @NotNull ISpan startChild( - @NotNull SpanContext spanContext, @NotNull SpanOptions spanOptions) { - return transaction.startChild(spanContext, spanOptions); - } - @Override public @NotNull ISpan startChild( @NotNull String operation, @@ -180,7 +182,7 @@ public void finish() { @Override public void finish(@Nullable SpanStatus status) { - finish(status, scopes.getOptions().getDateProvider().now()); + finish(status, hub.getOptions().getDateProvider().now()); } /** @@ -197,7 +199,7 @@ public void finish(final @Nullable SpanStatus status, final @Nullable SentryDate } this.context.setStatus(status); - this.timestamp = timestamp == null ? scopes.getOptions().getDateProvider().now() : timestamp; + this.timestamp = timestamp == null ? hub.getOptions().getDateProvider().now() : timestamp; if (options.isTrimStart() || options.isTrimEnd()) { @Nullable SentryDate minChildStart = null; @Nullable SentryDate maxChildEnd = null; @@ -230,7 +232,7 @@ public void finish(final @Nullable SpanStatus status, final @Nullable SentryDate } if (throwable != null) { - scopes.setSpanContext(throwable, this, this.transaction.getName()); + hub.setSpanContext(throwable, this, this.transaction.getName()); } if (spanFinishedCallback != null) { spanFinishedCallback.execute(this); @@ -292,7 +294,6 @@ public boolean isFinished() { return data; } - @Override public @Nullable Boolean isSampled() { return context.getSampled(); } @@ -301,7 +302,6 @@ public boolean isFinished() { return context.getProfileSampled(); } - @Override public @Nullable TracesSamplingDecision getSamplingDecision() { return context.getSamplingDecision(); } @@ -346,8 +346,7 @@ public void setData(final @NotNull String key, final @NotNull Object value) { @Override public void setMeasurement(final @NotNull String name, final @NotNull Number value) { if (isFinished()) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -369,8 +368,7 @@ public void setMeasurement( final @NotNull Number value, final @NotNull MeasurementUnit unit) { if (isFinished()) { - scopes - .getOptions() + hub.getOptions() .getLogger() .log( SentryLevel.DEBUG, @@ -410,16 +408,6 @@ public boolean isNoOp() { return metricsAggregator.getValue(); } - @Override - public void setContext(@NotNull String key, @NotNull Object context) { - this.contexts.put(key, context); - } - - @Override - public @NotNull Contexts getContexts() { - return contexts; - } - void setSpanFinishedCallback(final @Nullable SpanFinishedCallback callback) { this.spanFinishedCallback = callback; } @@ -451,9 +439,4 @@ private List getDirectChildren() { } return children; } - - @Override - public @NotNull ISentryLifecycleToken makeCurrent() { - return NoOpScopesLifecycleToken.getInstance(); - } } diff --git a/sentry/src/main/java/io/sentry/SpanContext.java b/sentry/src/main/java/io/sentry/SpanContext.java index 2d1b8c5fe7..be428708cb 100644 --- a/sentry/src/main/java/io/sentry/SpanContext.java +++ b/sentry/src/main/java/io/sentry/SpanContext.java @@ -16,7 +16,6 @@ @Open public class SpanContext implements JsonUnknown, JsonSerializable { public static final String TYPE = "trace"; - public static final String DEFAULT_ORIGIN = "manual"; /** Determines which trace the Span belongs to. */ private final @NotNull SentryId traceId; @@ -25,7 +24,7 @@ public class SpanContext implements JsonUnknown, JsonSerializable { private final @NotNull SpanId spanId; /** Id of a parent span. */ - private @Nullable SpanId parentSpanId; + private final @Nullable SpanId parentSpanId; private transient @Nullable TracesSamplingDecision samplingDecision; @@ -45,14 +44,10 @@ public class SpanContext implements JsonUnknown, JsonSerializable { protected @NotNull Map tags = new ConcurrentHashMap<>(); /** Describes the status of the Transaction. */ - protected @Nullable String origin = DEFAULT_ORIGIN; + protected @Nullable String origin = "manual"; private @Nullable Map unknown; - private @NotNull Instrumenter instrumenter = Instrumenter.SENTRY; - - protected @Nullable Baggage baggage; - public SpanContext( final @NotNull String operation, final @Nullable TracesSamplingDecision samplingDecision) { this(new SentryId(), new SpanId(), operation, null, samplingDecision); @@ -73,7 +68,7 @@ public SpanContext( final @NotNull String operation, final @Nullable SpanId parentSpanId, final @Nullable TracesSamplingDecision samplingDecision) { - this(traceId, spanId, parentSpanId, operation, null, samplingDecision, null, DEFAULT_ORIGIN); + this(traceId, spanId, parentSpanId, operation, null, samplingDecision, null, "manual"); } @ApiStatus.Internal @@ -150,7 +145,6 @@ public SpanId getParentSpanId() { } public @NotNull String getOperation() { - // TODO [POTEL] use span name here return op; } @@ -219,34 +213,6 @@ public void setOrigin(final @Nullable String origin) { this.origin = origin; } - public @NotNull Instrumenter getInstrumenter() { - return instrumenter; - } - - public void setInstrumenter(final @NotNull Instrumenter instrumenter) { - this.instrumenter = instrumenter; - } - - public @Nullable Baggage getBaggage() { - return baggage; - } - - @ApiStatus.Internal - public SpanContext copyForChild( - final @NotNull String operation, - final @Nullable SpanId parentSpanId, - final @Nullable SpanId spanId) { - return new SpanContext( - traceId, - spanId == null ? new SpanId() : spanId, - parentSpanId, - operation, - null, - samplingDecision, - null, - DEFAULT_ORIGIN); - } - @Override public boolean equals(Object o) { if (this == o) return true; @@ -257,12 +223,12 @@ public boolean equals(Object o) { && Objects.equals(parentSpanId, that.parentSpanId) && op.equals(that.op) && Objects.equals(description, that.description) - && getStatus() == that.getStatus(); + && status == that.status; } @Override public int hashCode() { - return Objects.hash(traceId, spanId, parentSpanId, op, description, getStatus()); + return Objects.hash(traceId, spanId, parentSpanId, op, description, status); } // region JsonSerializable @@ -294,8 +260,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger if (description != null) { writer.name(JsonKeys.DESCRIPTION).value(description); } - if (getStatus() != null) { - writer.name(JsonKeys.STATUS).value(logger, getStatus()); + if (status != null) { + writer.name(JsonKeys.STATUS).value(logger, status); } if (origin != null) { writer.name(JsonKeys.ORIGIN).value(logger, origin); diff --git a/sentry/src/main/java/io/sentry/SpanFinishedCallback.java b/sentry/src/main/java/io/sentry/SpanFinishedCallback.java index 55f5a66f0b..9ce34dc764 100644 --- a/sentry/src/main/java/io/sentry/SpanFinishedCallback.java +++ b/sentry/src/main/java/io/sentry/SpanFinishedCallback.java @@ -1,10 +1,8 @@ package io.sentry; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -@ApiStatus.Internal -public interface SpanFinishedCallback { +interface SpanFinishedCallback { /** * Called when observed span finishes. * diff --git a/sentry/src/main/java/io/sentry/SpanOptions.java b/sentry/src/main/java/io/sentry/SpanOptions.java index 41ac313ae5..42fc9906a3 100644 --- a/sentry/src/main/java/io/sentry/SpanOptions.java +++ b/sentry/src/main/java/io/sentry/SpanOptions.java @@ -1,36 +1,12 @@ package io.sentry; -import static io.sentry.SpanContext.DEFAULT_ORIGIN; - import com.jakewharton.nopen.annotation.Open; import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Nullable; @ApiStatus.Internal @Open public class SpanOptions { - /** The start timestamp of the transaction */ - private @Nullable SentryDate startTimestamp = null; - - /** - * 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; - } - /** * If `trimStart` is true, sets the start timestamp of the transaction to the lowest start * timestamp of child spans. @@ -51,8 +27,6 @@ public void setStartTimestamp(@Nullable SentryDate startTimestamp) { */ private boolean isIdle = false; - protected @Nullable String origin = DEFAULT_ORIGIN; - public boolean isTrimStart() { return trimStart; } @@ -76,12 +50,4 @@ public void setTrimEnd(boolean trimEnd) { public void setIdle(boolean idle) { isIdle = idle; } - - public @Nullable String getOrigin() { - return origin; - } - - public void setOrigin(final @Nullable String origin) { - this.origin = origin; - } } diff --git a/sentry/src/main/java/io/sentry/SpanStatus.java b/sentry/src/main/java/io/sentry/SpanStatus.java index 37991abd67..b0b1bf78c8 100644 --- a/sentry/src/main/java/io/sentry/SpanStatus.java +++ b/sentry/src/main/java/io/sentry/SpanStatus.java @@ -103,27 +103,12 @@ private boolean matches(int httpStatusCode) { return httpStatusCode >= minHttpStatusCode && httpStatusCode <= maxHttpStatusCode; } - public @NotNull String apiName() { - return name().toLowerCase(Locale.ROOT); - } - - public static @Nullable SpanStatus fromApiNameSafely(final @Nullable String apiName) { - if (apiName == null) { - return null; - } - try { - return SpanStatus.valueOf(apiName.toUpperCase(Locale.ROOT)); - } catch (IllegalArgumentException ex) { - return null; - } - } - // JsonSerializable @Override public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger logger) throws IOException { - writer.value(apiName()); + writer.value(name().toLowerCase(Locale.ROOT)); } public static final class Deserializer implements JsonDeserializer { diff --git a/sentry/src/main/java/io/sentry/SpotlightIntegration.java b/sentry/src/main/java/io/sentry/SpotlightIntegration.java index 0b69ae79be..6d488bcbce 100644 --- a/sentry/src/main/java/io/sentry/SpotlightIntegration.java +++ b/sentry/src/main/java/io/sentry/SpotlightIntegration.java @@ -26,7 +26,7 @@ public final class SpotlightIntegration private @NotNull ISentryExecutorService executorService = NoOpSentryExecutorService.getInstance(); @Override - public void register(@NotNull IScopes scopes, @NotNull SentryOptions options) { + public void register(@NotNull IHub hub, @NotNull SentryOptions options) { this.options = options; this.logger = options.getLogger(); diff --git a/sentry/src/main/java/io/sentry/TraceContext.java b/sentry/src/main/java/io/sentry/TraceContext.java index 56c9ee586f..ef2944a9e9 100644 --- a/sentry/src/main/java/io/sentry/TraceContext.java +++ b/sentry/src/main/java/io/sentry/TraceContext.java @@ -26,25 +26,9 @@ public final class TraceContext implements JsonUnknown, JsonSerializable { private @Nullable Map unknown; TraceContext(@NotNull SentryId traceId, @NotNull String publicKey) { - this(traceId, publicKey, null, null, null, null, null, null); + this(traceId, publicKey, null, null, null, null, null, null, null); } - TraceContext( - @NotNull SentryId traceId, - @NotNull String publicKey, - @Nullable String release, - @Nullable String environment, - @Nullable String userId, - @Nullable String transaction, - @Nullable String sampleRate, - @Nullable String sampled) { - this(traceId, publicKey, release, environment, userId, null, transaction, sampleRate, sampled); - } - - /** - * @deprecated segment has no effect and will be removed in the next major update. - */ - @Deprecated TraceContext( @NotNull SentryId traceId, @NotNull String publicKey, @@ -96,10 +80,6 @@ public final class TraceContext implements JsonUnknown, JsonSerializable { return userId; } - /** - * @deprecated has no effect and will be removed in the next major update. - */ - @Deprecated public @Nullable String getUserSegment() { return userSegment; } @@ -136,10 +116,6 @@ private TraceContextUser(final @Nullable String id, final @Nullable String segme return id; } - /** - * @deprecated has no effect and will be removed in the next major update. - */ - @Deprecated public @Nullable String getSegment() { return segment; } diff --git a/sentry/src/main/java/io/sentry/TracesSampler.java b/sentry/src/main/java/io/sentry/TracesSampler.java index f85aba1a9b..3b83a815cf 100644 --- a/sentry/src/main/java/io/sentry/TracesSampler.java +++ b/sentry/src/main/java/io/sentry/TracesSampler.java @@ -2,13 +2,11 @@ import io.sentry.util.Objects; import java.security.SecureRandom; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; -@ApiStatus.Internal -public final class TracesSampler { +final class TracesSampler { private static final @NotNull Double DEFAULT_TRACES_SAMPLE_RATE = 1.0; private final @NotNull SentryOptions options; @@ -25,7 +23,7 @@ public TracesSampler(final @NotNull SentryOptions options) { } @NotNull - public TracesSamplingDecision sample(final @NotNull SamplingContext samplingContext) { + TracesSamplingDecision sample(final @NotNull SamplingContext samplingContext) { final TracesSamplingDecision samplingContextSamplingDecision = samplingContext.getTransactionContext().getSamplingDecision(); if (samplingContextSamplingDecision != null) { diff --git a/sentry/src/main/java/io/sentry/TransactionContext.java b/sentry/src/main/java/io/sentry/TransactionContext.java index d81aa1059d..9dfebd3453 100644 --- a/sentry/src/main/java/io/sentry/TransactionContext.java +++ b/sentry/src/main/java/io/sentry/TransactionContext.java @@ -9,13 +9,15 @@ import org.jetbrains.annotations.Nullable; public final class TransactionContext extends SpanContext { - public static final @NotNull String DEFAULT_TRANSACTION_NAME = ""; + private static final @NotNull String DEFAULT_NAME = ""; private static final @NotNull TransactionNameSource DEFAULT_NAME_SOURCE = TransactionNameSource.CUSTOM; private static final @NotNull String DEFAULT_OPERATION = "default"; private @NotNull String name; private @NotNull TransactionNameSource transactionNameSource; private @Nullable TracesSamplingDecision parentSamplingDecision; + private @Nullable Baggage baggage; + private @NotNull Instrumenter instrumenter = Instrumenter.SENTRY; private boolean isForNextAppStart = false; /** @@ -134,7 +136,7 @@ public TransactionContext( final @Nullable TracesSamplingDecision parentSamplingDecision, final @Nullable Baggage baggage) { super(traceId, spanId, DEFAULT_OPERATION, parentSpanId, null); - this.name = DEFAULT_TRANSACTION_NAME; + this.name = DEFAULT_NAME; this.parentSamplingDecision = parentSamplingDecision; this.transactionNameSource = DEFAULT_NAME_SOURCE; this.baggage = baggage; @@ -156,6 +158,10 @@ public TransactionContext( return parentSamplingDecision; } + public @Nullable Baggage getBaggage() { + return baggage; + } + public void setParentSampled(final @Nullable Boolean parentSampled) { if (parentSampled == null) { this.parentSamplingDecision = null; @@ -180,6 +186,14 @@ public void setParentSampled( return transactionNameSource; } + public @NotNull Instrumenter getInstrumenter() { + return instrumenter; + } + + public void setInstrumenter(final @NotNull Instrumenter instrumenter) { + this.instrumenter = instrumenter; + } + public void setName(final @NotNull String name) { this.name = Objects.requireNonNull(name, "name is required"); } diff --git a/sentry/src/main/java/io/sentry/TransactionOptions.java b/sentry/src/main/java/io/sentry/TransactionOptions.java index 782e35a039..6d0eac8b7b 100644 --- a/sentry/src/main/java/io/sentry/TransactionOptions.java +++ b/sentry/src/main/java/io/sentry/TransactionOptions.java @@ -1,7 +1,6 @@ package io.sentry; import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** Sentry Transaction options */ @@ -18,6 +17,9 @@ public final class TransactionOptions extends SpanOptions { /** Defines if transaction should be bound to scope */ private boolean bindToScope = false; + /** The start timestamp of the transaction */ + private @Nullable SentryDate startTimestamp = null; + /** Defines if transaction refers to the app start process */ private boolean isAppStartTransaction = false; @@ -54,9 +56,6 @@ public final class TransactionOptions extends SpanOptions { */ private @Nullable TransactionFinishedCallback transactionFinishedCallback = null; - /** Span factory to use. Uses factory configured in {@link SentryOptions} if `null`. */ - @ApiStatus.Internal private @Nullable ISpanFactory spanFactory = null; - /** * Gets the customSamplingContext * @@ -93,6 +92,24 @@ 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; + } + /** * Checks if waitForChildren is enabled * @@ -179,14 +196,4 @@ public void setAppStartTransaction(final boolean appStartTransaction) { public boolean isAppStartTransaction() { return isAppStartTransaction; } - - @ApiStatus.Internal - public @Nullable ISpanFactory getSpanFactory() { - return this.spanFactory; - } - - @ApiStatus.Internal - public void setSpanFactory(final @NotNull ISpanFactory spanFactory) { - this.spanFactory = spanFactory; - } } diff --git a/sentry/src/main/java/io/sentry/TypeCheckHint.java b/sentry/src/main/java/io/sentry/TypeCheckHint.java index cbe784db5b..e9d219c385 100644 --- a/sentry/src/main/java/io/sentry/TypeCheckHint.java +++ b/sentry/src/main/java/io/sentry/TypeCheckHint.java @@ -55,9 +55,6 @@ public final class TypeCheckHint { /** Used for GraphQl handler exceptions. */ public static final String GRAPHQL_HANDLER_PARAMETERS = "graphql:handlerParameters"; - /** Used for GraphQl data fetcher breadcrumbs. */ - public static final String GRAPHQL_DATA_FETCHING_ENVIRONMENT = "graphql:dataFetchingEnvironment"; - /** Used for JUL breadcrumbs. */ public static final String JUL_LOG_RECORD = "jul:logRecord"; diff --git a/sentry/src/main/java/io/sentry/UncaughtExceptionHandlerIntegration.java b/sentry/src/main/java/io/sentry/UncaughtExceptionHandlerIntegration.java index a30639a79c..33e1a4a815 100644 --- a/sentry/src/main/java/io/sentry/UncaughtExceptionHandlerIntegration.java +++ b/sentry/src/main/java/io/sentry/UncaughtExceptionHandlerIntegration.java @@ -28,7 +28,7 @@ public final class UncaughtExceptionHandlerIntegration /** Reference to the pre-existing uncaught exception handler. */ private @Nullable Thread.UncaughtExceptionHandler defaultExceptionHandler; - private @Nullable IScopes scopes; + private @Nullable IHub hub; private @Nullable SentryOptions options; private boolean registered = false; @@ -43,7 +43,7 @@ public UncaughtExceptionHandlerIntegration() { } @Override - public final void register(final @NotNull IScopes scopes, final @NotNull SentryOptions options) { + public final void register(final @NotNull IHub hub, final @NotNull SentryOptions options) { if (registered) { options .getLogger() @@ -54,7 +54,7 @@ public final void register(final @NotNull IScopes scopes, final @NotNull SentryO } registered = true; - this.scopes = Objects.requireNonNull(scopes, "Scopes are required"); + this.hub = Objects.requireNonNull(hub, "Hub is required"); this.options = Objects.requireNonNull(options, "SentryOptions is required"); this.options @@ -75,14 +75,7 @@ public final void register(final @NotNull IScopes scopes, final @NotNull SentryO "default UncaughtExceptionHandler class='" + currentHandler.getClass().getName() + "'"); - - if (currentHandler instanceof UncaughtExceptionHandlerIntegration) { - final UncaughtExceptionHandlerIntegration currentHandlerIntegration = - (UncaughtExceptionHandlerIntegration) currentHandler; - defaultExceptionHandler = currentHandlerIntegration.defaultExceptionHandler; - } else { - defaultExceptionHandler = currentHandler; - } + defaultExceptionHandler = currentHandler; } threadAdapter.setDefaultUncaughtExceptionHandler(this); @@ -96,7 +89,7 @@ public final void register(final @NotNull IScopes scopes, final @NotNull SentryO @Override public void uncaughtException(Thread thread, Throwable thrown) { - if (options != null && scopes != null) { + if (options != null && hub != null) { options.getLogger().log(SentryLevel.INFO, "Uncaught exception received."); try { @@ -106,14 +99,14 @@ public void uncaughtException(Thread thread, Throwable thrown) { final SentryEvent event = new SentryEvent(throwable); event.setLevel(SentryLevel.FATAL); - final ITransaction transaction = scopes.getTransaction(); + final ITransaction transaction = hub.getTransaction(); if (transaction == null && event.getEventId() != null) { // if there's no active transaction on scope, this event can trigger flush notification exceptionHint.setFlushable(event.getEventId()); } final Hint hint = HintUtils.createWithTypeCheckHint(exceptionHint); - final @NotNull SentryId sentryId = scopes.captureEvent(event, hint); + final @NotNull SentryId sentryId = hub.captureEvent(event, hint); final boolean isEventDropped = sentryId.equals(SentryId.EMPTY_ID); final EventDropReason eventDropReason = HintUtils.getEventDropReason(hint); // in case the event has been dropped by multithreaded deduplicator, the other threads will diff --git a/sentry/src/main/java/io/sentry/backpressure/BackpressureMonitor.java b/sentry/src/main/java/io/sentry/backpressure/BackpressureMonitor.java index 6541a7586a..2008a38c76 100644 --- a/sentry/src/main/java/io/sentry/backpressure/BackpressureMonitor.java +++ b/sentry/src/main/java/io/sentry/backpressure/BackpressureMonitor.java @@ -1,6 +1,6 @@ package io.sentry.backpressure; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.ISentryExecutorService; import io.sentry.SentryLevel; import io.sentry.SentryOptions; @@ -12,13 +12,12 @@ public final class BackpressureMonitor implements IBackpressureMonitor, Runnable private static final int CHECK_INTERVAL_IN_MS = 10 * 1000; private final @NotNull SentryOptions sentryOptions; - private final @NotNull IScopes scopes; + private final @NotNull IHub hub; private int downsampleFactor = 0; - public BackpressureMonitor( - final @NotNull SentryOptions sentryOptions, final @NotNull IScopes scopes) { + public BackpressureMonitor(final @NotNull SentryOptions sentryOptions, final @NotNull IHub hub) { this.sentryOptions = sentryOptions; - this.scopes = scopes; + this.hub = hub; } @Override @@ -67,6 +66,6 @@ private void reschedule(final int delay) { } private boolean isHealthy() { - return scopes.isHealthy(); + return hub.isHealthy(); } } diff --git a/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java b/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java index 796a17cb3c..f51287d7d9 100644 --- a/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java +++ b/sentry/src/main/java/io/sentry/clientreport/ClientReportRecorder.java @@ -7,8 +7,6 @@ import io.sentry.SentryItemType; import io.sentry.SentryLevel; import io.sentry.SentryOptions; -import io.sentry.protocol.SentrySpan; -import io.sentry.protocol.SentryTransaction; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -86,19 +84,8 @@ public void recordLostEnvelopeItem( .log(SentryLevel.ERROR, "Unable to restore counts from previous client report."); } } else { - final @NotNull DataCategory itemCategory = categoryFromItemType(itemType); - if (itemCategory.equals(DataCategory.Transaction)) { - final @Nullable SentryTransaction transaction = - envelopeItem.getTransaction(options.getSerializer()); - if (transaction != null) { - final @NotNull List spans = transaction.getSpans(); - // When a transaction is dropped, we also record its spans as dropped plus one, - // since Relay extracts an additional span from the transaction. - recordLostEventInternal( - reason.getReason(), DataCategory.Span.getCategory(), spans.size() + 1L); - } - } - recordLostEventInternal(reason.getReason(), itemCategory.getCategory(), 1L); + recordLostEventInternal( + reason.getReason(), categoryFromItemType(itemType).getCategory(), 1L); } } catch (Throwable e) { options.getLogger().log(SentryLevel.ERROR, e, "Unable to record lost envelope item."); @@ -107,14 +94,8 @@ public void recordLostEnvelopeItem( @Override public void recordLostEvent(@NotNull DiscardReason reason, @NotNull DataCategory category) { - recordLostEvent(reason, category, 1); - } - - @Override - public void recordLostEvent( - @NotNull DiscardReason reason, @NotNull DataCategory category, long count) { try { - recordLostEventInternal(reason.getReason(), category.getCategory(), count); + recordLostEventInternal(reason.getReason(), category.getCategory(), 1L); } catch (Throwable e) { options.getLogger().log(SentryLevel.ERROR, e, "Unable to record lost event."); } diff --git a/sentry/src/main/java/io/sentry/clientreport/IClientReportRecorder.java b/sentry/src/main/java/io/sentry/clientreport/IClientReportRecorder.java index c3c2d2e9f6..72e23f5b8f 100644 --- a/sentry/src/main/java/io/sentry/clientreport/IClientReportRecorder.java +++ b/sentry/src/main/java/io/sentry/clientreport/IClientReportRecorder.java @@ -16,8 +16,6 @@ void recordLostEnvelopeItem( void recordLostEvent(@NotNull DiscardReason reason, @NotNull DataCategory category); - void recordLostEvent(@NotNull DiscardReason reason, @NotNull DataCategory category, long count); - @NotNull SentryEnvelope attachReportToEnvelope(@NotNull SentryEnvelope envelope); } diff --git a/sentry/src/main/java/io/sentry/clientreport/NoOpClientReportRecorder.java b/sentry/src/main/java/io/sentry/clientreport/NoOpClientReportRecorder.java index 360468e19d..64aa957680 100644 --- a/sentry/src/main/java/io/sentry/clientreport/NoOpClientReportRecorder.java +++ b/sentry/src/main/java/io/sentry/clientreport/NoOpClientReportRecorder.java @@ -25,12 +25,6 @@ public void recordLostEvent(@NotNull DiscardReason reason, @NotNull DataCategory // do nothing } - @Override - public void recordLostEvent( - @NotNull DiscardReason reason, @NotNull DataCategory category, long count) { - // do nothing - } - @Override public @NotNull SentryEnvelope attachReportToEnvelope(@NotNull SentryEnvelope envelope) { return envelope; diff --git a/sentry/src/main/java/io/sentry/instrumentation/file/FileIOSpanManager.java b/sentry/src/main/java/io/sentry/instrumentation/file/FileIOSpanManager.java index 52963413b6..956996ce04 100644 --- a/sentry/src/main/java/io/sentry/instrumentation/file/FileIOSpanManager.java +++ b/sentry/src/main/java/io/sentry/instrumentation/file/FileIOSpanManager.java @@ -1,6 +1,6 @@ package io.sentry.instrumentation.file; -import io.sentry.IScopes; +import io.sentry.IHub; import io.sentry.ISpan; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryOptions; @@ -28,8 +28,8 @@ final class FileIOSpanManager { private final @NotNull SentryStackTraceFactory stackTraceFactory; - static @Nullable ISpan startSpan(final @NotNull IScopes scopes, final @NotNull String op) { - final ISpan parent = Platform.isAndroid() ? scopes.getTransaction() : scopes.getSpan(); + static @Nullable ISpan startSpan(final @NotNull IHub hub, final @NotNull String op) { + final ISpan parent = Platform.isAndroid() ? hub.getTransaction() : hub.getSpan(); return parent != null ? parent.startChild(op) : null; } diff --git a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileInputStream.java b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileInputStream.java index ea7d7f09a5..04bb87ae7c 100644 --- a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileInputStream.java +++ b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileInputStream.java @@ -1,8 +1,8 @@ package io.sentry.instrumentation.file; -import io.sentry.IScopes; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ISpan; -import io.sentry.ScopesAdapter; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -25,24 +25,24 @@ public final class SentryFileInputStream extends FileInputStream { private final @NotNull FileIOSpanManager spanManager; public SentryFileInputStream(final @Nullable String name) throws FileNotFoundException { - this(name != null ? new File(name) : null, ScopesAdapter.getInstance()); + this(name != null ? new File(name) : null, HubAdapter.getInstance()); } public SentryFileInputStream(final @Nullable File file) throws FileNotFoundException { - this(file, ScopesAdapter.getInstance()); + this(file, HubAdapter.getInstance()); } public SentryFileInputStream(final @NotNull FileDescriptor fdObj) { - this(fdObj, ScopesAdapter.getInstance()); + this(fdObj, HubAdapter.getInstance()); } - SentryFileInputStream(final @Nullable File file, final @NotNull IScopes scopes) + SentryFileInputStream(final @Nullable File file, final @NotNull IHub hub) throws FileNotFoundException { - this(init(file, null, scopes)); + this(init(file, null, hub)); } - SentryFileInputStream(final @NotNull FileDescriptor fdObj, final @NotNull IScopes scopes) { - this(init(fdObj, null, scopes), fdObj); + SentryFileInputStream(final @NotNull FileDescriptor fdObj, final @NotNull IHub hub) { + this(init(fdObj, null, hub), fdObj); } private SentryFileInputStream( @@ -60,24 +60,24 @@ private SentryFileInputStream(final @NotNull FileInputStreamInitData data) } private static FileInputStreamInitData init( - final @Nullable File file, @Nullable FileInputStream delegate, final @NotNull IScopes scopes) + final @Nullable File file, @Nullable FileInputStream delegate, final @NotNull IHub hub) throws FileNotFoundException { - final ISpan span = FileIOSpanManager.startSpan(scopes, "file.read"); + final ISpan span = FileIOSpanManager.startSpan(hub, "file.read"); if (delegate == null) { delegate = new FileInputStream(file); } - return new FileInputStreamInitData(file, span, delegate, scopes.getOptions()); + return new FileInputStreamInitData(file, span, delegate, hub.getOptions()); } private static FileInputStreamInitData init( final @NotNull FileDescriptor fd, @Nullable FileInputStream delegate, - final @NotNull IScopes scopes) { - final ISpan span = FileIOSpanManager.startSpan(scopes, "file.read"); + final @NotNull IHub hub) { + final ISpan span = FileIOSpanManager.startSpan(hub, "file.read"); if (delegate == null) { delegate = new FileInputStream(fd); } - return new FileInputStreamInitData(null, span, delegate, scopes.getOptions()); + return new FileInputStreamInitData(null, span, delegate, hub.getOptions()); } @Override @@ -128,27 +128,25 @@ public static FileInputStream create( final @NotNull FileInputStream delegate, final @Nullable String name) throws FileNotFoundException { return new SentryFileInputStream( - init(name != null ? new File(name) : null, delegate, ScopesAdapter.getInstance())); + init(name != null ? new File(name) : null, delegate, HubAdapter.getInstance())); } public static FileInputStream create( final @NotNull FileInputStream delegate, final @Nullable File file) throws FileNotFoundException { - return new SentryFileInputStream(init(file, delegate, ScopesAdapter.getInstance())); + return new SentryFileInputStream(init(file, delegate, HubAdapter.getInstance())); } public static FileInputStream create( final @NotNull FileInputStream delegate, final @NotNull FileDescriptor descriptor) { return new SentryFileInputStream( - init(descriptor, delegate, ScopesAdapter.getInstance()), descriptor); + init(descriptor, delegate, HubAdapter.getInstance()), descriptor); } static FileInputStream create( - final @NotNull FileInputStream delegate, - final @Nullable File file, - final @NotNull IScopes scopes) + final @NotNull FileInputStream delegate, final @Nullable File file, final @NotNull IHub hub) throws FileNotFoundException { - return new SentryFileInputStream(init(file, delegate, scopes)); + return new SentryFileInputStream(init(file, delegate, hub)); } } } diff --git a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileOutputStream.java b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileOutputStream.java index 4ef5022e1c..9424710d71 100644 --- a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileOutputStream.java +++ b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileOutputStream.java @@ -1,8 +1,8 @@ package io.sentry.instrumentation.file; -import io.sentry.IScopes; +import io.sentry.HubAdapter; +import io.sentry.IHub; import io.sentry.ISpan; -import io.sentry.ScopesAdapter; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; @@ -24,31 +24,30 @@ public final class SentryFileOutputStream extends FileOutputStream { private final @NotNull FileIOSpanManager spanManager; public SentryFileOutputStream(final @Nullable String name) throws FileNotFoundException { - this(name != null ? new File(name) : null, false, ScopesAdapter.getInstance()); + this(name != null ? new File(name) : null, false, HubAdapter.getInstance()); } public SentryFileOutputStream(final @Nullable String name, final boolean append) throws FileNotFoundException { - this(init(name != null ? new File(name) : null, append, null, ScopesAdapter.getInstance())); + this(init(name != null ? new File(name) : null, append, null, HubAdapter.getInstance())); } public SentryFileOutputStream(final @Nullable File file) throws FileNotFoundException { - this(file, false, ScopesAdapter.getInstance()); + this(file, false, HubAdapter.getInstance()); } public SentryFileOutputStream(final @Nullable File file, final boolean append) throws FileNotFoundException { - this(init(file, append, null, ScopesAdapter.getInstance())); + this(init(file, append, null, HubAdapter.getInstance())); } public SentryFileOutputStream(final @NotNull FileDescriptor fdObj) { - this(init(fdObj, null, ScopesAdapter.getInstance()), fdObj); + this(init(fdObj, null, HubAdapter.getInstance()), fdObj); } - SentryFileOutputStream( - final @Nullable File file, final boolean append, final @NotNull IScopes scopes) + SentryFileOutputStream(final @Nullable File file, final boolean append, final @NotNull IHub hub) throws FileNotFoundException { - this(init(file, append, null, scopes)); + this(init(file, append, null, hub)); } private SentryFileOutputStream( @@ -69,24 +68,22 @@ private static FileOutputStreamInitData init( final @Nullable File file, final boolean append, @Nullable FileOutputStream delegate, - @NotNull IScopes scopes) + @NotNull IHub hub) throws FileNotFoundException { - final ISpan span = FileIOSpanManager.startSpan(scopes, "file.write"); + final ISpan span = FileIOSpanManager.startSpan(hub, "file.write"); if (delegate == null) { delegate = new FileOutputStream(file, append); } - return new FileOutputStreamInitData(file, append, span, delegate, scopes.getOptions()); + return new FileOutputStreamInitData(file, append, span, delegate, hub.getOptions()); } private static FileOutputStreamInitData init( - final @NotNull FileDescriptor fd, - @Nullable FileOutputStream delegate, - @NotNull IScopes scopes) { - final ISpan span = FileIOSpanManager.startSpan(scopes, "file.write"); + final @NotNull FileDescriptor fd, @Nullable FileOutputStream delegate, @NotNull IHub hub) { + final ISpan span = FileIOSpanManager.startSpan(hub, "file.write"); if (delegate == null) { delegate = new FileOutputStream(fd); } - return new FileOutputStreamInitData(null, false, span, delegate, scopes.getOptions()); + return new FileOutputStreamInitData(null, false, span, delegate, hub.getOptions()); } @Override @@ -135,32 +132,31 @@ public static FileOutputStream create( final @NotNull FileOutputStream delegate, final @Nullable String name) throws FileNotFoundException { return new SentryFileOutputStream( - init(name != null ? new File(name) : null, false, delegate, ScopesAdapter.getInstance())); + init(name != null ? new File(name) : null, false, delegate, HubAdapter.getInstance())); } public static FileOutputStream create( final @NotNull FileOutputStream delegate, final @Nullable String name, final boolean append) throws FileNotFoundException { return new SentryFileOutputStream( - init( - name != null ? new File(name) : null, append, delegate, ScopesAdapter.getInstance())); + init(name != null ? new File(name) : null, append, delegate, HubAdapter.getInstance())); } public static FileOutputStream create( final @NotNull FileOutputStream delegate, final @Nullable File file) throws FileNotFoundException { - return new SentryFileOutputStream(init(file, false, delegate, ScopesAdapter.getInstance())); + return new SentryFileOutputStream(init(file, false, delegate, HubAdapter.getInstance())); } public static FileOutputStream create( final @NotNull FileOutputStream delegate, final @Nullable File file, final boolean append) throws FileNotFoundException { - return new SentryFileOutputStream(init(file, append, delegate, ScopesAdapter.getInstance())); + return new SentryFileOutputStream(init(file, append, delegate, HubAdapter.getInstance())); } public static FileOutputStream create( final @NotNull FileOutputStream delegate, final @NotNull FileDescriptor fdObj) { - return new SentryFileOutputStream(init(fdObj, delegate, ScopesAdapter.getInstance()), fdObj); + return new SentryFileOutputStream(init(fdObj, delegate, HubAdapter.getInstance()), fdObj); } } } diff --git a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileReader.java b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileReader.java index 38a83c7ff6..0a225e65a5 100644 --- a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileReader.java +++ b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileReader.java @@ -1,6 +1,6 @@ package io.sentry.instrumentation.file; -import io.sentry.IScopes; +import io.sentry.IHub; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; @@ -20,8 +20,7 @@ public SentryFileReader(final @NotNull FileDescriptor fd) { super(new SentryFileInputStream(fd)); } - SentryFileReader(final @NotNull File file, final @NotNull IScopes scopes) - throws FileNotFoundException { - super(new SentryFileInputStream(file, scopes)); + SentryFileReader(final @NotNull File file, final @NotNull IHub hub) throws FileNotFoundException { + super(new SentryFileInputStream(file, hub)); } } diff --git a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileWriter.java b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileWriter.java index 93c901ec6c..9588984612 100644 --- a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileWriter.java +++ b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileWriter.java @@ -1,6 +1,6 @@ package io.sentry.instrumentation.file; -import io.sentry.IScopes; +import io.sentry.IHub; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; @@ -30,8 +30,8 @@ public SentryFileWriter(final @NotNull FileDescriptor fd) { super(new SentryFileOutputStream(fd)); } - SentryFileWriter(final @NotNull File file, final boolean append, final @NotNull IScopes scopes) + SentryFileWriter(final @NotNull File file, final boolean append, final @NotNull IHub hub) throws FileNotFoundException { - super(new SentryFileOutputStream(file, append, scopes)); + super(new SentryFileOutputStream(file, append, hub)); } } diff --git a/sentry/src/main/java/io/sentry/internal/eventprocessor/EventProcessorAndOrder.java b/sentry/src/main/java/io/sentry/internal/eventprocessor/EventProcessorAndOrder.java deleted file mode 100644 index 1f504f2555..0000000000 --- a/sentry/src/main/java/io/sentry/internal/eventprocessor/EventProcessorAndOrder.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.sentry.internal.eventprocessor; - -import io.sentry.EventProcessor; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public final class EventProcessorAndOrder implements Comparable { - - private final @NotNull EventProcessor eventProcessor; - private final @NotNull Long order; - - public EventProcessorAndOrder( - final @NotNull EventProcessor eventProcessor, final @Nullable Long order) { - this.eventProcessor = eventProcessor; - if (order == null) { - this.order = System.nanoTime(); - } else { - this.order = order; - } - } - - public @NotNull EventProcessor getEventProcessor() { - return eventProcessor; - } - - public @NotNull Long getOrder() { - return order; - } - - @Override - public int compareTo(@NotNull EventProcessorAndOrder o) { - return order.compareTo(o.order); - } -} diff --git a/sentry/src/main/java/io/sentry/metrics/MetricsHelper.java b/sentry/src/main/java/io/sentry/metrics/MetricsHelper.java index c4353c53a3..46e22ca8ad 100644 --- a/sentry/src/main/java/io/sentry/metrics/MetricsHelper.java +++ b/sentry/src/main/java/io/sentry/metrics/MetricsHelper.java @@ -1,11 +1,11 @@ package io.sentry.metrics; import io.sentry.MeasurementUnit; -import java.security.SecureRandom; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Random; import java.util.regex.Pattern; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -27,7 +27,7 @@ public final class MetricsHelper { private static final char TAGS_ESCAPE_CHAR = '\\'; private static long FLUSH_SHIFT_MS = - (long) (new SecureRandom().nextFloat() * (ROLLUP_IN_SECONDS * 1000f)); + (long) (new Random().nextFloat() * (ROLLUP_IN_SECONDS * 1000f)); public static long getTimeBucketKey(final long timestampMs) { final long seconds = timestampMs / 1000; diff --git a/sentry/src/main/java/io/sentry/protocol/Contexts.java b/sentry/src/main/java/io/sentry/protocol/Contexts.java index 40a5914151..21be9fd8a5 100644 --- a/sentry/src/main/java/io/sentry/protocol/Contexts.java +++ b/sentry/src/main/java/io/sentry/protocol/Contexts.java @@ -1,6 +1,5 @@ package io.sentry.protocol; -import com.jakewharton.nopen.annotation.Open; import io.sentry.ILogger; import io.sentry.JsonDeserializer; import io.sentry.JsonObjectReader; @@ -12,21 +11,15 @@ import io.sentry.vendor.gson.stream.JsonToken; import java.io.IOException; import java.util.Collections; -import java.util.Enumeration; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -@Open -public class Contexts implements JsonSerializable { +public final class Contexts extends ConcurrentHashMap implements JsonSerializable { private static final long serialVersionUID = 252445813254943011L; - private final @NotNull ConcurrentHashMap internalStorage = - new ConcurrentHashMap<>(); - /** Response lock, Ops should be atomic */ private final @NotNull Object responseLock = new Object(); @@ -145,71 +138,6 @@ public void setResponse(final @NotNull Response response) { } } - public int size() { - // since this used to extend map - return internalStorage.size(); - } - - public int getSize() { - // for kotlin .size - return size(); - } - - public boolean isEmpty() { - return internalStorage.isEmpty(); - } - - public boolean containsKey(final @NotNull Object key) { - return internalStorage.containsKey(key); - } - - public @Nullable Object get(final @NotNull Object key) { - return internalStorage.get(key); - } - - public @Nullable Object put(final @NotNull String key, final @Nullable Object value) { - return internalStorage.put(key, value); - } - - public @Nullable Object set(final @NotNull String key, final @Nullable Object value) { - return put(key, value); - } - - public @Nullable Object remove(final @NotNull Object key) { - return internalStorage.remove(key); - } - - public @NotNull Enumeration keys() { - return internalStorage.keys(); - } - - public @NotNull Set> entrySet() { - return internalStorage.entrySet(); - } - - public void putAll(Map m) { - internalStorage.putAll(m); - } - - public void putAll(final @NotNull Contexts contexts) { - internalStorage.putAll(contexts.internalStorage); - } - - @Override - public boolean equals(Object obj) { - if (obj != null && obj instanceof Contexts) { - final @NotNull Contexts otherContexts = (Contexts) obj; - return internalStorage.equals(otherContexts.internalStorage); - } - - return false; - } - - @Override - public int hashCode() { - return internalStorage.hashCode(); - } - // region json @Override diff --git a/sentry/src/main/java/io/sentry/protocol/Mechanism.java b/sentry/src/main/java/io/sentry/protocol/Mechanism.java index 8fc9aedf77..648aed39c2 100644 --- a/sentry/src/main/java/io/sentry/protocol/Mechanism.java +++ b/sentry/src/main/java/io/sentry/protocol/Mechanism.java @@ -67,18 +67,6 @@ public final class Mechanism implements JsonUnknown, JsonSerializable { * for grouping or display purposes. */ private @Nullable Boolean synthetic; - /** - * Exception ID. Used. e.g. for exception groups to build a hierarchy. This is referenced as - * parent by child exceptions which for Java SDK means Throwable.getSuppressed(). - */ - private @Nullable Integer exceptionId; - /** Parent exception ID. Used e.g. for exception groups to build a hierarchy. */ - private @Nullable Integer parentId; - /** - * Whether this is a group of exceptions. For Java SDK this means there were suppressed - * exceptions. - */ - private @Nullable Boolean exceptionGroup; @SuppressWarnings("unused") private @Nullable Map unknown; @@ -152,30 +140,6 @@ public void setSynthetic(final @Nullable Boolean synthetic) { this.synthetic = synthetic; } - public @Nullable Integer getExceptionId() { - return exceptionId; - } - - public void setExceptionId(final @Nullable Integer exceptionId) { - this.exceptionId = exceptionId; - } - - public @Nullable Integer getParentId() { - return parentId; - } - - public void setParentId(final @Nullable Integer parentId) { - this.parentId = parentId; - } - - public @Nullable Boolean isExceptionGroup() { - return exceptionGroup; - } - - public void setExceptionGroup(final @Nullable Boolean exceptionGroup) { - this.exceptionGroup = exceptionGroup; - } - // JsonKeys public static final class JsonKeys { @@ -186,9 +150,6 @@ public static final class JsonKeys { public static final String META = "meta"; public static final String DATA = "data"; public static final String SYNTHETIC = "synthetic"; - public static final String EXCEPTION_ID = "exception_id"; - public static final String PARENT_ID = "parent_id"; - public static final String IS_EXCEPTION_GROUP = "is_exception_group"; } // JsonUnknown @@ -230,15 +191,6 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger if (synthetic != null) { writer.name(JsonKeys.SYNTHETIC).value(synthetic); } - if (exceptionId != null) { - writer.name(JsonKeys.EXCEPTION_ID).value(logger, exceptionId); - } - if (parentId != null) { - writer.name(JsonKeys.PARENT_ID).value(logger, parentId); - } - if (exceptionGroup != null) { - writer.name(JsonKeys.IS_EXCEPTION_GROUP).value(exceptionGroup); - } if (unknown != null) { for (String key : unknown.keySet()) { Object value = unknown.get(key); @@ -286,15 +238,6 @@ public static final class Deserializer implements JsonDeserializer { case JsonKeys.SYNTHETIC: mechanism.synthetic = reader.nextBooleanOrNull(); break; - case JsonKeys.EXCEPTION_ID: - mechanism.exceptionId = reader.nextIntegerOrNull(); - break; - case JsonKeys.PARENT_ID: - mechanism.parentId = reader.nextIntegerOrNull(); - break; - case JsonKeys.IS_EXCEPTION_GROUP: - mechanism.exceptionGroup = reader.nextBooleanOrNull(); - break; default: if (unknown == null) { unknown = new HashMap<>(); diff --git a/sentry/src/main/java/io/sentry/protocol/User.java b/sentry/src/main/java/io/sentry/protocol/User.java index 619d870c66..1dc6af7811 100644 --- a/sentry/src/main/java/io/sentry/protocol/User.java +++ b/sentry/src/main/java/io/sentry/protocol/User.java @@ -34,11 +34,7 @@ public final class User implements JsonUnknown, JsonSerializable { /** Username of the user. */ private @Nullable String username; - /** - * @deprecated has no effect and will be removed in the next major update. Use a custom tag or - * context instead. - */ - @Deprecated private @Nullable String segment; + private @Nullable String segment; /** Remote IP address of the user. */ private @Nullable String ipAddress; @@ -228,9 +224,7 @@ public void setUsername(final @Nullable String username) { * Gets the segment of the user. * * @return the user segment. - * @deprecated has no effect and will be removed in the next major update. */ - @Deprecated public @Nullable String getSegment() { return segment; } @@ -239,9 +233,7 @@ public void setUsername(final @Nullable String username) { * Sets the segment of the user. * * @param segment the segment. - * @deprecated has no effect and will be removed in the next major update. */ - @Deprecated public void setSegment(final @Nullable String segment) { this.segment = segment; } diff --git a/sentry/src/main/java/io/sentry/util/CheckInUtils.java b/sentry/src/main/java/io/sentry/util/CheckInUtils.java index 3f13064cbb..e15603adaf 100644 --- a/sentry/src/main/java/io/sentry/util/CheckInUtils.java +++ b/sentry/src/main/java/io/sentry/util/CheckInUtils.java @@ -3,8 +3,7 @@ import io.sentry.CheckIn; import io.sentry.CheckInStatus; import io.sentry.DateUtils; -import io.sentry.IScopes; -import io.sentry.ISentryLifecycleToken; +import io.sentry.IHub; import io.sentry.MonitorConfig; import io.sentry.Sentry; import io.sentry.protocol.SentryId; @@ -31,30 +30,29 @@ public static U withCheckIn( final @Nullable MonitorConfig monitorConfig, final @NotNull Callable callable) throws Exception { - try (final @NotNull ISentryLifecycleToken ignored = - Sentry.forkedScopes("CheckInUtils").makeCurrent()) { - final @NotNull IScopes scopes = Sentry.getCurrentScopes(); - final long startTime = System.currentTimeMillis(); - boolean didError = false; + final @NotNull IHub hub = Sentry.getCurrentHub(); + final long startTime = System.currentTimeMillis(); + boolean didError = false; - TracingUtils.startNewTrace(scopes); + hub.pushScope(); + TracingUtils.startNewTrace(hub); - CheckIn inProgressCheckIn = new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS); - if (monitorConfig != null) { - inProgressCheckIn.setMonitorConfig(monitorConfig); - } - @Nullable SentryId checkInId = scopes.captureCheckIn(inProgressCheckIn); - try { - return callable.call(); - } catch (Throwable t) { - didError = true; - throw t; - } finally { - final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK; - CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); - checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); - scopes.captureCheckIn(checkIn); - } + CheckIn inProgressCheckIn = new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS); + if (monitorConfig != null) { + inProgressCheckIn.setMonitorConfig(monitorConfig); + } + @Nullable SentryId checkInId = hub.captureCheckIn(inProgressCheckIn); + try { + return callable.call(); + } catch (Throwable t) { + didError = true; + throw t; + } finally { + final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK; + CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status); + checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime)); + hub.captureCheckIn(checkIn); + hub.popScope(); } } diff --git a/sentry/src/main/java/io/sentry/util/EventProcessorUtils.java b/sentry/src/main/java/io/sentry/util/EventProcessorUtils.java deleted file mode 100644 index 8fb73982c0..0000000000 --- a/sentry/src/main/java/io/sentry/util/EventProcessorUtils.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.sentry.util; - -import io.sentry.EventProcessor; -import io.sentry.internal.eventprocessor.EventProcessorAndOrder; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import org.jetbrains.annotations.Nullable; - -public final class EventProcessorUtils { - - public static List unwrap( - final @Nullable List orderedEventProcessor) { - final List eventProcessors = new ArrayList<>(); - - if (orderedEventProcessor != null) { - for (EventProcessorAndOrder eventProcessorAndOrder : orderedEventProcessor) { - eventProcessors.add(eventProcessorAndOrder.getEventProcessor()); - } - } - - return new CopyOnWriteArrayList<>(eventProcessors); - } -} diff --git a/sentry/src/main/java/io/sentry/util/LifecycleHelper.java b/sentry/src/main/java/io/sentry/util/LifecycleHelper.java deleted file mode 100644 index 4a029f620c..0000000000 --- a/sentry/src/main/java/io/sentry/util/LifecycleHelper.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.sentry.util; - -import io.sentry.ISentryLifecycleToken; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public final class LifecycleHelper { - - public static void close(final @Nullable Object tokenObject) { - if (tokenObject != null && tokenObject instanceof ISentryLifecycleToken) { - final @NotNull ISentryLifecycleToken token = (ISentryLifecycleToken) tokenObject; - token.close(); - } - } -} diff --git a/sentry/src/main/java/io/sentry/util/LoadClass.java b/sentry/src/main/java/io/sentry/util/LoadClass.java deleted file mode 100644 index 11fef9ea01..0000000000 --- a/sentry/src/main/java/io/sentry/util/LoadClass.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.sentry.util; - -import com.jakewharton.nopen.annotation.Open; -import io.sentry.ILogger; -import io.sentry.SentryLevel; -import io.sentry.SentryOptions; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** An Adapter for making Class.forName testable */ -@Open -public class LoadClass { - - /** - * Try to load a class via reflection - * - * @param clazz the full class name - * @param logger an instance of ILogger - * @return a Class<?> if it's available, or null - */ - public @Nullable Class loadClass(final @NotNull String clazz, final @Nullable ILogger logger) { - try { - return Class.forName(clazz); - } catch (ClassNotFoundException e) { - if (logger != null) { - logger.log(SentryLevel.DEBUG, "Class not available:" + clazz, e); - } - } catch (UnsatisfiedLinkError e) { - if (logger != null) { - logger.log(SentryLevel.ERROR, "Failed to load (UnsatisfiedLinkError) " + clazz, e); - } - } catch (Throwable e) { - if (logger != null) { - logger.log(SentryLevel.ERROR, "Failed to initialize " + clazz, e); - } - } - return null; - } - - public boolean isClassAvailable(final @NotNull String clazz, final @Nullable ILogger logger) { - return loadClass(clazz, logger) != null; - } - - public boolean isClassAvailable( - final @NotNull String clazz, final @Nullable SentryOptions options) { - return isClassAvailable(clazz, options != null ? options.getLogger() : null); - } -} diff --git a/sentry/src/main/java/io/sentry/util/SpanUtils.java b/sentry/src/main/java/io/sentry/util/SpanUtils.java deleted file mode 100644 index 6c6a83182f..0000000000 --- a/sentry/src/main/java/io/sentry/util/SpanUtils.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.sentry.util; - -import java.util.ArrayList; -import java.util.List; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public final class SpanUtils { - - /** - * A list of span origins that are ignored by default when using OpenTelemetry. - * - * @return a list of span origins to be ignored - */ - public static @NotNull List ignoredSpanOriginsForOpenTelemetry() { - final @NotNull List origins = new ArrayList<>(); - - origins.add("auto.http.spring_jakarta.webmvc"); - origins.add("auto.http.spring.webmvc"); - origins.add("auto.spring_jakarta.webflux"); - origins.add("auto.spring.webflux"); - origins.add("auto.http.spring_jakarta.webclient"); - origins.add("auto.http.spring.webclient"); - origins.add("auto.http.spring_jakarta.restclient"); - origins.add("auto.http.spring.restclient"); - origins.add("auto.http.spring_jakarta.resttemplate"); - origins.add("auto.http.spring.resttemplate"); - origins.add("auto.http.openfeign"); - origins.add("auto.graphql.graphql"); - origins.add("auto.db.jdbc"); - - return origins; - } - - /** Checks if a span origin has been ignored. */ - @ApiStatus.Internal - public static boolean isIgnored( - final @Nullable List ignoredOrigins, final @Nullable String origin) { - if (origin == null || ignoredOrigins == null || ignoredOrigins.isEmpty()) { - return false; - } - - for (final String ignoredOrigin : ignoredOrigins) { - if (ignoredOrigin.equalsIgnoreCase(origin)) { - return true; - } - - try { - if (origin.matches(ignoredOrigin)) { - return true; - } - } catch (Throwable t) { - // ignore invalid regex - } - } - - return false; - } -} diff --git a/sentry/src/main/java/io/sentry/util/TracingUtils.java b/sentry/src/main/java/io/sentry/util/TracingUtils.java index 67f6459660..2aeb613f2d 100644 --- a/sentry/src/main/java/io/sentry/util/TracingUtils.java +++ b/sentry/src/main/java/io/sentry/util/TracingUtils.java @@ -2,8 +2,8 @@ import io.sentry.Baggage; import io.sentry.BaggageHeader; +import io.sentry.IHub; import io.sentry.IScope; -import io.sentry.IScopes; import io.sentry.ISpan; import io.sentry.PropagationContext; import io.sentry.SentryOptions; @@ -14,8 +14,8 @@ public final class TracingUtils { - public static void startNewTrace(final @NotNull IScopes scopes) { - scopes.configureScope( + public static void startNewTrace(final @NotNull IHub hub) { + hub.configureScope( scope -> { scope.withPropagationContext( propagationContext -> { @@ -25,30 +25,30 @@ public static void startNewTrace(final @NotNull IScopes scopes) { } public static @Nullable TracingHeaders traceIfAllowed( - final @NotNull IScopes scopes, + final @NotNull IHub hub, final @NotNull String requestUrl, @Nullable List thirdPartyBaggageHeaders, final @Nullable ISpan span) { - final @NotNull SentryOptions sentryOptions = scopes.getOptions(); + final @NotNull SentryOptions sentryOptions = hub.getOptions(); if (sentryOptions.isTraceSampling() && shouldAttachTracingHeaders(requestUrl, sentryOptions)) { - return trace(scopes, thirdPartyBaggageHeaders, span); + return trace(hub, thirdPartyBaggageHeaders, span); } return null; } public static @Nullable TracingHeaders trace( - final @NotNull IScopes scopes, + final @NotNull IHub hub, @Nullable List thirdPartyBaggageHeaders, final @Nullable ISpan span) { - final @NotNull SentryOptions sentryOptions = scopes.getOptions(); + final @NotNull SentryOptions sentryOptions = hub.getOptions(); if (span != null && !span.isNoOp()) { return new TracingHeaders( span.toSentryTrace(), span.toBaggageHeader(thirdPartyBaggageHeaders)); } else { final @NotNull PropagationContextHolder returnValue = new PropagationContextHolder(); - scopes.configureScope( + hub.configureScope( (scope) -> { returnValue.propagationContext = maybeUpdateBaggage(scope, sentryOptions); }); diff --git a/sentry/src/test/java/io/sentry/CombinedContextsViewTest.kt b/sentry/src/test/java/io/sentry/CombinedContextsViewTest.kt deleted file mode 100644 index 624ca2417d..0000000000 --- a/sentry/src/test/java/io/sentry/CombinedContextsViewTest.kt +++ /dev/null @@ -1,568 +0,0 @@ -package io.sentry - -import io.sentry.protocol.App -import io.sentry.protocol.Browser -import io.sentry.protocol.Contexts -import io.sentry.protocol.Device -import io.sentry.protocol.Gpu -import io.sentry.protocol.OperatingSystem -import io.sentry.protocol.Response -import io.sentry.protocol.SentryRuntime -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertNotNull -import kotlin.test.assertNull -import kotlin.test.assertTrue - -class CombinedContextsViewTest { - - private class Fixture { - lateinit var current: Contexts - lateinit var isolation: Contexts - lateinit var global: Contexts - - fun getSut(): CombinedContextsView { - current = Contexts() - isolation = Contexts() - global = Contexts() - - return CombinedContextsView(global, isolation, current, ScopeType.ISOLATION) - } - } - - private val fixture = Fixture() - - @Test - fun `uses default context CURRENT`() { - fixture.getSut() - val combined = CombinedContextsView(fixture.global, fixture.isolation, fixture.current, ScopeType.CURRENT) - combined.trace = SpanContext("some") - assertEquals("some", fixture.current.trace?.op) - } - - @Test - fun `uses default context ISOLATION`() { - fixture.getSut() - val combined = CombinedContextsView(fixture.global, fixture.isolation, fixture.current, ScopeType.ISOLATION) - combined.trace = SpanContext("some") - assertEquals("some", fixture.isolation.trace?.op) - } - - @Test - fun `uses default context GLOBAL`() { - fixture.getSut() - val combined = CombinedContextsView(fixture.global, fixture.isolation, fixture.current, ScopeType.GLOBAL) - combined.trace = SpanContext("some") - assertEquals("some", fixture.global.trace?.op) - } - - @Test - fun `prefers trace from current context`() { - val combined = fixture.getSut() - fixture.current.trace = SpanContext("current") - fixture.isolation.trace = SpanContext("isolation") - fixture.global.trace = SpanContext("global") - - assertEquals("current", combined.trace?.op) - } - - @Test - fun `uses isolation trace if current context does not have it`() { - val combined = fixture.getSut() - fixture.isolation.trace = SpanContext("isolation") - fixture.global.trace = SpanContext("global") - - assertEquals("isolation", combined.trace?.op) - } - - @Test - fun `uses global trace if current and isolation context do not have it`() { - val combined = fixture.getSut() - fixture.global.trace = SpanContext("global") - - assertEquals("global", combined.trace?.op) - } - - @Test - fun `sets trace on default context`() { - val combined = fixture.getSut() - combined.trace = SpanContext("some") - - assertNull(fixture.current.trace) - assertEquals("some", fixture.isolation.trace?.op) - assertNull(fixture.global.trace) - } - - @Test - fun `prefers app from current context`() { - val combined = fixture.getSut() - fixture.current.setApp(App().also { it.appName = "current" }) - fixture.isolation.setApp(App().also { it.appName = "isolation" }) - fixture.global.setApp(App().also { it.appName = "global" }) - - assertEquals("current", combined.app?.appName) - } - - @Test - fun `uses isolation app if current context does not have it`() { - val combined = fixture.getSut() - fixture.isolation.setApp(App().also { it.appName = "isolation" }) - fixture.global.setApp(App().also { it.appName = "global" }) - - assertEquals("isolation", combined.app?.appName) - } - - @Test - fun `uses global app if current and isolation context do not have it`() { - val combined = fixture.getSut() - fixture.global.setApp(App().also { it.appName = "global" }) - - assertEquals("global", combined.app?.appName) - } - - @Test - fun `sets app on default context`() { - val combined = fixture.getSut() - combined.setApp(App().also { it.appName = "some" }) - - assertNull(fixture.current.app) - assertEquals("some", fixture.isolation.app?.appName) - assertNull(fixture.global.app) - } - - @Test - fun `prefers browser from current context`() { - val combined = fixture.getSut() - fixture.current.setBrowser(Browser().also { it.name = "current" }) - fixture.isolation.setBrowser(Browser().also { it.name = "isolation" }) - fixture.global.setBrowser(Browser().also { it.name = "global" }) - - assertEquals("current", combined.browser?.name) - } - - @Test - fun `uses isolation browser if current context does not have it`() { - val combined = fixture.getSut() - fixture.isolation.setBrowser(Browser().also { it.name = "isolation" }) - fixture.global.setBrowser(Browser().also { it.name = "global" }) - - assertEquals("isolation", combined.browser?.name) - } - - @Test - fun `uses global browser if current and isolation context do not have it`() { - val combined = fixture.getSut() - fixture.global.setBrowser(Browser().also { it.name = "global" }) - - assertEquals("global", combined.browser?.name) - } - - @Test - fun `sets browser on default context`() { - val combined = fixture.getSut() - combined.setBrowser(Browser().also { it.name = "some" }) - - assertNull(fixture.current.browser) - assertEquals("some", fixture.isolation.browser?.name) - assertNull(fixture.global.browser) - } - - @Test - fun `prefers device from current context`() { - val combined = fixture.getSut() - fixture.current.setDevice(Device().also { it.name = "current" }) - fixture.isolation.setDevice(Device().also { it.name = "isolation" }) - fixture.global.setDevice(Device().also { it.name = "global" }) - - assertEquals("current", combined.device?.name) - } - - @Test - fun `uses isolation device if current context does not have it`() { - val combined = fixture.getSut() - fixture.isolation.setDevice(Device().also { it.name = "isolation" }) - fixture.global.setDevice(Device().also { it.name = "global" }) - - assertEquals("isolation", combined.device?.name) - } - - @Test - fun `uses global device if current and isolation context do not have it`() { - val combined = fixture.getSut() - fixture.global.setDevice(Device().also { it.name = "global" }) - - assertEquals("global", combined.device?.name) - } - - @Test - fun `sets device on default context`() { - val combined = fixture.getSut() - combined.setDevice(Device().also { it.name = "some" }) - - assertNull(fixture.current.device) - assertEquals("some", fixture.isolation.device?.name) - assertNull(fixture.global.device) - } - - @Test - fun `prefers operatingSystem from current context`() { - val combined = fixture.getSut() - fixture.current.setOperatingSystem(OperatingSystem().also { it.name = "current" }) - fixture.isolation.setOperatingSystem(OperatingSystem().also { it.name = "isolation" }) - fixture.global.setOperatingSystem(OperatingSystem().also { it.name = "global" }) - - assertEquals("current", combined.operatingSystem?.name) - } - - @Test - fun `uses isolation operatingSystem if current context does not have it`() { - val combined = fixture.getSut() - fixture.isolation.setOperatingSystem(OperatingSystem().also { it.name = "isolation" }) - fixture.global.setOperatingSystem(OperatingSystem().also { it.name = "global" }) - - assertEquals("isolation", combined.operatingSystem?.name) - } - - @Test - fun `uses global operatingSystem if current and isolation context do not have it`() { - val combined = fixture.getSut() - fixture.global.setOperatingSystem(OperatingSystem().also { it.name = "global" }) - - assertEquals("global", combined.operatingSystem?.name) - } - - @Test - fun `sets operatingSystem on default context`() { - val combined = fixture.getSut() - combined.setOperatingSystem(OperatingSystem().also { it.name = "some" }) - - assertNull(fixture.current.operatingSystem) - assertEquals("some", fixture.isolation.operatingSystem?.name) - assertNull(fixture.global.operatingSystem) - } - - @Test - fun `prefers runtime from current context`() { - val combined = fixture.getSut() - fixture.current.setRuntime(SentryRuntime().also { it.name = "current" }) - fixture.isolation.setRuntime(SentryRuntime().also { it.name = "isolation" }) - fixture.global.setRuntime(SentryRuntime().also { it.name = "global" }) - - assertEquals("current", combined.runtime?.name) - } - - @Test - fun `uses isolation runtime if current context does not have it`() { - val combined = fixture.getSut() - fixture.isolation.setRuntime(SentryRuntime().also { it.name = "isolation" }) - fixture.global.setRuntime(SentryRuntime().also { it.name = "global" }) - - assertEquals("isolation", combined.runtime?.name) - } - - @Test - fun `uses global runtime if current and isolation context do not have it`() { - val combined = fixture.getSut() - fixture.global.setRuntime(SentryRuntime().also { it.name = "global" }) - - assertEquals("global", combined.runtime?.name) - } - - @Test - fun `sets runtime on default context`() { - val combined = fixture.getSut() - combined.setRuntime(SentryRuntime().also { it.name = "some" }) - - assertNull(fixture.current.runtime) - assertEquals("some", fixture.isolation.runtime?.name) - assertNull(fixture.global.runtime) - } - - @Test - fun `prefers gpu from current context`() { - val combined = fixture.getSut() - fixture.current.setGpu(Gpu().also { it.name = "current" }) - fixture.isolation.setGpu(Gpu().also { it.name = "isolation" }) - fixture.global.setGpu(Gpu().also { it.name = "global" }) - - assertEquals("current", combined.gpu?.name) - } - - @Test - fun `uses isolation gpu if current context does not have it`() { - val combined = fixture.getSut() - fixture.isolation.setGpu(Gpu().also { it.name = "isolation" }) - fixture.global.setGpu(Gpu().also { it.name = "global" }) - - assertEquals("isolation", combined.gpu?.name) - } - - @Test - fun `uses global gpu if current and isolation context do not have it`() { - val combined = fixture.getSut() - fixture.global.setGpu(Gpu().also { it.name = "global" }) - - assertEquals("global", combined.gpu?.name) - } - - @Test - fun `sets gpu on default context`() { - val combined = fixture.getSut() - combined.setGpu(Gpu().also { it.name = "some" }) - - assertNull(fixture.current.gpu) - assertEquals("some", fixture.isolation.gpu?.name) - assertNull(fixture.global.gpu) - } - - @Test - fun `prefers response from current context`() { - val combined = fixture.getSut() - fixture.current.setResponse(Response().also { it.cookies = "current" }) - fixture.isolation.setResponse(Response().also { it.cookies = "isolation" }) - fixture.global.setResponse(Response().also { it.cookies = "global" }) - - assertEquals("current", combined.response?.cookies) - } - - @Test - fun `uses isolation response if current context does not have it`() { - val combined = fixture.getSut() - fixture.isolation.setResponse(Response().also { it.cookies = "isolation" }) - fixture.global.setResponse(Response().also { it.cookies = "global" }) - - assertEquals("isolation", combined.response?.cookies) - } - - @Test - fun `uses global response if current and isolation context do not have it`() { - val combined = fixture.getSut() - fixture.global.setResponse(Response().also { it.cookies = "global" }) - - assertEquals("global", combined.response?.cookies) - } - - @Test - fun `sets response on default context`() { - val combined = fixture.getSut() - combined.setResponse(Response().also { it.cookies = "some" }) - - assertNull(fixture.current.response) - assertEquals("some", fixture.isolation.response?.cookies) - assertNull(fixture.global.response) - } - - @Test - fun `withResponse is executed on current if present`() { - val combined = fixture.getSut() - fixture.current.setResponse(Response().also { it.cookies = "current" }) - fixture.isolation.setResponse(Response().also { it.cookies = "isolation" }) - fixture.global.setResponse(Response().also { it.cookies = "global" }) - - combined.withResponse { response -> - response.cookies = "updated" - } - - assertEquals("updated", fixture.current.response?.cookies) - assertEquals("isolation", fixture.isolation.response?.cookies) - assertEquals("global", fixture.global.response?.cookies) - } - - @Test - fun `withResponse is executed on isolation if current not present`() { - val combined = fixture.getSut() - fixture.isolation.setResponse(Response().also { it.cookies = "isolation" }) - fixture.global.setResponse(Response().also { it.cookies = "global" }) - - combined.withResponse { response -> - response.cookies = "updated" - } - - assertNull(fixture.current.response) - assertEquals("updated", fixture.isolation.response?.cookies) - assertEquals("global", fixture.global.response?.cookies) - } - - @Test - fun `withResponse is executed on global if current and isoaltion not present`() { - val combined = fixture.getSut() - fixture.global.setResponse(Response().also { it.cookies = "global" }) - - combined.withResponse { response -> - response.cookies = "updated" - } - - assertNull(fixture.current.response) - assertNull(fixture.isolation.response) - assertEquals("updated", fixture.global.response?.cookies) - } - - @Test - fun `withResponse is executed on default if not present anywhere`() { - val combined = fixture.getSut() - - combined.withResponse { response -> - response.cookies = "updated" - } - - assertNull(fixture.current.response) - assertEquals("updated", fixture.isolation.response?.cookies) - assertNull(fixture.global.response) - } - - @Test - fun `size combines contexts`() { - val combined = fixture.getSut() - fixture.current.trace = SpanContext("current") - fixture.isolation.setApp(App().also { it.appName = "isolation" }) - fixture.global.setGpu(Gpu().also { it.name = "global" }) - - assertEquals(3, combined.size) - } - - @Test - fun `size considers overrides`() { - val combined = fixture.getSut() - fixture.current.trace = SpanContext("current") - fixture.isolation.trace = SpanContext("isolation") - fixture.global.trace = SpanContext("global") - - assertEquals(1, combined.size) - } - - @Test - fun `isEmpty`() { - val combined = fixture.getSut() - assertTrue(combined.isEmpty) - } - - @Test - fun `isNotEmpty if current has value`() { - val combined = fixture.getSut() - fixture.current.trace = SpanContext("current") - - assertFalse(combined.isEmpty) - } - - @Test - fun `isNotEmpty if isolation has value`() { - val combined = fixture.getSut() - fixture.isolation.setApp(App().also { it.appName = "isolation" }) - - assertFalse(combined.isEmpty) - } - - @Test - fun `isNotEmpty if global has value`() { - val combined = fixture.getSut() - fixture.global.setGpu(Gpu().also { it.name = "global" }) - - assertFalse(combined.isEmpty) - } - - @Test - fun `containsKey false`() { - val combined = fixture.getSut() - assertFalse(combined.containsKey("trace")) - } - - @Test - fun `containsKey current`() { - val combined = fixture.getSut() - fixture.current.trace = SpanContext("current") - assertTrue(combined.containsKey("trace")) - } - - @Test - fun `containsKey isolation`() { - val combined = fixture.getSut() - fixture.isolation.trace = SpanContext("isolation") - assertTrue(combined.containsKey("trace")) - } - - @Test - fun `containsKey global`() { - val combined = fixture.getSut() - fixture.global.trace = SpanContext("global") - assertTrue(combined.containsKey("trace")) - } - - @Test - fun `keys combines contexts`() { - val combined = fixture.getSut() - fixture.current.trace = SpanContext("current") - fixture.isolation.setApp(App().also { it.appName = "isolation" }) - fixture.global.setGpu(Gpu().also { it.name = "global" }) - - assertEquals(listOf("app", "gpu", "trace"), combined.keys().toList().sorted()) - } - - @Test - fun `entrySet combines contexts`() { - val combined = fixture.getSut() - val trace = SpanContext("current") - fixture.current.trace = trace - val app = App().also { it.appName = "isolation" } - fixture.isolation.setApp(app) - val gpu = Gpu().also { it.name = "global" } - fixture.global.setGpu(gpu) - - val entrySet = combined.entrySet() - assertEquals(3, entrySet.size) - assertNotNull(entrySet.firstOrNull { it.key == "trace" && it.value == trace }) - assertNotNull(entrySet.firstOrNull { it.key == "app" && it.value == app }) - assertNotNull(entrySet.firstOrNull { it.key == "gpu" && it.value == gpu }) - } - - @Test - fun `get prefers current`() { - val combined = fixture.getSut() - fixture.current.put("test", "current") - fixture.isolation.put("test", "isolation") - fixture.global.put("test", "global") - - assertEquals("current", combined.get("test")) - } - - @Test - fun `get uses isolation if not in current`() { - val combined = fixture.getSut() - fixture.isolation.put("test", "isolation") - fixture.global.put("test", "global") - - assertEquals("isolation", combined.get("test")) - } - - @Test - fun `get uses global if not in current or isolation`() { - val combined = fixture.getSut() - fixture.global.put("test", "global") - - assertEquals("global", combined.get("test")) - } - - @Test - fun `put stores in default context`() { - val combined = fixture.getSut() - combined.put("test", "aValue") - - assertNull(fixture.current.get("test")) - assertEquals("aValue", fixture.isolation.get("test")) - assertNull(fixture.global.get("test")) - } - - @Test - fun `remove removes from default context`() { - val combined = fixture.getSut() - fixture.current.put("test", "current") - fixture.isolation.put("test", "isolation") - fixture.global.put("test", "global") - - combined.remove("test") - - assertEquals("current", fixture.current.get("test")) - assertNull(fixture.isolation.get("test")) - assertEquals("global", fixture.global.get("test")) - } -} diff --git a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt deleted file mode 100644 index b73a7adcc8..0000000000 --- a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt +++ /dev/null @@ -1,1116 +0,0 @@ -package io.sentry - -import io.sentry.protocol.Device -import io.sentry.protocol.Request -import io.sentry.protocol.SentryId -import io.sentry.protocol.User -import io.sentry.test.createTestScopes -import junit.framework.TestCase.assertFalse -import junit.framework.TestCase.assertTrue -import org.junit.Assert.assertNotEquals -import org.mockito.kotlin.any -import org.mockito.kotlin.eq -import org.mockito.kotlin.mock -import org.mockito.kotlin.never -import org.mockito.kotlin.same -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever -import java.lang.RuntimeException -import java.util.UUID -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotNull -import kotlin.test.assertNull -import kotlin.test.assertSame - -class CombinedScopeViewTest { - - private class Fixture { - lateinit var globalScope: IScope - lateinit var isolationScope: IScope - lateinit var scope: IScope - lateinit var options: SentryOptions - lateinit var scopes: IScopes - - fun getSut(options: SentryOptions = SentryOptions()): CombinedScopeView { - options.dsn = "https://key@sentry.io/proj" - options.release = "0.1" - this.options = options - globalScope = Scope(options) - isolationScope = Scope(options) - scope = Scope(options) - scopes = createTestScopes(options, scope = scope, isolationScope = isolationScope, globalScope = globalScope) - - return CombinedScopeView(globalScope, isolationScope, scope) - } - } - - private val fixture = Fixture() - - @Test - fun `adds breadcrumbs from all scopes in sorted order`() { - val combined = fixture.getSut() - - fixture.globalScope.addBreadcrumb(Breadcrumb.info("global 1")) - fixture.isolationScope.addBreadcrumb(Breadcrumb.info("isolation 1")) - fixture.scope.addBreadcrumb(Breadcrumb.info("current 1")) - - fixture.globalScope.addBreadcrumb(Breadcrumb.info("global 2")) - fixture.isolationScope.addBreadcrumb(Breadcrumb.info("isolation 2")) - fixture.scope.addBreadcrumb(Breadcrumb.info("current 2")) - - val breadcrumbs = combined.breadcrumbs - assertEquals("global 1", breadcrumbs.poll().message) - assertEquals("isolation 1", breadcrumbs.poll().message) - assertEquals("current 1", breadcrumbs.poll().message) - assertEquals("global 2", breadcrumbs.poll().message) - assertEquals("isolation 2", breadcrumbs.poll().message) - assertEquals("current 2", breadcrumbs.poll().message) - } - - @Test - fun `oldest breadcrumbs are dropped first`() { - val options = SentryOptions().also { it.maxBreadcrumbs = 5 } - val combined = fixture.getSut(options) - - fixture.globalScope.addBreadcrumb(Breadcrumb.info("global 1")) - fixture.isolationScope.addBreadcrumb(Breadcrumb.info("isolation 1")) - fixture.scope.addBreadcrumb(Breadcrumb.info("current 1")) - - fixture.globalScope.addBreadcrumb(Breadcrumb.info("global 2")) - fixture.isolationScope.addBreadcrumb(Breadcrumb.info("isolation 2")) - fixture.scope.addBreadcrumb(Breadcrumb.info("current 2")) - - val breadcrumbs = combined.breadcrumbs -// assertEquals("global 1", breadcrumbs.poll().message) <-- was dropped - assertEquals("isolation 1", breadcrumbs.poll().message) - assertEquals("current 1", breadcrumbs.poll().message) - assertEquals("global 2", breadcrumbs.poll().message) - assertEquals("isolation 2", breadcrumbs.poll().message) - assertEquals("current 2", breadcrumbs.poll().message) - - fixture.scope.addBreadcrumb(Breadcrumb.info("current 3")) - fixture.scope.addBreadcrumb(Breadcrumb.info("current 4")) - - val breadcrumbs2 = combined.breadcrumbs -// assertEquals("global 1", breadcrumbs.poll().message) <-- was dropped -// assertEquals("isolation 1", breadcrumbs2.poll().message) <-- dropped -// assertEquals("current 1", breadcrumbs2.poll().message) <-- dropped - assertEquals("global 2", breadcrumbs2.poll().message) - assertEquals("isolation 2", breadcrumbs2.poll().message) - assertEquals("current 2", breadcrumbs2.poll().message) - assertEquals("current 3", breadcrumbs2.poll().message) - assertEquals("current 4", breadcrumbs2.poll().message) - } - - @Test - fun `can add breadcrumb with hint`() { - var capturedHint: Hint? = null - val combined = fixture.getSut( - SentryOptions().also { - it.beforeBreadcrumb = - SentryOptions.BeforeBreadcrumbCallback { breadcrumb: Breadcrumb, hint: Hint -> - capturedHint = hint - breadcrumb - } - } - ) - - combined.addBreadcrumb(Breadcrumb.info("aBreadcrumb"), Hint().also { it.set("aTest", "aValue") }) - - assertNotNull(capturedHint) - assertEquals("aValue", capturedHint?.get("aTest")) - - val breadcrumbs = combined.breadcrumbs - assertEquals(1, breadcrumbs.size) - assertEquals("aBreadcrumb", breadcrumbs.first().message) - } - - @Test - fun `adds breadcrumb to default scope`() { - val combined = fixture.getSut() - combined.addBreadcrumb(Breadcrumb.info("aBreadcrumb")) - - assertEquals(ScopeType.ISOLATION, combined.options.defaultScopeType) - assertEquals(0, fixture.scope.breadcrumbs.size) - assertEquals(1, fixture.isolationScope.breadcrumbs.size) - assertEquals(0, fixture.globalScope.breadcrumbs.size) - } - - @Test - fun `clears breadcrumbs only from default scope`() { - val combined = fixture.getSut() - fixture.scope.addBreadcrumb(Breadcrumb.info("scopeBreadcrumb")) - fixture.isolationScope.addBreadcrumb(Breadcrumb.info("isolationBreadcrumb")) - fixture.globalScope.addBreadcrumb(Breadcrumb.info("globalBreadcrumb")) - - combined.clearBreadcrumbs() - - assertEquals(ScopeType.ISOLATION, combined.options.defaultScopeType) - assertEquals(1, fixture.scope.breadcrumbs.size) - assertEquals(0, fixture.isolationScope.breadcrumbs.size) - assertEquals(1, fixture.globalScope.breadcrumbs.size) - } - - @Test - fun `event processors from options are not returned`() { - val options = SentryOptions().also { - it.addEventProcessor(MainEventProcessor(it)) - } - val combined = fixture.getSut(options) - - assertEquals(0, combined.eventProcessors.size) - } - - @Test - fun `event processors from all scopes are returned in order`() { - val combined = fixture.getSut() - - val first = TestEventProcessor(0).also { fixture.scope.addEventProcessor(it) } - val second = TestEventProcessor(1000).also { fixture.globalScope.addEventProcessor(it) } - val third = TestEventProcessor(2000).also { fixture.isolationScope.addEventProcessor(it) } - val fourth = TestEventProcessor(3000).also { fixture.scope.addEventProcessor(it) } - - val eventProcessors = combined.eventProcessors - - assertEquals(first, eventProcessors[0]) - assertEquals(second, eventProcessors[1]) - assertEquals(third, eventProcessors[2]) - assertEquals(fourth, eventProcessors[3]) - } - - @Test - fun `adds event processor to default scope`() { - val combined = fixture.getSut() - - val eventProcessor = MainEventProcessor(fixture.options) - combined.addEventProcessor(eventProcessor) - - assertEquals(ScopeType.ISOLATION, combined.options.defaultScopeType) - assertFalse(fixture.scope.eventProcessors.contains(eventProcessor)) - assertTrue(fixture.isolationScope.eventProcessors.contains(eventProcessor)) - assertFalse(fixture.globalScope.eventProcessors.contains(eventProcessor)) - } - - @Test - fun `prefers level from current scope`() { - val combined = fixture.getSut() - fixture.scope.level = SentryLevel.DEBUG - fixture.isolationScope.level = SentryLevel.INFO - fixture.globalScope.level = SentryLevel.WARNING - - assertEquals(SentryLevel.DEBUG, combined.level) - } - - @Test - fun `uses isolation scope level if none in current scope`() { - val combined = fixture.getSut() - fixture.isolationScope.level = SentryLevel.INFO - fixture.globalScope.level = SentryLevel.WARNING - - assertEquals(SentryLevel.INFO, combined.level) - } - - @Test - fun `uses global scope level if none in current or isolation scope`() { - val combined = fixture.getSut() - fixture.globalScope.level = SentryLevel.WARNING - - assertEquals(SentryLevel.WARNING, combined.level) - } - - @Test - fun `returns null level if none in any scope`() { - val combined = fixture.getSut() - - assertNull(combined.level) - } - - @Test - fun `setLevel modifies default scope`() { - val combined = fixture.getSut() - combined.level = SentryLevel.ERROR - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertNull(fixture.scope.level) - assertEquals(SentryLevel.ERROR, fixture.isolationScope.level) - assertNull(fixture.globalScope.level) - } - - @Test - fun `prefers transaction name from current scope`() { - val combined = fixture.getSut() - fixture.scope.setTransaction("scopeTransaction") - fixture.isolationScope.setTransaction("isolationTransaction") - fixture.globalScope.setTransaction("globalTransaction") - - assertEquals("scopeTransaction", combined.transactionName) - } - - @Test - fun `uses isolation transaction name if none in current scope`() { - val combined = fixture.getSut() - fixture.isolationScope.setTransaction("isolationTransaction") - fixture.globalScope.setTransaction("globalTransaction") - - assertEquals("isolationTransaction", combined.transactionName) - } - - @Test - fun `uses global transaction name if none in current or isolation scope`() { - val combined = fixture.getSut() - fixture.globalScope.setTransaction("globalTransaction") - - assertEquals("globalTransaction", combined.transactionName) - } - - @Test - fun `returns null transaction name if none in any scope`() { - val combined = fixture.getSut() - - assertNull(combined.transactionName) - } - - @Test - fun `setTransaction(String) modifies default scope`() { - val combined = fixture.getSut() - combined.setTransaction("aTransaction") - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertNull(fixture.scope.transactionName) - assertEquals("aTransaction", fixture.isolationScope.transactionName) - assertNull(fixture.globalScope.transactionName) - } - - @Test - fun `prefers transaction and span from current scope`() { - val combined = fixture.getSut() - fixture.scope.setTransaction(createTransaction("scopeTransaction")) - fixture.isolationScope.setTransaction(createTransaction("isolationTransaction")) - fixture.globalScope.setTransaction(createTransaction("globalTransaction")) - - assertEquals("scopeTransaction", combined.transaction!!.name) - assertEquals("scopeTransactionSpan", combined.span!!.operation) - } - - @Test - fun `uses isolation scope transaction and span if none in current scope`() { - val combined = fixture.getSut() - fixture.isolationScope.setTransaction(createTransaction("isolationTransaction")) - fixture.globalScope.setTransaction(createTransaction("globalTransaction")) - - assertEquals("isolationTransaction", combined.transaction!!.name) - assertEquals("isolationTransactionSpan", combined.span!!.operation) - } - - @Test - fun `uses global transaction and scope span if none in current or isolation scope`() { - val combined = fixture.getSut() - fixture.globalScope.setTransaction(createTransaction("globalTransaction")) - - assertEquals("globalTransaction", combined.transaction!!.name) - assertEquals("globalTransactionSpan", combined.span!!.operation) - } - - @Test - fun `returns null transaction and span if none in any scope`() { - val combined = fixture.getSut() - - assertNull(combined.transaction) - assertNull(combined.span) - } - - @Test - fun `setTransaction(ITransaction) modifies default scope`() { - val combined = fixture.getSut() - val tx = createTransaction("aTransaction") - combined.setTransaction(tx) - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertNull(fixture.scope.transaction) - assertSame(tx, fixture.isolationScope.transaction) - assertNull(fixture.globalScope.transaction) - } - - @Test - fun `clears transaction from default scope`() { - val combined = fixture.getSut() - fixture.scope.setTransaction(createTransaction("scopeTransaction")) - fixture.isolationScope.setTransaction(createTransaction("isolationTransaction")) - fixture.globalScope.setTransaction(createTransaction("globalTransaction")) - - combined.clearTransaction() - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertNotNull(fixture.scope.transaction) - assertNull(fixture.isolationScope.transaction) - assertNotNull(fixture.globalScope.transaction) - } - - @Test - fun `prefers user from current scope`() { - val combined = fixture.getSut() - fixture.scope.user = User().also { it.name = "scopeUser" } - fixture.isolationScope.user = User().also { it.name = "isolationUser" } - fixture.globalScope.user = User().also { it.name = "globalUser" } - - assertEquals("scopeUser", combined.user!!.name) - } - - @Test - fun `uses isolation scope user if none in current scope`() { - val combined = fixture.getSut() - fixture.isolationScope.user = User().also { it.name = "isolationUser" } - fixture.globalScope.user = User().also { it.name = "globalUser" } - - assertEquals("isolationUser", combined.user!!.name) - } - - @Test - fun `uses global scope user if none in current or isolation scope`() { - val combined = fixture.getSut() - fixture.globalScope.user = User().also { it.name = "globalUser" } - - assertEquals("globalUser", combined.user!!.name) - } - - @Test - fun `returns null user if none in any scope`() { - val combined = fixture.getSut() - - assertNull(combined.user) - } - - @Test - fun `set user modifies default scope`() { - val combined = fixture.getSut() - val user = User().also { it.name = "aUser" } - combined.user = user - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertNull(fixture.scope.user) - assertSame(user, fixture.isolationScope.user) - assertNull(fixture.globalScope.user) - } - - @Test - fun `prefers screen from current scope`() { - val combined = fixture.getSut() - fixture.scope.screen = "scopeScreen" - fixture.isolationScope.screen = "isolationScreen" - fixture.globalScope.screen = "globalScreen" - - assertEquals("scopeScreen", combined.screen) - } - - @Test - fun `uses isolation scope screen if none in current scope`() { - val combined = fixture.getSut() - fixture.isolationScope.screen = "isolationScreen" - fixture.globalScope.screen = "globalScreen" - - assertEquals("isolationScreen", combined.screen) - } - - @Test - fun `uses global scope screen if none in current or isolation scope`() { - val combined = fixture.getSut() - fixture.globalScope.screen = "globalScreen" - - assertEquals("globalScreen", combined.screen) - } - - @Test - fun `returns null screen if none in any scope`() { - val combined = fixture.getSut() - - assertNull(combined.screen) - } - - @Test - fun `set screen modifies default scope`() { - val combined = fixture.getSut() - combined.screen = "aScreen" - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertNull(fixture.scope.screen) - assertEquals("aScreen", fixture.isolationScope.screen) - assertNull(fixture.globalScope.screen) - } - - @Test - fun `prefers request from current scope`() { - val combined = fixture.getSut() - fixture.scope.request = Request().also { it.queryString = "scopeRequest" } - fixture.isolationScope.request = Request().also { it.queryString = "isolationRequest" } - fixture.globalScope.request = Request().also { it.queryString = "globalRequest" } - - assertEquals("scopeRequest", combined.request!!.queryString) - } - - @Test - fun `uses isolation scope request if none in current scope`() { - val combined = fixture.getSut() - fixture.isolationScope.request = Request().also { it.queryString = "isolationRequest" } - fixture.globalScope.request = Request().also { it.queryString = "globalRequest" } - - assertEquals("isolationRequest", combined.request!!.queryString) - } - - @Test - fun `uses global scope request if none in current or isolation scope`() { - val combined = fixture.getSut() - fixture.globalScope.request = Request().also { it.queryString = "globalRequest" } - - assertEquals("globalRequest", combined.request!!.queryString) - } - - @Test - fun `returns null request if none in any scope`() { - val combined = fixture.getSut() - - assertNull(combined.request) - } - - @Test - fun `set request modifies default scope`() { - val combined = fixture.getSut() - val request = Request().also { it.queryString = "aRequest" } - combined.request = request - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertNull(fixture.scope.request) - assertSame(request, fixture.isolationScope.request) - assertNull(fixture.globalScope.request) - } - - @Test - fun `clear removes from default scope`() { - val combined = fixture.getSut() - - fixture.scope.level = SentryLevel.DEBUG - fixture.isolationScope.level = SentryLevel.INFO - fixture.globalScope.level = SentryLevel.WARNING - - combined.clear() - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertNotNull(fixture.scope.level) - assertNull(fixture.isolationScope.level) - assertNotNull(fixture.globalScope.level) - } - - @Test - fun `tags are combined from all scopes`() { - val combined = fixture.getSut() - - fixture.scope.setTag("scopeTag", "scopeValue") - fixture.isolationScope.setTag("isolationTag", "isolationValue") - fixture.globalScope.setTag("globalTag", "globalValue") - - val tags = combined.tags - assertEquals("scopeValue", tags["scopeTag"]) - assertEquals("isolationValue", tags["isolationTag"]) - assertEquals("globalValue", tags["globalTag"]) - } - - @Test - fun `setTag writes to default scope`() { - val combined = fixture.getSut() - combined.setTag("aTag", "aValue") - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertNull(fixture.scope.tags["aTag"]) - assertEquals("aValue", fixture.isolationScope.tags["aTag"]) - assertNull(fixture.globalScope.tags["aTag"]) - } - - @Test - fun `prefer current scope value for tags with same key`() { - val combined = fixture.getSut() - - fixture.scope.setTag("aTag", "scopeValue") - fixture.isolationScope.setTag("aTag", "isolationValue") - fixture.globalScope.setTag("aTag", "globalValue") - - assertEquals("scopeValue", combined.tags["aTag"]) - } - - @Test - fun `uses isolation scope value for tags with same key if scope does not have it`() { - val combined = fixture.getSut() - - fixture.isolationScope.setTag("aTag", "isolationValue") - fixture.globalScope.setTag("aTag", "globalValue") - - assertEquals("isolationValue", combined.tags["aTag"]) - } - - @Test - fun `uses global scope value for tags with same key if scope and isolation scope do not have it`() { - val combined = fixture.getSut() - - fixture.globalScope.setTag("aTag", "globalValue") - - assertEquals("globalValue", combined.tags["aTag"]) - } - - @Test - fun `removeTag removes from default scope`() { - val combined = fixture.getSut() - - fixture.scope.setTag("aTag", "scopeValue") - fixture.isolationScope.setTag("aTag", "isolationValue") - fixture.globalScope.setTag("aTag", "globalValue") - - combined.removeTag("aTag") - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertEquals("scopeValue", fixture.scope.tags["aTag"]) - assertNull(fixture.isolationScope.tags["aTag"]) - assertEquals("globalValue", fixture.globalScope.tags["aTag"]) - } - - @Test - fun `extras are combined from all scopes`() { - val combined = fixture.getSut() - - fixture.scope.setExtra("scopeExtra", "scopeValue") - fixture.isolationScope.setExtra("isolationExtra", "isolationValue") - fixture.globalScope.setExtra("globalExtra", "globalValue") - - val extras = combined.extras - assertEquals("scopeValue", extras["scopeExtra"]) - assertEquals("isolationValue", extras["isolationExtra"]) - assertEquals("globalValue", extras["globalExtra"]) - } - - @Test - fun `setExtra writes to default scope`() { - val combined = fixture.getSut() - combined.setExtra("someExtra", "aValue") - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertNull(fixture.scope.extras["someExtra"]) - assertEquals("aValue", fixture.isolationScope.extras["someExtra"]) - assertNull(fixture.globalScope.extras["someExtra"]) - } - - @Test - fun `prefer current scope value for extras with same key`() { - val combined = fixture.getSut() - - fixture.scope.setExtra("someExtra", "scopeValue") - fixture.isolationScope.setExtra("someExtra", "isolationValue") - fixture.globalScope.setExtra("someExtra", "globalValue") - - assertEquals("scopeValue", combined.extras["someExtra"]) - } - - @Test - fun `uses isolation scope value for extras with same key if scope does not have it`() { - val combined = fixture.getSut() - - fixture.isolationScope.setExtra("someExtra", "isolationValue") - fixture.globalScope.setExtra("someExtra", "globalValue") - - assertEquals("isolationValue", combined.extras["someExtra"]) - } - - @Test - fun `uses global scope value for extras with same key if scope and isolation scope do not have it`() { - val combined = fixture.getSut() - - fixture.globalScope.setExtra("someExtra", "globalValue") - - assertEquals("globalValue", combined.extras["someExtra"]) - } - - @Test - fun `removeExtra removes from default scope`() { - val combined = fixture.getSut() - - fixture.scope.setExtra("someExtra", "scopeValue") - fixture.isolationScope.setExtra("someExtra", "isolationValue") - fixture.globalScope.setExtra("someExtra", "globalValue") - - combined.removeExtra("someExtra") - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertEquals("scopeValue", fixture.scope.extras["someExtra"]) - assertNull(fixture.isolationScope.extras["someExtra"]) - assertEquals("globalValue", fixture.globalScope.extras["someExtra"]) - } - - @Test - fun `combines context from all scopes`() { - val combined = fixture.getSut() - fixture.scope.setContexts("scopeContext", "scopeValue") - fixture.isolationScope.setContexts("isolationContext", "isolationValue") - fixture.globalScope.setContexts("globalContext", "globalValue") - - val contexts = combined.contexts - assertEquals(mapOf("value" to "scopeValue"), contexts["scopeContext"]) - } - - @Test - fun `current scope context overrides context of other scopes`() { - val combined = fixture.getSut() - fixture.scope.setContexts("someContext", "scopeValue") - fixture.isolationScope.setContexts("someContext", "isolationValue") - fixture.globalScope.setContexts("someContext", "globalValue") - - val contexts = combined.contexts - assertEquals(mapOf("value" to "scopeValue"), contexts["someContext"]) - } - - @Test - fun `isolation scope context overrides global context`() { - val combined = fixture.getSut() - fixture.isolationScope.setContexts("someContext", "isolationValue") - fixture.globalScope.setContexts("someContext", "globalValue") - - val contexts = combined.contexts - assertEquals(mapOf("value" to "isolationValue"), contexts["someContext"]) - } - - @Test - fun `setContexts writes to default scope`() { - val combined = fixture.getSut() - combined.setContexts("aString", "stringValue") - combined.setContexts("aChar", 'c') - combined.setContexts("aNumber", 1) - combined.setContexts("someObject", Device().also { it.brand = "someDeviceBrand" }) - combined.setContexts("someArray", arrayOf("a", "b")) - combined.setContexts("someList", listOf("c", "d", "e")) - - assertNull(fixture.scope.contexts["aString"]) - assertNull(fixture.scope.contexts["aChar"]) - assertNull(fixture.scope.contexts["aNumber"]) - assertNull(fixture.scope.contexts["someObject"]) - assertNull(fixture.scope.contexts["someArray"]) - assertNull(fixture.scope.contexts["someList"]) - - assertEquals(mapOf("value" to "stringValue"), fixture.isolationScope.contexts["aString"]) - assertEquals(mapOf("value" to 'c'), fixture.isolationScope.contexts["aChar"]) - assertEquals(mapOf("value" to 1), fixture.isolationScope.contexts["aNumber"]) - assertEquals("someDeviceBrand", (fixture.isolationScope.contexts["someObject"] as? Device)?.brand) - val arrayValue = (fixture.isolationScope.contexts["someArray"] as? Map)?.get("value") as? Array - assertEquals(2, arrayValue?.size) - assertEquals("a", arrayValue?.get(0)) - assertEquals("b", arrayValue?.get(1)) - val listValue = (fixture.isolationScope.contexts["someList"] as? Map)?.get("value") as? List - assertEquals(3, listValue?.size) - assertEquals("c", listValue?.get(0)) - assertEquals("d", listValue?.get(1)) - assertEquals("e", listValue?.get(2)) - - assertNull(fixture.globalScope.contexts["aString"]) - assertNull(fixture.globalScope.contexts["aChar"]) - assertNull(fixture.globalScope.contexts["aNumber"]) - assertNull(fixture.globalScope.contexts["someObject"]) - assertNull(fixture.globalScope.contexts["someArray"]) - assertNull(fixture.globalScope.contexts["someList"]) - } - - @Test - fun `combines attachments from all scopes`() { - val combined = fixture.getSut() - - fixture.scope.addAttachment(createAttachment("scopeAttachment.png")) - fixture.isolationScope.addAttachment(createAttachment("isolationAttachment.png")) - fixture.globalScope.addAttachment(createAttachment("globalAttachment.png")) - - val attachments = combined.attachments - assertNotNull(attachments.firstOrNull { it.filename == "scopeAttachment.png" }) - assertNotNull(attachments.firstOrNull { it.filename == "isolationAttachment.png" }) - assertNotNull(attachments.firstOrNull { it.filename == "globalAttachment.png" }) - } - - @Test - fun `adds attachment to default scope`() { - val combined = fixture.getSut() - combined.addAttachment(createAttachment("someAttachment.png")) - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertNull(fixture.scope.attachments.firstOrNull { it.filename == "someAttachment.png" }) - assertNotNull(fixture.isolationScope.attachments.firstOrNull { it.filename == "someAttachment.png" }) - assertNull(fixture.globalScope.attachments.firstOrNull { it.filename == "someAttachment.png" }) - } - - @Test - fun `clears attachments only from default scope`() { - val combined = fixture.getSut() - - fixture.scope.addAttachment(createAttachment("scopeAttachment.png")) - fixture.isolationScope.addAttachment(createAttachment("isolationAttachment.png")) - fixture.globalScope.addAttachment(createAttachment("globalAttachment.png")) - - combined.clearAttachments() - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertNotNull(fixture.scope.attachments.firstOrNull { it.filename == "scopeAttachment.png" }) - assertNull(fixture.isolationScope.attachments.firstOrNull { it.filename == "isolationAttachment.png" }) - assertNotNull(fixture.globalScope.attachments.firstOrNull { it.filename == "globalAttachment.png" }) - } - - @Test - fun `returns options from global scope`() { - val scopeOptions = SentryOptions().also { it.dist = "scopeDist" } - val isolationOptions = SentryOptions().also { it.dist = "isolationDist" } - val globalOptions = SentryOptions().also { it.dist = "globalDist" } - - val combined = CombinedScopeView(Scope(globalOptions), Scope(isolationOptions), Scope(scopeOptions)) - assertEquals("globalDist", combined.options.dist) - } - - @Test - fun `replaces options on global scope`() { - val scopeOptions = SentryOptions().also { it.dist = "scopeDist" } - val isolationOptions = SentryOptions().also { it.dist = "isolationDist" } - val globalOptions = SentryOptions().also { it.dist = "globalDist" } - - val globalScope = Scope(globalOptions) - val isolationScope = Scope(isolationOptions) - val scope = Scope(scopeOptions) - val combined = CombinedScopeView(globalScope, isolationScope, scope) - - val newOptions = SentryOptions().also { it.dist = "newDist" } - combined.replaceOptions(newOptions) - - assertEquals("scopeDist", scope.options.dist) - assertEquals("isolationDist", isolationScope.options.dist) - assertEquals("newDist", globalScope.options.dist) - } - - @Test - fun `prefers client from scope`() { - val combined = fixture.getSut() - - val scopeClient = SentryClient(fixture.options) - fixture.scope.bindClient(scopeClient) - - val isolationClient = SentryClient(fixture.options) - fixture.isolationScope.bindClient(isolationClient) - - val globalClient = SentryClient(fixture.options) - fixture.globalScope.bindClient(globalClient) - - assertSame(scopeClient, combined.client) - } - - @Test - fun `uses isolation scope client if noop on current scope`() { - val combined = fixture.getSut() - fixture.scope.bindClient(NoOpSentryClient.getInstance()) - fixture.isolationScope.bindClient(NoOpSentryClient.getInstance()) - fixture.globalScope.bindClient(NoOpSentryClient.getInstance()) - - val isolationClient = SentryClient(fixture.options) - fixture.isolationScope.bindClient(isolationClient) - - val globalClient = SentryClient(fixture.options) - fixture.globalScope.bindClient(globalClient) - - assertSame(isolationClient, combined.client) - } - - @Test - fun `uses global scope client if noop on current and isolation scope`() { - val combined = fixture.getSut() - fixture.scope.bindClient(NoOpSentryClient.getInstance()) - fixture.isolationScope.bindClient(NoOpSentryClient.getInstance()) - fixture.globalScope.bindClient(NoOpSentryClient.getInstance()) - - val globalClient = SentryClient(fixture.options) - fixture.globalScope.bindClient(globalClient) - - assertSame(globalClient, combined.client) - } - - @Test - fun `binds client to default scope`() { - val combined = fixture.getSut() - fixture.scope.bindClient(NoOpSentryClient.getInstance()) - fixture.isolationScope.bindClient(NoOpSentryClient.getInstance()) - fixture.globalScope.bindClient(NoOpSentryClient.getInstance()) - - val client = SentryClient(fixture.options) - combined.bindClient(client) - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertTrue(fixture.scope.client is NoOpSentryClient) - assertSame(client, fixture.isolationScope.client) - assertTrue(fixture.globalScope.client is NoOpSentryClient) - } - - @Test - fun `getSpecificScope(null) returns scope defined in options CURRENT`() { - val combined = fixture.getSut(SentryOptions().also { it.defaultScopeType = ScopeType.CURRENT }) - assertSame(fixture.scope, combined.getSpecificScope(null)) - } - - @Test - fun `getSpecificScope(null) returns scope defined in options ISOLATION`() { - val combined = fixture.getSut(SentryOptions().also { it.defaultScopeType = ScopeType.ISOLATION }) - assertSame(fixture.isolationScope, combined.getSpecificScope(null)) - } - - @Test - fun `getSpecificScope(null) returns scope defined in options GLOBAL`() { - val combined = fixture.getSut(SentryOptions().also { it.defaultScopeType = ScopeType.GLOBAL }) - assertSame(fixture.globalScope, combined.getSpecificScope(null)) - } - - @Test - fun `getSpecificScope(CURRENT) returns current scope`() { - val combined = fixture.getSut(SentryOptions().also { it.defaultScopeType = ScopeType.ISOLATION }) - assertSame(fixture.scope, combined.getSpecificScope(ScopeType.CURRENT)) - } - - @Test - fun `getSpecificScope(ISOLATION) returns isolation scope`() { - val combined = fixture.getSut(SentryOptions().also { it.defaultScopeType = ScopeType.CURRENT }) - assertSame(fixture.isolationScope, combined.getSpecificScope(ScopeType.ISOLATION)) - } - - @Test - fun `getSpecificScope(GLOBAL) returns global scope`() { - val combined = fixture.getSut(SentryOptions().also { it.defaultScopeType = ScopeType.CURRENT }) - assertSame(fixture.globalScope, combined.getSpecificScope(ScopeType.GLOBAL)) - } - - @Test - fun `forwards setSpanContext to global scope`() { - val scope = mock() - val isolationScope = mock() - val globalScope = mock() - val combined = CombinedScopeView(globalScope, isolationScope, scope) - - val options = SentryOptions().also { it.dsn = "https://key@sentry.io/proj" } - whenever(globalScope.options).thenReturn(options) - - val exception = RuntimeException("someEx") - val transaction = createTransaction("aTransaction", createTestScopes(options = options, scope = scope, isolationScope = isolationScope, globalScope = globalScope)) - combined.setSpanContext(exception, transaction, "aTransaction") - - verify(scope, never()).setSpanContext(any(), any(), any()) - verify(isolationScope, never()).setSpanContext(any(), any(), any()) - verify(globalScope).setSpanContext(same(exception), same(transaction), eq("aTransaction")) - } - - @Test - fun `withTransaction uses default scope`() { - val combined = fixture.getSut() - fixture.scope.setTransaction(createTransaction("scopeTransaction")) - fixture.isolationScope.setTransaction(createTransaction("isolationTransaction")) - fixture.globalScope.setTransaction(createTransaction("globalTransaction")) - - var capturedTransaction: ITransaction? = null - combined.withTransaction { transaction -> - capturedTransaction = transaction - } - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertEquals("isolationTransaction", capturedTransaction?.name) - } - - @Test - fun `forwards assignTraceContext to global scope`() { - val scope = mock() - val isolationScope = mock() - val globalScope = mock() - val combined = CombinedScopeView(globalScope, isolationScope, scope) - - val event = SentryEvent() - combined.assignTraceContext(event) - - verify(scope, never()).assignTraceContext(any()) - verify(isolationScope, never()).assignTraceContext(any()) - verify(globalScope).assignTraceContext(same(event)) - } - - @Test - fun `retrieves last event id from global scope`() { - val combined = fixture.getSut() - fixture.scope.lastEventId = SentryId(UUID.fromString("c81d4e2e-bcf2-11e6-869b-7df92533d2dc")) - fixture.isolationScope.lastEventId = SentryId(UUID.fromString("d81d4e2e-bcf2-11e6-869b-7df92533d2dd")) - fixture.globalScope.lastEventId = SentryId(UUID.fromString("e81d4e2e-bcf2-11e6-869b-7df92533d2de")) - - assertEquals("e81d4e2ebcf211e6869b7df92533d2de", combined.lastEventId.toString()) - } - - @Test - fun `sets last event id on all scopes`() { - val combined = fixture.getSut() - combined.lastEventId = SentryId(UUID.fromString("c81d4e2e-bcf2-11e6-869b-7df92533d2db")) - - assertEquals("c81d4e2ebcf211e6869b7df92533d2db", fixture.scope.lastEventId.toString()) - assertEquals("c81d4e2ebcf211e6869b7df92533d2db", fixture.isolationScope.lastEventId.toString()) - assertEquals("c81d4e2ebcf211e6869b7df92533d2db", fixture.globalScope.lastEventId.toString()) - } - - @Test - fun `retrieves propagation context from default scope`() { - val combined = fixture.getSut() - fixture.scope.propagationContext = PropagationContext().also { it.traceId = SentryId(UUID.fromString("c81d4e2e-bcf2-11e6-869b-7df92533d2dc")) } - fixture.isolationScope.propagationContext = PropagationContext().also { it.traceId = SentryId(UUID.fromString("d81d4e2e-bcf2-11e6-869b-7df92533d2dd")) } - fixture.globalScope.propagationContext = PropagationContext().also { it.traceId = SentryId(UUID.fromString("e81d4e2e-bcf2-11e6-869b-7df92533d2de")) } - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertEquals("d81d4e2ebcf211e6869b7df92533d2dd", combined.propagationContext.traceId.toString()) - } - - @Test - fun `sets propagation context on default scope`() { - val combined = fixture.getSut() - - combined.propagationContext = PropagationContext().also { it.traceId = SentryId(UUID.fromString("c81d4e2e-bcf2-11e6-869b-7df92533d2db")) } - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertNotEquals("c81d4e2ebcf211e6869b7df92533d2db", fixture.scope.propagationContext.traceId.toString()) - assertEquals("c81d4e2ebcf211e6869b7df92533d2db", fixture.isolationScope.propagationContext.traceId.toString()) - assertNotEquals("c81d4e2ebcf211e6869b7df92533d2db", fixture.globalScope.propagationContext.traceId.toString()) - } - - @Test - fun `withPropagationContext uses default scope`() { - val combined = fixture.getSut() - fixture.scope.propagationContext = PropagationContext().also { it.traceId = SentryId(UUID.fromString("c81d4e2e-bcf2-11e6-869b-7df92533d2dc")) } - fixture.isolationScope.propagationContext = PropagationContext().also { it.traceId = SentryId(UUID.fromString("d81d4e2e-bcf2-11e6-869b-7df92533d2dd")) } - fixture.globalScope.propagationContext = PropagationContext().also { it.traceId = SentryId(UUID.fromString("e81d4e2e-bcf2-11e6-869b-7df92533d2de")) } - - var capturedPropagationContext: PropagationContext? = null - combined.withPropagationContext { propagationContext -> - capturedPropagationContext = propagationContext - } - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertEquals("d81d4e2ebcf211e6869b7df92533d2dd", capturedPropagationContext?.traceId.toString()) - } - - @Test - fun `starts session on default scope`() { - val combined = fixture.getSut() - - combined.startSession() - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertNull(fixture.scope.session) - assertNotNull(fixture.isolationScope.session) - assertNull(fixture.globalScope.session) - } - - @Test - fun `ends session on default scope`() { - val combined = fixture.getSut() - fixture.scope.startSession() - fixture.isolationScope.startSession() - fixture.globalScope.startSession() - - combined.endSession() - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertNotNull(fixture.scope.session) - assertNull(fixture.isolationScope.session) - assertNotNull(fixture.globalScope.session) - } - - @Test - fun `prefers session from current scope`() { - val combined = fixture.getSut() - fixture.scope.startSession() - fixture.isolationScope.startSession() - fixture.globalScope.startSession() - - assertSame(fixture.scope.session, combined.session) - } - - @Test - fun `uses isolation scope session if none on current scope`() { - val combined = fixture.getSut() - fixture.isolationScope.startSession() - fixture.globalScope.startSession() - - assertSame(fixture.isolationScope.session, combined.session) - } - - @Test - fun `uses global scope session if none on current or isolation scope`() { - val combined = fixture.getSut() - fixture.globalScope.startSession() - - assertSame(fixture.globalScope.session, combined.session) - } - - @Test - fun `withSession uses default scope`() { - val combined = fixture.getSut() - fixture.scope.startSession() - fixture.isolationScope.startSession() - fixture.globalScope.startSession() - - var capturedSession: Session? = null - combined.withSession { session -> - capturedSession = session - } - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertSame(fixture.isolationScope.session, capturedSession) - } - - @Test - fun `sets fingerprint on default scope`() { - val combined = fixture.getSut() - combined.fingerprint = listOf("aFingerprint") - - assertEquals(ScopeType.ISOLATION, fixture.options.defaultScopeType) - assertEquals(0, fixture.scope.fingerprint.size) - assertEquals(1, fixture.isolationScope.fingerprint.size) - assertEquals(0, fixture.globalScope.fingerprint.size) - } - - @Test - fun `prefers fingerprint from current scope`() { - val combined = fixture.getSut() - fixture.scope.fingerprint = listOf("scopeFingerprint") - fixture.isolationScope.fingerprint = listOf("isolationFingerprint") - fixture.globalScope.fingerprint = listOf("globalFingerprint") - - assertEquals(listOf("scopeFingerprint"), combined.fingerprint) - } - - @Test - fun `uses isolation scope fingerprint if current scope does not have one`() { - val combined = fixture.getSut() - fixture.isolationScope.fingerprint = listOf("isolationFingerprint") - fixture.globalScope.fingerprint = listOf("globalFingerprint") - - assertEquals(listOf("isolationFingerprint"), combined.fingerprint) - } - - @Test - fun `uses global scope fingerprint if current and isolation scope do not have one`() { - val combined = fixture.getSut() - fixture.globalScope.fingerprint = listOf("globalFingerprint") - - assertEquals(listOf("globalFingerprint"), combined.fingerprint) - } - - // TODO [HSM] test clone - - private fun createTransaction(name: String, scopes: Scopes? = null): ITransaction { - val scopesToUse = scopes ?: fixture.scopes - return SentryTracer(TransactionContext(name, "op", TracesSamplingDecision(true)), scopesToUse).also { - it.startChild("${name}Span") - } - } - - private fun createAttachment(name: String): Attachment { - return Attachment("a".toByteArray(), name, "image/png", false) - } - - class TestEventProcessor(val orderNumber: Long?) : EventProcessor { - override fun getOrder() = orderNumber - } -} diff --git a/sentry/src/test/java/io/sentry/DefaultTransactionPerformanceCollectorTest.kt b/sentry/src/test/java/io/sentry/DefaultTransactionPerformanceCollectorTest.kt index 1416fbbe3f..c0445efb23 100644 --- a/sentry/src/test/java/io/sentry/DefaultTransactionPerformanceCollectorTest.kt +++ b/sentry/src/test/java/io/sentry/DefaultTransactionPerformanceCollectorTest.kt @@ -33,7 +33,7 @@ class DefaultTransactionPerformanceCollectorTest { private class Fixture { lateinit var transaction1: ITransaction lateinit var transaction2: ITransaction - val scopes: IScopes = mock() + val hub: IHub = mock() val options = SentryOptions() var mockTimer: Timer? = null val deferredExecutorService = DeferredExecutorService() @@ -47,7 +47,7 @@ class DefaultTransactionPerformanceCollectorTest { } init { - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) } fun getSut(memoryCollector: IPerformanceSnapshotCollector? = JavaMemoryCollector(), cpuCollector: IPerformanceSnapshotCollector? = mockCpuCollector, executorService: ISentryExecutorService = deferredExecutorService): TransactionPerformanceCollector { @@ -59,8 +59,8 @@ class DefaultTransactionPerformanceCollectorTest { if (memoryCollector != null) { options.addPerformanceCollector(memoryCollector) } - transaction1 = SentryTracer(TransactionContext("", ""), scopes) - transaction2 = SentryTracer(TransactionContext("", ""), scopes) + transaction1 = SentryTracer(TransactionContext("", ""), hub) + transaction2 = SentryTracer(TransactionContext("", ""), hub) val collector = DefaultTransactionPerformanceCollector(options) val timer: Timer = collector.getProperty("timer") ?: Timer(true) mockTimer = spy(timer) diff --git a/sentry/src/test/java/io/sentry/DirectoryProcessorTest.kt b/sentry/src/test/java/io/sentry/DirectoryProcessorTest.kt index 0507b8499d..e87f4256d5 100644 --- a/sentry/src/test/java/io/sentry/DirectoryProcessorTest.kt +++ b/sentry/src/test/java/io/sentry/DirectoryProcessorTest.kt @@ -27,7 +27,7 @@ class DirectoryProcessorTest { private class Fixture { - var scopes: IScopes = mock() + var hub: IHub = mock() var envelopeReader: IEnvelopeReader = mock() var serializer: ISerializer = mock() var logger: ILogger = mock() @@ -40,7 +40,7 @@ class DirectoryProcessorTest { fun getSut(isRetryable: Boolean = false, isRateLimitingActive: Boolean = false): OutboxSender { val hintCaptor = argumentCaptor() - whenever(scopes.captureEvent(any(), hintCaptor.capture())).then { + whenever(hub.captureEvent(any(), hintCaptor.capture())).then { HintUtils.runIfHasType( hintCaptor.firstValue, Enqueable::class.java @@ -52,7 +52,7 @@ class DirectoryProcessorTest { val rateLimiter = mock { whenever(mock.isActiveForCategory(any())).thenReturn(true) } - whenever(scopes.rateLimiter).thenReturn(rateLimiter) + whenever(hub.rateLimiter).thenReturn(rateLimiter) } } HintUtils.runIfHasType( @@ -62,7 +62,7 @@ class DirectoryProcessorTest { retryable.isRetry = isRetryable } } - return OutboxSender(scopes, envelopeReader, serializer, logger, 500, 30) + return OutboxSender(hub, envelopeReader, serializer, logger, 500, 30) } } @@ -91,7 +91,7 @@ class DirectoryProcessorTest { whenever(fixture.serializer.deserialize(any(), eq(SentryEvent::class.java))).thenReturn(event) fixture.getSut().processDirectory(file) - verify(fixture.scopes).captureEvent(any(), argWhere { !HintUtils.hasType(it, ApplyScopeData::class.java) }) + verify(fixture.hub).captureEvent(any(), argWhere { !HintUtils.hasType(it, ApplyScopeData::class.java) }) } @Test @@ -100,7 +100,7 @@ class DirectoryProcessorTest { dir.mkdirs() assertTrue(dir.exists()) // sanity check fixture.getSut().processDirectory(file) - verify(fixture.scopes, never()).captureEnvelope(any(), any()) + verify(fixture.hub, never()).captureEnvelope(any(), any()) } @Test @@ -121,7 +121,7 @@ class DirectoryProcessorTest { sut.processDirectory(file) // should only capture once - verify(fixture.scopes).captureEvent(any(), anyOrNull()) + verify(fixture.hub).captureEvent(any(), anyOrNull()) } @Test @@ -139,7 +139,7 @@ class DirectoryProcessorTest { sut.processDirectory(file) // should only capture once - verify(fixture.scopes).captureEvent(any(), anyOrNull()) + verify(fixture.hub).captureEvent(any(), anyOrNull()) } private fun getTempEnvelope(fileName: String): String { diff --git a/sentry/src/test/java/io/sentry/EnvelopeSenderTest.kt b/sentry/src/test/java/io/sentry/EnvelopeSenderTest.kt index d63ee81854..6f0ea9cb8a 100644 --- a/sentry/src/test/java/io/sentry/EnvelopeSenderTest.kt +++ b/sentry/src/test/java/io/sentry/EnvelopeSenderTest.kt @@ -23,7 +23,7 @@ import kotlin.test.assertFalse class EnvelopeSenderTest { private class Fixture { - var scopes: IScopes? = mock() + var hub: IHub? = mock() var logger: ILogger? = mock() var serializer: ISerializer? = mock() var options = SentryOptions().noFlushTimeout() @@ -35,7 +35,7 @@ class EnvelopeSenderTest { fun getSut(): EnvelopeSender { return EnvelopeSender( - scopes!!, + hub!!, serializer!!, logger!!, options.flushTimeoutMillis, @@ -62,7 +62,7 @@ class EnvelopeSenderTest { val sut = fixture.getSut() sut.processDirectory(File("i don't exist")) verify(fixture.logger)!!.log(eq(SentryLevel.WARNING), eq("Directory '%s' doesn't exist. No cached events to send."), any()) - verifyNoMoreInteractions(fixture.scopes) + verifyNoMoreInteractions(fixture.hub) } @Test @@ -72,7 +72,7 @@ class EnvelopeSenderTest { testFile.deleteOnExit() sut.processDirectory(testFile) verify(fixture.logger)!!.log(eq(SentryLevel.ERROR), eq("Cache dir %s is not a directory."), any()) - verifyNoMoreInteractions(fixture.scopes) + verifyNoMoreInteractions(fixture.hub) } @Test @@ -82,11 +82,11 @@ class EnvelopeSenderTest { sut.processDirectory(File(tempDirectory.toUri())) testFile.deleteOnExit() verify(fixture.logger)!!.log(eq(SentryLevel.DEBUG), eq("File '%s' doesn't match extension expected."), any()) - verify(fixture.scopes, never())!!.captureEnvelope(any(), anyOrNull()) + verify(fixture.hub, never())!!.captureEnvelope(any(), anyOrNull()) } @Test - fun `when directory has event files, processDirectory captures with scopes`() { + fun `when directory has event files, processDirectory captures with hub`() { val event = SentryEvent() val envelope = SentryEnvelope.from(fixture.serializer!!, event, null) whenever(fixture.serializer!!.deserializeEnvelope(any())).thenReturn(envelope) @@ -94,7 +94,7 @@ class EnvelopeSenderTest { val testFile = File(Files.createTempFile(tempDirectory, "send-cached-event-test", EnvelopeCache.SUFFIX_ENVELOPE_FILE).toUri()) testFile.deleteOnExit() sut.processDirectory(File(tempDirectory.toUri())) - verify(fixture.scopes)!!.captureEnvelope(eq(envelope), any()) + verify(fixture.hub)!!.captureEnvelope(eq(envelope), any()) } @Test @@ -108,12 +108,12 @@ class EnvelopeSenderTest { val hints = HintUtils.createWithTypeCheckHint(mock()) sut.processFile(testFile, hints) verify(fixture.logger)!!.log(eq(SentryLevel.ERROR), eq(expected), eq("Failed to capture cached envelope %s"), eq(testFile.absolutePath)) - verifyNoMoreInteractions(fixture.scopes) + verifyNoMoreInteractions(fixture.hub) assertFalse(testFile.exists()) } @Test - fun `when scopes throws, file gets deleted`() { + fun `when hub throws, file gets deleted`() { val expected = RuntimeException() whenever(fixture.serializer!!.deserializeEnvelope(any())).doThrow(expected) val sut = fixture.getSut() @@ -121,6 +121,6 @@ class EnvelopeSenderTest { testFile.deleteOnExit() sut.processFile(testFile, Hint()) verify(fixture.logger)!!.log(eq(SentryLevel.ERROR), eq(expected), eq("Failed to capture cached envelope %s"), eq(testFile.absolutePath)) - verifyNoMoreInteractions(fixture.scopes) + verifyNoMoreInteractions(fixture.hub) } } diff --git a/sentry/src/test/java/io/sentry/ExternalOptionsTest.kt b/sentry/src/test/java/io/sentry/ExternalOptionsTest.kt index fd5b363219..04c181b194 100644 --- a/sentry/src/test/java/io/sentry/ExternalOptionsTest.kt +++ b/sentry/src/test/java/io/sentry/ExternalOptionsTest.kt @@ -286,13 +286,6 @@ class ExternalOptionsTest { } } - @Test - fun `creates options with sendDefaultPii set to true`() { - withPropertiesFile("send-default-pii=true") { options -> - assertTrue(options.isSendDefaultPii == true) - } - } - private fun withPropertiesFile(textLines: List = emptyList(), logger: ILogger = mock(), fn: (ExternalOptions) -> Unit) { // create a sentry.properties file in temporary folder val temporaryFolder = TemporaryFolder() diff --git a/sentry/src/test/java/io/sentry/HubAdapterTest.kt b/sentry/src/test/java/io/sentry/HubAdapterTest.kt index 9c6ff6ddf1..9686250d20 100644 --- a/sentry/src/test/java/io/sentry/HubAdapterTest.kt +++ b/sentry/src/test/java/io/sentry/HubAdapterTest.kt @@ -2,9 +2,7 @@ package io.sentry import io.sentry.protocol.SentryTransaction import io.sentry.protocol.User -import io.sentry.test.createSentryClientMock import org.mockito.kotlin.any -import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.reset @@ -15,11 +13,11 @@ import kotlin.test.Test class HubAdapterTest { - val scopes: IScopes = mock() + val hub: Hub = mock() @BeforeTest fun `set up`() { - Sentry.setCurrentScopes(scopes) + Sentry.setCurrentHub(hub) } @AfterTest @@ -29,7 +27,7 @@ class HubAdapterTest { @Test fun `isEnabled calls Hub`() { HubAdapter.getInstance().isEnabled - verify(scopes).isEnabled + verify(hub).isEnabled } @Test fun `captureEvent calls Hub`() { @@ -37,27 +35,27 @@ class HubAdapterTest { val hint = mock() val scopeCallback = mock() HubAdapter.getInstance().captureEvent(event, hint) - verify(scopes).captureEvent(eq(event), eq(hint)) + verify(hub).captureEvent(eq(event), eq(hint)) HubAdapter.getInstance().captureEvent(event, hint, scopeCallback) - verify(scopes).captureEvent(eq(event), eq(hint), eq(scopeCallback)) + verify(hub).captureEvent(eq(event), eq(hint), eq(scopeCallback)) } @Test fun `captureMessage calls Hub`() { val scopeCallback = mock() val sentryLevel = mock() HubAdapter.getInstance().captureMessage("message", sentryLevel) - verify(scopes).captureMessage(eq("message"), eq(sentryLevel)) + verify(hub).captureMessage(eq("message"), eq(sentryLevel)) HubAdapter.getInstance().captureMessage("message", sentryLevel, scopeCallback) - verify(scopes).captureMessage(eq("message"), eq(sentryLevel), eq(scopeCallback)) + verify(hub).captureMessage(eq("message"), eq(sentryLevel), eq(scopeCallback)) } @Test fun `captureEnvelope calls Hub`() { val envelope = mock() val hint = mock() HubAdapter.getInstance().captureEnvelope(envelope, hint) - verify(scopes).captureEnvelope(eq(envelope), eq(hint)) + verify(hub).captureEnvelope(eq(envelope), eq(hint)) } @Test fun `captureException calls Hub`() { @@ -65,145 +63,145 @@ class HubAdapterTest { val hint = mock() val scopeCallback = mock() HubAdapter.getInstance().captureException(throwable, hint) - verify(scopes).captureException(eq(throwable), eq(hint)) + verify(hub).captureException(eq(throwable), eq(hint)) HubAdapter.getInstance().captureException(throwable, hint, scopeCallback) - verify(scopes).captureException(eq(throwable), eq(hint), eq(scopeCallback)) + verify(hub).captureException(eq(throwable), eq(hint), eq(scopeCallback)) } @Test fun `captureUserFeedback calls Hub`() { val userFeedback = mock() HubAdapter.getInstance().captureUserFeedback(userFeedback) - verify(scopes).captureUserFeedback(eq(userFeedback)) + verify(hub).captureUserFeedback(eq(userFeedback)) } @Test fun `captureCheckIn calls Hub`() { val checkIn = mock() HubAdapter.getInstance().captureCheckIn(checkIn) - verify(scopes).captureCheckIn(eq(checkIn)) + verify(hub).captureCheckIn(eq(checkIn)) } @Test fun `startSession calls Hub`() { HubAdapter.getInstance().startSession() - verify(scopes).startSession() + verify(hub).startSession() } @Test fun `endSession calls Hub`() { HubAdapter.getInstance().endSession() - verify(scopes).endSession() + verify(hub).endSession() } @Test fun `close calls Hub`() { HubAdapter.getInstance().close() - verify(scopes).close(false) + verify(hub).close(false) } @Test fun `close with isRestarting true calls Hub with isRestarting false`() { HubAdapter.getInstance().close(true) - verify(scopes).close(false) + verify(hub).close(false) } @Test fun `close with isRestarting false calls Hub with isRestarting false`() { HubAdapter.getInstance().close(false) - verify(scopes).close(false) + verify(hub).close(false) } @Test fun `addBreadcrumb calls Hub`() { val breadcrumb = mock() val hint = mock() HubAdapter.getInstance().addBreadcrumb(breadcrumb, hint) - verify(scopes).addBreadcrumb(eq(breadcrumb), eq(hint)) + verify(hub).addBreadcrumb(eq(breadcrumb), eq(hint)) } @Test fun `setLevel calls Hub`() { val sentryLevel = mock() HubAdapter.getInstance().setLevel(sentryLevel) - verify(scopes).setLevel(eq(sentryLevel)) + verify(hub).setLevel(eq(sentryLevel)) } @Test fun `setTransaction calls Hub`() { HubAdapter.getInstance().setTransaction("transaction") - verify(scopes).setTransaction(eq("transaction")) + verify(hub).setTransaction(eq("transaction")) } @Test fun `setUser calls Hub`() { val user = mock() HubAdapter.getInstance().setUser(user) - verify(scopes).setUser(eq(user)) + verify(hub).setUser(eq(user)) } @Test fun `setFingerprint calls Hub`() { val fingerprint = ArrayList() HubAdapter.getInstance().setFingerprint(fingerprint) - verify(scopes).setFingerprint(eq(fingerprint)) + verify(hub).setFingerprint(eq(fingerprint)) } @Test fun `clearBreadcrumbs calls Hub`() { HubAdapter.getInstance().clearBreadcrumbs() - verify(scopes).clearBreadcrumbs() + verify(hub).clearBreadcrumbs() } @Test fun `setTag calls Hub`() { HubAdapter.getInstance().setTag("key", "value") - verify(scopes).setTag(eq("key"), eq("value")) + verify(hub).setTag(eq("key"), eq("value")) } @Test fun `removeTag calls Hub`() { HubAdapter.getInstance().removeTag("key") - verify(scopes).removeTag(eq("key")) + verify(hub).removeTag(eq("key")) } @Test fun `setExtra calls Hub`() { HubAdapter.getInstance().setExtra("key", "value") - verify(scopes).setExtra(eq("key"), eq("value")) + verify(hub).setExtra(eq("key"), eq("value")) } @Test fun `removeExtra calls Hub`() { HubAdapter.getInstance().removeExtra("key") - verify(scopes).removeExtra(eq("key")) + verify(hub).removeExtra(eq("key")) } @Test fun `getLastEventId calls Hub`() { HubAdapter.getInstance().lastEventId - verify(scopes).lastEventId + verify(hub).lastEventId } @Test fun `pushScope calls Hub`() { HubAdapter.getInstance().pushScope() - verify(scopes).pushScope() + verify(hub).pushScope() } @Test fun `popScope calls Hub`() { HubAdapter.getInstance().popScope() - verify(scopes).popScope() + verify(hub).popScope() } @Test fun `withScope calls Hub`() { val scopeCallback = mock() HubAdapter.getInstance().withScope(scopeCallback) - verify(scopes).withScope(eq(scopeCallback)) + verify(hub).withScope(eq(scopeCallback)) } @Test fun `configureScope calls Hub`() { val scopeCallback = mock() HubAdapter.getInstance().configureScope(scopeCallback) - verify(scopes).configureScope(anyOrNull(), eq(scopeCallback)) + verify(hub).configureScope(eq(scopeCallback)) } @Test fun `bindClient calls Hub`() { - val client = createSentryClientMock() + val client = mock() HubAdapter.getInstance().bindClient(client) - verify(scopes).bindClient(eq(client)) + verify(hub).bindClient(eq(client)) } @Test fun `flush calls Hub`() { HubAdapter.getInstance().flush(1) - verify(scopes).flush(eq(1)) + verify(hub).flush(eq(1)) } @Test fun `clone calls Hub`() { HubAdapter.getInstance().clone() - verify(scopes).clone() + verify(hub).clone() } @Test fun `captureTransaction calls Hub`() { @@ -212,7 +210,7 @@ class HubAdapterTest { val hint = mock() val profilingTraceData = mock() HubAdapter.getInstance().captureTransaction(transaction, traceContext, hint, profilingTraceData) - verify(scopes).captureTransaction(eq(transaction), eq(traceContext), eq(hint), eq(profilingTraceData)) + verify(hub).captureTransaction(eq(transaction), eq(traceContext), eq(hint), eq(profilingTraceData)) } @Test fun `startTransaction calls Hub`() { @@ -220,48 +218,48 @@ class HubAdapterTest { val samplingContext = mock() val transactionOptions = mock() HubAdapter.getInstance().startTransaction(transactionContext) - verify(scopes).startTransaction(eq(transactionContext), any()) + verify(hub).startTransaction(eq(transactionContext), any()) - reset(scopes) + reset(hub) HubAdapter.getInstance().startTransaction(transactionContext, transactionOptions) - verify(scopes).startTransaction(eq(transactionContext), eq(transactionOptions)) + verify(hub).startTransaction(eq(transactionContext), eq(transactionOptions)) } @Test fun `traceHeaders calls Hub`() { HubAdapter.getInstance().traceHeaders() - verify(scopes).traceHeaders() + verify(hub).traceHeaders() } @Test fun `setSpanContext calls Hub`() { val throwable = mock() val span = mock() HubAdapter.getInstance().setSpanContext(throwable, span, "transactionName") - verify(scopes).setSpanContext(eq(throwable), eq(span), eq("transactionName")) + verify(hub).setSpanContext(eq(throwable), eq(span), eq("transactionName")) } @Test fun `getSpan calls Hub`() { HubAdapter.getInstance().span - verify(scopes).span + verify(hub).span } @Test fun `getTransaction calls Hub`() { HubAdapter.getInstance().transaction - verify(scopes).transaction + verify(hub).transaction } @Test fun `getOptions calls Hub`() { HubAdapter.getInstance().options - verify(scopes).options + verify(hub).options } @Test fun `isCrashedLastRun calls Hub`() { HubAdapter.getInstance().isCrashedLastRun - verify(scopes).isCrashedLastRun + verify(hub).isCrashedLastRun } @Test fun `reportFullyDisplayed calls Hub`() { HubAdapter.getInstance().reportFullyDisplayed() - verify(scopes).reportFullyDisplayed() + verify(hub).reportFullyDisplayed() } } diff --git a/sentry/src/test/java/io/sentry/ScopesTest.kt b/sentry/src/test/java/io/sentry/HubTest.kt similarity index 72% rename from sentry/src/test/java/io/sentry/ScopesTest.kt rename to sentry/src/test/java/io/sentry/HubTest.kt index 8988470b23..8fac963e70 100644 --- a/sentry/src/test/java/io/sentry/ScopesTest.kt +++ b/sentry/src/test/java/io/sentry/HubTest.kt @@ -12,11 +12,8 @@ import io.sentry.protocol.SentryTransaction import io.sentry.protocol.User import io.sentry.test.DeferredExecutorService import io.sentry.test.callMethod -import io.sentry.test.createSentryClientMock -import io.sentry.test.createTestScopes import io.sentry.util.HintUtils import io.sentry.util.StringUtils -import junit.framework.TestCase.assertSame import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.argWhere @@ -39,7 +36,6 @@ import java.util.UUID import java.util.concurrent.atomic.AtomicReference import kotlin.test.AfterTest import kotlin.test.BeforeTest -import kotlin.test.Ignore import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -50,7 +46,7 @@ import kotlin.test.assertNull import kotlin.test.assertTrue import kotlin.test.fail -class ScopesTest { +class HubTest { private lateinit var file: File private lateinit var profilingTraceFile: File @@ -60,7 +56,6 @@ class ScopesTest { file = Files.createTempDirectory("sentry-disk-cache-test").toAbsolutePath().toFile() profilingTraceFile = Files.createTempFile("trace", ".trace").toFile() profilingTraceFile.writeText("sampledProfile") - SentryCrashLastRunState.getInstance().reset() } @AfterTest @@ -68,99 +63,59 @@ class ScopesTest { file.deleteRecursively() profilingTraceFile.delete() Sentry.close() - SentryCrashLastRunState.getInstance().reset() - } - - private fun createScopes(options: SentryOptions): Scopes { - return createTestScopes(options).also { - it.bindClient(SentryClient(options)) - } } @Test fun `when no dsn available, ctor throws illegal arg`() { - val ex = assertFailsWith { createScopes(SentryOptions()) } - assertEquals("Scopes requires a DSN to be instantiated. Considering using the NoOpScopes if no DSN is available.", ex.message) - } - - @Test - fun `when isolation scope is forked, integrations are not registered`() { - val integrationMock = mock() - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - options.addIntegration(integrationMock) - val scopes = createScopes(options) - scopes.forkedScopes("test") - verifyNoMoreInteractions(integrationMock) + val ex = assertFailsWith { Hub(SentryOptions()) } + assertEquals("Hub requires a DSN to be instantiated. Considering using the NoOpHub if no DSN is available.", ex.message) } @Test - fun `when current scope is forked, integrations are not registered`() { + fun `when hub is cloned, integrations are not registered`() { val integrationMock = mock() val options = SentryOptions() options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) options.addIntegration(integrationMock) - val scopes = createScopes(options) - scopes.forkedCurrentScope("test") +// val expected = HubAdapter.getInstance() + val hub = Hub(options) +// verify(integrationMock).register(expected, options) + hub.clone() verifyNoMoreInteractions(integrationMock) } @Test - fun `when isolation scope is forked, scope changes are isolated`() { - val options = SentryOptions() - options.cacheDirPath = file.absolutePath - options.dsn = "https://key@sentry.io/proj" - options.setSerializer(mock()) - val scopes = createScopes(options) - var firstScope: IScope? = null - scopes.configureScope { - firstScope = it - it.setTag("scopes", "a") - } - var cloneScope: IScope? = null - val clone = scopes.forkedScopes("test") - clone.configureScope { - cloneScope = it - it.setTag("scopes", "b") - } - assertEquals("a", firstScope!!.tags["scopes"]) - assertEquals("b", cloneScope!!.tags["scopes"]) - } - - @Test - fun `when current scope is forked, scope changes are not isolated`() { + fun `when hub is cloned, scope changes are isolated`() { val options = SentryOptions() options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val scopes = createScopes(options) + val hub = Hub(options) var firstScope: IScope? = null - scopes.configureScope { + hub.configureScope { firstScope = it - it.setTag("scopes", "a") + it.setTag("hub", "a") } var cloneScope: IScope? = null - val clone = scopes.forkedCurrentScope("test") + val clone = hub.clone() clone.configureScope { cloneScope = it - it.setTag("scopes", "b") + it.setTag("hub", "b") } - assertEquals("b", firstScope!!.tags["scopes"]) - assertEquals("b", cloneScope!!.tags["scopes"]) + assertEquals("a", firstScope!!.tags["hub"]) + assertEquals("b", cloneScope!!.tags["hub"]) } @Test - fun `when scopes is initialized, breadcrumbs are capped as per options`() { + fun `when hub is initialized, breadcrumbs are capped as per options`() { val options = SentryOptions() options.cacheDirPath = file.absolutePath options.maxBreadcrumbs = 5 options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) + val sut = Hub(options) (1..10).forEach { _ -> sut.addBreadcrumb(Breadcrumb(), null) } var actual = 0 sut.configureScope { @@ -176,7 +131,7 @@ class ScopesTest { options.beforeBreadcrumb = SentryOptions.BeforeBreadcrumbCallback { _: Breadcrumb, _: Any? -> null } options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) + val sut = Hub(options) sut.addBreadcrumb(Breadcrumb(), null) var breadcrumbs: Queue? = null sut.configureScope { breadcrumbs = it.breadcrumbs } @@ -191,7 +146,7 @@ class ScopesTest { options.beforeBreadcrumb = SentryOptions.BeforeBreadcrumbCallback { breadcrumb: Breadcrumb, _: Any? -> breadcrumb.message = expected; breadcrumb; } options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) + val sut = Hub(options) val crumb = Breadcrumb() crumb.message = "original" sut.addBreadcrumb(crumb) @@ -207,7 +162,7 @@ class ScopesTest { options.beforeBreadcrumb = null options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) + val sut = Hub(options) val expected = Breadcrumb() sut.addBreadcrumb(expected) var breadcrumbs: Queue? = null @@ -224,7 +179,7 @@ class ScopesTest { options.beforeBreadcrumb = SentryOptions.BeforeBreadcrumbCallback { _: Breadcrumb, _: Any? -> throw exception } options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) + val sut = Hub(options) val actual = Breadcrumb() sut.addBreadcrumb(actual) @@ -238,7 +193,7 @@ class ScopesTest { options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) + val sut = Hub(options) assertEquals(SentryId.EMPTY_ID, sut.lastEventId) } @@ -248,9 +203,9 @@ class ScopesTest { options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) + val sut = Hub(options) var breadcrumbs: Queue? = null - sut.configureScope(ScopeType.COMBINED) { breadcrumbs = it.breadcrumbs } + sut.configureScope { breadcrumbs = it.breadcrumbs } sut.close() sut.addBreadcrumb(Breadcrumb()) assertTrue(breadcrumbs!!.isEmpty()) @@ -262,7 +217,7 @@ class ScopesTest { options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) + val sut = Hub(options) var breadcrumbs: Queue? = null sut.configureScope { breadcrumbs = it.breadcrumbs } sut.addBreadcrumb("message", "category") @@ -276,7 +231,7 @@ class ScopesTest { options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) + val sut = Hub(options) var breadcrumbs: Queue? = null sut.configureScope { breadcrumbs = it.breadcrumbs } sut.addBreadcrumb("message", "category") @@ -286,7 +241,7 @@ class ScopesTest { @Test fun `when flush is called on disabled client, no-op`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.close() sut.flush(1000) @@ -295,7 +250,7 @@ class ScopesTest { @Test fun `when flush is called, client flush gets called`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.flush(1000) verify(mockClient).flush(1000) @@ -308,14 +263,14 @@ class ScopesTest { options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) + val sut = Hub(options) sut.callMethod("captureEvent", SentryEvent::class.java, null) assertEquals(SentryId.EMPTY_ID, sut.lastEventId) } @Test fun `when captureEvent is called on disabled client, do nothing`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.close() sut.captureEvent(SentryEvent()) @@ -324,7 +279,7 @@ class ScopesTest { @Test fun `when captureEvent is called with a valid argument, captureEvent on the client should be called`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() val event = SentryEvent() val hints = HintUtils.createWithTypeCheckHint({}) @@ -333,8 +288,8 @@ class ScopesTest { } @Test - fun `when captureEvent is called on disabled scopes, lastEventId does not get overwritten`() { - val (sut, mockClient) = getEnabledScopes() + fun `when captureEvent is called on disabled hub, lastEventId does not get overwritten`() { + val (sut, mockClient) = getEnabledHub() whenever(mockClient.captureEvent(any(), any(), anyOrNull())).thenReturn(SentryId(UUID.randomUUID())) val event = SentryEvent() val hints = HintUtils.createWithTypeCheckHint({}) @@ -347,7 +302,7 @@ class ScopesTest { @Test fun `when captureEvent is called and session tracking is disabled, it should not capture a session`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() val event = SentryEvent() val hints = HintUtils.createWithTypeCheckHint({}) @@ -358,7 +313,7 @@ class ScopesTest { @Test fun `when captureEvent is called but no session started, it should not capture a session`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() val event = SentryEvent() val hints = HintUtils.createWithTypeCheckHint({}) @@ -369,7 +324,7 @@ class ScopesTest { @Test fun `when captureEvent is called and event has exception which has been previously attached with span context, sets span context to the event`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() val exception = RuntimeException() val span = mock() whenever(span.spanContext).thenReturn(SpanContext("op")) @@ -385,7 +340,7 @@ class ScopesTest { @Test fun `when captureEvent is called and event has exception which root cause has been previously attached with span context, sets span context to the event`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() val rootCause = RuntimeException() val span = mock() whenever(span.spanContext).thenReturn(SpanContext("op")) @@ -401,7 +356,7 @@ class ScopesTest { @Test fun `when captureEvent is called and event has exception which non-root cause has been previously attached with span context, sets span context to the event`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() val rootCause = RuntimeException() val exceptionAssignedToSpan = RuntimeException(rootCause) val span = mock() @@ -418,7 +373,7 @@ class ScopesTest { @Test fun `when captureEvent is called and event has exception which has been previously attached with span context and trace context already set, does not set new span context to the event`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() val exception = RuntimeException() val span = mock() whenever(span.spanContext).thenReturn(SpanContext("op")) @@ -436,7 +391,7 @@ class ScopesTest { @Test fun `when captureEvent is called and event has exception which has not been previously attached with span context, does not set new span context to the event`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() val event = SentryEvent(RuntimeException()) @@ -448,7 +403,7 @@ class ScopesTest { @Test fun `when captureEvent is called with a ScopeCallback then the modified scope is sent to the client`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.captureEvent(SentryEvent(), null) { it.setTag("test", "testValue") @@ -465,7 +420,7 @@ class ScopesTest { @Test fun `when captureEvent is called with a ScopeCallback then subsequent calls to captureEvent send the unmodified Scope to the client`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() val argumentCaptor = argumentCaptor() sut.captureEvent(SentryEvent(), null) { @@ -486,7 +441,7 @@ class ScopesTest { @Test fun `when captureEvent is called with a ScopeCallback that crashes then the event should still be captured`() { - val (sut, mockClient, logger) = getEnabledScopes() + val (sut, mockClient, logger) = getEnabledHub() val exception = Exception("scope callback exception") sut.captureEvent(SentryEvent(), null) { @@ -510,14 +465,14 @@ class ScopesTest { options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) + val sut = Hub(options) sut.callMethod("captureMessage", String::class.java, null) assertEquals(SentryId.EMPTY_ID, sut.lastEventId) } @Test fun `when captureMessage is called on disabled client, do nothing`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.close() sut.captureMessage("test") @@ -526,7 +481,7 @@ class ScopesTest { @Test fun `when captureMessage is called with a valid message, captureMessage on the client should be called`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.captureMessage("test") verify(mockClient).captureMessage(any(), any(), any()) @@ -534,14 +489,14 @@ class ScopesTest { @Test fun `when captureMessage is called, level is INFO by default`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.captureMessage("test") verify(mockClient).captureMessage(eq("test"), eq(SentryLevel.INFO), any()) } @Test fun `when captureMessage is called with a ScopeCallback then the modified scope is sent to the client`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.captureMessage("test") { it.setTag("test", "testValue") @@ -558,7 +513,7 @@ class ScopesTest { @Test fun `when captureMessage is called with a ScopeCallback then subsequent calls to captureMessage send the unmodified Scope to the client`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() val argumentCaptor = argumentCaptor() sut.captureMessage("testMessage") { @@ -579,7 +534,7 @@ class ScopesTest { @Test fun `when captureMessage is called with a ScopeCallback that crashes then the message should still be captured`() { - val (sut, mockClient, logger) = getEnabledScopes() + val (sut, mockClient, logger) = getEnabledHub() val exception = Exception("scope callback exception") sut.captureMessage("Hello World") { @@ -604,14 +559,14 @@ class ScopesTest { options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) + val sut = Hub(options) sut.callMethod("captureException", Throwable::class.java, null) assertEquals(SentryId.EMPTY_ID, sut.lastEventId) } @Test fun `when captureException is called on disabled client, do nothing`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.close() sut.captureException(Throwable()) @@ -620,7 +575,7 @@ class ScopesTest { @Test fun `when captureException is called with a valid argument and hint, captureEvent on the client should be called`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() val hints = HintUtils.createWithTypeCheckHint({}) sut.captureException(Throwable(), hints) @@ -629,7 +584,7 @@ class ScopesTest { @Test fun `when captureException is called with a valid argument but no hint, captureEvent on the client should be called`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.captureException(Throwable()) verify(mockClient).captureEvent(any(), any(), any()) @@ -637,7 +592,7 @@ class ScopesTest { @Test fun `when captureException is called with an exception which has been previously attached with span context, span context should be set on the event before capturing`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() val throwable = Throwable() val span = mock() whenever(span.spanContext).thenReturn(SpanContext("op")) @@ -656,7 +611,7 @@ class ScopesTest { @Test fun `when captureException is called with an exception which has not been previously attached with span context, span context should not be set on the event before capturing`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() val span = mock() whenever(span.spanContext).thenReturn(SpanContext("op")) sut.setSpanContext(Throwable(), span, "tx-name") @@ -673,7 +628,7 @@ class ScopesTest { @Test fun `when captureException is called with a ScopeCallback then the modified scope is sent to the client`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.captureException(Throwable(), null) { it.setTag("test", "testValue") @@ -690,7 +645,7 @@ class ScopesTest { @Test fun `when captureException is called with a ScopeCallback then subsequent calls to captureException send the unmodified Scope to the client`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() val argumentCaptor = argumentCaptor() sut.captureException(Throwable(), null) { @@ -711,7 +666,7 @@ class ScopesTest { @Test fun `when captureException is called with a ScopeCallback that crashes then the exception should still be captured`() { - val (sut, mockClient, logger) = getEnabledScopes() + val (sut, mockClient, logger) = getEnabledHub() val exception = Exception("scope callback exception") sut.captureException(Throwable()) { @@ -733,7 +688,7 @@ class ScopesTest { @Test fun `when captureUserFeedback is called it is forwarded to the client`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.captureUserFeedback(userFeedback) verify(mockClient).captureUserFeedback( @@ -748,7 +703,7 @@ class ScopesTest { @Test fun `when captureUserFeedback is called on disabled client, do nothing`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.close() sut.captureUserFeedback(userFeedback) @@ -757,7 +712,7 @@ class ScopesTest { @Test fun `when captureUserFeedback is called and client throws, don't crash`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() whenever(mockClient.captureUserFeedback(any())).doThrow(IllegalArgumentException("")) @@ -777,7 +732,7 @@ class ScopesTest { @Test fun `when captureCheckIn is called it is forwarded to the client`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.captureCheckIn(checkIn) verify(mockClient).captureCheckIn( @@ -793,7 +748,7 @@ class ScopesTest { @Test fun `when captureCheckIn is called on disabled client, do nothing`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.close() sut.captureCheckIn(checkIn) @@ -802,7 +757,7 @@ class ScopesTest { @Test fun `when captureCheckIn is called and client throws, don't crash`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() whenever(mockClient.captureCheckIn(any(), any(), anyOrNull())).doThrow(IllegalArgumentException("")) @@ -816,7 +771,7 @@ class ScopesTest { //region close tests @Test fun `when close is called on disabled client, do nothing`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.close() sut.close() @@ -825,7 +780,7 @@ class ScopesTest { @Test fun `when close is called and client is alive, close on the client should be called`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.close() verify(mockClient).close(eq(false)) @@ -833,7 +788,7 @@ class ScopesTest { @Test fun `when close is called with isRestarting false and client is alive, close on the client should be called with isRestarting false`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.close(false) verify(mockClient).close(eq(false)) @@ -841,7 +796,7 @@ class ScopesTest { @Test fun `when close is called with isRestarting true and client is alive, close on the client should be called with isRestarting true`() { - val (sut, mockClient) = getEnabledScopes() + val (sut, mockClient) = getEnabledHub() sut.close(true) verify(mockClient).close(eq(true)) @@ -851,7 +806,7 @@ class ScopesTest { //region withScope tests @Test fun `when withScope is called on disabled client, execute on NoOpScope`() { - val (sut) = getEnabledScopes() + val (sut) = getEnabledHub() val scopeCallback = mock() sut.close() @@ -862,7 +817,7 @@ class ScopesTest { @Test fun `when withScope is called with alive client, run should be called`() { - val (sut) = getEnabledScopes() + val (sut) = getEnabledHub() val scopeCallback = mock() @@ -872,51 +827,14 @@ class ScopesTest { @Test fun `when withScope throws an exception then it should be caught`() { - val (scopes, _, logger) = getEnabledScopes() - - val exception = Exception("scope callback exception") - val scopeCallback = ScopeCallback { - throw exception - } - - scopes.withScope(scopeCallback) - - verify(logger).log(eq(SentryLevel.ERROR), any(), eq(exception)) - } - //endregion - - //region withIsolationScope tests - @Test - fun `when withIsolationScope is called on disabled client, execute on NoOpScope`() { - val (sut) = getEnabledScopes() - - val scopeCallback = mock() - sut.close() - - sut.withIsolationScope(scopeCallback) - verify(scopeCallback).run(NoOpScope.getInstance()) - } - - @Test - fun `when withIsolationScope is called with alive client, run should be called`() { - val (sut) = getEnabledScopes() - - val scopeCallback = mock() - - sut.withIsolationScope(scopeCallback) - verify(scopeCallback).run(any()) - } - - @Test - fun `when withIsolationScope throws an exception then it should be caught`() { - val (scopes, _, logger) = getEnabledScopes() + val (hub, _, logger) = getEnabledHub() val exception = Exception("scope callback exception") val scopeCallback = ScopeCallback { throw exception } - scopes.withIsolationScope(scopeCallback) + hub.withScope(scopeCallback) verify(logger).log(eq(SentryLevel.ERROR), any(), eq(exception)) } @@ -925,7 +843,7 @@ class ScopesTest { //region configureScope tests @Test fun `when configureScope is called on disabled client, do nothing`() { - val (sut) = getEnabledScopes() + val (sut) = getEnabledHub() val scopeCallback = mock() sut.close() @@ -936,7 +854,7 @@ class ScopesTest { @Test fun `when configureScope is called with alive client, run should be called`() { - val (sut) = getEnabledScopes() + val (sut) = getEnabledHub() val scopeCallback = mock() @@ -946,25 +864,25 @@ class ScopesTest { @Test fun `when configureScope throws an exception then it should be caught`() { - val (scopes, _, logger) = getEnabledScopes() + val (hub, _, logger) = getEnabledHub() val exception = Exception("scope callback exception") val scopeCallback = ScopeCallback { throw exception } - scopes.configureScope(scopeCallback) + hub.configureScope(scopeCallback) verify(logger).log(eq(SentryLevel.ERROR), any(), eq(exception)) } //endregion @Test - fun `when integration is registered, scopes is enabled`() { + fun `when integration is registered, hub is enabled`() { val mock = mock() var options: SentryOptions? = null - // init main scopes and make it enabled + // init main hub and make it enabled Sentry.init { it.addIntegration(mock) it.dsn = "https://key@sentry.io/proj" @@ -974,8 +892,8 @@ class ScopesTest { } doAnswer { - val scopes = it.arguments[0] as IScopes - assertTrue(scopes.isEnabled) + val hub = it.arguments[0] as IHub + assertTrue(hub.isEnabled) }.whenever(mock).register(any(), eq(options!!)) verify(mock).register(any(), eq(options!!)) @@ -984,26 +902,26 @@ class ScopesTest { //region setLevel tests @Test fun `when setLevel is called on disabled client, do nothing`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } - scopes.close() + hub.close() - scopes.setLevel(SentryLevel.INFO) + hub.setLevel(SentryLevel.INFO) assertNull(scope?.level) } @Test fun `when setLevel is called, level is set`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } - scopes.setLevel(SentryLevel.INFO) + hub.setLevel(SentryLevel.INFO) assertEquals(SentryLevel.INFO, scope?.level) } //endregion @@ -1011,78 +929,74 @@ class ScopesTest { //region setTransaction tests @Test fun `when setTransaction is called on disabled client, do nothing`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } - scopes.close() + hub.close() - scopes.setTransaction("test") + hub.setTransaction("test") assertNull(scope?.transactionName) } @Test fun `when setTransaction is called, and transaction is not set, transaction name is changed`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } - scopes.setTransaction("test") + hub.setTransaction("test") assertEquals("test", scope?.transactionName) } @Test fun `when setTransaction is called, and transaction is set, transaction name is changed`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } - val tx = scopes.startTransaction("test", "op") - scopes.configureScope { it.setTransaction(tx) } + val tx = hub.startTransaction("test", "op") + hub.configureScope { it.setTransaction(tx) } assertEquals("test", scope?.transactionName) } - // TODO [POTEL] how do we handle instrumenter? - @Ignore @Test fun `when startTransaction is called with different instrumenter, no-op is returned`() { - val scopes = generateScopes() + val hub = generateHub() val transactionContext = TransactionContext("test", "op").also { it.instrumenter = Instrumenter.OTEL } val transactionOptions = TransactionOptions() - val tx = scopes.startTransaction(transactionContext, transactionOptions) + val tx = hub.startTransaction(transactionContext, transactionOptions) assertTrue(tx is NoOpTransaction) } - // TODO [POTEL] how do we handle instrumenter? - @Ignore @Test fun `when startTransaction is called with different instrumenter, no-op is returned 2`() { - val scopes = generateScopes() { + val hub = generateHub() { it.instrumenter = Instrumenter.OTEL } - val tx = scopes.startTransaction("test", "op") + val tx = hub.startTransaction("test", "op") assertTrue(tx is NoOpTransaction) } @Test fun `when startTransaction is called with configured instrumenter, it works`() { - val scopes = generateScopes() { + val hub = generateHub() { it.instrumenter = Instrumenter.OTEL } val transactionContext = TransactionContext("test", "op").also { it.instrumenter = Instrumenter.OTEL } val transactionOptions = TransactionOptions() - val tx = scopes.startTransaction(transactionContext, transactionOptions) + val tx = hub.startTransaction(transactionContext, transactionOptions) assertFalse(tx is NoOpTransaction) } @@ -1091,27 +1005,27 @@ class ScopesTest { //region setUser tests @Test fun `when setUser is called on disabled client, do nothing`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } - scopes.close() + hub.close() - scopes.setUser(User()) + hub.setUser(User()) assertNull(scope?.user) } @Test fun `when setUser is called, user is set`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } val user = User() - scopes.setUser(user) + hub.setUser(user) assertEquals(user, scope?.user) } //endregion @@ -1119,40 +1033,40 @@ class ScopesTest { //region setFingerprint tests @Test fun `when setFingerprint is called on disabled client, do nothing`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } - scopes.close() + hub.close() val fingerprint = listOf("abc") - scopes.setFingerprint(fingerprint) + hub.setFingerprint(fingerprint) assertEquals(0, scope?.fingerprint?.count()) } @Test fun `when setFingerprint is called with null parameter, do nothing`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } - scopes.callMethod("setFingerprint", List::class.java, null) + hub.callMethod("setFingerprint", List::class.java, null) assertEquals(0, scope?.fingerprint?.count()) } @Test fun `when setFingerprint is called, fingerprint is set`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } val fingerprint = listOf("abc") - scopes.setFingerprint(fingerprint) + hub.setFingerprint(fingerprint) assertEquals(1, scope?.fingerprint?.count()) } //endregion @@ -1160,30 +1074,30 @@ class ScopesTest { //region clearBreadcrumbs tests @Test fun `when clearBreadcrumbs is called on disabled client, do nothing`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } - scopes.addBreadcrumb(Breadcrumb()) + hub.addBreadcrumb(Breadcrumb()) assertEquals(1, scope?.breadcrumbs?.count()) - scopes.close() + hub.close() assertEquals(0, scope?.breadcrumbs?.count()) } @Test fun `when clearBreadcrumbs is called, clear breadcrumbs`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } - scopes.addBreadcrumb(Breadcrumb()) + hub.addBreadcrumb(Breadcrumb()) assertEquals(1, scope?.breadcrumbs?.count()) - scopes.clearBreadcrumbs() + hub.clearBreadcrumbs() assertEquals(0, scope?.breadcrumbs?.count()) } //endregion @@ -1191,38 +1105,38 @@ class ScopesTest { //region setTag tests @Test fun `when setTag is called on disabled client, do nothing`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } - scopes.close() + hub.close() - scopes.setTag("test", "test") + hub.setTag("test", "test") assertEquals(0, scope?.tags?.count()) } @Test fun `when setTag is called with null parameters, do nothing`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } - scopes.callMethod("setTag", parameterTypes = arrayOf(String::class.java, String::class.java), null, null) + hub.callMethod("setTag", parameterTypes = arrayOf(String::class.java, String::class.java), null, null) assertEquals(0, scope?.tags?.count()) } @Test fun `when setTag is called, tag is set`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } - scopes.setTag("test", "test") + hub.setTag("test", "test") assertEquals(1, scope?.tags?.count()) } //endregion @@ -1230,38 +1144,38 @@ class ScopesTest { //region setExtra tests @Test fun `when setExtra is called on disabled client, do nothing`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } - scopes.close() + hub.close() - scopes.setExtra("test", "test") + hub.setExtra("test", "test") assertEquals(0, scope?.extras?.count()) } @Test fun `when setExtra is called with null parameters, do nothing`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } - scopes.callMethod("setExtra", parameterTypes = arrayOf(String::class.java, String::class.java), null, null) + hub.callMethod("setExtra", parameterTypes = arrayOf(String::class.java, String::class.java), null, null) assertEquals(0, scope?.extras?.count()) } @Test fun `when setExtra is called, extra is set`() { - val scopes = generateScopes() + val hub = generateHub() var scope: IScope? = null - scopes.configureScope { + hub.configureScope { scope = it } - scopes.setExtra("test", "test") + hub.setExtra("test", "test") assertEquals(1, scope?.extras?.count()) } //endregion @@ -1273,7 +1187,7 @@ class ScopesTest { options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) + val sut = Hub(options) try { sut.callMethod("captureEnvelope", SentryEnvelope::class.java, null) fail() @@ -1288,8 +1202,8 @@ class ScopesTest { options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) - val mockClient = createSentryClientMock(enabled = false) + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) sut.close() @@ -1303,8 +1217,8 @@ class ScopesTest { options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) - val mockClient = createSentryClientMock() + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) val envelope = SentryEnvelope(SentryId(UUID.randomUUID()), null, setOf()) @@ -1318,8 +1232,8 @@ class ScopesTest { dsn = "https://key@sentry.io/proj" setSerializer(mock()) } - val sut = createScopes(options) - val mockClient = createSentryClientMock() + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) whenever(mockClient.captureEnvelope(any(), anyOrNull())).thenReturn(SentryId()) val envelope = SentryEnvelope(SentryId(UUID.randomUUID()), null, setOf()) @@ -1336,15 +1250,11 @@ class ScopesTest { options.dsn = "https://key@sentry.io/proj" options.release = "0.0.1" options.setSerializer(mock()) - val sut = createScopes(options) - val mockClient = createSentryClientMock() + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) sut.close() - sut.configureScope(ScopeType.ISOLATION) { scope -> - scope.client.isEnabled - } - sut.startSession() verify(mockClient, never()).captureSession(any(), any()) } @@ -1356,8 +1266,8 @@ class ScopesTest { options.dsn = "https://key@sentry.io/proj" options.release = "0.0.1" options.setSerializer(mock()) - val sut = createScopes(options) - val mockClient = createSentryClientMock() + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) sut.startSession() @@ -1371,8 +1281,8 @@ class ScopesTest { options.dsn = "https://key@sentry.io/proj" options.release = "0.0.1" options.setSerializer(mock()) - val sut = createScopes(options) - val mockClient = createSentryClientMock() + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) sut.startSession() @@ -1390,8 +1300,8 @@ class ScopesTest { options.dsn = "https://key@sentry.io/proj" options.release = "0.0.1" options.setSerializer(mock()) - val sut = createScopes(options) - val mockClient = createSentryClientMock(enabled = false) + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) sut.close() @@ -1406,8 +1316,8 @@ class ScopesTest { options.dsn = "https://key@sentry.io/proj" options.release = "0.0.1" options.setSerializer(mock()) - val sut = createScopes(options) - val mockClient = createSentryClientMock() + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) sut.endSession() @@ -1421,8 +1331,8 @@ class ScopesTest { options.dsn = "https://key@sentry.io/proj" options.release = "0.0.1" options.setSerializer(mock()) - val sut = createScopes(options) - val mockClient = createSentryClientMock() + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) sut.startSession() @@ -1438,8 +1348,8 @@ class ScopesTest { options.dsn = "https://key@sentry.io/proj" options.release = "0.0.1" options.setSerializer(mock()) - val sut = createScopes(options) - val mockClient = createSentryClientMock() + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) sut.endSession() @@ -1454,8 +1364,8 @@ class ScopesTest { options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) - val mockClient = createSentryClientMock() + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) sut.close() @@ -1472,8 +1382,8 @@ class ScopesTest { options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) - val mockClient = createSentryClientMock() + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), sut) @@ -1488,8 +1398,8 @@ class ScopesTest { dsn = "https://key@sentry.io/proj" setSerializer(mock()) } - val sut = createScopes(options) - val mockClient = createSentryClientMock() + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) whenever(mockClient.captureTransaction(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())).thenReturn(SentryId()) @@ -1504,8 +1414,8 @@ class ScopesTest { options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) - val mockClient = createSentryClientMock() + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), sut) @@ -1519,8 +1429,8 @@ class ScopesTest { options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) - val mockClient = createSentryClientMock() + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(false)), sut) @@ -1535,21 +1445,16 @@ class ScopesTest { options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) - val mockClient = createSentryClientMock() + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(false)), sut) - // Unsampled spans are not added to the transaction, so they are not recorded - sentryTracer.startChild("dropped span", "span 1").finish() sentryTracer.finish() assertClientReport( options.clientReportRecorder, - listOf( - DiscardedEvent(DiscardReason.SAMPLE_RATE.reason, DataCategory.Transaction.category, 1), - DiscardedEvent(DiscardReason.SAMPLE_RATE.reason, DataCategory.Span.category, 1) - ) + listOf(DiscardedEvent(DiscardReason.SAMPLE_RATE.reason, DataCategory.Transaction.category, 1)) ) } @@ -1559,24 +1464,19 @@ class ScopesTest { options.cacheDirPath = file.absolutePath options.dsn = "https://key@sentry.io/proj" options.setSerializer(mock()) - val sut = createScopes(options) - val mockClient = createSentryClientMock() + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) val mockBackpressureMonitor = mock() options.backpressureMonitor = mockBackpressureMonitor whenever(mockBackpressureMonitor.downsampleFactor).thenReturn(1) val sentryTracer = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(false)), sut) - // Unsampled spans are not added to the transaction, so they are not recorded - sentryTracer.startChild("dropped span", "span 1").finish() sentryTracer.finish() assertClientReport( options.clientReportRecorder, - listOf( - DiscardedEvent(DiscardReason.BACKPRESSURE.reason, DataCategory.Transaction.category, 1), - DiscardedEvent(DiscardReason.BACKPRESSURE.reason, DataCategory.Span.category, 1) - ) + listOf(DiscardedEvent(DiscardReason.BACKPRESSURE.reason, DataCategory.Transaction.category, 1)) ) } //endregion @@ -1586,21 +1486,21 @@ class ScopesTest { @Test fun `when startTransaction and profiling is enabled, transaction is profiled only if sampled`() { val mockTransactionProfiler = mock() - val mockClient = createSentryClientMock() + val mockClient = mock() whenever(mockTransactionProfiler.onTransactionFinish(any(), anyOrNull(), anyOrNull())).thenAnswer { mockClient.captureEnvelope(mock()) } - val scopes = generateScopes { + val hub = generateHub { it.setTransactionProfiler(mockTransactionProfiler) } - scopes.bindClient(mockClient) + hub.bindClient(mockClient) // Transaction is not sampled, so it should not be profiled val contexts = TransactionContext("name", "op", TracesSamplingDecision(false, null, true, null)) - val transaction = scopes.startTransaction(contexts) + val transaction = hub.startTransaction(contexts) transaction.finish() verify(mockClient, never()).captureEnvelope(any()) // Transaction is sampled, so it should be profiled val sampledContexts = TransactionContext("name", "op", TracesSamplingDecision(true, null, true, null)) - val sampledTransaction = scopes.startTransaction(sampledContexts) + val sampledTransaction = hub.startTransaction(sampledContexts) sampledTransaction.finish() verify(mockClient).captureEnvelope(any()) } @@ -1608,15 +1508,15 @@ class ScopesTest { @Test fun `when startTransaction and is sampled but profiling is disabled, transaction is not profiled`() { val mockTransactionProfiler = mock() - val mockClient = createSentryClientMock() + val mockClient = mock() whenever(mockTransactionProfiler.onTransactionFinish(any(), anyOrNull(), anyOrNull())).thenAnswer { mockClient.captureEnvelope(mock()) } - val scopes = generateScopes { + val hub = generateHub { it.profilesSampleRate = 0.0 it.setTransactionProfiler(mockTransactionProfiler) } - scopes.bindClient(mockClient) + hub.bindClient(mockClient) val contexts = TransactionContext("name", "op") - val transaction = scopes.startTransaction(contexts) + val transaction = hub.startTransaction(contexts) transaction.finish() verify(mockClient, never()).captureEnvelope(any()) } @@ -1625,12 +1525,12 @@ class ScopesTest { fun `when profiler is running and isAppStartTransaction is false, startTransaction does not interact with profiler`() { val mockTransactionProfiler = mock() whenever(mockTransactionProfiler.isRunning).thenReturn(true) - val scopes = generateScopes { + val hub = generateHub { it.profilesSampleRate = 1.0 it.setTransactionProfiler(mockTransactionProfiler) } val context = TransactionContext("name", "op") - scopes.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = false }) + hub.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = false }) verify(mockTransactionProfiler, never()).start() verify(mockTransactionProfiler, never()).bindTransaction(any()) } @@ -1639,12 +1539,12 @@ class ScopesTest { fun `when profiler is running and isAppStartTransaction is true, startTransaction binds current profile`() { val mockTransactionProfiler = mock() whenever(mockTransactionProfiler.isRunning).thenReturn(true) - val scopes = generateScopes { + val hub = generateHub { it.profilesSampleRate = 1.0 it.setTransactionProfiler(mockTransactionProfiler) } val context = TransactionContext("name", "op") - val transaction = scopes.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = true }) + val transaction = hub.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = true }) verify(mockTransactionProfiler, never()).start() verify(mockTransactionProfiler).bindTransaction(eq(transaction)) } @@ -1653,12 +1553,12 @@ class ScopesTest { fun `when profiler is not running, startTransaction starts and binds current profile`() { val mockTransactionProfiler = mock() whenever(mockTransactionProfiler.isRunning).thenReturn(false) - val scopes = generateScopes { + val hub = generateHub { it.profilesSampleRate = 1.0 it.setTransactionProfiler(mockTransactionProfiler) } val context = TransactionContext("name", "op") - val transaction = scopes.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = false }) + val transaction = hub.startTransaction(context, TransactionOptions().apply { isAppStartTransaction = false }) verify(mockTransactionProfiler).start() verify(mockTransactionProfiler).bindTransaction(eq(transaction)) } @@ -1667,75 +1567,75 @@ class ScopesTest { //region startTransaction tests @Test fun `when startTransaction, creates transaction`() { - val scopes = generateScopes() + val hub = generateHub() val contexts = TransactionContext("name", "op") - val transaction = scopes.startTransaction(contexts) + val transaction = hub.startTransaction(contexts) assertTrue(transaction is SentryTracer) assertEquals(contexts, transaction.root.spanContext) } @Test fun `when startTransaction with bindToScope set to false, transaction is not attached to the scope`() { - val scopes = generateScopes() + val hub = generateHub() - scopes.startTransaction("name", "op", TransactionOptions()) + hub.startTransaction("name", "op", TransactionOptions()) - scopes.configureScope { + hub.configureScope { assertNull(it.span) } } @Test fun `when startTransaction without bindToScope set, transaction is not attached to the scope`() { - val scopes = generateScopes() + val hub = generateHub() - scopes.startTransaction("name", "op") + hub.startTransaction("name", "op") - scopes.configureScope { + hub.configureScope { assertNull(it.span) } } @Test fun `when startTransaction with bindToScope set to true, transaction is attached to the scope`() { - val scopes = generateScopes() + val hub = generateHub() - val transaction = scopes.startTransaction("name", "op", TransactionOptions().also { it.isBindToScope = true }) + val transaction = hub.startTransaction("name", "op", TransactionOptions().also { it.isBindToScope = true }) - scopes.configureScope { + hub.configureScope { assertEquals(transaction, it.span) } } @Test fun `when startTransaction and no tracing sampling is configured, event is not sampled`() { - val scopes = generateScopes { + val hub = generateHub { it.tracesSampleRate = 0.0 } - val transaction = scopes.startTransaction("name", "op") + val transaction = hub.startTransaction("name", "op") assertFalse(transaction.isSampled!!) } @Test fun `when startTransaction and no profile sampling is configured, profile is not sampled`() { - val scopes = generateScopes { + val hub = generateHub { it.tracesSampleRate = 1.0 it.profilesSampleRate = 0.0 } - val transaction = scopes.startTransaction("name", "op") + val transaction = hub.startTransaction("name", "op") assertTrue(transaction.isSampled!!) assertFalse(transaction.isProfileSampled!!) } @Test fun `when startTransaction with parent sampled and no traces sampler provided, transaction inherits sampling decision`() { - val scopes = generateScopes() + val hub = generateHub() val transactionContext = TransactionContext("name", "op") transactionContext.parentSampled = true - val transaction = scopes.startTransaction(transactionContext) + val transaction = hub.startTransaction(transactionContext) assertNotNull(transaction) assertNotNull(transaction.isSampled) assertTrue(transaction.isSampled!!) @@ -1743,15 +1643,15 @@ class ScopesTest { @Test fun `when startTransaction with parent profile sampled and no profile sampler provided, transaction inherits profile sampling decision`() { - val scopes = generateScopes() + val hub = generateHub() val transactionContext = TransactionContext("name", "op") transactionContext.setParentSampled(true, true) - val transaction = scopes.startTransaction(transactionContext) + val transaction = hub.startTransaction(transactionContext) assertTrue(transaction.isProfileSampled!!) } @Test - fun `Scopes should close the sentry executor processor, profiler and performance collector on close call`() { + fun `Hub should close the sentry executor processor, profiler and performance collector on close call`() { val executor = mock() val profiler = mock() val performanceCollector = mock() @@ -1762,7 +1662,7 @@ class ScopesTest { setTransactionProfiler(profiler) transactionPerformanceCollector = performanceCollector } - val sut = createScopes(options) + val sut = Hub(options) sut.close() verify(executor).close(any()) verify(profiler).close() @@ -1770,13 +1670,13 @@ class ScopesTest { } @Test - fun `Scopes with isRestarting true should close the sentry executor in the background`() { + fun `Hub with isRestarting true should close the sentry executor in the background`() { val executor = spy(DeferredExecutorService()) val options = SentryOptions().apply { dsn = "https://key@sentry.io/proj" executorService = executor } - val sut = createScopes(options) + val sut = Hub(options) sut.close(true) verify(executor, never()).close(any()) executor.runAll() @@ -1784,32 +1684,31 @@ class ScopesTest { } @Test - fun `Scopes with isRestarting false should close the sentry executor in the background`() { + fun `Hub with isRestarting false should close the sentry executor in the background`() { val executor = mock() val options = SentryOptions().apply { dsn = "https://key@sentry.io/proj" executorService = executor } - val sut = createScopes(options) + val sut = Hub(options) sut.close(false) verify(executor).close(any()) } @Test - fun `Scopes close should clear the scope`() { + fun `Hub close should clear the scope`() { val options = SentryOptions().apply { dsn = "https://key@sentry.io/proj" } - val sut = createScopes(options) + val sut = Hub(options) sut.addBreadcrumb("Test") sut.startTransaction("test", "test.op", TransactionOptions().also { it.isBindToScope = true }) sut.close() // we have to clone the scope, so its isEnabled returns true, but it's still built up from // the old scope preserving its data - val clone = sut.forkedScopes("test") - clone.bindClient(createSentryClientMock(enabled = true)) + val clone = sut.clone() var oldScope: IScope? = null clone.configureScope { scope -> oldScope = scope } assertNull(oldScope!!.transaction) @@ -1818,11 +1717,11 @@ class ScopesTest { @Test fun `when tracesSampleRate and tracesSampler are not set on SentryOptions, startTransaction returns NoOp`() { - val scopes = generateScopes { + val hub = generateHub { it.tracesSampleRate = null it.tracesSampler = null } - val transaction = scopes.startTransaction(TransactionContext("name", "op", TracesSamplingDecision(true))) + val transaction = hub.startTransaction(TransactionContext("name", "op", TracesSamplingDecision(true))) assertTrue(transaction is NoOpTransaction) } //endregion @@ -1830,81 +1729,81 @@ class ScopesTest { //region startTransaction tests @Test fun `when traceHeaders and no transaction is active, traceHeaders are generated from scope`() { - val scopes = generateScopes() + val hub = generateHub() var spanId: SpanId? = null - scopes.configureScope { spanId = it.propagationContext.spanId } + hub.configureScope { spanId = it.propagationContext.spanId } - val traceHeader = scopes.traceHeaders() + val traceHeader = hub.traceHeaders() assertNotNull(traceHeader) assertEquals(spanId, traceHeader.spanId) } @Test fun `when traceHeaders and there is an active transaction, traceHeaders are not null`() { - val scopes = generateScopes() - val tx = scopes.startTransaction("aTransaction", "op") - scopes.configureScope { it.setTransaction(tx) } + val hub = generateHub() + val tx = hub.startTransaction("aTransaction", "op") + hub.configureScope { it.setTransaction(tx) } - assertNotNull(scopes.traceHeaders()) + assertNotNull(hub.traceHeaders()) } //endregion //region getSpan tests @Test fun `when there is no active transaction, getSpan returns null`() { - val scopes = generateScopes() - assertNull(scopes.span) + val hub = generateHub() + assertNull(hub.span) } @Test fun `when there is no active transaction, getTransaction returns null`() { - val scopes = generateScopes() - assertNull(scopes.transaction) + val hub = generateHub() + assertNull(hub.transaction) } @Test fun `when there is active transaction bound to the scope, getTransaction and getSpan return active transaction`() { - val scopes = generateScopes() - val tx = scopes.startTransaction("aTransaction", "op") - scopes.configureScope { it.transaction = tx } + val hub = generateHub() + val tx = hub.startTransaction("aTransaction", "op") + hub.configureScope { it.transaction = tx } - assertEquals(tx, scopes.transaction) - assertEquals(tx, scopes.span) + assertEquals(tx, hub.transaction) + assertEquals(tx, hub.span) } @Test - fun `when there is a transaction but the scopes is closed, getTransaction returns null`() { - val scopes = generateScopes() - scopes.startTransaction("name", "op") - scopes.close() + fun `when there is a transaction but the hub is closed, getTransaction returns null`() { + val hub = generateHub() + hub.startTransaction("name", "op") + hub.close() - assertNull(scopes.transaction) + assertNull(hub.transaction) } @Test fun `when there is active span within a transaction bound to the scope, getSpan returns active span`() { - val scopes = generateScopes() - val tx = scopes.startTransaction("aTransaction", "op") - scopes.configureScope { it.setTransaction(tx) } - scopes.configureScope { it.setTransaction(tx) } + val hub = generateHub() + val tx = hub.startTransaction("aTransaction", "op") + hub.configureScope { it.setTransaction(tx) } + hub.configureScope { it.setTransaction(tx) } val span = tx.startChild("op") - assertEquals(tx, scopes.transaction) - assertEquals(span, scopes.span) + assertEquals(tx, hub.transaction) + assertEquals(span, hub.span) } // endregion //region setSpanContext @Test fun `associates span context with throwable`() { - val (scopes, mockClient) = getEnabledScopes() - val transaction = scopes.startTransaction("aTransaction", "op") + val (hub, mockClient) = getEnabledHub() + val transaction = hub.startTransaction("aTransaction", "op") val span = transaction.startChild("op") val exception = RuntimeException() - scopes.setSpanContext(exception, span, "tx-name") - scopes.captureEvent(SentryEvent(exception)) + hub.setSpanContext(exception, span, "tx-name") + hub.captureEvent(SentryEvent(exception)) verify(mockClient).captureEvent( check { @@ -1914,6 +1813,12 @@ class ScopesTest { anyOrNull() ) } + + @Test + fun `returns null when no span context associated with throwable`() { + val hub = generateHub() as Hub + assertNull(hub.getSpanContext(RuntimeException())) + } // endregion @Test @@ -1921,9 +1826,9 @@ class ScopesTest { val nativeMarker = File(hashedFolder(), EnvelopeCache.NATIVE_CRASH_MARKER_FILE) nativeMarker.mkdirs() nativeMarker.createNewFile() - val scopes = generateScopes() as Scopes + val hub = generateHub() as Hub - assertTrue(scopes.isCrashedLastRun!!) + assertTrue(hub.isCrashedLastRun!!) assertTrue(nativeMarker.exists()) } @@ -1932,69 +1837,69 @@ class ScopesTest { val nativeMarker = File(hashedFolder(), EnvelopeCache.NATIVE_CRASH_MARKER_FILE) nativeMarker.mkdirs() nativeMarker.createNewFile() - val scopes = generateScopes { + val hub = generateHub { it.isEnableAutoSessionTracking = false } - assertTrue(scopes.isCrashedLastRun!!) + assertTrue(hub.isCrashedLastRun!!) assertFalse(nativeMarker.exists()) } @Test fun `reportFullyDisplayed is ignored if TimeToFullDisplayTracing is disabled`() { var called = false - val scopes = generateScopes { + val hub = generateHub { it.fullyDisplayedReporter.registerFullyDrawnListener { called = !called } } - scopes.reportFullyDisplayed() + hub.reportFullyDisplayed() assertFalse(called) } @Test fun `reportFullyDisplayed calls FullyDisplayedReporter if TimeToFullDisplayTracing is enabled`() { var called = false - val scopes = generateScopes { + val hub = generateHub { it.isEnableTimeToFullDisplayTracing = true it.fullyDisplayedReporter.registerFullyDrawnListener { called = !called } } - scopes.reportFullyDisplayed() + hub.reportFullyDisplayed() assertTrue(called) } @Test fun `reportFullyDisplayed calls FullyDisplayedReporter only once`() { var called = false - val scopes = generateScopes { + val hub = generateHub { it.isEnableTimeToFullDisplayTracing = true it.fullyDisplayedReporter.registerFullyDrawnListener { called = !called } } - scopes.reportFullyDisplayed() + hub.reportFullyDisplayed() assertTrue(called) - scopes.reportFullyDisplayed() + hub.reportFullyDisplayed() assertTrue(called) } @Test fun `reportFullDisplayed calls reportFullyDisplayed`() { - val scopes = spy(generateScopes()) - scopes.reportFullDisplayed() - verify(scopes).reportFullyDisplayed() + val hub = spy(generateHub()) + hub.reportFullDisplayed() + verify(hub).reportFullyDisplayed() } @Test fun `continueTrace creates propagation context from headers and returns transaction context if performance enabled`() { - val scopes = generateScopes() + val hub = generateHub() val traceId = SentryId() val parentSpanId = SpanId() - val transactionContext = scopes.continueTrace("$traceId-$parentSpanId-1", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) + val transactionContext = hub.continueTrace("$traceId-$parentSpanId-1", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) - scopes.configureScope { scope -> + hub.configureScope { scope -> assertEquals(traceId, scope.propagationContext.traceId) assertEquals(parentSpanId, scope.propagationContext.parentSpanId) } @@ -2005,16 +1910,16 @@ class ScopesTest { @Test fun `continueTrace creates new propagation context if header invalid and returns transaction context if performance enabled`() { - val scopes = generateScopes() + val hub = generateHub() val traceId = SentryId() var propagationContextHolder = AtomicReference() - scopes.configureScope { propagationContextHolder.set(it.propagationContext) } + hub.configureScope { propagationContextHolder.set(it.propagationContext) } val propagationContextAtStart = propagationContextHolder.get()!! - val transactionContext = scopes.continueTrace("invalid", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) + val transactionContext = hub.continueTrace("invalid", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) - scopes.configureScope { scope -> + hub.configureScope { scope -> assertNotEquals(propagationContextAtStart.traceId, scope.propagationContext.traceId) assertNotEquals(propagationContextAtStart.parentSpanId, scope.propagationContext.parentSpanId) assertNotEquals(propagationContextAtStart.spanId, scope.propagationContext.spanId) @@ -2027,12 +1932,12 @@ class ScopesTest { @Test fun `continueTrace creates propagation context from headers and returns null if performance disabled`() { - val scopes = generateScopes { it.enableTracing = false } + val hub = generateHub { it.enableTracing = false } val traceId = SentryId() val parentSpanId = SpanId() - val transactionContext = scopes.continueTrace("$traceId-$parentSpanId-1", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) + val transactionContext = hub.continueTrace("$traceId-$parentSpanId-1", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) - scopes.configureScope { scope -> + hub.configureScope { scope -> assertEquals(traceId, scope.propagationContext.traceId) assertEquals(parentSpanId, scope.propagationContext.parentSpanId) } @@ -2042,16 +1947,16 @@ class ScopesTest { @Test fun `continueTrace creates new propagation context if header invalid and returns null if performance disabled`() { - val scopes = generateScopes { it.enableTracing = false } + val hub = generateHub { it.enableTracing = false } val traceId = SentryId() var propagationContextHolder = AtomicReference() - scopes.configureScope { propagationContextHolder.set(it.propagationContext) } + hub.configureScope { propagationContextHolder.set(it.propagationContext) } val propagationContextAtStart = propagationContextHolder.get()!! - val transactionContext = scopes.continueTrace("invalid", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) + val transactionContext = hub.continueTrace("invalid", listOf("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=$traceId,sentry-transaction=HTTP%20GET")) - scopes.configureScope { scope -> + hub.configureScope { scope -> assertNotEquals(propagationContextAtStart.traceId, scope.propagationContext.traceId) assertNotEquals(propagationContextAtStart.parentSpanId, scope.propagationContext.parentSpanId) assertNotEquals(propagationContextAtStart.spanId, scope.propagationContext.spanId) @@ -2061,53 +1966,53 @@ class ScopesTest { } @Test - fun `scopes provides no tags for metrics, if metric option is disabled`() { - val scopes = generateScopes { + fun `hub provides no tags for metrics, if metric option is disabled`() { + val hub = generateHub { it.isEnableMetrics = false it.isEnableDefaultTagsForMetrics = true - } as Scopes + } as Hub assertTrue( - scopes.defaultTagsForMetrics.isEmpty() + hub.defaultTagsForMetrics.isEmpty() ) } @Test - fun `scopes provides no tags for metrics, if default tags option is disabled`() { - val scopes = generateScopes { + fun `hub provides no tags for metrics, if default tags option is disabled`() { + val hub = generateHub { it.isEnableMetrics = true it.isEnableDefaultTagsForMetrics = false - } as Scopes + } as Hub assertTrue( - scopes.defaultTagsForMetrics.isEmpty() + hub.defaultTagsForMetrics.isEmpty() ) } @Test - fun `scopes provides minimum default tags for metrics, if nothing is set up`() { - val scopes = generateScopes { + fun `hub provides minimum default tags for metrics, if nothing is set up`() { + val hub = generateHub { it.isEnableMetrics = true it.isEnableDefaultTagsForMetrics = true - } as Scopes + } as Hub assertEquals( mapOf( "environment" to "production" ), - scopes.defaultTagsForMetrics + hub.defaultTagsForMetrics ) } @Test - fun `scopes provides default tags for metrics, based on options and running transaction`() { - val scopes = generateScopes { + fun `hub provides default tags for metrics, based on options and running transaction`() { + val hub = generateHub { it.isEnableMetrics = true it.isEnableDefaultTagsForMetrics = true it.environment = "test" it.release = "1.0" - } as Scopes - scopes.startTransaction( + } as Hub + hub.startTransaction( "name", "op", TransactionOptions().apply { isBindToScope = true } @@ -2119,133 +2024,81 @@ class ScopesTest { "release" to "1.0", "transaction" to "name" ), - scopes.defaultTagsForMetrics + hub.defaultTagsForMetrics ) } @Test - fun `scopes provides no local metric aggregator if metrics feature is disabled`() { - val scopes = generateScopes { + fun `hub provides no local metric aggregator if metrics feature is disabled`() { + val hub = generateHub { it.isEnableMetrics = false it.isEnableSpanLocalMetricAggregation = true - } as Scopes + } as Hub - scopes.startTransaction( + hub.startTransaction( "name", "op", TransactionOptions().apply { isBindToScope = true } ) - assertNull(scopes.localMetricsAggregator) + assertNull(hub.localMetricsAggregator) } @Test - fun `scopes provides no local metric aggregator if local aggregation feature is disabled`() { - val scopes = generateScopes { + fun `hub provides no local metric aggregator if local aggregation feature is disabled`() { + val hub = generateHub { it.isEnableMetrics = true it.isEnableSpanLocalMetricAggregation = false - } as Scopes + } as Hub - scopes.startTransaction( + hub.startTransaction( "name", "op", TransactionOptions().apply { isBindToScope = true } ) - assertNull(scopes.localMetricsAggregator) + assertNull(hub.localMetricsAggregator) } @Test - fun `scopes provides local metric aggregator if feature is enabled`() { - val scopes = generateScopes { + fun `hub provides local metric aggregator if feature is enabled`() { + val hub = generateHub { it.isEnableMetrics = true it.isEnableSpanLocalMetricAggregation = true - } as Scopes + } as Hub - scopes.startTransaction( + hub.startTransaction( "name", "op", TransactionOptions().apply { isBindToScope = true } ) - assertNotNull(scopes.localMetricsAggregator) + assertNotNull(hub.localMetricsAggregator) } @Test - fun `scopes startSpanForMetric starts a child span`() { - val scopes = generateScopes { + fun `hub startSpanForMetric starts a child span`() { + val hub = generateHub { it.isEnableMetrics = true it.isEnableSpanLocalMetricAggregation = true it.sampleRate = 1.0 - } as Scopes + } as Hub - val txn = scopes.startTransaction( + val txn = hub.startTransaction( "name.txn", "op.txn", TransactionOptions().apply { isBindToScope = true } ) - val span = scopes.startSpanForMetric("op", "key")!! + val span = hub.startSpanForMetric("op", "key")!! assertEquals("op", span.spanContext.op) assertEquals("key", span.spanContext.description) assertEquals(span.spanContext.parentSpanId, txn.spanContext.spanId) } - @Test - fun `is considered enabled if client is enabled()`() { - val scopes = generateScopes() as Scopes - val client = mock() - whenever(client.isEnabled).thenReturn(true) - scopes.bindClient(client) - assertTrue(scopes.isEnabled) - } - - @Test - fun `is considered disabled if client is disabled()`() { - val scopes = generateScopes() as Scopes - val client = mock() - whenever(client.isEnabled).thenReturn(false) - scopes.bindClient(client) - assertFalse(scopes.isEnabled) - } - - @Test - fun `creating a transaction with an ignored origin noops`() { - val scopes = generateScopes { - it.ignoredSpanOrigins = listOf("ignored.span.origin") - } - - val transactionContext = TransactionContext("transaction-name", "transaction-op") - val transactionOptions = TransactionOptions().also { - it.origin = "ignored.span.origin" - it.isBindToScope = true - } - - val transaction = scopes.startTransaction(transactionContext, transactionOptions) - assertTrue(transaction.isNoOp) - scopes.configureScope { assertNull(it.transaction) } - } - - @Test - fun `creating a transaction with a non ignored origin creates the transaction`() { - val scopes = generateScopes { - it.ignoredSpanOrigins = listOf("ignored.span.origin") - } - - val transactionContext = TransactionContext("transaction-name", "transaction-op") - val transactionOptions = TransactionOptions().also { - it.origin = "other.span.origin" - it.isBindToScope = true - } - - val transaction = scopes.startTransaction(transactionContext, transactionOptions) - assertFalse(transaction.isNoOp) - scopes.configureScope { assertSame(transaction, it.transaction) } - } - private val dsnTest = "https://key@sentry.io/proj" - private fun generateScopes(optionsConfiguration: Sentry.OptionsConfiguration? = null): IScopes { + private fun generateHub(optionsConfiguration: Sentry.OptionsConfiguration? = null): IHub { val options = SentryOptions().apply { dsn = dsnTest cacheDirPath = file.absolutePath @@ -2253,10 +2106,10 @@ class ScopesTest { tracesSampleRate = 1.0 } optionsConfiguration?.configure(options) - return createScopes(options) + return Hub(options) } - private fun getEnabledScopes(): Triple { + private fun getEnabledHub(): Triple { val logger = mock() val options = SentryOptions() @@ -2267,8 +2120,8 @@ class ScopesTest { options.isDebug = true options.setLogger(logger) - val sut = createScopes(options) - val mockClient = createSentryClientMock() + val sut = Hub(options) + val mockClient = mock() sut.bindClient(mockClient) return Triple(sut, mockClient, logger) } diff --git a/sentry/src/test/java/io/sentry/JsonSerializerTest.kt b/sentry/src/test/java/io/sentry/JsonSerializerTest.kt index 89181f6892..30f337dce4 100644 --- a/sentry/src/test/java/io/sentry/JsonSerializerTest.kt +++ b/sentry/src/test/java/io/sentry/JsonSerializerTest.kt @@ -43,7 +43,7 @@ class JsonSerializerTest { private class Fixture { val logger: ILogger = mock() val serializer: ISerializer - val scopes = mock() + val hub = mock() val traceFile = Files.createTempFile("test", "here").toFile() val options = SentryOptions() @@ -51,7 +51,7 @@ class JsonSerializerTest { options.dsn = "https://key@sentry.io/proj" options.setLogger(logger) options.isDebug = true - whenever(scopes.options).thenReturn(options) + whenever(hub.options).thenReturn(options) serializer = JsonSerializer(options) options.setSerializer(serializer) options.setEnvelopeReader(EnvelopeReader(serializer)) @@ -835,7 +835,7 @@ class JsonSerializerTest { trace.status = SpanStatus.OK trace.setTag("myTag", "myValue") trace.sampled = true - val tracer = SentryTracer(trace, fixture.scopes) + val tracer = SentryTracer(trace, fixture.hub) tracer.setData("dataKey", "dataValue") val span = tracer.startChild("child") span.finish(SpanStatus.OK) @@ -1305,7 +1305,7 @@ class JsonSerializerTest { status = SpanStatus.OK setTag("myTag", "myValue") } - val tracer = SentryTracer(trace, fixture.scopes) + val tracer = SentryTracer(trace, fixture.hub) val span = tracer.startChild("child") span.setMeasurement("test_measurement", 1, MeasurementUnit.Custom("test")) span.finish(SpanStatus.OK) diff --git a/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt b/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt index 682626f08c..ec932ebc86 100644 --- a/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt +++ b/sentry/src/test/java/io/sentry/MainEventProcessorTest.kt @@ -33,7 +33,7 @@ class MainEventProcessorTest { dist = "dist" sdkVersion = SdkVersion("test", "1.2.3") } - val scopes = mock() + val hub = mock() val getLocalhost = mock() lateinit var sentryTracer: SentryTracer private val hostnameCacheMock = Mockito.mockStatic(HostnameCache::class.java) @@ -72,8 +72,8 @@ class MainEventProcessorTest { } host } - whenever(scopes.options).thenReturn(sentryOptions) - sentryTracer = SentryTracer(TransactionContext("", ""), scopes) + whenever(hub.options).thenReturn(sentryOptions) + sentryTracer = SentryTracer(TransactionContext("", ""), hub) val hostnameCache = HostnameCache(hostnameCacheDuration) { getLocalhost } hostnameCacheMock.`when` { HostnameCache.getInstance() }.thenReturn(hostnameCache) diff --git a/sentry/src/test/java/io/sentry/NoOpHubTest.kt b/sentry/src/test/java/io/sentry/NoOpHubTest.kt index 94af1acc9f..dbbfb4b4f1 100644 --- a/sentry/src/test/java/io/sentry/NoOpHubTest.kt +++ b/sentry/src/test/java/io/sentry/NoOpHubTest.kt @@ -71,9 +71,7 @@ class NoOpHubTest { } @Test - fun `pushScope is no op`() { - sut.pushScope() - } + fun `pushScope is no op`() = sut.pushScope() @Test fun `popScope is no op`() = sut.popScope() diff --git a/sentry/src/test/java/io/sentry/OutboxSenderTest.kt b/sentry/src/test/java/io/sentry/OutboxSenderTest.kt index 9274ed4400..ab50e054d0 100644 --- a/sentry/src/test/java/io/sentry/OutboxSenderTest.kt +++ b/sentry/src/test/java/io/sentry/OutboxSenderTest.kt @@ -30,7 +30,7 @@ class OutboxSenderTest { private class Fixture { val options = mock() - val scopes = mock() + val hub = mock() var envelopeReader = mock() val serializer = mock() val logger = mock() @@ -39,11 +39,11 @@ class OutboxSenderTest { whenever(options.dsn).thenReturn("https://key@sentry.io/proj") whenever(options.dateProvider).thenReturn(SentryNanotimeDateProvider()) whenever(options.mainThreadChecker).thenReturn(NoOpMainThreadChecker.getInstance()) - whenever(scopes.options).thenReturn(this.options) + whenever(hub.options).thenReturn(this.options) } fun getSut(): OutboxSender { - return OutboxSender(scopes, envelopeReader, serializer, logger, 15000, 30) + return OutboxSender(hub, envelopeReader, serializer, logger, 15000, 30) } } @@ -83,7 +83,7 @@ class OutboxSenderTest { val hints = HintUtils.createWithTypeCheckHint(mock()) sut.processEnvelopeFile(path, hints) - verify(fixture.scopes).captureEvent(eq(expected), any()) + verify(fixture.hub).captureEvent(eq(expected), any()) assertFalse(File(path).exists()) // Additionally make sure we have no errors logged verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any()) @@ -94,7 +94,7 @@ class OutboxSenderTest { fun `when parser is EnvelopeReader and serializer return SentryTransaction, transaction captured, transactions sampled, file is deleted`() { fixture.envelopeReader = EnvelopeReader(JsonSerializer(fixture.options)) whenever(fixture.options.maxSpans).thenReturn(1000) - whenever(fixture.scopes.options).thenReturn(fixture.options) + whenever(fixture.hub.options).thenReturn(fixture.options) whenever(fixture.options.transactionProfiler).thenReturn(NoOpTransactionProfiler.getInstance()) val transactionContext = TransactionContext("fixture-name", "http") @@ -102,7 +102,7 @@ class OutboxSenderTest { transactionContext.status = SpanStatus.OK transactionContext.setTag("fixture-tag", "fixture-value") - val sentryTracer = SentryTracer(transactionContext, fixture.scopes) + val sentryTracer = SentryTracer(transactionContext, fixture.hub) val span = sentryTracer.startChild("child") span.finish(SpanStatus.OK) sentryTracer.finish() @@ -120,7 +120,7 @@ class OutboxSenderTest { val hints = HintUtils.createWithTypeCheckHint(mock()) sut.processEnvelopeFile(path, hints) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(expected, it) assertTrue(it.isSampled) @@ -139,7 +139,7 @@ class OutboxSenderTest { fun `restores sampleRate`() { fixture.envelopeReader = EnvelopeReader(JsonSerializer(fixture.options)) whenever(fixture.options.maxSpans).thenReturn(1000) - whenever(fixture.scopes.options).thenReturn(fixture.options) + whenever(fixture.hub.options).thenReturn(fixture.options) whenever(fixture.options.transactionProfiler).thenReturn(NoOpTransactionProfiler.getInstance()) val transactionContext = TransactionContext("fixture-name", "http") @@ -148,7 +148,7 @@ class OutboxSenderTest { transactionContext.setTag("fixture-tag", "fixture-value") transactionContext.samplingDecision = TracesSamplingDecision(true, 0.00000021) - val sentryTracer = SentryTracer(transactionContext, fixture.scopes) + val sentryTracer = SentryTracer(transactionContext, fixture.hub) val span = sentryTracer.startChild("child") span.finish(SpanStatus.OK) sentryTracer.finish() @@ -166,7 +166,7 @@ class OutboxSenderTest { val hints = HintUtils.createWithTypeCheckHint(mock()) sut.processEnvelopeFile(path, hints) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(expected, it) assertTrue(it.isSampled) @@ -207,7 +207,7 @@ class OutboxSenderTest { val hints = HintUtils.createWithTypeCheckHint(mock()) sut.processEnvelopeFile(path, hints) - verify(fixture.scopes).captureEvent(any(), any()) + verify(fixture.hub).captureEvent(any(), any()) assertFalse(File(path).exists()) // Additionally make sure we have no errors logged verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any()) @@ -225,7 +225,7 @@ class OutboxSenderTest { val hints = HintUtils.createWithTypeCheckHint(mock()) sut.processEnvelopeFile(path, hints) - verify(fixture.scopes).captureEnvelope(any(), any()) + verify(fixture.hub).captureEnvelope(any(), any()) assertFalse(File(path).exists()) // Additionally make sure we have no errors logged verify(fixture.logger, never()).log(eq(SentryLevel.ERROR), any(), any()) @@ -245,7 +245,7 @@ class OutboxSenderTest { // Additionally make sure we have no errors logged verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), any()) - verify(fixture.scopes, never()).captureEvent(any()) + verify(fixture.hub, never()).captureEvent(any()) assertFalse(File(path).exists()) } @@ -263,7 +263,7 @@ class OutboxSenderTest { // Additionally make sure we have no errors logged verify(fixture.logger).log(eq(SentryLevel.ERROR), any(), any()) - verify(fixture.scopes, never()).captureEvent(any()) + verify(fixture.hub, never()).captureEvent(any()) assertFalse(File(path).exists()) } diff --git a/sentry/src/test/java/io/sentry/PreviousSessionFinalizerTest.kt b/sentry/src/test/java/io/sentry/PreviousSessionFinalizerTest.kt index 87aa5e6715..239e90905e 100644 --- a/sentry/src/test/java/io/sentry/PreviousSessionFinalizerTest.kt +++ b/sentry/src/test/java/io/sentry/PreviousSessionFinalizerTest.kt @@ -21,7 +21,7 @@ class PreviousSessionFinalizerTest { class Fixture { val options = SentryOptions() - val scopes = mock() + val hub = mock() val logger = mock() lateinit var sessionFile: File @@ -61,7 +61,7 @@ class PreviousSessionFinalizerTest { nativeCrashMarker.writeText(nativeCrashTimestamp.toString()) } } - return PreviousSessionFinalizer(options, scopes) + return PreviousSessionFinalizer(options, hub) } fun sessionFromEnvelope(envelope: SentryEnvelope): Session { @@ -80,7 +80,7 @@ class PreviousSessionFinalizerTest { val finalizer = fixture.getSut(null) finalizer.run() - verify(fixture.scopes, never()).captureEnvelope(any()) + verify(fixture.hub, never()).captureEnvelope(any()) } @Test @@ -88,7 +88,7 @@ class PreviousSessionFinalizerTest { val finalizer = fixture.getSut(tmpDir, sessionFileExists = false) finalizer.run() - verify(fixture.scopes, never()).captureEnvelope(any()) + verify(fixture.hub, never()).captureEnvelope(any()) } @Test @@ -96,7 +96,7 @@ class PreviousSessionFinalizerTest { val finalizer = fixture.getSut(tmpDir, sessionFileExists = true, session = null) finalizer.run() - verify(fixture.scopes, never()).captureEnvelope(any()) + verify(fixture.hub, never()).captureEnvelope(any()) } @Test @@ -107,7 +107,7 @@ class PreviousSessionFinalizerTest { ) finalizer.run() - verify(fixture.scopes).captureEnvelope( + verify(fixture.hub).captureEnvelope( argThat { val session = fixture.sessionFromEnvelope(this) session.release == "io.sentry.sample@1.0" && @@ -133,7 +133,7 @@ class PreviousSessionFinalizerTest { ) finalizer.run() - verify(fixture.scopes).captureEnvelope( + verify(fixture.hub).captureEnvelope( argThat { val session = fixture.sessionFromEnvelope(this) session.release == "io.sentry.sample@1.0" && @@ -156,7 +156,7 @@ class PreviousSessionFinalizerTest { ) finalizer.run() - verify(fixture.scopes).captureEnvelope( + verify(fixture.hub).captureEnvelope( argThat { val session = fixture.sessionFromEnvelope(this) session.release == "io.sentry.sample@1.0" && @@ -170,7 +170,7 @@ class PreviousSessionFinalizerTest { val finalizer = fixture.getSut(tmpDir, sessionFileExists = true) finalizer.run() - verify(fixture.scopes, never()).captureEnvelope(any()) + verify(fixture.hub, never()).captureEnvelope(any()) assertFalse(fixture.sessionFile.exists()) } @@ -189,7 +189,7 @@ class PreviousSessionFinalizerTest { argThat { startsWith("Timed out waiting to flush previous session to its own file in session finalizer.") }, any() ) - verify(fixture.scopes, never()).captureEnvelope(any()) + verify(fixture.hub, never()).captureEnvelope(any()) } @Test @@ -202,6 +202,6 @@ class PreviousSessionFinalizerTest { argThat { startsWith("Timed out waiting to flush previous session to its own file in session finalizer.") }, any() ) - verify(fixture.scopes, never()).captureEnvelope(any()) + verify(fixture.hub, never()).captureEnvelope(any()) } } diff --git a/sentry/src/test/java/io/sentry/ScopeTest.kt b/sentry/src/test/java/io/sentry/ScopeTest.kt index 86794d7b19..906c897c62 100644 --- a/sentry/src/test/java/io/sentry/ScopeTest.kt +++ b/sentry/src/test/java/io/sentry/ScopeTest.kt @@ -114,7 +114,7 @@ class ScopeTest { scope.setExtra("extra", "extra") val transaction = - SentryTracer(TransactionContext("transaction-name", "op"), NoOpScopes.getInstance()) + SentryTracer(TransactionContext("transaction-name", "op"), NoOpHub.getInstance()) scope.transaction = transaction val attachment = Attachment("path/log.txt") @@ -192,7 +192,7 @@ class ScopeTest { scope.setTransaction( SentryTracer( TransactionContext("newTransaction", "op"), - NoOpScopes.getInstance() + NoOpHub.getInstance() ) ) @@ -265,7 +265,7 @@ class ScopeTest { fun `clear scope resets scope to default state`() { val scope = Scope(SentryOptions()) scope.level = SentryLevel.WARNING - scope.setTransaction(SentryTracer(TransactionContext("", "op"), NoOpScopes.getInstance())) + scope.setTransaction(SentryTracer(TransactionContext("", "op"), NoOpHub.getInstance())) scope.user = User() scope.request = Request() scope.fingerprint = mutableListOf("finger") @@ -822,7 +822,7 @@ class ScopeTest { @Test fun `Scope getTransaction returns the transaction if there is no active span`() { val scope = Scope(SentryOptions()) - val transaction = SentryTracer(TransactionContext("name", "op"), NoOpScopes.getInstance()) + val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) scope.transaction = transaction assertEquals(transaction, scope.span) } @@ -830,7 +830,7 @@ class ScopeTest { @Test fun `Scope getTransaction returns the current span if there is an unfinished span`() { val scope = Scope(SentryOptions()) - val transaction = SentryTracer(TransactionContext("name", "op"), NoOpScopes.getInstance()) + val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) scope.transaction = transaction val span = transaction.startChild("op") assertEquals(span, scope.span) @@ -839,7 +839,7 @@ class ScopeTest { @Test fun `Scope getTransaction returns the current span if there is a finished span`() { val scope = Scope(SentryOptions()) - val transaction = SentryTracer(TransactionContext("name", "op"), NoOpScopes.getInstance()) + val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) scope.transaction = transaction val span = transaction.startChild("op") span.finish() @@ -849,7 +849,7 @@ class ScopeTest { @Test fun `Scope getTransaction returns the latest span if there is a list of active span`() { val scope = Scope(SentryOptions()) - val transaction = SentryTracer(TransactionContext("name", "op"), NoOpScopes.getInstance()) + val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) scope.transaction = transaction val span = transaction.startChild("op") val innerSpan = span.startChild("op") @@ -859,7 +859,7 @@ class ScopeTest { @Test fun `Scope setTransaction sets transaction name`() { val scope = Scope(SentryOptions()) - val transaction = SentryTracer(TransactionContext("name", "op"), NoOpScopes.getInstance()) + val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) scope.transaction = transaction scope.setTransaction("new-name") assertNotNull(scope.transaction) { @@ -871,7 +871,7 @@ class ScopeTest { @Test fun `Scope setTransaction with null does not clear transaction`() { val scope = Scope(SentryOptions()) - val transaction = SentryTracer(TransactionContext("name", "op"), NoOpScopes.getInstance()) + val transaction = SentryTracer(TransactionContext("name", "op"), NoOpHub.getInstance()) scope.transaction = transaction scope.callMethod("setTransaction", String::class.java, null) assertNotNull(scope.transaction) @@ -936,7 +936,7 @@ class ScopeTest { fun `when transaction is started, sets transaction name on the transaction object`() { val scope = Scope(SentryOptions()) val sentryTransaction = - SentryTracer(TransactionContext("transaction-name", "op"), NoOpScopes.getInstance()) + SentryTracer(TransactionContext("transaction-name", "op"), NoOpHub.getInstance()) scope.transaction = sentryTransaction assertEquals("transaction-name", scope.transactionName) scope.setTransaction("new-name") @@ -950,7 +950,7 @@ class ScopeTest { val scope = Scope(SentryOptions()) scope.setTransaction("transaction-a") val sentryTransaction = - SentryTracer(TransactionContext("transaction-name", "op"), NoOpScopes.getInstance()) + SentryTracer(TransactionContext("transaction-name", "op"), NoOpHub.getInstance()) scope.setTransaction(sentryTransaction) assertEquals("transaction-name", scope.transactionName) scope.clearTransaction() @@ -961,7 +961,7 @@ class ScopeTest { fun `withTransaction returns the current Transaction bound to the Scope`() { val scope = Scope(SentryOptions()) val sentryTransaction = - SentryTracer(TransactionContext("transaction-name", "op"), NoOpScopes.getInstance()) + SentryTracer(TransactionContext("transaction-name", "op"), NoOpHub.getInstance()) scope.setTransaction(sentryTransaction) scope.withTransaction { diff --git a/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt b/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt deleted file mode 100644 index 19123a23ed..0000000000 --- a/sentry/src/test/java/io/sentry/ScopesAdapterTest.kt +++ /dev/null @@ -1,267 +0,0 @@ -package io.sentry - -import io.sentry.protocol.SentryTransaction -import io.sentry.protocol.User -import io.sentry.test.createSentryClientMock -import org.mockito.kotlin.any -import org.mockito.kotlin.anyOrNull -import org.mockito.kotlin.eq -import org.mockito.kotlin.mock -import org.mockito.kotlin.reset -import org.mockito.kotlin.verify -import kotlin.test.AfterTest -import kotlin.test.BeforeTest -import kotlin.test.Test - -class ScopesAdapterTest { - - val scopes: IScopes = mock() - - @BeforeTest - fun `set up`() { - Sentry.setCurrentScopes(scopes) - } - - @AfterTest - fun shutdown() { - Sentry.close() - } - - @Test fun `isEnabled calls Scopes`() { - ScopesAdapter.getInstance().isEnabled - verify(scopes).isEnabled - } - - @Test fun `captureEvent calls Scopes`() { - val event = mock() - val hint = mock() - val scopeCallback = mock() - ScopesAdapter.getInstance().captureEvent(event, hint) - verify(scopes).captureEvent(eq(event), eq(hint)) - - ScopesAdapter.getInstance().captureEvent(event, hint, scopeCallback) - verify(scopes).captureEvent(eq(event), eq(hint), eq(scopeCallback)) - } - - @Test fun `captureMessage calls Scopes`() { - val scopeCallback = mock() - val sentryLevel = mock() - ScopesAdapter.getInstance().captureMessage("message", sentryLevel) - verify(scopes).captureMessage(eq("message"), eq(sentryLevel)) - - ScopesAdapter.getInstance().captureMessage("message", sentryLevel, scopeCallback) - verify(scopes).captureMessage(eq("message"), eq(sentryLevel), eq(scopeCallback)) - } - - @Test fun `captureEnvelope calls Scopes`() { - val envelope = mock() - val hint = mock() - ScopesAdapter.getInstance().captureEnvelope(envelope, hint) - verify(scopes).captureEnvelope(eq(envelope), eq(hint)) - } - - @Test fun `captureException calls Scopes`() { - val throwable = mock() - val hint = mock() - val scopeCallback = mock() - ScopesAdapter.getInstance().captureException(throwable, hint) - verify(scopes).captureException(eq(throwable), eq(hint)) - - ScopesAdapter.getInstance().captureException(throwable, hint, scopeCallback) - verify(scopes).captureException(eq(throwable), eq(hint), eq(scopeCallback)) - } - - @Test fun `captureUserFeedback calls Scopes`() { - val userFeedback = mock() - ScopesAdapter.getInstance().captureUserFeedback(userFeedback) - verify(scopes).captureUserFeedback(eq(userFeedback)) - } - - @Test fun `captureCheckIn calls Scopes`() { - val checkIn = mock() - ScopesAdapter.getInstance().captureCheckIn(checkIn) - verify(scopes).captureCheckIn(eq(checkIn)) - } - - @Test fun `startSession calls Scopes`() { - ScopesAdapter.getInstance().startSession() - verify(scopes).startSession() - } - - @Test fun `endSession calls Scopes`() { - ScopesAdapter.getInstance().endSession() - verify(scopes).endSession() - } - - @Test fun `close calls Scopes`() { - ScopesAdapter.getInstance().close() - verify(scopes).close(false) - } - - @Test fun `close with isRestarting true calls Scopes with isRestarting false`() { - ScopesAdapter.getInstance().close(true) - verify(scopes).close(false) - } - - @Test fun `close with isRestarting false calls Scopes with isRestarting false`() { - ScopesAdapter.getInstance().close(false) - verify(scopes).close(false) - } - - @Test fun `addBreadcrumb calls Scopes`() { - val breadcrumb = mock() - val hint = mock() - ScopesAdapter.getInstance().addBreadcrumb(breadcrumb, hint) - verify(scopes).addBreadcrumb(eq(breadcrumb), eq(hint)) - } - - @Test fun `setLevel calls Scopes`() { - val sentryLevel = mock() - ScopesAdapter.getInstance().setLevel(sentryLevel) - verify(scopes).setLevel(eq(sentryLevel)) - } - - @Test fun `setTransaction calls Scopes`() { - ScopesAdapter.getInstance().setTransaction("transaction") - verify(scopes).setTransaction(eq("transaction")) - } - - @Test fun `setUser calls Scopes`() { - val user = mock() - ScopesAdapter.getInstance().setUser(user) - verify(scopes).setUser(eq(user)) - } - - @Test fun `setFingerprint calls Scopes`() { - val fingerprint = ArrayList() - ScopesAdapter.getInstance().setFingerprint(fingerprint) - verify(scopes).setFingerprint(eq(fingerprint)) - } - - @Test fun `clearBreadcrumbs calls Scopes`() { - ScopesAdapter.getInstance().clearBreadcrumbs() - verify(scopes).clearBreadcrumbs() - } - - @Test fun `setTag calls Scopes`() { - ScopesAdapter.getInstance().setTag("key", "value") - verify(scopes).setTag(eq("key"), eq("value")) - } - - @Test fun `removeTag calls Scopes`() { - ScopesAdapter.getInstance().removeTag("key") - verify(scopes).removeTag(eq("key")) - } - - @Test fun `setExtra calls Scopes`() { - ScopesAdapter.getInstance().setExtra("key", "value") - verify(scopes).setExtra(eq("key"), eq("value")) - } - - @Test fun `removeExtra calls Scopes`() { - ScopesAdapter.getInstance().removeExtra("key") - verify(scopes).removeExtra(eq("key")) - } - - @Test fun `getLastEventId calls Scopes`() { - ScopesAdapter.getInstance().lastEventId - verify(scopes).lastEventId - } - - @Test fun `pushScope calls Scopes`() { - ScopesAdapter.getInstance().pushScope() - verify(scopes).pushScope() - } - - @Test fun `popScope calls Scopes`() { - ScopesAdapter.getInstance().popScope() - verify(scopes).popScope() - } - - @Test fun `withScope calls Scopes`() { - val scopeCallback = mock() - ScopesAdapter.getInstance().withScope(scopeCallback) - verify(scopes).withScope(eq(scopeCallback)) - } - - @Test fun `configureScope calls Scopes`() { - val scopeCallback = mock() - ScopesAdapter.getInstance().configureScope(scopeCallback) - verify(scopes).configureScope(anyOrNull(), eq(scopeCallback)) - } - - @Test fun `bindClient calls Scopes`() { - val client = createSentryClientMock() - ScopesAdapter.getInstance().bindClient(client) - verify(scopes).bindClient(eq(client)) - } - - @Test fun `flush calls Scopes`() { - ScopesAdapter.getInstance().flush(1) - verify(scopes).flush(eq(1)) - } - - @Test fun `clone calls Scopes`() { - ScopesAdapter.getInstance().clone() - verify(scopes).clone() - } - - @Test fun `captureTransaction calls Scopes`() { - val transaction = mock() - val traceContext = mock() - val hint = mock() - val profilingTraceData = mock() - ScopesAdapter.getInstance().captureTransaction(transaction, traceContext, hint, profilingTraceData) - verify(scopes).captureTransaction(eq(transaction), eq(traceContext), eq(hint), eq(profilingTraceData)) - } - - @Test fun `startTransaction calls Scopes`() { - val transactionContext = mock() - val samplingContext = mock() - val transactionOptions = mock() - ScopesAdapter.getInstance().startTransaction(transactionContext) - verify(scopes).startTransaction(eq(transactionContext), any()) - - reset(scopes) - - ScopesAdapter.getInstance().startTransaction(transactionContext, transactionOptions) - verify(scopes).startTransaction(eq(transactionContext), eq(transactionOptions)) - } - - @Test fun `traceHeaders calls Scopes`() { - ScopesAdapter.getInstance().traceHeaders() - verify(scopes).traceHeaders() - } - - @Test fun `setSpanContext calls Scopes`() { - val throwable = mock() - val span = mock() - ScopesAdapter.getInstance().setSpanContext(throwable, span, "transactionName") - verify(scopes).setSpanContext(eq(throwable), eq(span), eq("transactionName")) - } - - @Test fun `getSpan calls Scopes`() { - ScopesAdapter.getInstance().span - verify(scopes).span - } - - @Test fun `getTransaction calls Scopes`() { - ScopesAdapter.getInstance().transaction - verify(scopes).transaction - } - - @Test fun `getOptions calls Scopes`() { - ScopesAdapter.getInstance().options - verify(scopes).options - } - - @Test fun `isCrashedLastRun calls Scopes`() { - ScopesAdapter.getInstance().isCrashedLastRun - verify(scopes).isCrashedLastRun - } - - @Test fun `reportFullyDisplayed calls Scopes`() { - ScopesAdapter.getInstance().reportFullyDisplayed() - verify(scopes).reportFullyDisplayed() - } -} diff --git a/sentry/src/test/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegrationTest.kt b/sentry/src/test/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegrationTest.kt index beed31a198..78623f90a7 100644 --- a/sentry/src/test/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/SendCachedEnvelopeFireAndForgetIntegrationTest.kt @@ -19,7 +19,7 @@ import kotlin.test.assertTrue class SendCachedEnvelopeFireAndForgetIntegrationTest { private class Fixture { - var scopes: IScopes = mock() + var hub: IHub = mock() var logger: ILogger = mock() var options = SentryOptions() val sender = mock() @@ -45,7 +45,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fun `when cacheDirPath returns null, register logs and exit`() { fixture.options.cacheDirPath = null val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.logger).log(eq(SentryLevel.ERROR), eq("No cache dir path is defined in options.")) verify(fixture.sender, never()).send() } @@ -73,7 +73,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { val sut = SendCachedEnvelopeFireAndForgetIntegration(CustomFactory()) fixture.options.cacheDirPath = "abc" fixture.options.executorService = ImmediateExecutorService() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.logger).log(eq(SentryLevel.ERROR), eq("SendFireAndForget factory is null.")) verify(fixture.sender, never()).send() } @@ -85,7 +85,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { mock() ) val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) assertNotNull(fixture.options.sdkVersion) assert(fixture.options.sdkVersion!!.integrationSet.contains("SendCachedEnvelopeFireAndForget")) } @@ -96,7 +96,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fixture.options.executorService.close(0) whenever(fixture.callback.create(any(), any())).thenReturn(mock()) val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.logger).log(eq(SentryLevel.ERROR), eq("Failed to call the executor. Cached events will not be sent. Did you call Sentry.close()?"), any()) } @@ -108,7 +108,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fixture.options.cacheDirPath = "cache" val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(connectionStatusProvider).addConnectionStatusObserver(any()) } @@ -122,9 +122,9 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fixture.options.cacheDirPath = "cache" val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.sender, never()).send() } @@ -139,7 +139,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fixture.options.cacheDirPath = "cache" val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.sender).send() } @@ -155,7 +155,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fixture.options.cacheDirPath = "cache" val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) // when there's no connection no factory create call should be done verify(fixture.sender, never()).send() @@ -183,9 +183,9 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { val rateLimiter = mock { whenever(mock.isActiveForCategory(any())).thenReturn(true) } - whenever(fixture.scopes.rateLimiter).thenReturn(rateLimiter) + whenever(fixture.hub.rateLimiter).thenReturn(rateLimiter) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) // no factory call should be done if there's rate limiting active verify(fixture.sender, never()).send() @@ -196,8 +196,8 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fixture.options.executorService = ImmediateExecutorService() fixture.options.cacheDirPath = "cache" val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) - verify(fixture.callback).create(eq(fixture.scopes), eq(fixture.options)) + sut.register(fixture.hub, fixture.options) + verify(fixture.callback).create(eq(fixture.hub), eq(fixture.options)) } @Test @@ -205,7 +205,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fixture.options.executorService = mock() fixture.options.cacheDirPath = "cache" val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.callback, never()).create(any(), any()) } @@ -215,7 +215,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { fixture.options.executorService = deferredExecutorService val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.sender, never()).send() sut.close() @@ -224,7 +224,7 @@ class SendCachedEnvelopeFireAndForgetIntegrationTest { } private class CustomFactory : SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForgetFactory { - override fun create(scopes: IScopes, options: SentryOptions): SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget? { + override fun create(hub: IHub, options: SentryOptions): SendCachedEnvelopeFireAndForgetIntegration.SendFireAndForget? { return null } } diff --git a/sentry/src/test/java/io/sentry/SentryClientTest.kt b/sentry/src/test/java/io/sentry/SentryClientTest.kt index 6573dbc7a5..de540bf90c 100644 --- a/sentry/src/test/java/io/sentry/SentryClientTest.kt +++ b/sentry/src/test/java/io/sentry/SentryClientTest.kt @@ -70,7 +70,7 @@ class SentryClientTest { var transport = mock() var factory = mock() val maxAttachmentSize: Long = (5 * 1024 * 1024).toLong() - val scopes = mock() + val hub = mock() val sentryTracer: SentryTracer var sentryOptions: SentryOptions = SentryOptions().apply { @@ -88,9 +88,8 @@ class SentryClientTest { init { whenever(factory.create(any(), any())).thenReturn(transport) - whenever(scopes.options).thenReturn(sentryOptions) - sentryTracer = SentryTracer(TransactionContext("a-transaction", "op", TracesSamplingDecision(true)), scopes) - sentryTracer.startChild("a-span", "span 1").finish() + whenever(hub.options).thenReturn(sentryOptions) + sentryTracer = SentryTracer(TransactionContext("a-transaction", "op"), hub) } var attachment = Attachment("hello".toByteArray(), "hello.txt", "text/plain", true) @@ -808,10 +807,7 @@ class SentryClientTest { assertClientReport( fixture.sentryOptions.clientReportRecorder, - listOf( - DiscardedEvent(DiscardReason.EVENT_PROCESSOR.reason, DataCategory.Transaction.category, 1), - DiscardedEvent(DiscardReason.EVENT_PROCESSOR.reason, DataCategory.Span.category, 2) - ) + listOf(DiscardedEvent(DiscardReason.EVENT_PROCESSOR.reason, DataCategory.Transaction.category, 1)) ) } @@ -897,10 +893,7 @@ class SentryClientTest { assertClientReport( fixture.sentryOptions.clientReportRecorder, - listOf( - DiscardedEvent(DiscardReason.BEFORE_SEND.reason, DataCategory.Transaction.category, 1), - DiscardedEvent(DiscardReason.BEFORE_SEND.reason, DataCategory.Span.category, 2) - ) + listOf(DiscardedEvent(DiscardReason.BEFORE_SEND.reason, DataCategory.Transaction.category, 1)) ) } @@ -916,36 +909,7 @@ class SentryClientTest { assertClientReport( fixture.sentryOptions.clientReportRecorder, - listOf( - DiscardedEvent(DiscardReason.EVENT_PROCESSOR.reason, DataCategory.Transaction.category, 1), - DiscardedEvent(DiscardReason.EVENT_PROCESSOR.reason, DataCategory.Span.category, 2) - ) - ) - } - - @Test - fun `span dropped by event processor is recorded`() { - fixture.sentryTracer.startChild("dropped span", "span1").finish() - fixture.sentryTracer.startChild("dropped span", "span2").finish() - val transaction = SentryTransaction(fixture.sentryTracer) - val scope = createScope() - scope.addEventProcessor(object : EventProcessor { - override fun process(transaction: SentryTransaction, hint: Hint): SentryTransaction? { - // we are removing span1 and a-span from the fixture - transaction.spans.removeIf { it.description != "span2" } - return transaction - } - }) - - fixture.getSut().captureTransaction(transaction, scope, null) - - verify(fixture.transport).send(any(), anyOrNull()) - - assertClientReport( - fixture.sentryOptions.clientReportRecorder, - listOf( - DiscardedEvent(DiscardReason.EVENT_PROCESSOR.reason, DataCategory.Span.category, 2) - ) + listOf(DiscardedEvent(DiscardReason.EVENT_PROCESSOR.reason, DataCategory.Transaction.category, 1)) ) } @@ -1005,10 +969,7 @@ class SentryClientTest { assertClientReport( fixture.sentryOptions.clientReportRecorder, - listOf( - DiscardedEvent(DiscardReason.BEFORE_SEND.reason, DataCategory.Transaction.category, 1), - DiscardedEvent(DiscardReason.BEFORE_SEND.reason, DataCategory.Span.category, 2) - ) + listOf(DiscardedEvent(DiscardReason.BEFORE_SEND.reason, DataCategory.Transaction.category, 1)) ) } @@ -1046,34 +1007,7 @@ class SentryClientTest { assertClientReport( fixture.sentryOptions.clientReportRecorder, - listOf( - DiscardedEvent(DiscardReason.BEFORE_SEND.reason, DataCategory.Transaction.category, 1), - DiscardedEvent(DiscardReason.BEFORE_SEND.reason, DataCategory.Span.category, 2) - ) - ) - } - - @Test - fun `when beforeSendTransaction drops a span, dropped span is recorded`() { - fixture.sentryTracer.startChild("dropped span", "span1").finish() - fixture.sentryTracer.startChild("dropped span", "span2").finish() - fixture.sentryOptions.setBeforeSendTransaction { t: SentryTransaction, _: Any? -> - t.apply { - // we are removing span1 and a-span from the fixture - spans.removeIf { it.description != "span2" } - } - } - - val transaction = SentryTransaction(fixture.sentryTracer) - fixture.getSut().captureTransaction(transaction, fixture.sentryTracer.traceContext()) - - verify(fixture.transport).send(any(), anyOrNull()) - - assertClientReport( - fixture.sentryOptions.clientReportRecorder, - listOf( - DiscardedEvent(DiscardReason.BEFORE_SEND.reason, DataCategory.Span.category, 2) - ) + listOf(DiscardedEvent(DiscardReason.BEFORE_SEND.reason, DataCategory.Transaction.category, 1)) ) } @@ -1522,9 +1456,9 @@ class SentryClientTest { @Test fun `when captureTransaction with scope, transaction should use user data`() { - val scopes: IScopes = mock() - whenever(scopes.options).thenReturn(SentryOptions()) - val transaction = SentryTransaction(SentryTracer(TransactionContext("tx", "op"), scopes)) + val hub: IHub = mock() + whenever(hub.options).thenReturn(SentryOptions()) + val transaction = SentryTransaction(SentryTracer(TransactionContext("tx", "op"), hub)) val scope = createScope() val sut = fixture.getSut() @@ -1553,7 +1487,7 @@ class SentryClientTest { val event = SentryEvent() val sut = fixture.getSut() val scope = createScope() - val transaction = SentryTracer(TransactionContext("a-transaction", "op"), fixture.scopes) + val transaction = SentryTracer(TransactionContext("a-transaction", "op"), fixture.hub) scope.setTransaction(transaction) val span = transaction.startChild("op") sut.captureEvent(event, scope) @@ -1624,7 +1558,7 @@ class SentryClientTest { fixture.sentryOptions.release = "optionsRelease" fixture.sentryOptions.environment = "optionsEnvironment" val sut = fixture.getSut() - val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.scopes) + val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.hub) val transaction = SentryTransaction(sentryTracer) transaction.release = "transactionRelease" transaction.environment = "transactionEnvironment" @@ -1637,7 +1571,7 @@ class SentryClientTest { fun `when transaction does not have SDK version set, and the SDK version is set on options, options values are applied to transactions`() { fixture.sentryOptions.sdkVersion = SdkVersion("sdk.name", "version") val sut = fixture.getSut() - val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.scopes) + val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.hub) val transaction = SentryTransaction(sentryTracer) sut.captureTransaction(transaction, sentryTracer.traceContext()) assertEquals(fixture.sentryOptions.sdkVersion, transaction.sdk) @@ -1647,7 +1581,7 @@ class SentryClientTest { fun `when transaction has SDK version set, and the SDK version is set on options, options values are not applied to transactions`() { fixture.sentryOptions.sdkVersion = SdkVersion("sdk.name", "version") val sut = fixture.getSut() - val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.scopes) + val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.hub) val transaction = SentryTransaction(sentryTracer) val sdkVersion = SdkVersion("transaction.sdk.name", "version") transaction.sdk = sdkVersion @@ -1659,7 +1593,7 @@ class SentryClientTest { fun `when transaction does not have tags, and tags are set on options, options values are applied to transactions`() { fixture.sentryOptions.setTag("tag1", "value1") val sut = fixture.getSut() - val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.scopes) + val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.hub) val transaction = SentryTransaction(sentryTracer) sut.captureTransaction(transaction, sentryTracer.traceContext()) assertEquals(mapOf("tag1" to "value1"), transaction.tags) @@ -1670,7 +1604,7 @@ class SentryClientTest { fixture.sentryOptions.setTag("tag1", "value1") fixture.sentryOptions.setTag("tag2", "value2") val sut = fixture.getSut() - val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.scopes) + val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.hub) val transaction = SentryTransaction(sentryTracer) transaction.setTag("tag3", "value3") transaction.setTag("tag2", "transaction-tag") @@ -1684,7 +1618,7 @@ class SentryClientTest { @Test fun `captured transactions without a platform, have the default platform set`() { val sut = fixture.getSut() - val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.scopes) + val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.hub) val transaction = SentryTransaction(sentryTracer) sut.captureTransaction(transaction, sentryTracer.traceContext()) assertEquals("java", transaction.platform) @@ -1693,7 +1627,7 @@ class SentryClientTest { @Test fun `captured transactions with a platform, do not get the platform overwritten`() { val sut = fixture.getSut() - val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.scopes) + val sentryTracer = SentryTracer(TransactionContext("name", "op"), fixture.hub) val transaction = SentryTransaction(sentryTracer) transaction.platform = "abc" sut.captureTransaction(transaction, sentryTracer.traceContext()) diff --git a/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt b/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt index 8eb7f0e42d..888f17e0a3 100644 --- a/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt @@ -209,193 +209,6 @@ class SentryExceptionFactoryTest { assertEquals(777, frame.lineno) } - @Test - fun `when exception with mechanism suppressed exceptions, add them and show as group`() { - val exception = Exception("message") - val suppressedException = Exception("suppressed") - exception.addSuppressed(suppressedException) - - val mechanism = Mechanism() - mechanism.type = "ANR" - val thread = Thread() - val throwable = ExceptionMechanismException(mechanism, exception, thread) - - val queue = fixture.getSut().extractExceptionQueue(throwable) - - val suppressedInQueue = queue.pop() - val mainInQueue = queue.pop() - - assertEquals("suppressed", suppressedInQueue.value) - assertEquals(1, suppressedInQueue.mechanism?.exceptionId) - assertEquals(0, suppressedInQueue.mechanism?.parentId) - - assertEquals("message", mainInQueue.value) - assertEquals(0, mainInQueue.mechanism?.exceptionId) - assertEquals(true, mainInQueue.mechanism?.isExceptionGroup) - } - - @Test - fun `nested exception that contains suppressed exceptions is marked as group`() { - val exception = Exception("inner") - val suppressedException = Exception("suppressed") - exception.addSuppressed(suppressedException) - - val outerException = Exception("outer", exception) - - val queue = fixture.getSut().extractExceptionQueue(outerException) - - val suppressedInQueue = queue.pop() - val mainInQueue = queue.pop() - val outerInQueue = queue.pop() - - assertEquals("suppressed", suppressedInQueue.value) - assertEquals(2, suppressedInQueue.mechanism?.exceptionId) - assertEquals(1, suppressedInQueue.mechanism?.parentId) - - assertEquals("inner", mainInQueue.value) - assertEquals(1, mainInQueue.mechanism?.exceptionId) - assertEquals(0, mainInQueue.mechanism?.parentId) - assertEquals(true, mainInQueue.mechanism?.isExceptionGroup) - - assertEquals("outer", outerInQueue.value) - assertEquals(0, outerInQueue.mechanism?.exceptionId) - assertNull(outerInQueue.mechanism?.parentId) - assertNull(outerInQueue.mechanism?.isExceptionGroup) - } - - @Test - fun `nested exception within Mechanism that contains suppressed exceptions is marked as group`() { - val exception = Exception("inner") - val suppressedException = Exception("suppressed") - exception.addSuppressed(suppressedException) - - val mechanism = Mechanism() - mechanism.type = "ANR" - val thread = Thread() - - val outerException = ExceptionMechanismException(mechanism, Exception("outer", exception), thread) - - val queue = fixture.getSut().extractExceptionQueue(outerException) - - val suppressedInQueue = queue.pop() - val mainInQueue = queue.pop() - val outerInQueue = queue.pop() - - assertEquals("suppressed", suppressedInQueue.value) - assertEquals(2, suppressedInQueue.mechanism?.exceptionId) - assertEquals(1, suppressedInQueue.mechanism?.parentId) - - assertEquals("inner", mainInQueue.value) - assertEquals(1, mainInQueue.mechanism?.exceptionId) - assertEquals(0, mainInQueue.mechanism?.parentId) - assertEquals(true, mainInQueue.mechanism?.isExceptionGroup) - - assertEquals("outer", outerInQueue.value) - assertEquals(0, outerInQueue.mechanism?.exceptionId) - assertNull(outerInQueue.mechanism?.parentId) - assertNull(outerInQueue.mechanism?.isExceptionGroup) - } - - @Test - fun `nested exception with nested exception that contain suppressed exceptions are marked as group`() { - val innerMostException = Exception("innermost") - val innerMostSuppressed = Exception("innermostSuppressed") - innerMostException.addSuppressed(innerMostSuppressed) - - val innerException = Exception("inner", innerMostException) - val innerSuppressed = Exception("suppressed") - innerException.addSuppressed(innerSuppressed) - - val outerException = Exception("outer", innerException) - - val queue = fixture.getSut().extractExceptionQueue(outerException) - - val innerMostSuppressedInQueue = queue.pop() - val innerMostExceptionInQueue = queue.pop() - val innerSuppressedInQueue = queue.pop() - val innerExceptionInQueue = queue.pop() - val outerInQueue = queue.pop() - - assertEquals("innermostSuppressed", innerMostSuppressedInQueue.value) - assertEquals(4, innerMostSuppressedInQueue.mechanism?.exceptionId) - assertEquals(3, innerMostSuppressedInQueue.mechanism?.parentId) - assertNull(innerMostSuppressedInQueue.mechanism?.isExceptionGroup) - - assertEquals("innermost", innerMostExceptionInQueue.value) - assertEquals(3, innerMostExceptionInQueue.mechanism?.exceptionId) - assertEquals(1, innerMostExceptionInQueue.mechanism?.parentId) - assertEquals(true, innerMostExceptionInQueue.mechanism?.isExceptionGroup) - - assertEquals("suppressed", innerSuppressedInQueue.value) - assertEquals(2, innerSuppressedInQueue.mechanism?.exceptionId) - assertEquals(1, innerSuppressedInQueue.mechanism?.parentId) - assertNull(innerSuppressedInQueue.mechanism?.isExceptionGroup) - - assertEquals("inner", innerExceptionInQueue.value) - assertEquals(1, innerExceptionInQueue.mechanism?.exceptionId) - assertEquals(0, innerExceptionInQueue.mechanism?.parentId) - assertEquals(true, innerExceptionInQueue.mechanism?.isExceptionGroup) - - assertEquals("outer", outerInQueue.value) - assertEquals(0, outerInQueue.mechanism?.exceptionId) - assertNull(outerInQueue.mechanism?.parentId) - assertNull(outerInQueue.mechanism?.isExceptionGroup) - } - - @Test - fun `nested exception with nested exception that contain suppressed exceptions with a nested exception are marked as group`() { - val innerMostException = Exception("innermost") - - val innerMostSuppressedNestedException = Exception("innermostSuppressedNested") - val innerMostSuppressed = Exception("innermostSuppressed", innerMostSuppressedNestedException) - innerMostException.addSuppressed(innerMostSuppressed) - - val innerException = Exception("inner", innerMostException) - val innerSuppressed = Exception("suppressed") - innerException.addSuppressed(innerSuppressed) - - val outerException = Exception("outer", innerException) - - val queue = fixture.getSut().extractExceptionQueue(outerException) - - val innerMostSuppressedNestedExceptionInQueue = queue.pop() - val innerMostSuppressedInQueue = queue.pop() - val innerMostExceptionInQueue = queue.pop() - val innerSuppressedInQueue = queue.pop() - val innerExceptionInQueue = queue.pop() - val outerInQueue = queue.pop() - - assertEquals("innermostSuppressedNested", innerMostSuppressedNestedExceptionInQueue.value) - assertEquals(5, innerMostSuppressedNestedExceptionInQueue.mechanism?.exceptionId) - assertEquals(4, innerMostSuppressedNestedExceptionInQueue.mechanism?.parentId) - assertNull(innerMostSuppressedNestedExceptionInQueue.mechanism?.isExceptionGroup) - - assertEquals("innermostSuppressed", innerMostSuppressedInQueue.value) - assertEquals(4, innerMostSuppressedInQueue.mechanism?.exceptionId) - assertEquals(3, innerMostSuppressedInQueue.mechanism?.parentId) - assertNull(innerMostSuppressedInQueue.mechanism?.isExceptionGroup) - - assertEquals("innermost", innerMostExceptionInQueue.value) - assertEquals(3, innerMostExceptionInQueue.mechanism?.exceptionId) - assertEquals(1, innerMostExceptionInQueue.mechanism?.parentId) - assertEquals(true, innerMostExceptionInQueue.mechanism?.isExceptionGroup) - - assertEquals("suppressed", innerSuppressedInQueue.value) - assertEquals(2, innerSuppressedInQueue.mechanism?.exceptionId) - assertEquals(1, innerSuppressedInQueue.mechanism?.parentId) - assertNull(innerSuppressedInQueue.mechanism?.isExceptionGroup) - - assertEquals("inner", innerExceptionInQueue.value) - assertEquals(1, innerExceptionInQueue.mechanism?.exceptionId) - assertEquals(0, innerExceptionInQueue.mechanism?.parentId) - assertEquals(true, innerExceptionInQueue.mechanism?.isExceptionGroup) - - assertEquals("outer", outerInQueue.value) - assertEquals(0, outerInQueue.mechanism?.exceptionId) - assertNull(outerInQueue.mechanism?.parentId) - assertNull(outerInQueue.mechanism?.isExceptionGroup) - } - internal class InnerClassThrowable constructor(cause: Throwable? = null) : Throwable(cause) private val anonymousException = object : Exception() { diff --git a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt index c11eafdc5a..b474d4e4e0 100644 --- a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt +++ b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt @@ -1,6 +1,5 @@ package io.sentry -import io.sentry.SentryOptions.RequestSize import io.sentry.util.StringUtils import org.mockito.kotlin.mock import java.io.File @@ -372,8 +371,6 @@ class SentryOptionsTest { externalOptions.isSendModules = false externalOptions.ignoredCheckIns = listOf("slug1", "slug-B") externalOptions.isEnableBackpressureHandling = false - externalOptions.maxRequestBodySize = SentryOptions.RequestSize.MEDIUM - externalOptions.isSendDefaultPii = true externalOptions.cron = SentryOptions.Cron().apply { defaultCheckinMargin = 10L defaultMaxRuntime = 30L @@ -418,8 +415,6 @@ class SentryOptionsTest { assertEquals(40L, options.cron?.defaultFailureIssueThreshold) assertEquals(50L, options.cron?.defaultRecoveryThreshold) assertEquals("America/New_York", options.cron?.defaultTimezone) - assertTrue(options.isSendDefaultPii) - assertEquals(RequestSize.MEDIUM, options.maxRequestBodySize) } @Test diff --git a/sentry/src/test/java/io/sentry/SentryTest.kt b/sentry/src/test/java/io/sentry/SentryTest.kt index 2229e18818..0f4966b44a 100644 --- a/sentry/src/test/java/io/sentry/SentryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTest.kt @@ -15,7 +15,6 @@ import io.sentry.protocol.SdkVersion import io.sentry.protocol.SentryId import io.sentry.protocol.SentryThread import io.sentry.test.ImmediateExecutorService -import io.sentry.test.createSentryClientMock import io.sentry.util.PlatformTestManipulator import io.sentry.util.thread.IMainThreadChecker import io.sentry.util.thread.MainThreadChecker @@ -64,59 +63,27 @@ class SentryTest { } @Test - fun `init multiple times calls scopes close with isRestarting true`() { - val scopes = mock() + fun `init multiple times calls hub close with isRestarting true`() { + val hub = mock() Sentry.init { it.dsn = dsn } - Sentry.setCurrentScopes(scopes) + Sentry.setCurrentHub(hub) Sentry.init { it.dsn = dsn } - verify(scopes).close(eq(true)) + verify(hub).close(eq(true)) } @Test - fun `global client is enabled after restart`() { - val scopes = mock() - whenever(scopes.close()).then { Sentry.getGlobalScope().client.close() } - whenever(scopes.close(anyOrNull())).then { Sentry.getGlobalScope().client.close() } - - Sentry.init { - it.dsn = dsn - } - Sentry.setCurrentScopes(scopes) - Sentry.init { - it.dsn = dsn - } - verify(scopes).close(eq(true)) - assertTrue(Sentry.getGlobalScope().client.isEnabled) - } - - @Test - fun `global client is disabled after close`() { - val scopes = mock() - whenever(scopes.close()).then { Sentry.getGlobalScope().client.close() } - whenever(scopes.close(anyOrNull())).then { Sentry.getGlobalScope().client.close() } - + fun `close calls hub close with isRestarting false`() { + val hub = mock() Sentry.init { it.dsn = dsn } - Sentry.setCurrentScopes(scopes) + Sentry.setCurrentHub(hub) Sentry.close() - verify(scopes).close(eq(false)) - assertFalse(Sentry.getGlobalScope().client.isEnabled) - } - - @Test - fun `close calls scopes close with isRestarting false`() { - val scopes = mock() - Sentry.init { - it.dsn = dsn - } - Sentry.setCurrentScopes(scopes) - Sentry.close() - verify(scopes).close(eq(false)) + verify(hub).close(eq(false)) } @Test @@ -246,7 +213,7 @@ class SentryTest { Sentry.init { it.isEnableExternalConfiguration = true } - assertTrue(ScopesAdapter.getInstance().isEnabled) + assertTrue(HubAdapter.getInstance().isEnabled) } finally { temporaryFolder.delete() } @@ -262,10 +229,10 @@ class SentryTest { Sentry.setTag("none", "shouldNotExist") var value: String? = null - Sentry.getCurrentScopes().configureScope { + Sentry.getCurrentHub().configureScope { value = it.tags[value] } - assertTrue(Sentry.getCurrentScopes().isNoOp) + assertTrue(Sentry.getCurrentHub() is NoOpHub) assertNull(value) } @@ -278,10 +245,10 @@ class SentryTest { Sentry.setTag("none", "shouldNotExist") var value: String? = null - Sentry.getCurrentScopes().configureScope { + Sentry.getCurrentHub().configureScope { value = it.tags[value] } - assertTrue(Sentry.getCurrentScopes().isNoOp) + assertTrue(Sentry.getCurrentHub() is NoOpHub) assertNull(value) } @@ -299,8 +266,8 @@ class SentryTest { fun `captureUserFeedback gets forwarded to client`() { Sentry.init { it.dsn = dsn } - val client = createSentryClientMock() - Sentry.getCurrentScopes().bindClient(client) + val client = mock() + Sentry.getCurrentHub().bindClient(client) val userFeedback = UserFeedback(SentryId.EMPTY_ID) Sentry.captureUserFeedback(userFeedback) @@ -402,11 +369,11 @@ class SentryTest { } @Test - fun `using sentry before calling init creates NoOpScopes but after init Sentry uses a new clone`() { - // noop as not yet initialized, caches NoOpScopes in ThreadLocal + fun `using sentry before calling init creates NoOpHub but after init Sentry uses a new clone`() { + // noop as not yet initialized, caches NoOpHub in ThreadLocal Sentry.captureMessage("noop caused") - assertTrue(Sentry.getCurrentScopes().isNoOp) + assertTrue(Sentry.getCurrentHub() is NoOpHub) // init Sentry in another thread val thread = Thread() { @@ -420,18 +387,18 @@ class SentryTest { Sentry.captureMessage("should work now") - val scopes = Sentry.getCurrentScopes() - assertNotNull(scopes) - assertFalse(scopes.isNoOp) + val hub = Sentry.getCurrentHub() + assertNotNull(hub) + assertFalse(hub is NoOpHub) } @Test - fun `main scopes can be cloned and does not share scope with current scopes`() { - // noop as not yet initialized, caches NoOpScopes in ThreadLocal + fun `main hub can be cloned and does not share scope with current hub`() { + // noop as not yet initialized, caches NoOpHub in ThreadLocal Sentry.addBreadcrumb("breadcrumbNoOp") Sentry.captureMessage("messageNoOp") - assertTrue(Sentry.getCurrentScopes().isNoOp) + assertTrue(Sentry.getCurrentHub() is NoOpHub) val capturedEvents = mutableListOf() @@ -451,38 +418,38 @@ class SentryTest { Sentry.addBreadcrumb("breadcrumbCurrent") - val scopes = Sentry.getCurrentScopes() - assertNotNull(scopes) - assertFalse(Sentry.getCurrentScopes().isNoOp) + val hub = Sentry.getCurrentHub() + assertNotNull(hub) + assertFalse(hub is NoOpHub) - val forkedRootScopes = Sentry.forkedRootScopes("test") - forkedRootScopes.addBreadcrumb("breadcrumbMainClone") + val newMainHubClone = Sentry.cloneMainHub() + newMainHubClone.addBreadcrumb("breadcrumbMainClone") - scopes.captureMessage("messageCurrent") - forkedRootScopes.captureMessage("messageMainClone") + hub.captureMessage("messageCurrent") + newMainHubClone.captureMessage("messageMainClone") assertEquals(2, capturedEvents.size) val mainCloneEvent = capturedEvents.firstOrNull { it.message?.formatted == "messageMainClone" } - val currentScopesEvent = capturedEvents.firstOrNull { it.message?.formatted == "messageCurrent" } + val currentHubEvent = capturedEvents.firstOrNull { it.message?.formatted == "messageCurrent" } assertNotNull(mainCloneEvent) assertNotNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbMainClone" }) assertNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbCurrent" }) assertNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbNoOp" }) - assertNotNull(currentScopesEvent) - assertNull(currentScopesEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbMainClone" }) - assertNotNull(currentScopesEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbCurrent" }) - assertNull(currentScopesEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbNoOp" }) + assertNotNull(currentHubEvent) + assertNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbMainClone" }) + assertNotNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbCurrent" }) + assertNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbNoOp" }) } @Test - fun `main scopes is not cloned in global scopes mode and shares scope with current scopes`() { - // noop as not yet initialized, caches NoOpScopes in ThreadLocal + fun `main hub is not cloned in global hub mode and shares scope with current hub`() { + // noop as not yet initialized, caches NoOpHub in ThreadLocal Sentry.addBreadcrumb("breadcrumbNoOp") Sentry.captureMessage("messageNoOp") - assertTrue(Sentry.getCurrentScopes().isNoOp) + assertTrue(Sentry.getCurrentHub() is NoOpHub) val capturedEvents = mutableListOf() @@ -502,29 +469,29 @@ class SentryTest { Sentry.addBreadcrumb("breadcrumbCurrent") - val scopes = Sentry.getCurrentScopes() - assertNotNull(scopes) - assertFalse(scopes.isNoOp) + val hub = Sentry.getCurrentHub() + assertNotNull(hub) + assertFalse(hub is NoOpHub) - val forkedRootScopes = Sentry.forkedRootScopes("test") - forkedRootScopes.addBreadcrumb("breadcrumbMainClone") + val newMainHubClone = Sentry.cloneMainHub() + newMainHubClone.addBreadcrumb("breadcrumbMainClone") - scopes.captureMessage("messageCurrent") - forkedRootScopes.captureMessage("messageMainClone") + hub.captureMessage("messageCurrent") + newMainHubClone.captureMessage("messageMainClone") assertEquals(2, capturedEvents.size) val mainCloneEvent = capturedEvents.firstOrNull { it.message?.formatted == "messageMainClone" } - val currentScopesEvent = capturedEvents.firstOrNull { it.message?.formatted == "messageCurrent" } + val currentHubEvent = capturedEvents.firstOrNull { it.message?.formatted == "messageCurrent" } assertNotNull(mainCloneEvent) assertNotNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbMainClone" }) assertNotNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbCurrent" }) assertNull(mainCloneEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbNoOp" }) - assertNotNull(currentScopesEvent) - assertNotNull(currentScopesEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbMainClone" }) - assertNotNull(currentScopesEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbCurrent" }) - assertNull(currentScopesEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbNoOp" }) + assertNotNull(currentHubEvent) + assertNotNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbMainClone" }) + assertNotNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbCurrent" }) + assertNull(currentHubEvent.breadcrumbs?.firstOrNull { it.message == "breadcrumbNoOp" }) } @Test @@ -702,25 +669,25 @@ class SentryTest { } @Test - fun `reportFullyDisplayed calls scopes reportFullyDisplayed`() { - val scopes = mock() + fun `reportFullyDisplayed calls hub reportFullyDisplayed`() { + val hub = mock() Sentry.init { it.dsn = dsn } - Sentry.setCurrentScopes(scopes) + Sentry.setCurrentHub(hub) Sentry.reportFullyDisplayed() - verify(scopes).reportFullyDisplayed() + verify(hub).reportFullyDisplayed() } @Test fun `reportFullDisplayed calls reportFullyDisplayed`() { - val scopes = mock() + val hub = mock() Sentry.init { it.dsn = dsn } - Sentry.setCurrentScopes(scopes) + Sentry.setCurrentHub(hub) Sentry.reportFullDisplayed() - verify(scopes).reportFullyDisplayed() + verify(hub).reportFullyDisplayed() } @Test @@ -839,11 +806,11 @@ class SentryTest { it.serializer.deserialize(previousSessionFile.bufferedReader(), Session::class.java)!!.environment ) - it.addIntegration { scopes, _ -> + it.addIntegration { hub, _ -> // this is just a hack to trigger the previousSessionFlush latch, so the finalizer // does not time out waiting. We have to do it as integration, because this is where - // the scopes is already initialized - scopes.startSession() + // the hub is already initialized + hub.startSession() } } @@ -893,8 +860,8 @@ class SentryTest { fun `captureCheckIn gets forwarded to client`() { Sentry.init { it.dsn = dsn } - val client = createSentryClientMock() - Sentry.getCurrentScopes().bindClient(client) + val client = mock() + Sentry.getCurrentHub().bindClient(client) val checkIn = CheckIn("some_slug", CheckInStatus.OK) Sentry.captureCheckIn(checkIn) @@ -943,20 +910,18 @@ class SentryTest { } @Test - fun `getSpan calls scopes getSpan`() { - val scopes = mock() - val options = SentryOptions().also { it.dsn = dsn } - whenever(scopes.options).thenReturn(options) - - Sentry.init(options) - - Sentry.setCurrentScopes(scopes) + fun `getSpan calls hub getSpan`() { + val hub = mock() + Sentry.init({ + it.dsn = dsn + }, false) + Sentry.setCurrentHub(hub) Sentry.getSpan() - verify(scopes).span + verify(hub).span } @Test - fun `getSpan calls returns root span if globalHubMode is enabled on Android`() { + fun `getSpan calls returns root span if globalhub mode is enabled on Android`() { PlatformTestManipulator.pretendIsAndroid(true) Sentry.init({ it.dsn = dsn @@ -973,7 +938,7 @@ class SentryTest { } @Test - fun `getSpan calls returns child span if globalHubMode is enabled, but the platform is not Android`() { + fun `getSpan calls returns child span if globalhub mode is enabled, but the platform is not Android`() { PlatformTestManipulator.pretendIsAndroid(false) Sentry.init({ it.dsn = dsn @@ -989,7 +954,7 @@ class SentryTest { } @Test - fun `getSpan calls returns child span if globalHubMode is disabled`() { + fun `getSpan calls returns child span if globalhub mode is disabled`() { Sentry.init({ it.dsn = dsn it.enableTracing = true @@ -1175,15 +1140,15 @@ class SentryTest { } @Test - fun `metrics calls scopes getMetrics`() { - val scopes = mock() + fun `metrics calls hub getMetrics`() { + val hub = mock() Sentry.init({ it.dsn = dsn }, false) - Sentry.setCurrentScopes(scopes) + Sentry.setCurrentHub(hub) Sentry.metrics() - verify(scopes).metrics() + verify(hub).metrics() } private class InMemoryOptionsObserver : IOptionsObserver { diff --git a/sentry/src/test/java/io/sentry/SentryTracerTest.kt b/sentry/src/test/java/io/sentry/SentryTracerTest.kt index e10311679a..0942768a8a 100644 --- a/sentry/src/test/java/io/sentry/SentryTracerTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTracerTest.kt @@ -2,7 +2,6 @@ package io.sentry import io.sentry.protocol.TransactionNameSource import io.sentry.protocol.User -import io.sentry.test.createTestScopes import io.sentry.util.thread.IMainThreadChecker import org.awaitility.kotlin.await import org.mockito.kotlin.any @@ -30,15 +29,16 @@ class SentryTracerTest { private class Fixture { val options = SentryOptions() - val scopes: Scopes + val hub: Hub val transactionPerformanceCollector: TransactionPerformanceCollector init { options.dsn = "https://key@sentry.io/proj" options.environment = "environment" options.release = "release@3.0.0" - scopes = spy(createTestScopes(options)) + hub = spy(Hub(options)) transactionPerformanceCollector = spy(DefaultTransactionPerformanceCollector(options)) + hub.bindClient(mock()) } fun getSut( @@ -61,28 +61,12 @@ class SentryTracerTest { transactionOptions.deadlineTimeout = deadlineTimeout transactionOptions.isTrimEnd = trimEnd transactionOptions.transactionFinishedCallback = transactionFinishedCallback - return SentryTracer(TransactionContext("name", "op", samplingDecision), scopes, transactionOptions, performanceCollector) + return SentryTracer(TransactionContext("name", "op", samplingDecision), hub, transactionOptions, performanceCollector) } } private val fixture = Fixture() - @Test - fun `transfer origin from transaction options to transaction context`() { - fixture.getSut() - val transactionOptions = TransactionOptions().also { - it.origin = "new-origin" - } - val transactionContext = TransactionContext("name", "op", null).also { - it.origin = "old-origin" - } - - val transaction = SentryTracer(transactionContext, fixture.scopes, transactionOptions, null) - assertEquals("new-origin", transaction.spanContext.origin) - } - - // TODO [POTEL] test child creation is ignored because of span origin - @Test fun `does not add more spans than configured in options`() { val tracer = fixture.getSut({ @@ -166,7 +150,7 @@ class SentryTracerTest { fun `when transaction is finished, transaction is captured`() { val tracer = fixture.getSut() tracer.finish() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(it.transaction, tracer.name) }, @@ -201,10 +185,10 @@ class SentryTracerTest { @Test fun `when transaction is finished, transaction is cleared from the scope`() { val tracer = fixture.getSut() - fixture.scopes.configureScope { it.transaction = tracer } - assertNotNull(fixture.scopes.span) + fixture.hub.configureScope { it.transaction = tracer } + assertNotNull(fixture.hub.span) tracer.finish() - assertNull(fixture.scopes.span) + assertNull(fixture.hub.span) } @Test @@ -213,7 +197,7 @@ class SentryTracerTest { val ex = RuntimeException() tracer.throwable = ex tracer.finish() - verify(fixture.scopes).setSpanContext(ex, tracer.root, "name") + verify(fixture.hub).setSpanContext(ex, tracer.root, "name") } @Test @@ -222,7 +206,7 @@ class SentryTracerTest { tracer.setTag("tag1", "val1") tracer.setTag("tag2", "val2") tracer.finish() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(mapOf("tag1" to "val1", "tag2" to "val2"), it.tags) assertNotNull(it.contexts.trace) { @@ -242,7 +226,7 @@ class SentryTracerTest { val span = tracer.startChild("op2") span.spanContext.sampled = false tracer.finish() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(1, it.spans.size) assertEquals("op1", it.spans.first().op) @@ -269,7 +253,7 @@ class SentryTracerTest { tracer.setContext("otel", otelContext) tracer.finish() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(otelContext, it.contexts["otel"]) }, @@ -420,8 +404,8 @@ class SentryTracerTest { transaction.finish(SpanStatus.UNKNOWN_ERROR) // call only once - verify(fixture.scopes).setSpanContext(ex, transaction.root, "name") - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).setSpanContext(ex, transaction.root, "name") + verify(fixture.hub).captureTransaction( check { assertNotNull(it.contexts.trace) { assertEquals(SpanStatus.OK, it.status) @@ -502,20 +486,20 @@ class SentryTracerTest { } @Test - fun `when waiting for children, finishing transaction does not call scopes if all children are not finished`() { + fun `when waiting for children, finishing transaction does not call hub if all children are not finished`() { val transaction = fixture.getSut(waitForChildren = true) transaction.startChild("op") transaction.finish() - verify(fixture.scopes, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) + verify(fixture.hub, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) } @Test - fun `when waiting for children, finishing transaction calls scopes if all children are finished`() { + fun `when waiting for children, finishing transaction calls hub if all children are finished`() { val transaction = fixture.getSut(waitForChildren = true) val child = transaction.startChild("op") child.finish() transaction.finish() - verify(fixture.scopes).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) + verify(fixture.hub).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) } @Test @@ -532,21 +516,21 @@ class SentryTracerTest { } @Test - fun `when waiting for children, scopes is not called until transaction is finished`() { + fun `when waiting for children, hub is not called until transaction is finished`() { val transaction = fixture.getSut(waitForChildren = true) val child = transaction.startChild("op") child.finish() - verify(fixture.scopes, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) + verify(fixture.hub, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) } @Test - fun `when waiting for children, finishing last child calls scopes if transaction is already finished`() { + fun `when waiting for children, finishing last child calls hub if transaction is already finished`() { val transaction = fixture.getSut(waitForChildren = true) val child = transaction.startChild("op") transaction.finish(SpanStatus.INVALID_ARGUMENT) - verify(fixture.scopes, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) + verify(fixture.hub, never()).captureTransaction(any(), any(), anyOrNull(), anyOrNull()) child.finish() - verify(fixture.scopes, times(1)).captureTransaction( + verify(fixture.hub, times(1)).captureTransaction( check { assertEquals(SpanStatus.INVALID_ARGUMENT, it.status) }, @@ -568,7 +552,7 @@ class SentryTracerTest { transaction.finish(SpanStatus.INVALID_ARGUMENT) - verify(fixture.scopes, times(1)).captureTransaction( + verify(fixture.hub, times(1)).captureTransaction( check { assertEquals(2, it.spans.size) // span status/timestamp is retained @@ -591,7 +575,7 @@ class SentryTracerTest { it.isTraceSampling = true it.isSendDefaultPii = true }) - fixture.scopes.setUser( + fixture.hub.setUser( User().apply { id = "user-id" others = mapOf("segment" to "pro") @@ -604,6 +588,8 @@ class SentryTracerTest { assertEquals("environment", it.environment) assertEquals("release@3.0.0", it.release) assertEquals(transaction.name, it.transaction) + // assertEquals("user-id", it.userId) + assertEquals("pro", it.userSegment) } } @@ -612,7 +598,7 @@ class SentryTracerTest { val transaction = fixture.getSut({ it.isTraceSampling = true }) - fixture.scopes.setUser( + fixture.hub.setUser( User().apply { id = "user-id" others = mapOf("segment" to "pro") @@ -636,7 +622,7 @@ class SentryTracerTest { it.isTraceSampling = true }) val traceBeforeUserSet = transaction.traceContext() - fixture.scopes.setUser( + fixture.hub.setUser( User().apply { id = "user-id" } @@ -666,7 +652,7 @@ class SentryTracerTest { it.isSendDefaultPii = true }) - fixture.scopes.setUser( + fixture.hub.setUser( User().apply { id = "userId12345" others = mapOf("segment" to "pro") @@ -696,7 +682,7 @@ class SentryTracerTest { it.release = "1.0.99-rc.7" }) - fixture.scopes.setUser( + fixture.hub.setUser( User().apply { id = "userId12345" others = mapOf("segment" to "pro") @@ -727,7 +713,7 @@ class SentryTracerTest { it.isSendDefaultPii = true }) - fixture.scopes.setUser(null) + fixture.hub.setUser(null) val header = transaction.toBaggageHeader(null) assertNotNull(header) { @@ -749,7 +735,7 @@ class SentryTracerTest { val transaction = fixture.getSut(samplingDecision = TracesSamplingDecision(true)) transaction.setData("key", "val") transaction.finish() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals("val", it.getExtra("key")) }, @@ -766,7 +752,7 @@ class SentryTracerTest { span.setData("key", "val") span.finish() transaction.finish() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertNotNull(it.spans.first().data) { assertEquals("val", it["key"]) @@ -854,7 +840,7 @@ class SentryTracerTest { await.untilFalse(transaction.isFinishTimerRunning) - verify(fixture.scopes, never()).captureTransaction( + verify(fixture.hub, never()).captureTransaction( anyOrNull(), anyOrNull(), anyOrNull(), @@ -871,7 +857,7 @@ class SentryTracerTest { await.untilFalse(transaction.isFinishTimerRunning) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( anyOrNull(), anyOrNull(), anyOrNull(), @@ -930,7 +916,7 @@ class SentryTracerTest { await.untilFalse(transaction.isFinishTimerRunning) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(2, it.spans.size) assertEquals(transaction.root.finishDate, span2.finishDate) @@ -968,7 +954,7 @@ class SentryTracerTest { transaction.setMeasurement("days", 2, MeasurementUnit.Duration.DAY) transaction.finish() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(1.0f, it.measurements["metric1"]!!.value) assertEquals(null, it.measurements["metric1"]!!.unit) @@ -989,7 +975,7 @@ class SentryTracerTest { transaction.setMeasurement("metric1", 2, MeasurementUnit.Duration.DAY) transaction.finish() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(2, it.measurements["metric1"]!!.value) assertEquals("day", it.measurements["metric1"]!!.unit) @@ -1007,7 +993,7 @@ class SentryTracerTest { transaction.setMeasurementFromChild("metric1", 2, MeasurementUnit.Duration.DAY) transaction.finish() - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(1.0f, it.measurements["metric1"]!!.value) assertNull(it.measurements["metric1"]!!.unit) @@ -1077,7 +1063,7 @@ class SentryTracerTest { assertTrue(span.isFinished) // and the transaction should be captured - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(1, it.spans.size) assertEquals(transaction.root.finishDate!!.nanoTimestamp(), span.finishDate!!.nanoTimestamp()) @@ -1107,7 +1093,7 @@ class SentryTracerTest { assertTrue(span.isFinished) // and the transaction should be captured - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(1, it.spans.size) assertEquals(transactionFinishDate, span.finishDate) @@ -1148,7 +1134,7 @@ class SentryTracerTest { assertEquals(expectedParentStartDate, parentSpan.startDate) assertEquals(expectedParentEndDate, parentSpan.finishDate) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(3, it.spans.size) }, @@ -1188,7 +1174,7 @@ class SentryTracerTest { assertEquals(expectedParentStartDate, parentSpan.startDate) assertEquals(expectedParentEndDate, parentSpan.finishDate) - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(3, it.spans.size) }, @@ -1279,7 +1265,7 @@ class SentryTracerTest { assertEquals(transaction.finishDate, span1.finishDate) // and the transaction should be captured with both spans - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(2, it.spans.size) }, @@ -1302,7 +1288,7 @@ class SentryTracerTest { transaction.forceFinish(SpanStatus.ABORTED, false, null) // then a transaction should be captured with 0 spans - verify(fixture.scopes).captureTransaction( + verify(fixture.hub).captureTransaction( check { assertEquals(0, it.spans.size) }, @@ -1325,7 +1311,7 @@ class SentryTracerTest { transaction.forceFinish(SpanStatus.ABORTED, true, null) // then the transaction should be captured with 0 spans - verify(fixture.scopes, never()).captureTransaction( + verify(fixture.hub, never()).captureTransaction( anyOrNull(), anyOrNull(), anyOrNull(), @@ -1349,7 +1335,7 @@ class SentryTracerTest { tracer.scheduleFinish() assertTrue(tracer.isFinished) - verify(fixture.scopes).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) + verify(fixture.hub).captureTransaction(any(), anyOrNull(), anyOrNull(), anyOrNull()) } @Test diff --git a/sentry/src/test/java/io/sentry/SentryWrapperTest.kt b/sentry/src/test/java/io/sentry/SentryWrapperTest.kt index 6fd32ac57b..7f6d449eac 100644 --- a/sentry/src/test/java/io/sentry/SentryWrapperTest.kt +++ b/sentry/src/test/java/io/sentry/SentryWrapperTest.kt @@ -27,7 +27,7 @@ class SentryWrapperTest { } @Test - fun `scopes is reset to state within the thread after isolated supply is done`() { + fun `hub is reset to its state within the thread after supply is done`() { Sentry.init { it.dsn = dsn it.beforeSend = SentryOptions.BeforeSendCallback { event, hint -> @@ -35,20 +35,20 @@ class SentryWrapperTest { } } - val mainScopes = Sentry.getCurrentScopes() - val threadedScopes = Sentry.getCurrentScopes().forkedCurrentScope("test") + val mainHub = Sentry.getCurrentHub() + val threadedHub = Sentry.getCurrentHub().clone() executor.submit { - Sentry.setCurrentScopes(threadedScopes) + Sentry.setCurrentHub(threadedHub) }.get() - assertEquals(mainScopes, Sentry.getCurrentScopes()) + assertEquals(mainHub, Sentry.getCurrentHub()) val callableFuture = CompletableFuture.supplyAsync( SentryWrapper.wrapSupplier { - assertNotEquals(mainScopes, Sentry.getCurrentScopes()) - assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) + assertNotEquals(mainHub, Sentry.getCurrentHub()) + assertNotEquals(threadedHub, Sentry.getCurrentHub()) "Result 1" }, executor @@ -57,13 +57,13 @@ class SentryWrapperTest { callableFuture.join() executor.submit { - assertNotEquals(mainScopes, Sentry.getCurrentScopes()) - assertEquals(threadedScopes, Sentry.getCurrentScopes()) + assertNotEquals(mainHub, Sentry.getCurrentHub()) + assertEquals(threadedHub, Sentry.getCurrentHub()) }.get() } @Test - fun `wrapped supply async isolates Scopes`() { + fun `wrapped supply async isolates Hubs`() { val capturedEvents = mutableListOf() Sentry.init { @@ -115,7 +115,7 @@ class SentryWrapperTest { } @Test - fun `wrapped callable isolates Scopes`() { + fun `wrapped callable isolates Hubs`() { val capturedEvents = mutableListOf() Sentry.init { @@ -164,25 +164,25 @@ class SentryWrapperTest { } @Test - fun `scopes is reset to state within the thread after isolated callable is done`() { + fun `hub is reset to its state within the thread after callable is done`() { Sentry.init { it.dsn = dsn } - val mainScopes = Sentry.getCurrentScopes() - val threadedScopes = Sentry.getCurrentScopes().forkedCurrentScope("test") + val mainHub = Sentry.getCurrentHub() + val threadedHub = Sentry.getCurrentHub().clone() executor.submit { - Sentry.setCurrentScopes(threadedScopes) + Sentry.setCurrentHub(threadedHub) }.get() - assertEquals(mainScopes, Sentry.getCurrentScopes()) + assertEquals(mainHub, Sentry.getCurrentHub()) val callableFuture = executor.submit( SentryWrapper.wrapCallable { - assertNotEquals(mainScopes, Sentry.getCurrentScopes()) - assertNotEquals(threadedScopes, Sentry.getCurrentScopes()) + assertNotEquals(mainHub, Sentry.getCurrentHub()) + assertNotEquals(threadedHub, Sentry.getCurrentHub()) "Result 1" } ) @@ -190,8 +190,8 @@ class SentryWrapperTest { callableFuture.get() executor.submit { - assertNotEquals(mainScopes, Sentry.getCurrentScopes()) - assertEquals(threadedScopes, Sentry.getCurrentScopes()) + assertNotEquals(mainHub, Sentry.getCurrentHub()) + assertEquals(threadedHub, Sentry.getCurrentHub()) }.get() } } diff --git a/sentry/src/test/java/io/sentry/ShutdownHookIntegrationTest.kt b/sentry/src/test/java/io/sentry/ShutdownHookIntegrationTest.kt index 94250685a8..8218740b89 100644 --- a/sentry/src/test/java/io/sentry/ShutdownHookIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/ShutdownHookIntegrationTest.kt @@ -16,7 +16,7 @@ class ShutdownHookIntegrationTest { private class Fixture { val runtime = mock() val options = SentryOptions() - val scopes = mock() + val hub = mock() fun getSut(): ShutdownHookIntegration { return ShutdownHookIntegration(runtime) @@ -29,7 +29,7 @@ class ShutdownHookIntegrationTest { fun `registration attaches shutdown hook to runtime`() { val integration = fixture.getSut() - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) verify(fixture.runtime).addShutdownHook(any()) } @@ -39,7 +39,7 @@ class ShutdownHookIntegrationTest { val integration = fixture.getSut() fixture.options.isEnableShutdownHook = false - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) verify(fixture.runtime, never()).addShutdownHook(any()) } @@ -48,7 +48,7 @@ class ShutdownHookIntegrationTest { fun `registration removes shutdown hook from runtime`() { val integration = fixture.getSut() - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) integration.close() verify(fixture.runtime).removeShutdownHook(any()) @@ -58,13 +58,13 @@ class ShutdownHookIntegrationTest { fun `hook calls flush`() { val integration = fixture.getSut() - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) assertNotNull(integration.hook) { it.start() it.join() } - verify(fixture.scopes).flush(any()) + verify(fixture.hub).flush(any()) } @Test @@ -72,13 +72,13 @@ class ShutdownHookIntegrationTest { val integration = fixture.getSut() fixture.options.flushTimeoutMillis = 10000 - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) assertNotNull(integration.hook) { it.start() it.join() } - verify(fixture.scopes).flush(eq(10000)) + verify(fixture.hub).flush(eq(10000)) } @Test @@ -86,7 +86,7 @@ class ShutdownHookIntegrationTest { val integration = fixture.getSut() whenever(fixture.runtime.removeShutdownHook(any())).thenThrow(java.lang.IllegalStateException("Shutdown in progress")) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) integration.close() verify(fixture.runtime).removeShutdownHook(any()) @@ -97,7 +97,7 @@ class ShutdownHookIntegrationTest { val integration = fixture.getSut() whenever(fixture.runtime.addShutdownHook(any())).thenThrow(java.lang.IllegalStateException("VM already shutting down")) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) verify(fixture.runtime).addShutdownHook(any()) } @@ -107,7 +107,7 @@ class ShutdownHookIntegrationTest { val integration = fixture.getSut() whenever(fixture.runtime.removeShutdownHook(any())).thenThrow(java.lang.IllegalStateException()) - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) assertFails { integration.close() @@ -120,7 +120,7 @@ class ShutdownHookIntegrationTest { fun `Integration adds itself to integration list`() { val integration = fixture.getSut() - integration.register(fixture.scopes, fixture.options) + integration.register(fixture.hub, fixture.options) assertTrue( fixture.options.sdkVersion!!.integrationSet.contains("ShutdownHook") diff --git a/sentry/src/test/java/io/sentry/SpanTest.kt b/sentry/src/test/java/io/sentry/SpanTest.kt index 0a144f5ce8..09bf01c791 100644 --- a/sentry/src/test/java/io/sentry/SpanTest.kt +++ b/sentry/src/test/java/io/sentry/SpanTest.kt @@ -21,10 +21,10 @@ import kotlin.test.assertTrue class SpanTest { private class Fixture { - val scopes = mock() + val hub = mock() init { - whenever(scopes.options).thenReturn( + whenever(hub.options).thenReturn( SentryOptions().apply { dsn = "https://key@sentry.io/proj" isTraceSampling = true @@ -33,27 +33,20 @@ class SpanTest { } fun getSut(options: SpanOptions = SpanOptions()): Span { - val context = SpanContext( + return Span( SentryId(), SpanId(), - SpanId(), + SentryTracer(TransactionContext("name", "op"), hub), "op", + hub, null, - null, - null, - null - ) - return Span( - SentryTracer(TransactionContext("name", "op"), scopes), - scopes, - context, options, null ) } fun getRootSut(options: TransactionOptions = TransactionOptions()): Span { - return SentryTracer(TransactionContext("name", "op"), scopes, options).root + return SentryTracer(TransactionContext("name", "op"), hub, options).root } } @@ -108,25 +101,15 @@ class SpanTest { fun `converts to Sentry trace header`() { val traceId = SentryId() val parentSpanId = SpanId() - val spanContext = SpanContext( + val span = Span( traceId, - SpanId(), parentSpanId, - "op", - null, - TracesSamplingDecision(true), - null, - null - ) - val span = Span( SentryTracer( TransactionContext("name", "op", TracesSamplingDecision(true)), - fixture.scopes + fixture.hub ), - fixture.scopes, - spanContext, - SpanOptions(), - null + "op", + fixture.hub ) val sentryTrace = span.toSentryTrace() @@ -137,38 +120,6 @@ class SpanTest { } } - @Test - fun `transfers span origin from options to span context`() { - val traceId = SentryId() - val parentSpanId = SpanId() - val spanContext = SpanContext( - traceId, - SpanId(), - parentSpanId, - "op", - null, - TracesSamplingDecision(true), - null, - "old-origin" - ) - - val spanOptions = SpanOptions() - spanOptions.origin = "new-origin" - - val span = Span( - SentryTracer( - TransactionContext("name", "op", TracesSamplingDecision(true)), - fixture.scopes - ), - fixture.scopes, - spanContext, - spanOptions, - null - ) - - assertEquals("new-origin", span.spanContext.origin) - } - @Test fun `starting a child with details adds span to transaction`() { val transaction = getTransaction() @@ -212,17 +163,17 @@ class SpanTest { } @Test - fun `when span has throwable set set, it assigns itself to throwable on the Scopes`() { + fun `when span has throwable set set, it assigns itself to throwable on the Hub`() { val transaction = SentryTracer( TransactionContext("name", "op"), - fixture.scopes + fixture.hub ) val span = transaction.startChild("op") val ex = RuntimeException() span.throwable = ex span.finish() - verify(fixture.scopes).setSpanContext(ex, span, "name") + verify(fixture.hub).setSpanContext(ex, span, "name") } @Test @@ -237,7 +188,7 @@ class SpanTest { span.finish(SpanStatus.UNKNOWN_ERROR) // call only once - verify(fixture.scopes).setSpanContext(any(), any(), any()) + verify(fixture.hub).setSpanContext(any(), any(), any()) assertEquals(SpanStatus.OK, span.status) assertEquals(timestamp, span.finishDate) } @@ -558,7 +509,7 @@ class SpanTest { } private fun getTransaction(transactionContext: TransactionContext = TransactionContext("name", "op")): SentryTracer { - return SentryTracer(transactionContext, fixture.scopes) + return SentryTracer(transactionContext, fixture.hub) } private fun startChildFromSpan(): Span { diff --git a/sentry/src/test/java/io/sentry/StackTest.kt b/sentry/src/test/java/io/sentry/StackTest.kt index c8b0aa8a9f..13089ab6a4 100644 --- a/sentry/src/test/java/io/sentry/StackTest.kt +++ b/sentry/src/test/java/io/sentry/StackTest.kt @@ -1,7 +1,6 @@ package io.sentry import io.sentry.Stack.StackItem -import io.sentry.test.createSentryClientMock import org.mockito.kotlin.mock import kotlin.test.Test import kotlin.test.assertEquals @@ -11,7 +10,7 @@ class StackTest { private class Fixture { val options = SentryOptions() - val client = createSentryClientMock() + val client = mock() val scope = Scope(options) lateinit var rootItem: StackItem diff --git a/sentry/src/test/java/io/sentry/TraceContextSerializationTest.kt b/sentry/src/test/java/io/sentry/TraceContextSerializationTest.kt index 66f34f41c4..e79e5ebf8c 100644 --- a/sentry/src/test/java/io/sentry/TraceContextSerializationTest.kt +++ b/sentry/src/test/java/io/sentry/TraceContextSerializationTest.kt @@ -1,7 +1,6 @@ package io.sentry import io.sentry.protocol.SentryId -import io.sentry.protocol.TransactionNameSource import io.sentry.protocol.User import org.junit.Test import org.mockito.kotlin.mock @@ -55,10 +54,10 @@ class TraceContextSerializationTest { private fun createTraceContext(sRate: Double): TraceContext { val baggage = Baggage(fixture.logger) - val scopes: IScopes = mock() - whenever(scopes.options).thenReturn(SentryOptions()) + val hub: IHub = mock() + whenever(hub.options).thenReturn(SentryOptions()) baggage.setValuesFromTransaction( - SentryId(), + SentryTracer(TransactionContext("name", "op"), hub), User().apply { id = "user-id" others = mapOf("segment" to "pro") @@ -69,9 +68,7 @@ class TraceContextSerializationTest { release = "1.0.17" tracesSampleRate = sRate }, - TracesSamplingDecision(sRate > 0.5, sRate), - "name", - TransactionNameSource.ROUTE + TracesSamplingDecision(sRate > 0.5, sRate) ) return baggage.toTraceContext()!! } diff --git a/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt b/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt index 409d3b971b..01353d5ac0 100644 --- a/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt @@ -5,10 +5,8 @@ import io.sentry.exception.ExceptionMechanismException import io.sentry.hints.DiskFlushNotification import io.sentry.hints.EventDropReason.MULTITHREADED_DEDUPLICATION import io.sentry.protocol.SentryId -import io.sentry.test.createTestScopes import io.sentry.util.HintUtils import org.mockito.kotlin.any -import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.argThat import org.mockito.kotlin.argWhere import org.mockito.kotlin.argumentCaptor @@ -21,7 +19,6 @@ import java.io.PrintStream import java.nio.file.Files import kotlin.concurrent.thread import kotlin.test.Test -import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertTrue @@ -33,7 +30,7 @@ class UncaughtExceptionHandlerIntegrationTest { val defaultHandler = mock() val thread = mock() val throwable = Throwable("test") - val scopes = mock() + val hub = mock() val options = SentryOptions() val logger = mock() @@ -66,17 +63,17 @@ class UncaughtExceptionHandlerIntegrationTest { fun `when uncaughtException is called, sentry captures exception`() { val sut = fixture.getSut(isPrintUncaughtStackTrace = false) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.uncaughtException(fixture.thread, fixture.throwable) - verify(fixture.scopes).captureEvent(any(), any()) + verify(fixture.hub).captureEvent(any(), any()) } @Test fun `when register is called, current handler is not lost`() { val sut = fixture.getSut(hasDefaultHandler = true, isPrintUncaughtStackTrace = false) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.uncaughtException(fixture.thread, fixture.throwable) verify(fixture.defaultHandler).uncaughtException(fixture.thread, fixture.throwable) @@ -84,7 +81,7 @@ class UncaughtExceptionHandlerIntegrationTest { @Test fun `when uncaughtException is called, exception captured has handled=false`() { - whenever(fixture.scopes.captureException(any())).thenAnswer { invocation -> + whenever(fixture.hub.captureException(any())).thenAnswer { invocation -> val e = invocation.getArgument(1) assertNotNull(e) assertNotNull(e.exceptionMechanism) @@ -94,22 +91,22 @@ class UncaughtExceptionHandlerIntegrationTest { val sut = fixture.getSut(isPrintUncaughtStackTrace = false) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.uncaughtException(fixture.thread, fixture.throwable) - verify(fixture.scopes).captureEvent(any(), any()) + verify(fixture.hub).captureEvent(any(), any()) } @Test - fun `when scopes is closed, integrations should be closed`() { + fun `when hub is closed, integrations should be closed`() { val integrationMock = mock() val options = SentryOptions() options.dsn = "https://key@sentry.io/proj" options.addIntegration(integrationMock) options.cacheDirPath = fixture.file.absolutePath options.setSerializer(mock()) - val scopes = createTestScopes(options) - scopes.close() + val hub = Hub(options) + hub.close() verify(integrationMock).close() } @@ -120,7 +117,7 @@ class UncaughtExceptionHandlerIntegrationTest { isPrintUncaughtStackTrace = false ) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.handler, never()).defaultUncaughtExceptionHandler = any() } @@ -129,7 +126,7 @@ class UncaughtExceptionHandlerIntegrationTest { fun `When defaultUncaughtExceptionHandler is enabled, should install Sentry UncaughtExceptionHandler`() { val sut = fixture.getSut(isPrintUncaughtStackTrace = false) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) verify(fixture.handler).defaultUncaughtExceptionHandler = argWhere { it is UncaughtExceptionHandlerIntegration } @@ -139,7 +136,7 @@ class UncaughtExceptionHandlerIntegrationTest { fun `When defaultUncaughtExceptionHandler is set and integration is closed, default uncaught exception handler is reset to previous handler`() { val sut = fixture.getSut(hasDefaultHandler = true, isPrintUncaughtStackTrace = false) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) whenever(fixture.handler.defaultUncaughtExceptionHandler) .thenReturn(sut) sut.close() @@ -151,7 +148,7 @@ class UncaughtExceptionHandlerIntegrationTest { fun `When defaultUncaughtExceptionHandler is not set and integration is closed, default uncaught exception handler is reset to null`() { val sut = fixture.getSut(isPrintUncaughtStackTrace = false) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) whenever(fixture.handler.defaultUncaughtExceptionHandler) .thenReturn(sut) sut.close() @@ -168,7 +165,7 @@ class UncaughtExceptionHandlerIntegrationTest { val sut = fixture.getSut(isPrintUncaughtStackTrace = true) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.uncaughtException(fixture.thread, RuntimeException("This should be printed!")) assertTrue( @@ -188,7 +185,7 @@ class UncaughtExceptionHandlerIntegrationTest { fun `waits for event to flush on disk`() { val capturedEventId = SentryId() - whenever(fixture.scopes.captureEvent(any(), any())).thenAnswer { invocation -> + whenever(fixture.hub.captureEvent(any(), any())).thenAnswer { invocation -> val hint = HintUtils.getSentrySdkHint(invocation.getArgument(1)) as DiskFlushNotification thread { @@ -200,10 +197,10 @@ class UncaughtExceptionHandlerIntegrationTest { val sut = fixture.getSut(flushTimeoutMillis = 5000) - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.uncaughtException(fixture.thread, fixture.throwable) - verify(fixture.scopes).captureEvent(any(), any()) + verify(fixture.hub).captureEvent(any(), any()) // shouldn't fall into timed out state, because we marked event as flushed on another thread verify(fixture.logger, never()).log( any(), @@ -214,14 +211,14 @@ class UncaughtExceptionHandlerIntegrationTest { @Test fun `does not block flushing when the event was dropped`() { - whenever(fixture.scopes.captureEvent(any(), any())).thenReturn(SentryId.EMPTY_ID) + whenever(fixture.hub.captureEvent(any(), any())).thenReturn(SentryId.EMPTY_ID) val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.uncaughtException(fixture.thread, fixture.throwable) - verify(fixture.scopes).captureEvent(any(), any()) + verify(fixture.hub).captureEvent(any(), any()) // we do not call markFlushed, hence it should time out waiting for flush, but because // we drop the event, it should not even come to this if-check verify(fixture.logger, never()).log( @@ -234,17 +231,17 @@ class UncaughtExceptionHandlerIntegrationTest { @Test fun `waits for event to flush on disk if it was dropped by multithreaded deduplicator`() { val hintCaptor = argumentCaptor() - whenever(fixture.scopes.captureEvent(any(), hintCaptor.capture())).thenAnswer { + whenever(fixture.hub.captureEvent(any(), hintCaptor.capture())).thenAnswer { HintUtils.setEventDropReason(hintCaptor.firstValue, MULTITHREADED_DEDUPLICATION) return@thenAnswer SentryId.EMPTY_ID } val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.uncaughtException(fixture.thread, fixture.throwable) - verify(fixture.scopes).captureEvent(any(), any()) + verify(fixture.hub).captureEvent(any(), any()) // we do not call markFlushed, even though we dropped the event, the reason was // MULTITHREADED_DEDUPLICATION, so it should time out verify(fixture.logger).log( @@ -257,15 +254,15 @@ class UncaughtExceptionHandlerIntegrationTest { @Test fun `when there is no active transaction on scope, sets current event id as flushable`() { val eventCaptor = argumentCaptor() - whenever(fixture.scopes.captureEvent(eventCaptor.capture(), any())) + whenever(fixture.hub.captureEvent(eventCaptor.capture(), any())) .thenReturn(SentryId.EMPTY_ID) val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.uncaughtException(fixture.thread, fixture.throwable) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( any(), argThat { (HintUtils.getSentrySdkHint(this) as UncaughtExceptionHint) @@ -277,16 +274,16 @@ class UncaughtExceptionHandlerIntegrationTest { @Test fun `when there is active transaction on scope, does not set current event id as flushable`() { val eventCaptor = argumentCaptor() - whenever(fixture.scopes.transaction).thenReturn(mock()) - whenever(fixture.scopes.captureEvent(eventCaptor.capture(), any())) + whenever(fixture.hub.transaction).thenReturn(mock()) + whenever(fixture.hub.captureEvent(eventCaptor.capture(), any())) .thenReturn(SentryId.EMPTY_ID) val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) + sut.register(fixture.hub, fixture.options) sut.uncaughtException(fixture.thread, fixture.throwable) - verify(fixture.scopes).captureEvent( + verify(fixture.hub).captureEvent( any(), argThat { !(HintUtils.getSentrySdkHint(this) as UncaughtExceptionHint) @@ -294,54 +291,4 @@ class UncaughtExceptionHandlerIntegrationTest { } ) } - - @Test - fun `multiple registrations do not cause the build-up of a tree of UncaughtExceptionHandlerIntegrations`() { - var currentDefaultHandler: Thread.UncaughtExceptionHandler? = null - - val handler = mock() - whenever(handler.defaultUncaughtExceptionHandler).thenAnswer { currentDefaultHandler } - - whenever(handler.setDefaultUncaughtExceptionHandler(anyOrNull())).then { - currentDefaultHandler = it.getArgument(0) - null - } - - val integration1 = UncaughtExceptionHandlerIntegration(handler) - integration1.register(fixture.scopes, fixture.options) - - val integration2 = UncaughtExceptionHandlerIntegration(handler) - integration2.register(fixture.scopes, fixture.options) - - assertEquals(currentDefaultHandler, integration2) - integration2.close() - - assertEquals(null, currentDefaultHandler) - } - - @Test - fun `multiple registrations do not cause the build-up of a tree of UncaughtExceptionHandlerIntegrations, reset to inital`() { - val initialUncaughtExceptionHandler = Thread.UncaughtExceptionHandler { _, _ -> } - - var currentDefaultHandler: Thread.UncaughtExceptionHandler? = initialUncaughtExceptionHandler - - val handler = mock() - whenever(handler.defaultUncaughtExceptionHandler).thenAnswer { currentDefaultHandler } - - whenever(handler.setDefaultUncaughtExceptionHandler(anyOrNull())).then { - currentDefaultHandler = it.getArgument(0) - null - } - - val integration1 = UncaughtExceptionHandlerIntegration(handler) - integration1.register(fixture.scopes, fixture.options) - - val integration2 = UncaughtExceptionHandlerIntegration(handler) - integration2.register(fixture.scopes, fixture.options) - - assertEquals(currentDefaultHandler, integration2) - integration2.close() - - assertEquals(initialUncaughtExceptionHandler, currentDefaultHandler) - } } diff --git a/sentry/src/test/java/io/sentry/backpressure/BackpressureMonitorTest.kt b/sentry/src/test/java/io/sentry/backpressure/BackpressureMonitorTest.kt index cf234574bf..c010c97238 100644 --- a/sentry/src/test/java/io/sentry/backpressure/BackpressureMonitorTest.kt +++ b/sentry/src/test/java/io/sentry/backpressure/BackpressureMonitorTest.kt @@ -1,6 +1,6 @@ package io.sentry.backpressure -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ISentryExecutorService import io.sentry.SentryOptions import io.sentry.backpressure.BackpressureMonitor.MAX_DOWNSAMPLE_FACTOR @@ -17,13 +17,13 @@ class BackpressureMonitorTest { class Fixture { val options = SentryOptions() - val scopes = mock() + val hub = mock() val executor = mock() fun getSut(): BackpressureMonitor { options.executorService = executor whenever(executor.isClosed).thenReturn(false) whenever(executor.schedule(any(), any())).thenReturn(mock>()) - return BackpressureMonitor(options, scopes) + return BackpressureMonitor(options, hub) } } @@ -38,7 +38,7 @@ class BackpressureMonitorTest { @Test fun `downsampleFactor increases with negative health checks up to max`() { val sut = fixture.getSut() - whenever(fixture.scopes.isHealthy).thenReturn(false) + whenever(fixture.hub.isHealthy).thenReturn(false) assertEquals(0, sut.downsampleFactor) (1..MAX_DOWNSAMPLE_FACTOR).forEach { i -> @@ -54,13 +54,13 @@ class BackpressureMonitorTest { @Test fun `downsampleFactor goes back to 0 after positive health check`() { val sut = fixture.getSut() - whenever(fixture.scopes.isHealthy).thenReturn(false) + whenever(fixture.hub.isHealthy).thenReturn(false) assertEquals(0, sut.downsampleFactor) sut.checkHealth() assertEquals(1, sut.downsampleFactor) - whenever(fixture.scopes.isHealthy).thenReturn(true) + whenever(fixture.hub.isHealthy).thenReturn(true) sut.checkHealth() assertEquals(0, sut.downsampleFactor) } diff --git a/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt b/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt index 0cd9be9094..527e1b5531 100644 --- a/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt +++ b/sentry/src/test/java/io/sentry/clientreport/ClientReportTest.kt @@ -7,7 +7,7 @@ import io.sentry.DataCategory import io.sentry.DateUtils import io.sentry.EventProcessor import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.NoOpLogger import io.sentry.ProfilingTraceData import io.sentry.Sentry @@ -18,7 +18,6 @@ import io.sentry.SentryEvent import io.sentry.SentryOptions import io.sentry.SentryTracer import io.sentry.Session -import io.sentry.TracesSamplingDecision import io.sentry.TransactionContext import io.sentry.UncaughtExceptionHandlerIntegration.UncaughtExceptionHint import io.sentry.UserFeedback @@ -49,9 +48,9 @@ class ClientReportTest { @Test fun `lost envelope can be recorded`() { givenClientReportRecorder() - val scopes = mock() - whenever(scopes.options).thenReturn(opts) - val transaction = SentryTracer(TransactionContext("name", "op"), scopes) + val hub = mock() + whenever(hub.options).thenReturn(opts) + val transaction = SentryTracer(TransactionContext("name", "op"), hub) val lostClientReport = ClientReport( DateUtils.getCurrentDateTime(), @@ -64,7 +63,6 @@ class ClientReportTest { val envelope = testHelper.newEnvelope( SentryEnvelopeItem.fromClientReport(opts.serializer, lostClientReport), - SentryEnvelopeItem.fromEvent(opts.serializer, SentryTransaction(transaction)), SentryEnvelopeItem.fromEvent(opts.serializer, SentryEvent()), SentryEnvelopeItem.fromSession(opts.serializer, Session("dis", User(), "env", "0.0.1")), SentryEnvelopeItem.fromUserFeedback(opts.serializer, UserFeedback(SentryId(UUID.randomUUID()))), @@ -77,12 +75,10 @@ class ClientReportTest { clientReportRecorder.recordLostEnvelope(DiscardReason.NETWORK_ERROR, envelope) val clientReportAtEnd = clientReportRecorder.resetCountsAndGenerateClientReport() - testHelper.assertTotalCount(15, clientReportAtEnd) + testHelper.assertTotalCount(13, clientReportAtEnd) testHelper.assertCountFor(DiscardReason.SAMPLE_RATE, DataCategory.Error, 3, clientReportAtEnd) testHelper.assertCountFor(DiscardReason.BEFORE_SEND, DataCategory.Error, 2, clientReportAtEnd) testHelper.assertCountFor(DiscardReason.QUEUE_OVERFLOW, DataCategory.Transaction, 1, clientReportAtEnd) - testHelper.assertCountFor(DiscardReason.NETWORK_ERROR, DataCategory.Span, 1, clientReportAtEnd) - testHelper.assertCountFor(DiscardReason.NETWORK_ERROR, DataCategory.Transaction, 1, clientReportAtEnd) testHelper.assertCountFor(DiscardReason.NETWORK_ERROR, DataCategory.Error, 1, clientReportAtEnd) testHelper.assertCountFor(DiscardReason.NETWORK_ERROR, DataCategory.UserReport, 1, clientReportAtEnd) testHelper.assertCountFor(DiscardReason.NETWORK_ERROR, DataCategory.Session, 1, clientReportAtEnd) @@ -92,29 +88,6 @@ class ClientReportTest { testHelper.assertCountFor(DiscardReason.NETWORK_ERROR, DataCategory.MetricBucket, 1, clientReportAtEnd) } - @Test - fun `lost transaction records dropped spans`() { - givenClientReportRecorder() - val scopes = mock() - whenever(scopes.options).thenReturn(opts) - val transaction = SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), scopes) - transaction.startChild("lost span", "span1").finish() - transaction.startChild("lost span", "span2").finish() - transaction.startChild("lost span", "span3").finish() - transaction.startChild("lost span", "span4").finish() - - val envelope = testHelper.newEnvelope( - SentryEnvelopeItem.fromEvent(opts.serializer, SentryTransaction(transaction)) - ) - - clientReportRecorder.recordLostEnvelope(DiscardReason.NETWORK_ERROR, envelope) - - val clientReportAtEnd = clientReportRecorder.resetCountsAndGenerateClientReport() - testHelper.assertTotalCount(6, clientReportAtEnd) - testHelper.assertCountFor(DiscardReason.NETWORK_ERROR, DataCategory.Span, 5, clientReportAtEnd) - testHelper.assertCountFor(DiscardReason.NETWORK_ERROR, DataCategory.Transaction, 1, clientReportAtEnd) - } - @Test fun `lost event can be recorded`() { givenClientReportRecorder() @@ -216,7 +189,7 @@ class DropEverythingEventProcessor : EventProcessor { class ClientReportTestHelper(val options: SentryOptions) { val reasons = DiscardReason.values() - val categories = DataCategory.values() + val categories = listOf(DataCategory.Error, DataCategory.Attachment, DataCategory.Session, DataCategory.Transaction, DataCategory.UserReport) fun assertTotalCount(expectedCount: Long, clientReport: ClientReport?) { assertEquals(expectedCount, clientReport?.discardedEvents?.sumOf { it.quantity } ?: 0L) diff --git a/sentry/src/test/java/io/sentry/instrumentation/file/FileIOSpanManagerTest.kt b/sentry/src/test/java/io/sentry/instrumentation/file/FileIOSpanManagerTest.kt index f6271b5b5e..00c89f27be 100644 --- a/sentry/src/test/java/io/sentry/instrumentation/file/FileIOSpanManagerTest.kt +++ b/sentry/src/test/java/io/sentry/instrumentation/file/FileIOSpanManagerTest.kt @@ -1,6 +1,6 @@ package io.sentry.instrumentation.file -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ISpan import io.sentry.ITransaction import io.sentry.util.PlatformTestManipulator @@ -20,25 +20,25 @@ class FileIOSpanManagerTest { @Test fun `startSpan uses transaction on Android platform`() { - val scopes = mock() + val hub = mock() val transaction = mock() - whenever(scopes.transaction).thenReturn(transaction) + whenever(hub.transaction).thenReturn(transaction) PlatformTestManipulator.pretendIsAndroid(true) - FileIOSpanManager.startSpan(scopes, "op.read") + FileIOSpanManager.startSpan(hub, "op.read") verify(transaction).startChild(any()) } @Test fun `startSpan uses last span on non-Android platforms`() { - val scopes = mock() + val hub = mock() val span = mock() - whenever(scopes.span).thenReturn(span) + whenever(hub.span).thenReturn(span) PlatformTestManipulator.pretendIsAndroid(false) - FileIOSpanManager.startSpan(scopes, "op.read") + FileIOSpanManager.startSpan(hub, "op.read") verify(span).startChild(any()) } } diff --git a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileInputStreamTest.kt b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileInputStreamTest.kt index 063b6d6428..5e27eb451d 100644 --- a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileInputStreamTest.kt +++ b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileInputStreamTest.kt @@ -1,6 +1,6 @@ package io.sentry.instrumentation.file -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryOptions import io.sentry.SentryTracer import io.sentry.SpanDataConvention @@ -29,7 +29,7 @@ import kotlin.test.assertTrue class SentryFileInputStreamTest { class Fixture { - val scopes = mock() + val hub = mock() lateinit var sentryTracer: SentryTracer private val options = SentryOptions() @@ -40,21 +40,21 @@ class SentryFileInputStreamTest { sendDefaultPii: Boolean = false ): SentryFileInputStream { tmpFile?.writeText("Text") - whenever(scopes.options).thenReturn( + whenever(hub.options).thenReturn( options.apply { isSendDefaultPii = sendDefaultPii mainThreadChecker = MainThreadChecker.getInstance() addInAppInclude("org.junit") } ) - sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) + sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) if (activeTransaction) { - whenever(scopes.span).thenReturn(sentryTracer) + whenever(hub.span).thenReturn(sentryTracer) } return if (fileDescriptor == null) { - SentryFileInputStream(tmpFile, scopes) + SentryFileInputStream(tmpFile, hub) } else { - SentryFileInputStream(fileDescriptor, scopes) + SentryFileInputStream(fileDescriptor, hub) } } @@ -62,13 +62,13 @@ class SentryFileInputStreamTest { tmpFile: File? = null, delegate: FileInputStream ): SentryFileInputStream { - whenever(scopes.options).thenReturn(options) - sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) - whenever(scopes.span).thenReturn(sentryTracer) + whenever(hub.options).thenReturn(options) + sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) + whenever(hub.span).thenReturn(sentryTracer) return SentryFileInputStream.Factory.create( delegate, tmpFile, - scopes + hub ) as SentryFileInputStream } } diff --git a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileOutputStreamTest.kt b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileOutputStreamTest.kt index 8b175adc2d..f6a09830c2 100644 --- a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileOutputStreamTest.kt +++ b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileOutputStreamTest.kt @@ -1,6 +1,6 @@ package io.sentry.instrumentation.file -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryOptions import io.sentry.SentryTracer import io.sentry.SpanDataConvention @@ -24,7 +24,7 @@ import kotlin.test.assertTrue class SentryFileOutputStreamTest { class Fixture { - val scopes = mock() + val hub = mock() lateinit var sentryTracer: SentryTracer internal fun getSut( @@ -32,17 +32,17 @@ class SentryFileOutputStreamTest { activeTransaction: Boolean = true, append: Boolean = false ): SentryFileOutputStream { - whenever(scopes.options).thenReturn( + whenever(hub.options).thenReturn( SentryOptions().apply { mainThreadChecker = MainThreadChecker.getInstance() addInAppInclude("org.junit") } ) - sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) + sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) if (activeTransaction) { - whenever(scopes.span).thenReturn(sentryTracer) + whenever(hub.span).thenReturn(sentryTracer) } - return SentryFileOutputStream(tmpFile, append, scopes) + return SentryFileOutputStream(tmpFile, append, hub) } } diff --git a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileReaderTest.kt b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileReaderTest.kt index 38781d718b..2485579e7a 100644 --- a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileReaderTest.kt +++ b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileReaderTest.kt @@ -1,6 +1,6 @@ package io.sentry.instrumentation.file -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryOptions import io.sentry.SentryTracer import io.sentry.SpanDataConvention @@ -17,7 +17,7 @@ import kotlin.test.assertEquals class SentryFileReaderTest { class Fixture { - val scopes = mock() + val hub = mock() lateinit var sentryTracer: SentryTracer internal fun getSut( @@ -25,16 +25,16 @@ class SentryFileReaderTest { activeTransaction: Boolean = true ): SentryFileReader { tmpFile.writeText("TEXT") - whenever(scopes.options).thenReturn( + whenever(hub.options).thenReturn( SentryOptions().apply { mainThreadChecker = MainThreadChecker.getInstance() } ) - sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) + sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) if (activeTransaction) { - whenever(scopes.span).thenReturn(sentryTracer) + whenever(hub.span).thenReturn(sentryTracer) } - return SentryFileReader(tmpFile, scopes) + return SentryFileReader(tmpFile, hub) } } diff --git a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileWriterTest.kt b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileWriterTest.kt index 8f3d96b4d7..f0738d8725 100644 --- a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileWriterTest.kt +++ b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileWriterTest.kt @@ -1,6 +1,6 @@ package io.sentry.instrumentation.file -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryOptions import io.sentry.SentryTracer import io.sentry.SpanDataConvention @@ -17,7 +17,7 @@ import kotlin.test.assertEquals class SentryFileWriterTest { class Fixture { - val scopes = mock() + val hub = mock() lateinit var sentryTracer: SentryTracer internal fun getSut( @@ -25,16 +25,16 @@ class SentryFileWriterTest { activeTransaction: Boolean = true, append: Boolean = false ): SentryFileWriter { - whenever(scopes.options).thenReturn( + whenever(hub.options).thenReturn( SentryOptions().apply { mainThreadChecker = MainThreadChecker.getInstance() } ) - sentryTracer = SentryTracer(TransactionContext("name", "op"), scopes) + sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) if (activeTransaction) { - whenever(scopes.span).thenReturn(sentryTracer) + whenever(hub.span).thenReturn(sentryTracer) } - return SentryFileWriter(tmpFile, append, scopes) + return SentryFileWriter(tmpFile, append, hub) } } diff --git a/sentry/src/test/java/io/sentry/internal/SpotlightIntegrationTest.kt b/sentry/src/test/java/io/sentry/internal/SpotlightIntegrationTest.kt index 0a91bec562..73aa339f26 100644 --- a/sentry/src/test/java/io/sentry/internal/SpotlightIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/internal/SpotlightIntegrationTest.kt @@ -1,6 +1,6 @@ package io.sentry.internal -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryOptions import io.sentry.SentryOptions.BeforeEnvelopeCallback import io.sentry.SpotlightIntegration @@ -19,7 +19,7 @@ class SpotlightIntegrationTest { } val spotlight = SpotlightIntegration() - spotlight.register(mock(), options) + spotlight.register(mock(), options) assertNull(options.beforeEnvelopeCallback) } @@ -33,7 +33,7 @@ class SpotlightIntegrationTest { } val spotlight = SpotlightIntegration() - spotlight.register(mock(), options) + spotlight.register(mock(), options) assertEquals(envelopeCallback, options.beforeEnvelopeCallback) } @@ -45,7 +45,7 @@ class SpotlightIntegrationTest { } val spotlight = SpotlightIntegration() - spotlight.register(mock(), options) + spotlight.register(mock(), options) assertEquals(options.beforeEnvelopeCallback, spotlight) spotlight.close() @@ -71,7 +71,7 @@ class SpotlightIntegrationTest { } val spotlight = SpotlightIntegration() - spotlight.register(mock(), options) + spotlight.register(mock(), options) assertEquals("http://example.com:1234/stream", spotlight.spotlightConnectionUrl) } diff --git a/sentry/src/test/java/io/sentry/metrics/MetricsIntegrationTest.kt b/sentry/src/test/java/io/sentry/metrics/MetricsIntegrationTest.kt index a5850ab5a0..56d40f5b86 100644 --- a/sentry/src/test/java/io/sentry/metrics/MetricsIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/metrics/MetricsIntegrationTest.kt @@ -11,16 +11,10 @@ import org.mockito.kotlin.check import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever -import kotlin.test.BeforeTest import kotlin.test.Test class MetricsIntegrationTest { - @BeforeTest - fun setup() { - Sentry.close() - } - @Test fun `metrics are collected`() { val options = SentryOptions().apply { @@ -76,7 +70,6 @@ class MetricsIntegrationTest { Sentry.init(options) val client = mock() - whenever(client.isEnabled).thenReturn(true) val aggregator = MetricsAggregator(options, client) whenever(client.metricsAggregator).thenReturn(aggregator) Sentry.bindClient(client) diff --git a/sentry/src/test/java/io/sentry/protocol/CombinedContextsViewSerializationTest.kt b/sentry/src/test/java/io/sentry/protocol/CombinedContextsViewSerializationTest.kt deleted file mode 100644 index 87cb226abc..0000000000 --- a/sentry/src/test/java/io/sentry/protocol/CombinedContextsViewSerializationTest.kt +++ /dev/null @@ -1,89 +0,0 @@ -package io.sentry.protocol - -import io.sentry.CombinedContextsView -import io.sentry.ILogger -import io.sentry.JsonObjectWriter -import io.sentry.ScopeType -import org.junit.Test -import org.mockito.kotlin.any -import org.mockito.kotlin.mock -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever -import kotlin.test.assertEquals - -class CombinedContextsViewSerializationTest { - - class Fixture { - val logger = mock() - - fun getSut(): CombinedContextsView { - val current = Contexts() - val isolation = Contexts() - val global = Contexts() - val combined = CombinedContextsView(global, isolation, current, ScopeType.ISOLATION) - - current.setApp(AppSerializationTest.Fixture().getSut()) - current.setBrowser(BrowserSerializationTest.Fixture().getSut()) - current.trace = SpanContextSerializationTest.Fixture().getSut() - - isolation.setDevice(DeviceSerializationTest.Fixture().getSut()) - isolation.setOperatingSystem(OperatingSystemSerializationTest.Fixture().getSut()) - isolation.setResponse(ResponseSerializationTest.Fixture().getSut()) - - global.setRuntime(SentryRuntimeSerializationTest.Fixture().getSut()) - global.setGpu(GpuSerializationTest.Fixture().getSut()) - - return combined - } - } - private val fixture = Fixture() - - @Test - fun serialize() { - val expected = SerializationUtils.sanitizedFile("json/contexts.json") - val actual = SerializationUtils.serializeToString(fixture.getSut(), fixture.logger) - - assertEquals(expected, actual) - } - - @Test - fun serializeUnknownEntry() { - val sut = fixture.getSut() - sut["fixture-key"] = "fixture-value" - - val writer = mock().apply { - whenever(name(any())).thenReturn(this) - } - sut.serialize(writer, fixture.logger) - - verify(writer).name("fixture-key") - verify(writer).value(fixture.logger, "fixture-value") - } - - @Test - fun deserialize() { - val expectedJson = SerializationUtils.sanitizedFile("json/contexts.json") - val actual = SerializationUtils.deserializeJson( - expectedJson, - Contexts.Deserializer(), - fixture.logger - ) - val actualJson = SerializationUtils.serializeToString(actual, fixture.logger) - - assertEquals(expectedJson, actualJson) - } - - @Test - fun deserializeUnknownEntry() { - val sut = fixture.getSut() - sut["fixture-key"] = "fixture-value" - val serialized = SerializationUtils.serializeToString(sut, fixture.logger) - val deserialized = SerializationUtils.deserializeJson( - serialized, - Contexts.Deserializer(), - fixture.logger - ) - - assertEquals("fixture-value", deserialized["fixture-key"]) - } -} diff --git a/sentry/src/test/java/io/sentry/protocol/SentrySpanTest.kt b/sentry/src/test/java/io/sentry/protocol/SentrySpanTest.kt index 504f5bcccc..27499be0a0 100644 --- a/sentry/src/test/java/io/sentry/protocol/SentrySpanTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/SentrySpanTest.kt @@ -1,6 +1,6 @@ package io.sentry.protocol -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.SentryLongDate import io.sentry.SentryTracer import io.sentry.Span @@ -19,8 +19,9 @@ class SentrySpanTest { val span = Span( TransactionContext("name", "op"), mock(), - mock(), - SpanOptions().also { it.startTimestamp = SentryLongDate(1000000) } + mock(), + SentryLongDate(1000000), + SpanOptions() ) val sentrySpan = SentrySpan(span) diff --git a/sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt b/sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt index 557085031c..8346ddf102 100644 --- a/sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt +++ b/sentry/src/test/java/io/sentry/transport/RateLimiterTest.kt @@ -4,7 +4,7 @@ import io.sentry.Attachment import io.sentry.CheckIn import io.sentry.CheckInStatus import io.sentry.Hint -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.ISerializer import io.sentry.NoOpLogger import io.sentry.ProfilingTraceData @@ -81,10 +81,10 @@ class RateLimiterTest { fun `parse X-Sentry-Rate-Limit and set its values and retry after should be true`() { val rateLimiter = fixture.getSUT() whenever(fixture.currentDateProvider.currentTimeMillis).thenReturn(0) - val scopes: IScopes = mock() - whenever(scopes.options).thenReturn(SentryOptions()) + val hub: IHub = mock() + whenever(hub.options).thenReturn(SentryOptions()) val eventItem = SentryEnvelopeItem.fromEvent(fixture.serializer, SentryEvent()) - val transaction = SentryTransaction(SentryTracer(TransactionContext("name", "op"), scopes)) + val transaction = SentryTransaction(SentryTracer(TransactionContext("name", "op"), hub)) val transactionItem = SentryEnvelopeItem.fromEvent(fixture.serializer, transaction) val envelope = SentryEnvelope(SentryEnvelopeHeader(), arrayListOf(eventItem, transactionItem)) @@ -98,10 +98,10 @@ class RateLimiterTest { fun `parse X-Sentry-Rate-Limit and set its values and retry after should be false`() { val rateLimiter = fixture.getSUT() whenever(fixture.currentDateProvider.currentTimeMillis).thenReturn(0, 0, 1001) - val scopes: IScopes = mock() - whenever(scopes.options).thenReturn(SentryOptions()) + val hub: IHub = mock() + whenever(hub.options).thenReturn(SentryOptions()) val eventItem = SentryEnvelopeItem.fromEvent(fixture.serializer, SentryEvent()) - val transaction = SentryTransaction(SentryTracer(TransactionContext("name", "op"), scopes)) + val transaction = SentryTransaction(SentryTracer(TransactionContext("name", "op"), hub)) val transactionItem = SentryEnvelopeItem.fromEvent(fixture.serializer, transaction) val statsdItem = SentryEnvelopeItem.fromMetrics(EncodedMetrics(emptyMap())) val envelope = SentryEnvelope(SentryEnvelopeHeader(), arrayListOf(eventItem, transactionItem, statsdItem)) @@ -193,9 +193,9 @@ class RateLimiterTest { it.setName("John Me") } ) - val scopes = mock() - whenever(scopes.options).thenReturn(SentryOptions()) - val transaction = SentryTracer(TransactionContext("name", "op"), scopes) + val hub = mock() + whenever(hub.options).thenReturn(SentryOptions()) + val transaction = SentryTracer(TransactionContext("name", "op"), hub) val sessionItem = SentryEnvelopeItem.fromSession(fixture.serializer, Session("123", User(), "env", "release")) val attachmentItem = SentryEnvelopeItem.fromAttachment(fixture.serializer, NoOpLogger.getInstance(), Attachment("{ \"number\": 10 }".toByteArray(), "log.json"), 1000) @@ -223,8 +223,8 @@ class RateLimiterTest { @Test fun `records only dropped items as lost`() { val rateLimiter = fixture.getSUT() - val scopes = mock() - whenever(scopes.options).thenReturn(SentryOptions()) + val hub = mock() + whenever(hub.options).thenReturn(SentryOptions()) val eventItem = SentryEnvelopeItem.fromEvent(fixture.serializer, SentryEvent()) val userFeedbackItem = SentryEnvelopeItem.fromUserFeedback( @@ -237,7 +237,7 @@ class RateLimiterTest { it.setName("John Me") } ) - val transaction = SentryTracer(TransactionContext("name", "op"), scopes) + val transaction = SentryTracer(TransactionContext("name", "op"), hub) val profileItem = SentryEnvelopeItem.fromProfilingTrace(ProfilingTraceData(File(""), transaction), 1000, fixture.serializer) val sessionItem = SentryEnvelopeItem.fromSession(fixture.serializer, Session("123", User(), "env", "release")) val attachmentItem = SentryEnvelopeItem.fromAttachment(fixture.serializer, NoOpLogger.getInstance(), Attachment("{ \"number\": 10 }".toByteArray(), "log.json"), 1000) @@ -257,12 +257,12 @@ class RateLimiterTest { @Test fun `drop profile items as lost`() { val rateLimiter = fixture.getSUT() - val scopes = mock() - whenever(scopes.options).thenReturn(SentryOptions()) + val hub = mock() + whenever(hub.options).thenReturn(SentryOptions()) val eventItem = SentryEnvelopeItem.fromEvent(fixture.serializer, SentryEvent()) val f = File.createTempFile("test", "trace") - val transaction = SentryTracer(TransactionContext("name", "op"), scopes) + val transaction = SentryTracer(TransactionContext("name", "op"), hub) val profileItem = SentryEnvelopeItem.fromProfilingTrace(ProfilingTraceData(f, transaction), 1000, fixture.serializer) val envelope = SentryEnvelope(SentryEnvelopeHeader(), arrayListOf(eventItem, profileItem)) @@ -279,12 +279,12 @@ class RateLimiterTest { @Test fun `drop metrics items as lost`() { val rateLimiter = fixture.getSUT() - val scopes = mock() - whenever(scopes.options).thenReturn(SentryOptions()) + val hub = mock() + whenever(hub.options).thenReturn(SentryOptions()) val eventItem = SentryEnvelopeItem.fromEvent(fixture.serializer, SentryEvent()) val f = File.createTempFile("test", "trace") - val transaction = SentryTracer(TransactionContext("name", "op"), scopes) + val transaction = SentryTracer(TransactionContext("name", "op"), hub) val profileItem = SentryEnvelopeItem.fromProfilingTrace(ProfilingTraceData(f, transaction), 1000, fixture.serializer) val statsdItem = SentryEnvelopeItem.fromMetrics(EncodedMetrics(emptyMap())) val envelope = SentryEnvelope(SentryEnvelopeHeader(), arrayListOf(eventItem, profileItem, statsdItem)) diff --git a/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt b/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt index 943837d697..a285cd5832 100644 --- a/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt @@ -1,8 +1,7 @@ package io.sentry.util import io.sentry.CheckInStatus -import io.sentry.IScopes -import io.sentry.ISentryLifecycleToken +import io.sentry.IHub import io.sentry.MonitorConfig import io.sentry.MonitorSchedule import io.sentry.MonitorScheduleUnit @@ -57,37 +56,30 @@ class CheckInUtilsTest { @Test fun `sends check-in for wrapped supplier`() { Mockito.mockStatic(Sentry::class.java).use { sentry -> - val scopes = mock() - val lifecycleToken = mock() - sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) - sentry.`when` { Sentry.forkedScopes(any()) }.then { - scopes.forkedScopes("test") - } - whenever(scopes.forkedScopes(any())).thenReturn(scopes) - whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) - whenever(scopes.options).thenReturn(SentryOptions()) + val hub = mock() + sentry.`when` { Sentry.getCurrentHub() }.thenReturn(hub) + whenever(hub.options).thenReturn(SentryOptions()) val returnValue = CheckInUtils.withCheckIn("monitor-1") { return@withCheckIn "test1" } assertEquals("test1", returnValue) - inOrder(scopes, lifecycleToken) { - verify(scopes).forkedScopes(any()) - verify(scopes).makeCurrent() - verify(scopes).configureScope(any()) - verify(scopes).captureCheckIn( + inOrder(hub) { + verify(hub).pushScope() + verify(hub).configureScope(any()) + verify(hub).captureCheckIn( check { assertEquals("monitor-1", it.monitorSlug) assertEquals(CheckInStatus.IN_PROGRESS.apiName(), it.status) } ) - verify(scopes).captureCheckIn( + verify(hub).captureCheckIn( check { assertEquals("monitor-1", it.monitorSlug) assertEquals(CheckInStatus.OK.apiName(), it.status) } ) - verify(lifecycleToken).close() + verify(hub).popScope() } } } @@ -95,14 +87,8 @@ class CheckInUtilsTest { @Test fun `sends check-in for wrapped supplier with exception`() { Mockito.mockStatic(Sentry::class.java).use { sentry -> - val scopes = mock() - val lifecycleToken = mock() - sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) - sentry.`when` { Sentry.forkedScopes(any()) }.then { - scopes.forkedScopes("test") - } - whenever(scopes.forkedScopes(any())).thenReturn(scopes) - whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) + val hub = mock() + sentry.`when` { Sentry.getCurrentHub() }.thenReturn(hub) try { CheckInUtils.withCheckIn("monitor-1") { @@ -113,23 +99,22 @@ class CheckInUtilsTest { assertEquals("thrown on purpose", e.message) } - inOrder(scopes, lifecycleToken) { - verify(scopes).forkedScopes(any()) - verify(scopes).makeCurrent() - verify(scopes).configureScope(any()) - verify(scopes).captureCheckIn( + inOrder(hub) { + verify(hub).pushScope() + verify(hub).configureScope(any()) + verify(hub).captureCheckIn( check { assertEquals("monitor-1", it.monitorSlug) assertEquals(CheckInStatus.IN_PROGRESS.apiName(), it.status) } ) - verify(scopes).captureCheckIn( + verify(hub).captureCheckIn( check { assertEquals("monitor-1", it.monitorSlug) assertEquals(CheckInStatus.ERROR.apiName(), it.status) } ) - verify(lifecycleToken).close() + verify(hub).popScope() } } } @@ -137,39 +122,32 @@ class CheckInUtilsTest { @Test fun `sends check-in for wrapped supplier with upsert`() { Mockito.mockStatic(Sentry::class.java).use { sentry -> - val scopes = mock() - val lifecycleToken = mock() - sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) - sentry.`when` { Sentry.forkedScopes(any()) }.then { - scopes.forkedScopes("test") - } - whenever(scopes.forkedScopes(any())).thenReturn(scopes) - whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) - whenever(scopes.options).thenReturn(SentryOptions()) + val hub = mock() + sentry.`when` { Sentry.getCurrentHub() }.thenReturn(hub) + whenever(hub.options).thenReturn(SentryOptions()) val monitorConfig = MonitorConfig(MonitorSchedule.interval(7, MonitorScheduleUnit.DAY)) val returnValue = CheckInUtils.withCheckIn("monitor-1", monitorConfig) { "test1" } assertEquals("test1", returnValue) - inOrder(scopes, lifecycleToken) { - verify(scopes).forkedScopes(any()) - verify(scopes).makeCurrent() - verify(scopes).configureScope(any()) - verify(scopes).captureCheckIn( + inOrder(hub) { + verify(hub).pushScope() + verify(hub).configureScope(any()) + verify(hub).captureCheckIn( check { assertEquals("monitor-1", it.monitorSlug) assertSame(monitorConfig, it.monitorConfig) assertEquals(CheckInStatus.IN_PROGRESS.apiName(), it.status) } ) - verify(scopes).captureCheckIn( + verify(hub).captureCheckIn( check { assertEquals("monitor-1", it.monitorSlug) assertEquals(CheckInStatus.OK.apiName(), it.status) } ) - verify(lifecycleToken).close() + verify(hub).popScope() } } } @@ -177,15 +155,9 @@ class CheckInUtilsTest { @Test fun `sends check-in for wrapped supplier with upsert and thresholds`() { Mockito.mockStatic(Sentry::class.java).use { sentry -> - val scopes = mock() - val lifecycleToken = mock() - sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) - sentry.`when` { Sentry.forkedScopes(any()) }.then { - scopes.forkedScopes("test") - } - whenever(scopes.forkedScopes(any())).thenReturn(scopes) - whenever(scopes.makeCurrent()).thenReturn(lifecycleToken) - whenever(scopes.options).thenReturn(SentryOptions()) + val hub = mock() + sentry.`when` { Sentry.getCurrentHub() }.thenReturn(hub) + whenever(hub.options).thenReturn(SentryOptions()) val monitorConfig = MonitorConfig(MonitorSchedule.interval(7, MonitorScheduleUnit.DAY)).apply { failureIssueThreshold = 10 recoveryThreshold = 20 @@ -195,24 +167,23 @@ class CheckInUtilsTest { } assertEquals("test1", returnValue) - inOrder(scopes, lifecycleToken) { - verify(scopes).forkedScopes(any()) - verify(scopes).makeCurrent() - verify(scopes).configureScope(any()) - verify(scopes).captureCheckIn( + inOrder(hub) { + verify(hub).pushScope() + verify(hub).configureScope(any()) + verify(hub).captureCheckIn( check { assertEquals("monitor-1", it.monitorSlug) assertSame(monitorConfig, it.monitorConfig) assertEquals(CheckInStatus.IN_PROGRESS.apiName(), it.status) } ) - verify(scopes).captureCheckIn( + verify(hub).captureCheckIn( check { assertEquals("monitor-1", it.monitorSlug) assertEquals(CheckInStatus.OK.apiName(), it.status) } ) - verify(lifecycleToken).close() + verify(hub).popScope() } } } @@ -220,9 +191,9 @@ class CheckInUtilsTest { @Test fun `sets defaults for MonitorConfig from SentryOptions`() { Mockito.mockStatic(Sentry::class.java).use { sentry -> - val scopes = mock() - sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) - whenever(scopes.options).thenReturn( + val hub = mock() + sentry.`when` { Sentry.getCurrentHub() }.thenReturn(hub) + whenever(hub.options).thenReturn( SentryOptions().apply { cron = SentryOptions.Cron().apply { defaultCheckinMargin = 20 @@ -247,9 +218,9 @@ class CheckInUtilsTest { @Test fun `defaults for MonitorConfig from SentryOptions can be overridden`() { Mockito.mockStatic(Sentry::class.java).use { sentry -> - val scopes = mock() - sentry.`when` { Sentry.getCurrentScopes() }.thenReturn(scopes) - whenever(scopes.options).thenReturn( + val hub = mock() + sentry.`when` { Sentry.getCurrentHub() }.thenReturn(hub) + whenever(hub.options).thenReturn( SentryOptions().apply { cron = SentryOptions.Cron().apply { defaultCheckinMargin = 20 diff --git a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt index 62e2ba0055..e38410641e 100644 --- a/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt +++ b/sentry/src/test/java/io/sentry/util/TracingUtilsTest.kt @@ -1,7 +1,7 @@ package io.sentry.util import io.sentry.Baggage -import io.sentry.IScopes +import io.sentry.IHub import io.sentry.NoOpSpan import io.sentry.Scope import io.sentry.ScopeCallback @@ -29,18 +29,19 @@ class TracingUtilsTest { val options = SentryOptions().apply { dsn = "https://key@sentry.io/proj" } - val scopes = mock() + val hub = mock() val scope = Scope(options) lateinit var span: Span val preExistingBaggage = listOf("some-baggage-key=some-baggage-value") fun setup() { - whenever(scopes.options).thenReturn(options) - doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(scopes).configureScope(any()) + whenever(hub.options).thenReturn(options) + doAnswer { (it.arguments[0] as ScopeCallback).run(scope) }.whenever(hub).configureScope(any()) span = Span( TransactionContext("name", "op", TracesSamplingDecision(true)), - SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), scopes), - scopes, + SentryTracer(TransactionContext("name", "op", TracesSamplingDecision(true)), hub), + hub, + null, SpanOptions() ) } @@ -52,7 +53,7 @@ class TracingUtilsTest { fun `returns headers if allowed from scope without span`() { fixture.setup() - val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, null) + val headers = TracingUtils.traceIfAllowed(fixture.hub, "https://sentry.io/hello", fixture.preExistingBaggage, null) assertNotNull(headers) assertNotNull(headers.baggageHeader) @@ -67,7 +68,7 @@ class TracingUtilsTest { fun `returns headers if allowed from scope if span is noop`() { fixture.setup() - val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, NoOpSpan.getInstance()) + val headers = TracingUtils.traceIfAllowed(fixture.hub, "https://sentry.io/hello", fixture.preExistingBaggage, NoOpSpan.getInstance()) assertNotNull(headers) assertNotNull(headers.baggageHeader) @@ -82,7 +83,7 @@ class TracingUtilsTest { fixture.scope.propagationContext.baggage = Baggage.fromHeader("sentry-public_key=502f25099c204a2fbf4cb16edc5975d1,sentry-sample_rate=1,sentry-trace_id=2722d9f6ec019ade60c776169d9a8904,sentry-transaction=HTTP%20GET").also { it.freeze() } fixture.setup() - val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, null) + val headers = TracingUtils.traceIfAllowed(fixture.hub, "https://sentry.io/hello", fixture.preExistingBaggage, null) assertNotNull(headers) assertNotNull(headers.baggageHeader) @@ -97,7 +98,7 @@ class TracingUtilsTest { fun `returns headers if allowed from span`() { fixture.setup() - val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, fixture.span) + val headers = TracingUtils.traceIfAllowed(fixture.hub, "https://sentry.io/hello", fixture.preExistingBaggage, fixture.span) assertNotNull(headers) assertNotNull(headers.baggageHeader) @@ -111,7 +112,7 @@ class TracingUtilsTest { fixture.options.isTraceSampling = false fixture.setup() - val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, null) + val headers = TracingUtils.traceIfAllowed(fixture.hub, "https://sentry.io/hello", fixture.preExistingBaggage, null) assertNull(headers) } @@ -121,7 +122,7 @@ class TracingUtilsTest { fixture.options.isTraceSampling = false fixture.setup() - val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, fixture.span) + val headers = TracingUtils.traceIfAllowed(fixture.hub, "https://sentry.io/hello", fixture.preExistingBaggage, fixture.span) assertNull(headers) } @@ -131,7 +132,7 @@ class TracingUtilsTest { fixture.options.setTracePropagationTargets(listOf("some-host-that-does-not-exist")) fixture.setup() - val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, null) + val headers = TracingUtils.traceIfAllowed(fixture.hub, "https://sentry.io/hello", fixture.preExistingBaggage, null) assertNull(headers) } @@ -141,7 +142,7 @@ class TracingUtilsTest { fixture.options.setTracePropagationTargets(listOf("some-host-that-does-not-exist")) fixture.setup() - val headers = TracingUtils.traceIfAllowed(fixture.scopes, "https://sentry.io/hello", fixture.preExistingBaggage, fixture.span) + val headers = TracingUtils.traceIfAllowed(fixture.hub, "https://sentry.io/hello", fixture.preExistingBaggage, fixture.span) assertNull(headers) } @@ -152,7 +153,7 @@ class TracingUtilsTest { val propagationContextBefore = fixture.scope.propagationContext - TracingUtils.startNewTrace(fixture.scopes) + TracingUtils.startNewTrace(fixture.hub) assertNotEquals(propagationContextBefore.traceId, fixture.scope.propagationContext.traceId) assertNotEquals(propagationContextBefore.spanId, fixture.scope.propagationContext.spanId) diff --git a/settings.gradle.kts b/settings.gradle.kts index 822c8e9db0..028037372d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,6 +17,7 @@ include( "sentry-android-ndk", "sentry-android", "sentry-android-timber", + "sentry-android-okhttp", "sentry-android-fragment", "sentry-android-navigation", "sentry-android-sqlite", @@ -41,7 +42,6 @@ include( "sentry-openfeign", "sentry-graphql", "sentry-jdbc", - "sentry-opentelemetry:sentry-opentelemetry-bootstrap", "sentry-opentelemetry:sentry-opentelemetry-core", "sentry-opentelemetry:sentry-opentelemetry-agentcustomization", "sentry-opentelemetry:sentry-opentelemetry-agent", @@ -66,3 +66,12 @@ include( "sentry-android-integration-tests:test-app-sentry", "sentry-samples:sentry-samples-openfeign" ) + +gradle.beforeProject { + if (project.name == "sentry-android-ndk" || project.name == "sentry-samples-android") { + exec { + logger.log(LogLevel.LIFECYCLE, "Initializing git submodules") + commandLine("git", "submodule", "update", "--init", "--recursive") + } + } +}