Skip to content

Commit

Permalink
feat: track network connectivity changes with breadcrumbs (#3232)
Browse files Browse the repository at this point in the history
  • Loading branch information
armcknight authored Sep 20, 2023
1 parent 51307b7 commit 97797b4
Show file tree
Hide file tree
Showing 17 changed files with 262 additions and 133 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Features

- Record changes to network connectivity in breadcrumbs (#3232)

## 8.12.0

### Fixes
Expand Down
2 changes: 2 additions & 0 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1595,6 +1595,7 @@
84AF45A529A7FFA500FBB177 /* SentryProfiledTracerConcurrency.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryProfiledTracerConcurrency.mm; sourceTree = "<group>"; };
84B7FA3B29B2866200AD93B1 /* SentryTestUtils-ObjC-BridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SentryTestUtils-ObjC-BridgingHeader.h"; sourceTree = "<group>"; };
84B7FA4729B2995A00AD93B1 /* DeploymentTargets.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DeploymentTargets.xcconfig; sourceTree = "<group>"; };
84C404B02ABA9F9C007F69C5 /* SentryReachability+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SentryReachability+Private.h"; sourceTree = "<group>"; };
84C47B2B2A09239100DAEB8A /* .codecov.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .codecov.yml; sourceTree = "<group>"; };
84E4F5692914F020004C7358 /* Brewfile */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = text; path = Brewfile; sourceTree = "<group>"; tabWidth = 2; };
84F993C32A62A74000EC0190 /* SentryCurrentDateProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryCurrentDateProvider.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1888,6 +1889,7 @@
84AC61D129F7541E009EEF61 /* SentryDispatchSourceWrapper.m */,
0AAE202028ED9BCC00D0CD80 /* SentryReachability.h */,
0AAE201D28ED9B9400D0CD80 /* SentryReachability.m */,
84C404B02ABA9F9C007F69C5 /* SentryReachability+Private.h */,
);
name = Networking;
sourceTree = "<group>";
Expand Down
55 changes: 40 additions & 15 deletions Sources/Sentry/SentryBreadcrumbTracker.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#import "SentryDependencyContainer.h"
#import "SentryHub.h"
#import "SentryLog.h"
#import "SentryReachability.h"
#import "SentryScope.h"
#import "SentrySwift.h"
#import "SentrySwizzle.h"
Expand All @@ -15,7 +16,7 @@
# import <UIKit/UIKit.h>
#elif TARGET_OS_OSX || TARGET_OS_MACCATALYST
# import <Cocoa/Cocoa.h>
#endif
#endif // !TARGET_OS_WATCH

NS_ASSUME_NONNULL_BEGIN

Expand All @@ -24,23 +25,31 @@

@interface
SentryBreadcrumbTracker ()
#if !TARGET_OS_WATCH
<SentryReachabilityObserver>
#endif // !TARGET_OS_WATCH

@property (nonatomic, weak) id<SentryBreadcrumbDelegate> delegate;

@end

@implementation SentryBreadcrumbTracker

- (instancetype)init
#if !TARGET_OS_WATCH
- (void)dealloc
{
return [super init];
[SentryDependencyContainer.sharedInstance.reachability removeObserver:self];
}
#endif // !TARGET_OS_WATCH

- (void)startWithDelegate:(id<SentryBreadcrumbDelegate>)delegate
{
_delegate = delegate;
[self addEnabledCrumb];
[self trackApplicationUIKitNotifications];
#if !TARGET_OS_WATCH
[self trackNetworkConnectivityChanges];
#endif // !TARGET_OS_WATCH
}

- (void)startSwizzle
Expand All @@ -56,7 +65,7 @@ - (void)stop
#if SENTRY_HAS_UIKIT
[SentryDependencyContainer.sharedInstance.swizzleWrapper
removeSwizzleSendActionForKey:SentryBreadcrumbTrackerSwizzleSendAction];
#endif
#endif // SENTRY_HAS_UIKIT
_delegate = nil;
}

Expand All @@ -70,10 +79,10 @@ - (void)trackApplicationUIKitNotifications
// Will resign Active notification is the nearest one to
// UIApplicationDidEnterBackgroundNotification
NSNotificationName backgroundNotificationName = NSApplicationWillResignActiveNotification;
#else
#else // TARGET_OS_WATCH
SENTRY_LOG_DEBUG(@"NO UIKit, OSX and Catalyst -> [SentryBreadcrumbTracker "
@"trackApplicationUIKitNotifications] does nothing.");
#endif
#endif // !TARGET_OS_WATCH

// not available for macOS
#if SENTRY_HAS_UIKIT
Expand All @@ -90,9 +99,9 @@ - (void)trackApplicationUIKitNotifications
crumb.message = @"Low memory";
[self.delegate addBreadcrumb:crumb];
}];
#endif
#endif // SENTRY_HAS_UIKIT

#if SENTRY_HAS_UIKIT || TARGET_OS_OSX || TARGET_OS_MACCATALYST
#if !TARGET_OS_WATCH
[NSNotificationCenter.defaultCenter addObserverForName:backgroundNotificationName
object:nil
queue:nil
Expand All @@ -114,9 +123,25 @@ - (void)trackApplicationUIKitNotifications
withDataKey:@"state"
withDataValue:@"foreground"];
}];
#endif
#endif // !TARGET_OS_WATCH
}

#if !TARGET_OS_WATCH
- (void)trackNetworkConnectivityChanges
{
[SentryDependencyContainer.sharedInstance.reachability
addObserver:self
withCallback:^(BOOL connected, NSString *_Nonnull typeDescription) {
SentryBreadcrumb *crumb =
[[SentryBreadcrumb alloc] initWithLevel:kSentryLevelInfo
category:@"device.connectivity"];
crumb.type = @"connectivity";
crumb.data = [NSDictionary dictionaryWithObject:typeDescription forKey:@"connectivity"];
[self.delegate addBreadcrumb:crumb];
}];
}
#endif // !TARGET_OS_WATCH

- (void)addBreadcrumbWithType:(NSString *)type
withCategory:(NSString *)category
withLevel:(SentryLevel)level
Expand Down Expand Up @@ -155,7 +180,7 @@ + (BOOL)avoidSender:(id)sender forTarget:(id)target action:(NSString *)action
}
return NO;
}
#endif
#endif // SENTRY_HAS_UIKIT

- (void)swizzleSendAction
{
Expand Down Expand Up @@ -183,9 +208,9 @@ - (void)swizzleSendAction
}
forKey:SentryBreadcrumbTrackerSwizzleSendAction];

#else
#else // !SENTRY_HAS_UIKIT
SENTRY_LOG_DEBUG(@"NO UIKit -> [SentryBreadcrumbTracker swizzleSendAction] does nothing.");
#endif
#endif // SENTRY_HAS_UIKIT
}

- (void)swizzleViewDidAppear
Expand Down Expand Up @@ -223,9 +248,9 @@ - (void)swizzleViewDidAppear
}),
mode, swizzleViewDidAppearKey);
# pragma clang diagnostic pop
#else
#else // !SENTRY_HAS_UIKIT
SENTRY_LOG_DEBUG(@"NO UIKit -> [SentryBreadcrumbTracker swizzleViewDidAppear] does nothing.");
#endif
#endif // SENTRY_HAS_UIKIT
}

#if SENTRY_HAS_UIKIT
Expand Down Expand Up @@ -287,7 +312,7 @@ + (NSDictionary *)fetchInfoAboutViewController:(UIViewController *)controller

return info;
}
#endif
#endif // SENTRY_HAS_UIKIT

@end

Expand Down
18 changes: 18 additions & 0 deletions Sources/Sentry/SentryDependencyContainer.m
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
# import "SentryUIDeviceWrapper.h"
#endif // TARGET_OS_IOS

#if !TARGET_OS_WATCH
# import "SentryReachability.h"
#endif // !TARGET_OS_WATCH

@implementation SentryDependencyContainer

static SentryDependencyContainer *instance;
Expand Down Expand Up @@ -368,4 +372,18 @@ - (SentryMXManager *)metricKitManager

#endif // SENTRY_HAS_METRIC_KIT

#if !TARGET_OS_WATCH
- (SentryReachability *)reachability
{
if (_reachability == nil) {
@synchronized(sentryDependencyContainerLock) {
if (_reachability == nil) {
_reachability = [[SentryReachability alloc] init];
}
}
}
return _reachability;
}
#endif // !TARGET_OS_WATCH

@end
47 changes: 22 additions & 25 deletions Sources/Sentry/SentryHttpTransport.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@

@interface
SentryHttpTransport ()
#if !TARGET_OS_WATCH
<SentryReachabilityObserver>
#endif // !TARGET_OS_WATCH

@property (nonatomic, strong) SentryFileManager *fileManager;
@property (nonatomic, strong) id<SentryRequestManager> requestManager;
Expand All @@ -37,9 +40,6 @@
@property (nonatomic, strong) SentryEnvelopeRateLimit *envelopeRateLimit;
@property (nonatomic, strong) SentryDispatchQueueWrapper *dispatchQueue;
@property (nonatomic, strong) dispatch_group_t dispatchGroup;
#if !TARGET_OS_WATCH
@property (nonatomic, strong) SentryReachability *reachability;
#endif // !TARGET_OS_WATCH

#if TEST || TESTCI
@property (nullable, nonatomic, strong) void (^startFlushCallback)(void);
Expand Down Expand Up @@ -73,9 +73,6 @@ - (id)initWithOptions:(SentryOptions *)options
rateLimits:(id<SentryRateLimits>)rateLimits
envelopeRateLimit:(SentryEnvelopeRateLimit *)envelopeRateLimit
dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper
#if !TARGET_OS_WATCH
reachability:(SentryReachability *)reachability
#endif // !TARGET_OS_WATCH
{
if (self = [super init]) {
self.options = options;
Expand All @@ -95,33 +92,33 @@ - (id)initWithOptions:(SentryOptions *)options
[self sendAllCachedEnvelopes];

#if !TARGET_OS_WATCH
self.reachability = reachability;
__weak SentryHttpTransport *weakSelf = self;
[self.reachability monitorURL:[NSURL URLWithString:@"https://sentry.io"]
usingCallback:^(BOOL connected, NSString *_Nonnull typeDescription) {
if (weakSelf == nil) {
SENTRY_LOG_DEBUG(@"WeakSelf is nil. Not doing anything.");
return;
}

if (connected) {
SENTRY_LOG_DEBUG(@"Internet connection is back.");
[weakSelf sendAllCachedEnvelopes];
} else {
SENTRY_LOG_DEBUG(@"Lost internet connection.");
}
}];
#endif
[SentryDependencyContainer.sharedInstance.reachability
addObserver:self
withCallback:^(BOOL connected, NSString *_Nonnull typeDescription) {
if (weakSelf == nil) {
SENTRY_LOG_DEBUG(@"WeakSelf is nil. Not doing anything.");
return;
}

if (connected) {
SENTRY_LOG_DEBUG(@"Internet connection is back.");
[weakSelf sendAllCachedEnvelopes];
} else {
SENTRY_LOG_DEBUG(@"Lost internet connection.");
}
}];
#endif // !TARGET_OS_WATCH
}
return self;
}

#if !TARGET_OS_WATCH
- (void)dealloc
{
#if !TARGET_OS_WATCH
[self.reachability stopMonitoring];
#endif
[SentryDependencyContainer.sharedInstance.reachability removeObserver:self];
}
#endif // !TARGET_OS_WATCH

- (void)sendEnvelope:(SentryEnvelope *)envelope
{
Expand Down
15 changes: 15 additions & 0 deletions Sources/Sentry/SentryReachability+Private.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#import "SentryReachability.h"

#if !TARGET_OS_WATCH

void SentryConnectivityCallback(__unused SCNetworkReachabilityRef target,
SCNetworkReachabilityFlags flags, __unused void *info);

@interface
SentryReachability ()

@property SCNetworkReachabilityRef sentry_reachability_ref;

@end

#endif // !TARGET_OS_WATCH
Loading

0 comments on commit 97797b4

Please sign in to comment.