Skip to content

Commit 13edfa9

Browse files
authored
Merge fd7bcac into 7ab32b6
2 parents 7ab32b6 + fd7bcac commit 13edfa9

File tree

8 files changed

+225
-20
lines changed

8 files changed

+225
-20
lines changed

sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import io.sentry.cache.PersistingScopeObserver;
2828
import io.sentry.compose.gestures.ComposeGestureTargetLocator;
2929
import io.sentry.compose.viewhierarchy.ComposeViewHierarchyExporter;
30+
import io.sentry.internal.SpotlightIntegration;
3031
import io.sentry.internal.gestures.GestureTargetLocator;
3132
import io.sentry.internal.viewhierarchy.ViewHierarchyExporter;
3233
import io.sentry.transport.NoOpEnvelopeCache;
@@ -295,6 +296,7 @@ static void installDefaultIntegrations(
295296
new NetworkBreadcrumbsIntegration(context, buildInfoProvider, options.getLogger()));
296297
options.addIntegration(new TempSensorBreadcrumbsIntegration(context));
297298
options.addIntegration(new PhoneStateBreadcrumbsIntegration(context));
299+
options.addIntegration(new SpotlightIntegration());
298300
}
299301

300302
/**
@@ -331,6 +333,12 @@ private static void readDefaultOptionValues(
331333
options.getLogger().log(SentryLevel.ERROR, "Could not generate distinct Id.", e);
332334
}
333335
}
336+
337+
if (options.getSpotlightConnectionUrl() == null) {
338+
// developer machine should be the same across emulators
339+
// see https://developer.android.com/studio/run/emulator-networking.html
340+
options.setSpotlightConnectionUrl("http://10.0.2.2:8969/stream");
341+
}
334342
}
335343

336344
/**

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
android:label="@string/app_name"
2727
android:roundIcon="@mipmap/ic_launcher_round"
2828
android:theme="@style/AppTheme"
29+
android:networkSecurityConfig="@xml/network"
2930
tools:ignore="GoogleAppIndexingWarning, UnusedAttribute">
3031

3132
<activity
@@ -143,7 +144,7 @@
143144
<!-- <meta-data android:name="io.sentry.breadcrumbs.app-components" android:value="false" />-->
144145

145146
<!-- how to enable the attach screenshot feature-->
146-
<meta-data android:name="io.sentry.attach-screenshot" android:value="true" />
147+
<meta-data android:name="io.sentry.attach-screenshot" android:value="false" />
147148

148149
<!-- how to enable the attach view hierarchy feature-->
149150
<meta-data android:name="io.sentry.attach-view-hierarchy" android:value="true" />

sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MainActivity.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,12 @@ protected void onCreate(Bundle savedInstanceState) {
5858
Sentry.captureException(e);
5959
}
6060

61-
final Attachment image = new Attachment(imageFile.getAbsolutePath(), "sentry.png", "image/png");
62-
Sentry.configureScope(
63-
scope -> {
64-
scope.addAttachment(image);
65-
});
61+
// final Attachment image = new Attachment(imageFile.getAbsolutePath(), "sentry.png",
62+
// "image/png");
63+
// Sentry.configureScope(
64+
// scope -> {
65+
// scope.addAttachment(image);
66+
// });
6667

6768
binding.crashFromJava.setOnClickListener(
6869
view -> {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<network-security-config>
3+
<domain-config cleartextTrafficPermitted="true">
4+
<domain includeSubdomains="true">10.0.2.2</domain>
5+
</domain-config>
6+
</network-security-config>

sentry/api/sentry.api

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2147,6 +2147,7 @@ public class io/sentry/SentryOptions {
21472147
public static fun empty ()Lio/sentry/SentryOptions;
21482148
public fun getBackpressureMonitor ()Lio/sentry/backpressure/IBackpressureMonitor;
21492149
public fun getBeforeBreadcrumb ()Lio/sentry/SentryOptions$BeforeBreadcrumbCallback;
2150+
public fun getBeforeEnvelopeCallback ()Lio/sentry/SentryOptions$BeforeEnvelopeCallback;
21502151
public fun getBeforeSend ()Lio/sentry/SentryOptions$BeforeSendCallback;
21512152
public fun getBeforeSendTransaction ()Lio/sentry/SentryOptions$BeforeSendTransactionCallback;
21522153
public fun getBundleIds ()Ljava/util/Set;
@@ -2209,6 +2210,7 @@ public class io/sentry/SentryOptions {
22092210
public fun getSessionTrackingIntervalMillis ()J
22102211
public fun getShutdownTimeout ()J
22112212
public fun getShutdownTimeoutMillis ()J
2213+
public fun getSpotlightConnectionUrl ()Ljava/lang/String;
22122214
public fun getSslSocketFactory ()Ljavax/net/ssl/SSLSocketFactory;
22132215
public fun getTags ()Ljava/util/Map;
22142216
public fun getTracePropagationTargets ()Ljava/util/List;
@@ -2250,6 +2252,7 @@ public class io/sentry/SentryOptions {
22502252
public fun setAttachThreads (Z)V
22512253
public fun setBackpressureMonitor (Lio/sentry/backpressure/IBackpressureMonitor;)V
22522254
public fun setBeforeBreadcrumb (Lio/sentry/SentryOptions$BeforeBreadcrumbCallback;)V
2255+
public fun setBeforeEnvelopeCallback (Lio/sentry/SentryOptions$BeforeEnvelopeCallback;)V
22532256
public fun setBeforeSend (Lio/sentry/SentryOptions$BeforeSendCallback;)V
22542257
public fun setBeforeSendTransaction (Lio/sentry/SentryOptions$BeforeSendTransactionCallback;)V
22552258
public fun setCacheDirPath (Ljava/lang/String;)V
@@ -2316,6 +2319,7 @@ public class io/sentry/SentryOptions {
23162319
public fun setSessionTrackingIntervalMillis (J)V
23172320
public fun setShutdownTimeout (J)V
23182321
public fun setShutdownTimeoutMillis (J)V
2322+
public fun setSpotlightConnectionUrl (Ljava/lang/String;)V
23192323
public fun setSslSocketFactory (Ljavax/net/ssl/SSLSocketFactory;)V
23202324
public fun setTag (Ljava/lang/String;Ljava/lang/String;)V
23212325
public fun setTraceOptionsRequests (Z)V
@@ -2335,6 +2339,10 @@ public abstract interface class io/sentry/SentryOptions$BeforeBreadcrumbCallback
23352339
public abstract fun execute (Lio/sentry/Breadcrumb;Lio/sentry/Hint;)Lio/sentry/Breadcrumb;
23362340
}
23372341

2342+
public abstract interface class io/sentry/SentryOptions$BeforeEnvelopeCallback {
2343+
public abstract fun execute (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/SentryEnvelope;
2344+
}
2345+
23382346
public abstract interface class io/sentry/SentryOptions$BeforeSendCallback {
23392347
public abstract fun execute (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent;
23402348
}
@@ -3245,6 +3253,13 @@ public final class io/sentry/instrumentation/file/SentryFileWriter : java/io/Out
32453253
public fun <init> (Ljava/lang/String;Z)V
32463254
}
32473255

3256+
public final class io/sentry/internal/SpotlightIntegration : io/sentry/Integration, io/sentry/SentryOptions$BeforeEnvelopeCallback, java/io/Closeable {
3257+
public fun <init> ()V
3258+
public fun close ()V
3259+
public fun execute (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/SentryEnvelope;
3260+
public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V
3261+
}
3262+
32483263
public abstract interface class io/sentry/internal/debugmeta/IDebugMetaLoader {
32493264
public abstract fun loadDebugMeta ()Ljava/util/List;
32503265
}

sentry/src/main/java/io/sentry/SentryClient.java

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -212,12 +212,12 @@ private boolean shouldApplyScopeData(final @NotNull CheckIn event, final @NotNul
212212

213213
final boolean shouldSendAttachments = event != null;
214214
List<Attachment> attachments = shouldSendAttachments ? getAttachments(hint) : null;
215-
final SentryEnvelope envelope =
216-
buildEnvelope(event, attachments, session, traceContext, null);
215+
@Nullable
216+
SentryEnvelope envelope = buildEnvelope(event, attachments, session, traceContext, null);
217217

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

447447
try {
448-
final SentryEnvelope envelope = buildEnvelope(userFeedback);
449-
transport.send(envelope);
448+
@Nullable SentryEnvelope envelope = buildEnvelope(userFeedback);
449+
sendEnvelope(envelope, null);
450450
} catch (IOException e) {
451451
options
452452
.getLogger()
@@ -582,14 +582,29 @@ public void captureSession(final @NotNull Session session, final @Nullable Hint
582582

583583
try {
584584
hint.clear();
585-
transport.send(envelope, hint);
585+
return sendEnvelope(envelope, hint);
586586
} catch (IOException e) {
587587
options.getLogger().log(SentryLevel.ERROR, "Failed to capture envelope.", e);
588-
return SentryId.EMPTY_ID;
589588
}
590-
final SentryId eventId = envelope.getHeader().getEventId();
591-
if (eventId != null) {
592-
return eventId;
589+
return SentryId.EMPTY_ID;
590+
}
591+
592+
private @NotNull SentryId sendEnvelope(
593+
@NotNull final SentryEnvelope envelope, @Nullable final Hint hint) throws IOException {
594+
final @Nullable SentryOptions.BeforeEnvelopeCallback beforeEnvelopeCallback =
595+
options.getBeforeEnvelopeCallback();
596+
@Nullable SentryEnvelope envelopeToSend = envelope;
597+
if (beforeEnvelopeCallback != null) {
598+
envelopeToSend = beforeEnvelopeCallback.execute(envelope, hint);
599+
}
600+
if (envelopeToSend != null) {
601+
if (hint == null) {
602+
transport.send(envelope);
603+
} else {
604+
transport.send(envelope, hint);
605+
}
606+
final @Nullable SentryId id = envelope.getHeader().getEventId();
607+
return id != null ? id : SentryId.EMPTY_ID;
593608
} else {
594609
return SentryId.EMPTY_ID;
595610
}
@@ -665,9 +680,7 @@ public void captureSession(final @NotNull Session session, final @Nullable Hint
665680

666681
hint.clear();
667682
if (envelope != null) {
668-
transport.send(envelope, hint);
669-
} else {
670-
sentryId = SentryId.EMPTY_ID;
683+
sentryId = sendEnvelope(envelope, hint);
671684
}
672685
} catch (IOException | SentryEnvelopeException e) {
673686
options.getLogger().log(SentryLevel.WARNING, e, "Capturing transaction %s failed.", sentryId);
@@ -732,7 +745,7 @@ public void captureSession(final @NotNull Session session, final @Nullable Hint
732745
final SentryEnvelope envelope = buildEnvelope(checkIn, traceContext);
733746

734747
hint.clear();
735-
transport.send(envelope, hint);
748+
sentryId = sendEnvelope(envelope, hint);
736749
} catch (IOException e) {
737750
options.getLogger().log(SentryLevel.WARNING, e, "Capturing check-in %s failed.", sentryId);
738751
// if there was an error capturing the event, we return an emptyId

sentry/src/main/java/io/sentry/SentryOptions.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,10 @@ public class SentryOptions {
442442
/** Whether to send modules containing information about versions. */
443443
private boolean sendModules = true;
444444

445+
private @Nullable BeforeEnvelopeCallback beforeEnvelopeCallback;
446+
447+
private @Nullable String spotlightConnectionUrl;
448+
445449
/** Contains a list of monitor slugs for which check-ins should not be sent. */
446450
@ApiStatus.Experimental private @Nullable List<String> ignoredCheckIns = null;
447451

@@ -2274,6 +2278,29 @@ public void setSessionFlushTimeoutMillis(final long sessionFlushTimeoutMillis) {
22742278
this.sessionFlushTimeoutMillis = sessionFlushTimeoutMillis;
22752279
}
22762280

2281+
@ApiStatus.Internal
2282+
@Nullable
2283+
public BeforeEnvelopeCallback getBeforeEnvelopeCallback() {
2284+
return beforeEnvelopeCallback;
2285+
}
2286+
2287+
@ApiStatus.Internal
2288+
public void setBeforeEnvelopeCallback(
2289+
@Nullable final BeforeEnvelopeCallback beforeEnvelopeCallback) {
2290+
this.beforeEnvelopeCallback = beforeEnvelopeCallback;
2291+
}
2292+
2293+
@ApiStatus.Experimental
2294+
@Nullable
2295+
public String getSpotlightConnectionUrl() {
2296+
return spotlightConnectionUrl;
2297+
}
2298+
2299+
@ApiStatus.Experimental
2300+
public void setSpotlightConnectionUrl(final @Nullable String spotlightConnectionUrl) {
2301+
this.spotlightConnectionUrl = spotlightConnectionUrl;
2302+
}
2303+
22772304
/** The BeforeSend callback */
22782305
public interface BeforeSendCallback {
22792306

@@ -2345,6 +2372,21 @@ public interface ProfilesSamplerCallback {
23452372
Double sample(@NotNull SamplingContext samplingContext);
23462373
}
23472374

2375+
/** The BeforeSend callback */
2376+
public interface BeforeEnvelopeCallback {
2377+
2378+
/**
2379+
* Mutates or drop an event before being sent
2380+
*
2381+
* @param envelope the envelope
2382+
* @param hint the hints
2383+
* @return the original envelope or the mutated envelope or null if the envelope should be
2384+
* dropped
2385+
*/
2386+
@Nullable
2387+
SentryEnvelope execute(@NotNull SentryEnvelope envelope, @Nullable Hint hint);
2388+
}
2389+
23482390
/**
23492391
* Creates SentryOptions instance without initializing any of the internal parts.
23502392
*
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package io.sentry.internal;
2+
3+
import static io.sentry.SentryLevel.DEBUG;
4+
import static io.sentry.SentryLevel.ERROR;
5+
6+
import io.sentry.Hint;
7+
import io.sentry.IHub;
8+
import io.sentry.Integration;
9+
import io.sentry.SentryEnvelope;
10+
import io.sentry.SentryOptions;
11+
import io.sentry.util.Objects;
12+
import java.io.Closeable;
13+
import java.io.IOException;
14+
import java.io.OutputStream;
15+
import java.net.HttpURLConnection;
16+
import java.net.URI;
17+
import java.util.concurrent.ExecutorService;
18+
import java.util.concurrent.Executors;
19+
import java.util.concurrent.RejectedExecutionException;
20+
import java.util.zip.GZIPOutputStream;
21+
import org.jetbrains.annotations.ApiStatus;
22+
import org.jetbrains.annotations.NotNull;
23+
import org.jetbrains.annotations.Nullable;
24+
25+
@ApiStatus.Internal
26+
public final class SpotlightIntegration
27+
implements Integration, SentryOptions.BeforeEnvelopeCallback, Closeable {
28+
29+
private @NotNull SentryOptions options = SentryOptions.empty();
30+
31+
private final @NotNull ExecutorService executorService = Executors.newSingleThreadExecutor();
32+
33+
@Override
34+
public void register(@NotNull IHub hub, @NotNull SentryOptions options) {
35+
this.options = options;
36+
37+
if (options.getBeforeEnvelopeCallback() == null
38+
&& options.getSpotlightConnectionUrl() != null) {
39+
options.setBeforeEnvelopeCallback(this);
40+
}
41+
}
42+
43+
@Override
44+
public @NotNull SentryEnvelope execute(@NotNull SentryEnvelope envelope, @Nullable Hint hint) {
45+
try {
46+
executorService.execute(() -> sendEnvelope(envelope));
47+
} catch (RejectedExecutionException ex) {
48+
// ignored
49+
}
50+
return envelope;
51+
}
52+
53+
private void sendEnvelope(final @NotNull SentryEnvelope envelope) {
54+
try {
55+
final @NotNull String spotlightConnectionUrl =
56+
Objects.requireNonNull(
57+
options.getSpotlightConnectionUrl(), "Spotlight URL can't be null");
58+
59+
final HttpURLConnection connection = createConnection(spotlightConnectionUrl);
60+
try (final OutputStream outputStream = connection.getOutputStream();
61+
final GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
62+
options.getSerializer().serialize(envelope, gzip);
63+
} catch (Throwable e) {
64+
options
65+
.getLogger()
66+
.log(
67+
ERROR,
68+
e,
69+
"An exception occurred while submitting the envelope to the Sentry server.");
70+
} finally {
71+
final int responseCode = connection.getResponseCode();
72+
options.getLogger().log(DEBUG, "Envelope sent to spotlight: %d", responseCode);
73+
closeAndDisconnect(connection);
74+
}
75+
} catch (final Exception e) {
76+
options
77+
.getLogger()
78+
.log(ERROR, e, "An exception occurred while creating the connection to spotlight.");
79+
}
80+
}
81+
82+
private @NotNull HttpURLConnection createConnection(final @NotNull String url) throws Exception {
83+
84+
final @NotNull HttpURLConnection connection =
85+
(HttpURLConnection) URI.create(url).toURL().openConnection();
86+
connection.setRequestMethod("POST");
87+
connection.setDoOutput(true);
88+
89+
connection.setRequestProperty("Content-Encoding", "gzip");
90+
connection.setRequestProperty("Content-Type", "application/x-sentry-envelope");
91+
connection.setRequestProperty("Accept", "application/json");
92+
93+
// https://stackoverflow.com/questions/52726909/java-io-ioexception-unexpected-end-of-stream-on-connection/53089882
94+
connection.setRequestProperty("Connection", "close");
95+
96+
connection.connect();
97+
return connection;
98+
}
99+
100+
/**
101+
* Closes the Response stream and disconnect the connection
102+
*
103+
* @param connection the HttpURLConnection
104+
*/
105+
private void closeAndDisconnect(final @NotNull HttpURLConnection connection) {
106+
try {
107+
connection.getInputStream().close();
108+
} catch (IOException ignored) {
109+
// connection is already closed
110+
} finally {
111+
connection.disconnect();
112+
}
113+
}
114+
115+
@Override
116+
public void close() throws IOException {
117+
executorService.shutdown();
118+
}
119+
}

0 commit comments

Comments
 (0)