Skip to content

Commit

Permalink
Integrate Spotlight (#3166)
Browse files Browse the repository at this point in the history
* initial spotlight integration

* Format code

* Add options.getSpotlightConnectionUrl, move Spotlight integration to sentry core

* Fix formatting, api spec

* Spotlight fixes for Backend (#3176)

* Address PR feedback, add tests

* Format code

* Cleanup PR

* Update changelog

* Update sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml

Co-authored-by: Roman Zavarnitsyn <rom4ek93@gmail.com>

* Fix typos

* Address PR feedback

* Simplify beforeEnvelopeCallback

* Fix test

* Fix log argument order

---------

Co-authored-by: Sentry Github Bot <bot+github-bot@sentry.io>
Co-authored-by: Alexander Dinauer <adinauer@users.noreply.github.com>
Co-authored-by: Roman Zavarnitsyn <rom4ek93@gmail.com>
  • Loading branch information
4 people committed Feb 20, 2024
1 parent 154440a commit 536c1b1
Show file tree
Hide file tree
Showing 12 changed files with 375 additions and 17 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
- Report process init time as a span for app start performance ([#3159](https://github.com/getsentry/sentry-java/pull/3159))
- (perf-v2): Calculate frame delay on a span level ([#3197](https://github.com/getsentry/sentry-java/pull/3197))
- Resolve spring properties in @SentryCheckIn annotation ([#3194](https://github.com/getsentry/sentry-java/pull/3194))
- Experimental: Add Spotlight integration ([#3166](https://github.com/getsentry/sentry-java/pull/3166))
- For more details about Spotlight head over to https://spotlightjs.com/
- Set `options.isEnableSpotlight = true` to enable Spotlight

### Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import io.sentry.SentryOptions
import io.sentry.SentryOptions.BeforeSendCallback
import io.sentry.Session
import io.sentry.ShutdownHookIntegration
import io.sentry.SpotlightIntegration
import io.sentry.SystemOutLogger
import io.sentry.UncaughtExceptionHandlerIntegration
import io.sentry.android.core.cache.AndroidEnvelopeCache
Expand Down Expand Up @@ -412,7 +413,7 @@ class SentryAndroidTest {
fixture.initSut(context = mock<Application>()) { options ->
optionsRef = options
options.dsn = "https://key@sentry.io/123"
assertEquals(19, options.integrations.size)
assertEquals(20, options.integrations.size)
options.integrations.removeAll {
it is UncaughtExceptionHandlerIntegration ||
it is ShutdownHookIntegration ||
Expand All @@ -431,7 +432,8 @@ class SentryAndroidTest {
it is SystemEventsBreadcrumbsIntegration ||
it is NetworkBreadcrumbsIntegration ||
it is TempSensorBreadcrumbsIntegration ||
it is PhoneStateBreadcrumbsIntegration
it is PhoneStateBreadcrumbsIntegration ||
it is SpotlightIntegration
}
}
assertEquals(0, optionsRef.integrations.size)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network"
tools:ignore="GoogleAppIndexingWarning, UnusedAttribute">

<activity
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<!-- To enable spotlight we need to allow cleartext traffic from the emulator to the host machine for now-->
<!-- See https://developer.android.com/studio/run/emulator-networking for more details -->
<domain includeSubdomains="true">10.0.2.2</domain>
</domain-config>
</network-security-config>
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ sentry.ignored-checkins=ignored_monitor_slug_1,ignored_monitor_slug_2
sentry.debug=true
sentry.graphql.ignored-error-types=SOME_ERROR,ANOTHER_ERROR
sentry.enable-backpressure-handling=true
sentry.enable-spotlight=true
sentry.enablePrettySerializationOutput=false
in-app-includes="io.sentry.samples"

# Uncomment and set to true to enable aot compatibility
Expand Down
18 changes: 18 additions & 0 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -2159,6 +2159,7 @@ public class io/sentry/SentryOptions {
public static fun empty ()Lio/sentry/SentryOptions;
public fun getBackpressureMonitor ()Lio/sentry/backpressure/IBackpressureMonitor;
public fun getBeforeBreadcrumb ()Lio/sentry/SentryOptions$BeforeBreadcrumbCallback;
public fun getBeforeEnvelopeCallback ()Lio/sentry/SentryOptions$BeforeEnvelopeCallback;
public fun getBeforeSend ()Lio/sentry/SentryOptions$BeforeSendCallback;
public fun getBeforeSendTransaction ()Lio/sentry/SentryOptions$BeforeSendTransactionCallback;
public fun getBundleIds ()Ljava/util/Set;
Expand Down Expand Up @@ -2221,6 +2222,7 @@ public class io/sentry/SentryOptions {
public fun getSessionTrackingIntervalMillis ()J
public fun getShutdownTimeout ()J
public fun getShutdownTimeoutMillis ()J
public fun getSpotlightConnectionUrl ()Ljava/lang/String;
public fun getSslSocketFactory ()Ljavax/net/ssl/SSLSocketFactory;
public fun getTags ()Ljava/util/Map;
public fun getTracePropagationTargets ()Ljava/util/List;
Expand All @@ -2243,6 +2245,7 @@ public class io/sentry/SentryOptions {
public fun isEnableExternalConfiguration ()Z
public fun isEnablePrettySerializationOutput ()Z
public fun isEnableShutdownHook ()Z
public fun isEnableSpotlight ()Z
public fun isEnableTimeToFullDisplayTracing ()Z
public fun isEnableUncaughtExceptionHandler ()Z
public fun isEnableUserInteractionBreadcrumbs ()Z
Expand All @@ -2262,6 +2265,7 @@ public class io/sentry/SentryOptions {
public fun setAttachThreads (Z)V
public fun setBackpressureMonitor (Lio/sentry/backpressure/IBackpressureMonitor;)V
public fun setBeforeBreadcrumb (Lio/sentry/SentryOptions$BeforeBreadcrumbCallback;)V
public fun setBeforeEnvelopeCallback (Lio/sentry/SentryOptions$BeforeEnvelopeCallback;)V
public fun setBeforeSend (Lio/sentry/SentryOptions$BeforeSendCallback;)V
public fun setBeforeSendTransaction (Lio/sentry/SentryOptions$BeforeSendTransactionCallback;)V
public fun setCacheDirPath (Ljava/lang/String;)V
Expand All @@ -2281,6 +2285,7 @@ public class io/sentry/SentryOptions {
public fun setEnableExternalConfiguration (Z)V
public fun setEnablePrettySerializationOutput (Z)V
public fun setEnableShutdownHook (Z)V
public fun setEnableSpotlight (Z)V
public fun setEnableTimeToFullDisplayTracing (Z)V
public fun setEnableTracing (Ljava/lang/Boolean;)V
public fun setEnableUncaughtExceptionHandler (Z)V
Expand Down Expand Up @@ -2328,6 +2333,7 @@ public class io/sentry/SentryOptions {
public fun setSessionTrackingIntervalMillis (J)V
public fun setShutdownTimeout (J)V
public fun setShutdownTimeoutMillis (J)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
public fun setTraceOptionsRequests (Z)V
Expand All @@ -2347,6 +2353,10 @@ public abstract interface class io/sentry/SentryOptions$BeforeBreadcrumbCallback
public abstract fun execute (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)Lio/sentry/Breadcrumb;
}

public abstract interface class io/sentry/SentryOptions$BeforeEnvelopeCallback {
public abstract fun execute (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)V
}

public abstract interface class io/sentry/SentryOptions$BeforeSendCallback {
public abstract fun execute (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent;
}
Expand Down Expand Up @@ -2721,6 +2731,14 @@ public final class io/sentry/SpanStatus$Deserializer : io/sentry/JsonDeserialize
public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object;
}

public final class io/sentry/SpotlightIntegration : io/sentry/Integration, io/sentry/SentryOptions$BeforeEnvelopeCallback, java/io/Closeable {
public fun <init> ()V
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 final class io/sentry/SystemOutLogger : io/sentry/ILogger {
public fun <init> ()V
public fun isEnabled (Lio/sentry/SentryLevel;)Z
Expand Down
44 changes: 29 additions & 15 deletions sentry/src/main/java/io/sentry/SentryClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -212,12 +212,12 @@ private boolean shouldApplyScopeData(final @NotNull CheckIn event, final @NotNul

final boolean shouldSendAttachments = event != null;
List<Attachment> attachments = shouldSendAttachments ? getAttachments(hint) : null;
final SentryEnvelope envelope =
final @Nullable SentryEnvelope envelope =
buildEnvelope(event, attachments, session, traceContext, null);

hint.clear();
if (envelope != null) {
transport.send(envelope, hint);
sentryId = sendEnvelope(envelope, hint);
}
} catch (IOException | SentryEnvelopeException e) {
options.getLogger().log(SentryLevel.WARNING, e, "Capturing event %s failed.", sentryId);
Expand Down Expand Up @@ -445,8 +445,8 @@ public void captureUserFeedback(final @NotNull UserFeedback userFeedback) {
.log(SentryLevel.DEBUG, "Capturing userFeedback: %s", userFeedback.getEventId());

try {
final SentryEnvelope envelope = buildEnvelope(userFeedback);
transport.send(envelope);
final @NotNull SentryEnvelope envelope = buildEnvelope(userFeedback);
sendEnvelope(envelope, null);
} catch (IOException e) {
options
.getLogger()
Expand Down Expand Up @@ -582,17 +582,33 @@ public void captureSession(final @NotNull Session session, final @Nullable Hint

try {
hint.clear();
transport.send(envelope, hint);
return sendEnvelope(envelope, hint);
} catch (IOException e) {
options.getLogger().log(SentryLevel.ERROR, "Failed to capture envelope.", e);
return SentryId.EMPTY_ID;
}
final SentryId eventId = envelope.getHeader().getEventId();
if (eventId != null) {
return eventId;
return SentryId.EMPTY_ID;
}

private @NotNull SentryId sendEnvelope(
@NotNull final SentryEnvelope envelope, @Nullable final Hint hint) throws IOException {
final @Nullable SentryOptions.BeforeEnvelopeCallback beforeEnvelopeCallback =
options.getBeforeEnvelopeCallback();
if (beforeEnvelopeCallback != null) {
try {
beforeEnvelopeCallback.execute(envelope, hint);
} catch (Throwable e) {
options
.getLogger()
.log(SentryLevel.ERROR, "The BeforeEnvelope callback threw an exception.", e);
}
}
if (hint == null) {
transport.send(envelope);
} else {
return SentryId.EMPTY_ID;
transport.send(envelope, hint);
}
final @Nullable SentryId id = envelope.getHeader().getEventId();
return id != null ? id : SentryId.EMPTY_ID;
}

@Override
Expand Down Expand Up @@ -665,9 +681,7 @@ public void captureSession(final @NotNull Session session, final @Nullable Hint

hint.clear();
if (envelope != null) {
transport.send(envelope, hint);
} else {
sentryId = SentryId.EMPTY_ID;
sentryId = sendEnvelope(envelope, hint);
}
} catch (IOException | SentryEnvelopeException e) {
options.getLogger().log(SentryLevel.WARNING, e, "Capturing transaction %s failed.", sentryId);
Expand Down Expand Up @@ -729,10 +743,10 @@ public void captureSession(final @NotNull Session session, final @Nullable Hint
}
}

final SentryEnvelope envelope = buildEnvelope(checkIn, traceContext);
final @NotNull SentryEnvelope envelope = buildEnvelope(checkIn, traceContext);

hint.clear();
transport.send(envelope, hint);
sentryId = sendEnvelope(envelope, hint);
} catch (IOException e) {
options.getLogger().log(SentryLevel.WARNING, e, "Capturing check-in %s failed.", sentryId);
// if there was an error capturing the event, we return an emptyId
Expand Down
53 changes: 53 additions & 0 deletions sentry/src/main/java/io/sentry/SentryOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,12 @@ public class SentryOptions {
/** Whether to send modules containing information about versions. */
private boolean sendModules = true;

private @Nullable BeforeEnvelopeCallback beforeEnvelopeCallback;

private boolean enableSpotlight = false;

private @Nullable String spotlightConnectionUrl;

/** Contains a list of monitor slugs for which check-ins should not be sent. */
@ApiStatus.Experimental private @Nullable List<String> ignoredCheckIns = null;

Expand Down Expand Up @@ -2274,6 +2280,39 @@ public void setSessionFlushTimeoutMillis(final long sessionFlushTimeoutMillis) {
this.sessionFlushTimeoutMillis = sessionFlushTimeoutMillis;
}

@ApiStatus.Internal
@Nullable
public BeforeEnvelopeCallback getBeforeEnvelopeCallback() {
return beforeEnvelopeCallback;
}

@ApiStatus.Internal
public void setBeforeEnvelopeCallback(
@Nullable final BeforeEnvelopeCallback beforeEnvelopeCallback) {
this.beforeEnvelopeCallback = beforeEnvelopeCallback;
}

@ApiStatus.Experimental
@Nullable
public String getSpotlightConnectionUrl() {
return spotlightConnectionUrl;
}

@ApiStatus.Experimental
public void setSpotlightConnectionUrl(final @Nullable String spotlightConnectionUrl) {
this.spotlightConnectionUrl = spotlightConnectionUrl;
}

@ApiStatus.Experimental
public boolean isEnableSpotlight() {
return enableSpotlight;
}

@ApiStatus.Experimental
public void setEnableSpotlight(final boolean enableSpotlight) {
this.enableSpotlight = enableSpotlight;
}

/** The BeforeSend callback */
public interface BeforeSendCallback {

Expand Down Expand Up @@ -2345,6 +2384,19 @@ public interface ProfilesSamplerCallback {
Double sample(@NotNull SamplingContext samplingContext);
}

/** The BeforeEnvelope callback */
@ApiStatus.Internal
public interface BeforeEnvelopeCallback {

/**
* A callback which gets called right before an envelope is about to be sent
*
* @param envelope the envelope
* @param hint the hints
*/
void execute(@NotNull SentryEnvelope envelope, @Nullable Hint hint);
}

/**
* Creates SentryOptions instance without initializing any of the internal parts.
*
Expand Down Expand Up @@ -2378,6 +2430,7 @@ private SentryOptions(final boolean empty) {
integrations.add(new UncaughtExceptionHandlerIntegration());

integrations.add(new ShutdownHookIntegration());
integrations.add(new SpotlightIntegration());

eventProcessors.add(new MainEventProcessor(this));
eventProcessors.add(new DuplicateEventDetectionEventProcessor(this));
Expand Down

0 comments on commit 536c1b1

Please sign in to comment.