-
-
Notifications
You must be signed in to change notification settings - Fork 468
Expand file tree
/
Copy pathSpotlightIntegration.java
More file actions
152 lines (133 loc) · 5.34 KB
/
SpotlightIntegration.java
File metadata and controls
152 lines (133 loc) · 5.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package io.sentry.spotlight;
import static io.sentry.SentryLevel.DEBUG;
import static io.sentry.SentryLevel.ERROR;
import static io.sentry.SentryLevel.WARNING;
import static io.sentry.util.IntegrationUtils.addIntegrationToSdkVersion;
import io.sentry.Hint;
import io.sentry.ILogger;
import io.sentry.IScopes;
import io.sentry.ISentryExecutorService;
import io.sentry.Integration;
import io.sentry.NoOpLogger;
import io.sentry.NoOpSentryExecutorService;
import io.sentry.SentryEnvelope;
import io.sentry.SentryExecutorService;
import io.sentry.SentryIntegrationPackageStorage;
import io.sentry.SentryOptions;
import io.sentry.util.Platform;
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
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 {
static {
SentryIntegrationPackageStorage.getInstance()
.addPackage("maven:io.sentry:sentry-spotlight", BuildConfig.VERSION_NAME);
}
private @Nullable SentryOptions options;
private @NotNull ILogger logger = NoOpLogger.getInstance();
private @NotNull ISentryExecutorService executorService = NoOpSentryExecutorService.getInstance();
@Override
public void register(@NotNull IScopes scopes, @NotNull SentryOptions options) {
this.options = options;
this.logger = options.getLogger();
if (options.getBeforeEnvelopeCallback() == null && options.isEnableSpotlight()) {
executorService = new SentryExecutorService(options);
options.setBeforeEnvelopeCallback(this);
logger.log(DEBUG, "SpotlightIntegration enabled.");
addIntegrationToSdkVersion("Spotlight");
} else {
logger.log(
DEBUG,
"SpotlightIntegration is not enabled. "
+ "BeforeEnvelopeCallback is already set or spotlight is not enabled.");
}
}
@Override
@SuppressWarnings("FutureReturnValueIgnored")
public void execute(final @NotNull SentryEnvelope envelope, final @Nullable Hint hint) {
try {
executorService.submit(() -> sendEnvelope(envelope));
} catch (RejectedExecutionException e) {
logger.log(WARNING, "Spotlight envelope submission rejected.", e);
}
}
private void sendEnvelope(final @NotNull SentryEnvelope envelope) {
try {
if (options == null) {
throw new IllegalArgumentException("SentryOptions are required to send envelopes.");
}
final String spotlightConnectionUrl = getSpotlightConnectionUrl();
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) {
logger.log(
ERROR, "An exception occurred while submitting the envelope to the Sentry server.", e);
} finally {
final int responseCode = connection.getResponseCode();
logger.log(DEBUG, "Envelope sent to spotlight: %d", responseCode);
closeAndDisconnect(connection);
}
} catch (final Exception e) {
logger.log(ERROR, "An exception occurred while creating the connection to spotlight.", e);
}
}
String getSpotlightConnectionUrl() {
if (options != null && options.getSpotlightConnectionUrl() != null) {
return options.getSpotlightConnectionUrl();
}
if (Platform.isAndroid()) {
// developer machine should be the same across emulators
// see https://developer.android.com/studio/run/emulator-networking.html
return "http://10.0.2.2:8969/stream";
} else {
return "http://localhost:8969/stream";
}
}
private @NotNull HttpURLConnection createConnection(final @NotNull String url) throws Exception {
final @NotNull HttpURLConnection connection =
(HttpURLConnection) URI.create(url).toURL().openConnection();
connection.setReadTimeout(1000);
connection.setConnectTimeout(1000);
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.close(0);
if (options != null && options.getBeforeEnvelopeCallback() == this) {
options.setBeforeEnvelopeCallback(null);
}
}
}