Skip to content

Commit d503c14

Browse files
authored
Merge e6ae22f into 910071f
2 parents 910071f + e6ae22f commit d503c14

10 files changed

Lines changed: 841 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Features
66

7+
- Add breadcrumbs on network changes ([#2608](https://github.com/getsentry/sentry-java/pull/2608))
78
- Improve versatility of exception resolver component for Spring with more flexible API for consumers. ([#2577](https://github.com/getsentry/sentry-java/pull/2577))
89
- Automatic performance instrumentation for WebFlux ([#2597](https://github.com/getsentry/sentry-java/pull/2597))
910
- You can enable it by adding `sentry.enable-tracing=true` to your `application.properties`

sentry-android-core/api/sentry-android-core.api

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ public final class io/sentry/android/core/NdkIntegration : io/sentry/Integration
151151
public final fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V
152152
}
153153

154+
public class io/sentry/android/core/NetworkBreadcrumbsIntegration : io/sentry/Integration, java/io/Closeable {
155+
public fun <init> (Landroid/content/Context;Lio/sentry/android/core/BuildInfoProvider;Lio/sentry/ILogger;)V
156+
public fun close ()V
157+
public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V
158+
}
159+
154160
public final class io/sentry/android/core/PhoneStateBreadcrumbsIntegration : io/sentry/Integration, java/io/Closeable {
155161
public fun <init> (Landroid/content/Context;)V
156162
public fun close ()V
@@ -193,6 +199,7 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr
193199
public fun isEnableAppLifecycleBreadcrumbs ()Z
194200
public fun isEnableAutoActivityLifecycleTracing ()Z
195201
public fun isEnableFramesTracking ()Z
202+
public fun isEnableNetworkEventBreadcrumbs ()Z
196203
public fun isEnableSystemEventBreadcrumbs ()Z
197204
public fun setAnrEnabled (Z)V
198205
public fun setAnrReportInDebug (Z)V
@@ -207,6 +214,7 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr
207214
public fun setEnableAppLifecycleBreadcrumbs (Z)V
208215
public fun setEnableAutoActivityLifecycleTracing (Z)V
209216
public fun setEnableFramesTracking (Z)V
217+
public fun setEnableNetworkEventBreadcrumbs (Z)V
210218
public fun setEnableSystemEventBreadcrumbs (Z)V
211219
public fun setProfilingTracesHz (I)V
212220
public fun setProfilingTracesIntervalMillis (I)V

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ private static void installDefaultIntegrations(
238238
}
239239
options.addIntegration(new AppComponentsBreadcrumbsIntegration(context));
240240
options.addIntegration(new SystemEventsBreadcrumbsIntegration(context));
241+
options.addIntegration(
242+
new NetworkBreadcrumbsIntegration(context, buildInfoProvider, options.getLogger()));
241243
options.addIntegration(new TempSensorBreadcrumbsIntegration(context));
242244
options.addIntegration(new PhoneStateBreadcrumbsIntegration(context));
243245
}

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ final class ManifestMetadataReader {
4747
"io.sentry.breadcrumbs.activity-lifecycle";
4848
static final String BREADCRUMBS_APP_LIFECYCLE_ENABLE = "io.sentry.breadcrumbs.app-lifecycle";
4949
static final String BREADCRUMBS_SYSTEM_EVENTS_ENABLE = "io.sentry.breadcrumbs.system-events";
50+
static final String BREADCRUMBS_NETWORK_EVENTS_ENABLE = "io.sentry.breadcrumbs.network-events";
5051
static final String BREADCRUMBS_APP_COMPONENTS_ENABLE = "io.sentry.breadcrumbs.app-components";
5152
static final String BREADCRUMBS_USER_INTERACTION_ENABLE =
5253
"io.sentry.breadcrumbs.user-interaction";
@@ -188,7 +189,7 @@ static void applyMetadata(
188189
metadata,
189190
logger,
190191
BREADCRUMBS_APP_LIFECYCLE_ENABLE,
191-
options.isEnableAppComponentBreadcrumbs()));
192+
options.isEnableAppLifecycleBreadcrumbs()));
192193

193194
options.setEnableSystemEventBreadcrumbs(
194195
readBool(
@@ -211,6 +212,13 @@ static void applyMetadata(
211212
BREADCRUMBS_USER_INTERACTION_ENABLE,
212213
options.isEnableUserInteractionBreadcrumbs()));
213214

215+
options.setEnableNetworkEventBreadcrumbs(
216+
readBool(
217+
metadata,
218+
logger,
219+
BREADCRUMBS_NETWORK_EVENTS_ENABLE,
220+
options.isEnableNetworkEventBreadcrumbs()));
221+
214222
options.setEnableUncaughtExceptionHandler(
215223
readBool(
216224
metadata,
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
package io.sentry.android.core;
2+
3+
import android.annotation.SuppressLint;
4+
import android.content.Context;
5+
import android.net.ConnectivityManager;
6+
import android.net.Network;
7+
import android.net.NetworkCapabilities;
8+
import android.os.Build;
9+
import androidx.annotation.NonNull;
10+
import androidx.annotation.RequiresApi;
11+
import io.sentry.Breadcrumb;
12+
import io.sentry.Hint;
13+
import io.sentry.IHub;
14+
import io.sentry.ILogger;
15+
import io.sentry.Integration;
16+
import io.sentry.SentryLevel;
17+
import io.sentry.SentryOptions;
18+
import io.sentry.android.core.internal.util.ConnectivityChecker;
19+
import io.sentry.util.Objects;
20+
import java.io.Closeable;
21+
import java.io.IOException;
22+
import org.jetbrains.annotations.NotNull;
23+
import org.jetbrains.annotations.Nullable;
24+
import org.jetbrains.annotations.TestOnly;
25+
26+
public class NetworkBreadcrumbsIntegration implements Integration, Closeable {
27+
28+
private final @NotNull Context context;
29+
30+
private final @NotNull BuildInfoProvider buildInfoProvider;
31+
32+
private final @NotNull ILogger logger;
33+
34+
@TestOnly @Nullable NetworkBreadcrumbsNetworkCallback networkCallback;
35+
36+
public NetworkBreadcrumbsIntegration(
37+
final @NotNull Context context,
38+
final @NotNull BuildInfoProvider buildInfoProvider,
39+
final @NotNull ILogger logger) {
40+
this.context = Objects.requireNonNull(context, "Context is required");
41+
this.buildInfoProvider =
42+
Objects.requireNonNull(buildInfoProvider, "BuildInfoProvider is required");
43+
this.logger = Objects.requireNonNull(logger, "ILogger is required");
44+
}
45+
46+
@SuppressLint("NewApi")
47+
@Override
48+
public void register(final @NotNull IHub hub, final @NotNull SentryOptions options) {
49+
Objects.requireNonNull(hub, "Hub is required");
50+
SentryAndroidOptions androidOptions =
51+
Objects.requireNonNull(
52+
(options instanceof SentryAndroidOptions) ? (SentryAndroidOptions) options : null,
53+
"SentryAndroidOptions is required");
54+
55+
logger.log(
56+
SentryLevel.DEBUG,
57+
"NetworkBreadcrumbsIntegration enabled: %s",
58+
androidOptions.isEnableNetworkEventBreadcrumbs());
59+
60+
if (androidOptions.isEnableNetworkEventBreadcrumbs()) {
61+
62+
// The specific error is logged in the ConnectivityChecker method
63+
if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.LOLLIPOP) {
64+
networkCallback = null;
65+
logger.log(SentryLevel.DEBUG, "NetworkBreadcrumbsIntegration requires Android 5+");
66+
return;
67+
}
68+
69+
networkCallback = new NetworkBreadcrumbsNetworkCallback(hub, buildInfoProvider);
70+
final boolean registered =
71+
ConnectivityChecker.registerNetworkCallback(
72+
context, logger, buildInfoProvider, networkCallback);
73+
74+
// The specific error is logged in the ConnectivityChecker method
75+
if (!registered) {
76+
networkCallback = null;
77+
logger.log(SentryLevel.DEBUG, "NetworkBreadcrumbsIntegration not installed.");
78+
return;
79+
}
80+
logger.log(SentryLevel.DEBUG, "NetworkBreadcrumbsIntegration installed.");
81+
addIntegrationToSdkVersion();
82+
}
83+
}
84+
85+
@Override
86+
public void close() throws IOException {
87+
if (networkCallback != null) {
88+
ConnectivityChecker.unregisterNetworkCallback(
89+
context, logger, buildInfoProvider, networkCallback);
90+
logger.log(SentryLevel.DEBUG, "NetworkBreadcrumbsIntegration remove.");
91+
}
92+
networkCallback = null;
93+
}
94+
95+
@SuppressLint("ObsoleteSdkInt")
96+
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
97+
static final class NetworkBreadcrumbsNetworkCallback extends ConnectivityManager.NetworkCallback {
98+
final @NotNull IHub hub;
99+
final @NotNull BuildInfoProvider buildInfoProvider;
100+
101+
@Nullable Network currentNetwork = null;
102+
103+
@Nullable NetworkCapabilities lastCapabilities = null;
104+
105+
NetworkBreadcrumbsNetworkCallback(
106+
final @NotNull IHub hub, final @NotNull BuildInfoProvider buildInfoProvider) {
107+
this.hub = Objects.requireNonNull(hub, "Hub is required");
108+
this.buildInfoProvider =
109+
Objects.requireNonNull(buildInfoProvider, "BuildInfoProvider is required");
110+
}
111+
112+
@Override
113+
public void onAvailable(final @NonNull Network network) {
114+
if (network.equals(currentNetwork)) {
115+
return;
116+
}
117+
final Breadcrumb breadcrumb = createBreadcrumb("networkAvailable");
118+
hub.addBreadcrumb(breadcrumb);
119+
currentNetwork = network;
120+
lastCapabilities = null;
121+
}
122+
123+
@Override
124+
public void onCapabilitiesChanged(
125+
final @NonNull Network network, final @NonNull NetworkCapabilities networkCapabilities) {
126+
if (!network.equals(currentNetwork)) {
127+
return;
128+
}
129+
final @Nullable NetworkBreadcrumbConnectionDetail connectionDetail =
130+
getNewConnectionDetails(lastCapabilities, networkCapabilities);
131+
if (connectionDetail == null) {
132+
return;
133+
}
134+
lastCapabilities = networkCapabilities;
135+
final Breadcrumb breadcrumb = createBreadcrumb("networkCapabilitiesChanged");
136+
Hint hint = new Hint();
137+
hint.set("data", connectionDetail);
138+
hub.addBreadcrumb(breadcrumb, hint);
139+
}
140+
141+
@Override
142+
public void onLost(final @NonNull Network network) {
143+
if (!network.equals(currentNetwork)) {
144+
return;
145+
}
146+
final Breadcrumb breadcrumb = createBreadcrumb("networkLost");
147+
hub.addBreadcrumb(breadcrumb);
148+
currentNetwork = null;
149+
lastCapabilities = null;
150+
}
151+
152+
private Breadcrumb createBreadcrumb(String action) {
153+
final Breadcrumb breadcrumb = new Breadcrumb();
154+
breadcrumb.setType("system");
155+
breadcrumb.setCategory("network.event");
156+
breadcrumb.setData("action", action);
157+
breadcrumb.setLevel(SentryLevel.INFO);
158+
return breadcrumb;
159+
}
160+
161+
private @Nullable NetworkBreadcrumbConnectionDetail getNewConnectionDetails(
162+
final @Nullable NetworkCapabilities oldCapabilities,
163+
final @NotNull NetworkCapabilities newCapabilities) {
164+
if (oldCapabilities == null) {
165+
return new NetworkBreadcrumbConnectionDetail(newCapabilities, buildInfoProvider);
166+
}
167+
NetworkBreadcrumbConnectionDetail oldConnectionDetails =
168+
new NetworkBreadcrumbConnectionDetail(oldCapabilities, buildInfoProvider);
169+
NetworkBreadcrumbConnectionDetail newConnectionDetails =
170+
new NetworkBreadcrumbConnectionDetail(newCapabilities, buildInfoProvider);
171+
172+
// We compare the details and if they are similar we return null, so that we don't spam the
173+
// user with lots of breadcrumbs for e.g. an increase of signal strength of 1 point
174+
if (newConnectionDetails.isSimilar(oldConnectionDetails)) {
175+
return null;
176+
}
177+
return newConnectionDetails;
178+
}
179+
}
180+
181+
static class NetworkBreadcrumbConnectionDetail {
182+
final int downBandwidth, upBandwidth, signalStrength;
183+
final boolean isVpn;
184+
final @NotNull String type;
185+
186+
@SuppressLint({"NewApi", "ObsoleteSdkInt"})
187+
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
188+
NetworkBreadcrumbConnectionDetail(
189+
final @NotNull NetworkCapabilities networkCapabilities,
190+
final @NotNull BuildInfoProvider buildInfoProvider) {
191+
Objects.requireNonNull(networkCapabilities, "NetworkCapabilities is required");
192+
Objects.requireNonNull(buildInfoProvider, "BuildInfoProvider is required");
193+
this.downBandwidth = networkCapabilities.getLinkDownstreamBandwidthKbps();
194+
this.upBandwidth = networkCapabilities.getLinkUpstreamBandwidthKbps();
195+
int strength =
196+
buildInfoProvider.getSdkInfoVersion() >= Build.VERSION_CODES.Q
197+
? networkCapabilities.getSignalStrength()
198+
: 0;
199+
// If the system reports a signalStrength of Integer.MIN_VALUE, we adjust it to be 0
200+
this.signalStrength = strength > -100 ? strength : 0;
201+
this.isVpn = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
202+
String connectionType =
203+
ConnectivityChecker.getConnectionType(networkCapabilities, buildInfoProvider);
204+
this.type = connectionType != null ? connectionType : "";
205+
}
206+
207+
/**
208+
* Compares this connection detail to another one.
209+
*
210+
* @param other The other NetworkBreadcrumbConnectionDetail to compare
211+
* @return true if the details are similar enough, false otherwise
212+
*/
213+
boolean isSimilar(final @NotNull NetworkBreadcrumbConnectionDetail other) {
214+
return isVpn == other.isVpn
215+
&& type.equals(other.type)
216+
&& (-5 <= signalStrength - other.signalStrength
217+
&& signalStrength - other.signalStrength <= 5)
218+
&& (-1000 <= downBandwidth - other.downBandwidth
219+
&& downBandwidth - other.downBandwidth <= 1000)
220+
&& (-1000 <= upBandwidth - other.upBandwidth && upBandwidth - other.upBandwidth <= 1000);
221+
}
222+
}
223+
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ public final class SentryAndroidOptions extends SentryOptions {
3939
/** Enable or disable automatic breadcrumbs for App Components Using ComponentCallbacks */
4040
private boolean enableAppComponentBreadcrumbs = true;
4141

42+
/** Enable or disable automatic breadcrumbs for Network Events Using NetworkCallback */
43+
private boolean enableNetworkEventBreadcrumbs = true;
44+
4245
/**
4346
* Enables the Auto instrumentation for Activity lifecycle tracing.
4447
*
@@ -238,6 +241,14 @@ public void setEnableAppComponentBreadcrumbs(boolean enableAppComponentBreadcrum
238241
this.enableAppComponentBreadcrumbs = enableAppComponentBreadcrumbs;
239242
}
240243

244+
public boolean isEnableNetworkEventBreadcrumbs() {
245+
return enableNetworkEventBreadcrumbs;
246+
}
247+
248+
public void setEnableNetworkEventBreadcrumbs(boolean enableNetworkEventBreadcrumbs) {
249+
this.enableNetworkEventBreadcrumbs = enableNetworkEventBreadcrumbs;
250+
}
251+
241252
/**
242253
* Enable or disable all the automatic breadcrumbs
243254
*
@@ -248,6 +259,7 @@ public void enableAllAutoBreadcrumbs(boolean enable) {
248259
enableAppComponentBreadcrumbs = enable;
249260
enableSystemEventBreadcrumbs = enable;
250261
enableAppLifecycleBreadcrumbs = enable;
262+
enableNetworkEventBreadcrumbs = enable;
251263
setEnableUserInteractionBreadcrumbs(enable);
252264
}
253265

0 commit comments

Comments
 (0)