Skip to content

Commit

Permalink
Merge fd7bcac into 7ab32b6
Browse files Browse the repository at this point in the history
  • Loading branch information
markushi committed Jan 29, 2024
2 parents 7ab32b6 + fd7bcac commit 13edfa9
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import io.sentry.cache.PersistingScopeObserver;
import io.sentry.compose.gestures.ComposeGestureTargetLocator;
import io.sentry.compose.viewhierarchy.ComposeViewHierarchyExporter;
import io.sentry.internal.SpotlightIntegration;
import io.sentry.internal.gestures.GestureTargetLocator;
import io.sentry.internal.viewhierarchy.ViewHierarchyExporter;
import io.sentry.transport.NoOpEnvelopeCache;
Expand Down Expand Up @@ -295,6 +296,7 @@ static void installDefaultIntegrations(
new NetworkBreadcrumbsIntegration(context, buildInfoProvider, options.getLogger()));
options.addIntegration(new TempSensorBreadcrumbsIntegration(context));
options.addIntegration(new PhoneStateBreadcrumbsIntegration(context));
options.addIntegration(new SpotlightIntegration());
}

/**
Expand Down Expand Up @@ -331,6 +333,12 @@ private static void readDefaultOptionValues(
options.getLogger().log(SentryLevel.ERROR, "Could not generate distinct Id.", e);
}
}

if (options.getSpotlightConnectionUrl() == null) {
// developer machine should be the same across emulators
// see https://developer.android.com/studio/run/emulator-networking.html
options.setSpotlightConnectionUrl("http://10.0.2.2:8969/stream");
}
}

/**
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 Expand Up @@ -143,7 +144,7 @@
<!-- <meta-data android:name="io.sentry.breadcrumbs.app-components" android:value="false" />-->

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

<!-- how to enable the attach view hierarchy feature-->
<meta-data android:name="io.sentry.attach-view-hierarchy" android:value="true" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,12 @@ protected void onCreate(Bundle savedInstanceState) {
Sentry.captureException(e);
}

final Attachment image = new Attachment(imageFile.getAbsolutePath(), "sentry.png", "image/png");
Sentry.configureScope(
scope -> {
scope.addAttachment(image);
});
// final Attachment image = new Attachment(imageFile.getAbsolutePath(), "sentry.png",
// "image/png");
// Sentry.configureScope(
// scope -> {
// scope.addAttachment(image);
// });

binding.crashFromJava.setOnClickListener(
view -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">10.0.2.2</domain>
</domain-config>
</network-security-config>
15 changes: 15 additions & 0 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -2147,6 +2147,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 @@ -2209,6 +2210,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 Down Expand Up @@ -2250,6 +2252,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 Down Expand Up @@ -2316,6 +2319,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 @@ -2335,6 +2339,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;)Lio/sentry/SentryEnvelope;
}

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 @@ -3245,6 +3253,13 @@ public final class io/sentry/instrumentation/file/SentryFileWriter : java/io/Out
public fun <init> (Ljava/lang/String;Z)V
}

public final class io/sentry/internal/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;)Lio/sentry/SentryEnvelope;
public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V
}

public abstract interface class io/sentry/internal/debugmeta/IDebugMetaLoader {
public abstract fun loadDebugMeta ()Ljava/util/List;
}
Expand Down
41 changes: 27 additions & 14 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 =
buildEnvelope(event, attachments, session, traceContext, null);
@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);
@Nullable SentryEnvelope envelope = buildEnvelope(userFeedback);
sendEnvelope(envelope, null);
} catch (IOException e) {
options
.getLogger()
Expand Down Expand Up @@ -582,14 +582,29 @@ 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();
@Nullable SentryEnvelope envelopeToSend = envelope;
if (beforeEnvelopeCallback != null) {
envelopeToSend = beforeEnvelopeCallback.execute(envelope, hint);
}
if (envelopeToSend != null) {
if (hint == null) {
transport.send(envelope);
} else {
transport.send(envelope, hint);
}
final @Nullable SentryId id = envelope.getHeader().getEventId();
return id != null ? id : SentryId.EMPTY_ID;
} else {
return SentryId.EMPTY_ID;
}
Expand Down Expand Up @@ -665,9 +680,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 @@ -732,7 +745,7 @@ public void captureSession(final @NotNull Session session, final @Nullable Hint
final 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
42 changes: 42 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,10 @@ public class SentryOptions {
/** Whether to send modules containing information about versions. */
private boolean sendModules = true;

private @Nullable BeforeEnvelopeCallback beforeEnvelopeCallback;

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 +2278,29 @@ 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;
}

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

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

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

/**
* Mutates or drop an event before being sent
*
* @param envelope the envelope
* @param hint the hints
* @return the original envelope or the mutated envelope or null if the envelope should be
* dropped
*/
@Nullable
SentryEnvelope execute(@NotNull SentryEnvelope envelope, @Nullable Hint hint);
}

/**
* Creates SentryOptions instance without initializing any of the internal parts.
*
Expand Down
119 changes: 119 additions & 0 deletions sentry/src/main/java/io/sentry/internal/SpotlightIntegration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package io.sentry.internal;

import static io.sentry.SentryLevel.DEBUG;
import static io.sentry.SentryLevel.ERROR;

import io.sentry.Hint;
import io.sentry.IHub;
import io.sentry.Integration;
import io.sentry.SentryEnvelope;
import io.sentry.SentryOptions;
import io.sentry.util.Objects;
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.zip.GZIPOutputStream;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@ApiStatus.Internal
public final class SpotlightIntegration
implements Integration, SentryOptions.BeforeEnvelopeCallback, Closeable {

private @NotNull SentryOptions options = SentryOptions.empty();

private final @NotNull ExecutorService executorService = Executors.newSingleThreadExecutor();

@Override
public void register(@NotNull IHub hub, @NotNull SentryOptions options) {
this.options = options;

if (options.getBeforeEnvelopeCallback() == null
&& options.getSpotlightConnectionUrl() != null) {
options.setBeforeEnvelopeCallback(this);
}
}

@Override
public @NotNull SentryEnvelope execute(@NotNull SentryEnvelope envelope, @Nullable Hint hint) {
try {
executorService.execute(() -> sendEnvelope(envelope));
} catch (RejectedExecutionException ex) {
// ignored
}
return envelope;
}

private void sendEnvelope(final @NotNull SentryEnvelope envelope) {
try {
final @NotNull String spotlightConnectionUrl =
Objects.requireNonNull(
options.getSpotlightConnectionUrl(), "Spotlight URL can't be null");

final HttpURLConnection connection = createConnection(spotlightConnectionUrl);
try (final OutputStream outputStream = connection.getOutputStream();
final GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
options.getSerializer().serialize(envelope, gzip);
} catch (Throwable e) {
options
.getLogger()
.log(
ERROR,
e,
"An exception occurred while submitting the envelope to the Sentry server.");
} finally {
final int responseCode = connection.getResponseCode();
options.getLogger().log(DEBUG, "Envelope sent to spotlight: %d", responseCode);
closeAndDisconnect(connection);
}
} catch (final Exception e) {
options
.getLogger()
.log(ERROR, e, "An exception occurred while creating the connection to spotlight.");
}
}

private @NotNull HttpURLConnection createConnection(final @NotNull String url) throws Exception {

final @NotNull HttpURLConnection connection =
(HttpURLConnection) URI.create(url).toURL().openConnection();
connection.setRequestMethod("POST");
connection.setDoOutput(true);

connection.setRequestProperty("Content-Encoding", "gzip");
connection.setRequestProperty("Content-Type", "application/x-sentry-envelope");
connection.setRequestProperty("Accept", "application/json");

// https://stackoverflow.com/questions/52726909/java-io-ioexception-unexpected-end-of-stream-on-connection/53089882
connection.setRequestProperty("Connection", "close");

connection.connect();
return connection;
}

/**
* Closes the Response stream and disconnect the connection
*
* @param connection the HttpURLConnection
*/
private void closeAndDisconnect(final @NotNull HttpURLConnection connection) {
try {
connection.getInputStream().close();
} catch (IOException ignored) {
// connection is already closed
} finally {
connection.disconnect();
}
}

@Override
public void close() throws IOException {
executorService.shutdown();
}
}

0 comments on commit 13edfa9

Please sign in to comment.