Skip to content

Commit 9fbff5d

Browse files
NR-199912: Background Instrumentation (#192)
* NR-199912: Background Instrumentation Proof of Concept * NR-199912: Adds NRFeatureFlag_BackgroundInstrumentation to NewRelicFeatureFlags and NRMAFlags * feat: bg instrumentation * rename FF BackgroundInstrumentation -> BackgroundReporting * NR-238931: Add Background attribute to events when backgrounded. * NR-238931: Wrap up TODOs, use proper bundleID, and use cached UIApplicationState * CR comments addressed: NR-238932 * NR-238932: wrap up a couple of TODOs.
1 parent de07b1c commit 9fbff5d

File tree

28 files changed

+202
-224
lines changed

28 files changed

+202
-224
lines changed

Agent.xcodeproj/project.pbxproj

Lines changed: 0 additions & 30 deletions
Large diffs are not rendered by default.

Agent/Analytics/Constants.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ extern NSString *const kNRMA_Attrib_dtId;
8080
extern NSString *const kNRMA_Attrib_dtTraceId;
8181
extern NSString *const kNRMA_Attrib_name;
8282
extern NSString *const kNRMA_Attrib_offline;
83+
extern NSString *const kNRMA_Attrib_background;
8384

8485
extern NSString *const kNRMA_Val_errorType_HTTP;
8586
extern NSString *const kNRMA_Val_errorType_Network;

Agent/Analytics/Constants.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
NSString * const kNRMA_Attrib_dtTraceId = @"trace.id";
8686
NSString * const kNRMA_Attrib_name = @"name";
8787
NSString * const kNRMA_Attrib_offline = @"offline";
88+
NSString * const kNRMA_Attrib_background = @"background";
8889

8990
NSString * const kNRMA_Val_errorType_HTTP = @"HTTPError";
9091
NSString * const kNRMA_Val_errorType_Network = @"NetworkFailure";

Agent/Analytics/Events/NRMAMobileEvent.m

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66
// Copyright © 2023 New Relic. All rights reserved.
77
//
88

9+
#import <UIKit/UIKit.h>
10+
911
#import "NRMAMobileEvent.h"
1012
#import "Constants.h"
1113
#import "NRMAFlags.h"
1214
#import "NewRelicInternalUtils.h"
15+
#import "NewRelicAgentInternal.h"
1316

1417
static NSString* const kTimestampKey = @"Timestamp";
1518
static NSString* const kSessionElapsedTimeKey = @"SessionElapsedTime";
@@ -38,6 +41,12 @@ - (nonnull instancetype) initWithTimestamp:(NSTimeInterval)timestamp
3841
}
3942
}
4043
}
44+
// Handle Background attribute addition.
45+
if([NRMAFlags shouldEnableBackgroundReporting]) {
46+
if ([NewRelicAgentInternal sharedInstance].currentApplicationState == UIApplicationStateBackground) {
47+
[self addAttribute:kNRMA_Attrib_background value:@YES];
48+
}
49+
}
4150
}
4251

4352
return self;

Agent/Analytics/NRMAAnalytics.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
- (void) clearLastSessionsAnalytics;
5555

5656
- (BOOL) checkOfflineStatus;
57-
57+
- (BOOL) checkBackgroundStatus;
5858
//this utilizes setSessionAttribute:value: which validates the user input 'name'.
5959
- (BOOL) setLastInteraction:(NSString*)name;
6060

Agent/Analytics/NRMAAnalytics.mm

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ - (BOOL) addInteractionEvent:(NSString*)name
263263

264264
return [_eventManager addEvent:[event autorelease]];
265265
} else {
266-
return _analyticsController->addInteractionEvent([name UTF8String], duration_secs, [self checkOfflineStatus]);
266+
return _analyticsController->addInteractionEvent([name UTF8String], duration_secs, [self checkOfflineStatus], [self checkBackgroundStatus]);
267267
}
268268
}
269269

@@ -504,7 +504,7 @@ - (BOOL)addNetworkRequestEvent:(NRMANetworkRequestData *)requestData
504504
if ([NRMAFlags shouldEnableNetworkRequestEvents]) {
505505
NewRelic::NetworkRequestData* networkRequestData = [requestData getNetworkRequestData];
506506
NewRelic::NetworkResponseData* networkResponseData = [responseData getNetworkResponseData];
507-
return _analyticsController->addRequestEvent(*networkRequestData, *networkResponseData, std::move(payload), [self checkOfflineStatus]);
507+
return _analyticsController->addRequestEvent(*networkRequestData, *networkResponseData, std::move(payload), [self checkOfflineStatus], [self checkBackgroundStatus]);
508508
}
509509
return NO;
510510
}
@@ -516,7 +516,7 @@ - (BOOL)addNetworkErrorEvent:(NRMANetworkRequestData *)requestData
516516
NewRelic::NetworkRequestData* networkRequestData = [requestData getNetworkRequestData];
517517
NewRelic::NetworkResponseData* networkResponseData = [responseData getNetworkResponseData];
518518

519-
return _analyticsController->addNetworkErrorEvent(*networkRequestData, *networkResponseData,std::move(payload), [self checkOfflineStatus]);
519+
return _analyticsController->addNetworkErrorEvent(*networkRequestData, *networkResponseData,std::move(payload), [self checkOfflineStatus], [self checkBackgroundStatus]);
520520
}
521521

522522
return NO;
@@ -529,7 +529,7 @@ - (BOOL)addHTTPErrorEvent:(NRMANetworkRequestData *)requestData
529529
NewRelic::NetworkRequestData* networkRequestData = [requestData getNetworkRequestData];
530530
NewRelic::NetworkResponseData* networkResponseData = [responseData getNetworkResponseData];
531531

532-
return _analyticsController->addHTTPErrorEvent(*networkRequestData, *networkResponseData, std::move(payload), [self checkOfflineStatus]);
532+
return _analyticsController->addHTTPErrorEvent(*networkRequestData, *networkResponseData, std::move(payload), [self checkOfflineStatus], [self checkBackgroundStatus]);
533533
}
534534
return NO;
535535
}
@@ -913,6 +913,13 @@ - (BOOL) checkOfflineStatus {
913913
return false;
914914
}
915915

916+
- (BOOL) checkBackgroundStatus {
917+
if([NRMAFlags shouldEnableBackgroundReporting]) {
918+
return ([NewRelicAgentInternal sharedInstance].currentApplicationState == UIApplicationStateBackground);
919+
}
920+
return false;
921+
}
922+
916923
- (BOOL)recordUserAction:(NRMAUserAction *)userAction {
917924
if (userAction == nil) { return NO; };
918925

Agent/FeatureFlags/NRMAFlags.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353

5454
+ (BOOL) shouldEnableNewEventSystem;
5555

56+
+ (BOOL) shouldEnableBackgroundReporting;
57+
5658
+ (NSArray<NSString*>*) namesForFlags:(NRMAFeatureFlags)flags;
5759

5860
// Private Setting

Agent/FeatureFlags/NRMAFlags.m

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,11 @@ + (BOOL) shouldEnableLogReporting {
173173
+ (BOOL) shouldEnableNewEventSystem {
174174
return ([NRMAFlags featureFlags] & NRFeatureFlag_NewEventSystem) != 0;
175175
}
176+
177+
+ (BOOL) shouldEnableBackgroundReporting {
178+
return ([NRMAFlags featureFlags] & NRFeatureFlag_BackgroundReporting) != 0;
179+
}
180+
176181
+ (NSArray<NSString*>*) namesForFlags:(NRMAFeatureFlags)flags {
177182
NSMutableArray *retArray = [NSMutableArray array];
178183
if ((flags & NRFeatureFlag_InteractionTracing) == NRFeatureFlag_InteractionTracing) {
@@ -234,6 +239,10 @@ + (BOOL) shouldEnableNewEventSystem {
234239
if ((flags & NRFeatureFlag_NewEventSystem) == NRFeatureFlag_NewEventSystem) {
235240
[retArray addObject:@"NewEventSystem"];
236241
}
242+
if ((flags & NRFeatureFlag_BackgroundReporting) == NRFeatureFlag_BackgroundReporting) {
243+
[retArray addObject:@"BackgroundReporting"];
244+
}
245+
237246
return retArray;
238247
}
239248

Agent/General/NewRelicAgentInternal.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#import "NRMAUserActionFacade.h"
1414
#import "NRMAURLTransformer.h"
1515

16+
#import <BackgroundTasks/BackgroundTasks.h>
17+
1618
// Keys used for harvester data request.
1719
#define NEW_RELIC_APP_VERSION_HEADER_KEY @"X-NewRelic-App-Version"
1820
#define NEW_RELIC_OS_NAME_HEADER_KEY @"X-NewRelic-OS-Name"
@@ -44,6 +46,8 @@
4446

4547
@property (nonatomic, assign) BOOL isShutdown;
4648

49+
@property (nonatomic, readonly, assign) UIApplicationState currentApplicationState;
50+
4751
+ (void)shutdown;
4852

4953
+ (void)startWithApplicationToken:(NSString*)appToken

Agent/General/NewRelicAgentInternal.m

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,16 @@ - (id) initWithCollectorAddress:(NSString*)collectorHost
149149

150150
self = [super init];
151151
if (self) {
152+
153+
// NOTE: BackgroundReporting is only enabled for iOS 13+.
154+
if ([NRMAFlags shouldEnableBackgroundReporting]) {
155+
if (@available(iOS 13.0, *)) {
156+
[[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:[[NSBundle mainBundle] bundleIdentifier] usingQueue:nil launchHandler:^(__kindof BGTask * _Nonnull task) {
157+
[self handleAppRefreshTask: task];
158+
}];
159+
}
160+
}
161+
152162
self.appWillTerminate = NO;
153163
[NRMACPUVitals setAppStartCPUTime];
154164
if ([UIApplication sharedApplication].applicationState != UIApplicationStateBackground) {
@@ -187,6 +197,10 @@ - (id) initWithCollectorAddress:(NSString*)collectorHost
187197
name:UIApplicationWillTerminateNotification
188198
object:[UIApplication sharedApplication]];
189199

200+
if ([NRMAFlags shouldEnableBackgroundReporting]) {
201+
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
202+
}
203+
190204
NRLOG_INFO(@"Agent enabled");
191205

192206
// Store Data For Crash Reporter
@@ -532,6 +546,8 @@ - (void) applicationWillEnterForeground {
532546
return;
533547
}
534548

549+
_currentApplicationState = UIApplicationStateActive;
550+
535551
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
536552
0),
537553
^{
@@ -621,6 +637,8 @@ - (void) applicationDidEnterBackground {
621637
}
622638
// We are leaving the background.
623639
didFireEnterForeground = NO;
640+
641+
_currentApplicationState = UIApplicationStateBackground;
624642

625643
[[NRMAHarvestController harvestController].harvestTimer stop];
626644

@@ -652,7 +670,7 @@ - (void) applicationDidEnterBackground {
652670
if ([[UIDevice currentDevice] respondsToSelector:@selector(isMultitaskingSupported)] &&
653671
[[UIDevice currentDevice] isMultitaskingSupported]) {
654672

655-
673+
// Current One time background task is dispatched upon going to the background.
656674
UIApplication* application = [UIApplication sharedApplication];
657675

658676
// Mark the start of our background task.
@@ -700,6 +718,8 @@ - (void) applicationDidEnterBackground {
700718
if (self.appWillTerminate) {
701719
return;
702720
}
721+
722+
// Currently this is where the actual harvest occurs when we go to background
703723
NRLOG_VERBOSE(@"Harvesting data in background");
704724
[[[NRMAHarvestController harvestController] harvester] execute];
705725
#ifndef DISABLE_NRMA_EXCEPTION_WRAPPER
@@ -708,7 +728,13 @@ - (void) applicationDidEnterBackground {
708728
class:NSStringFromClass([NRMAHarvester class])
709729
selector:@"execute"];
710730
} @finally {
711-
[self agentShutdown];
731+
732+
if ([NRMAFlags shouldEnableBackgroundReporting]) {
733+
NRLOG_VERBOSE(@"used to agentShutdown. Continuing since BackgroundInstrumentation enabled.");
734+
}
735+
else {
736+
[self agentShutdown];
737+
}
712738
}
713739
#endif
714740

@@ -717,6 +743,11 @@ - (void) applicationDidEnterBackground {
717743
[application endBackgroundTask:background_task];
718744
// Invalidate the background_task.
719745
background_task = UIBackgroundTaskInvalid;
746+
747+
if ([NRMAFlags shouldEnableBackgroundReporting]) {
748+
// Schedule the next background harvest.
749+
[self scheduleHeartbeatTask];
750+
}
720751
}
721752
});
722753
} else {
@@ -750,6 +781,53 @@ - (void) agentShutdown {
750781
[NRMALastActivityTraceController clearLastActivityStamp];
751782
}
752783

784+
// We only support background fetch in iOS 13+
785+
- (void) scheduleHeartbeatTask {
786+
if (@available(iOS 13.0, *)) {
787+
BGAppRefreshTaskRequest *request = [[BGAppRefreshTaskRequest alloc] initWithIdentifier:[[NSBundle mainBundle] bundleIdentifier]];
788+
request.earliestBeginDate = nil;//[NSDate dateWithTimeIntervalSinceNow:5 * 60];
789+
790+
NSError *error = nil;
791+
792+
[[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error];
793+
794+
if (error) {
795+
NRLOG_ERROR(@"Error schedule heartbeat: %@", error);
796+
}
797+
else {
798+
NRLOG_VERBOSE(@"Scheduled heartbeat");
799+
800+
}
801+
}
802+
}
803+
804+
- (void) handleAppRefreshTask:(BGAppRefreshTask *)task {
805+
NRLOG_VERBOSE(@"handleAppRefreshTask BGAppRefreshTask");
806+
if (@available(iOS 13.0, *)) {
807+
808+
// We always reschedule the heartbeat task.
809+
[self scheduleHeartbeatTask];
810+
811+
__weak BGAppRefreshTask *weakTask = task;
812+
813+
[task setExpirationHandler:^{
814+
__strong BGAppRefreshTask *strongTask = weakTask;
815+
if (strongTask) {
816+
[strongTask setTaskCompletedWithSuccess:false];
817+
}
818+
}];
819+
820+
[[[NRMAHarvestController harvestController] harvester] execute];
821+
822+
[task setTaskCompletedWithSuccess:true];
823+
}
824+
else {
825+
NRLOG_VERBOSE(@"No background tasks pre iOS 13");
826+
827+
}
828+
829+
}
830+
753831
+ (BOOL) harvestNow {
754832
return [NRMAHarvestController harvestNow];
755833
}

Agent/HandledException/NRMAHandledExceptions.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ - (void) checkOffline:(std::shared_ptr<NewRelic::Hex::Report::HexReport>) report
163163
@synchronized(r) {
164164
NRMANetworkStatus status = [r currentReachabilityStatus];
165165
if (status == NotReachable) {
166-
report->setAttributeNoValidation("offline", true);
166+
report->setAttributeNoValidation(__kNRMA_Attrib_offline, true);
167167
}
168168
}
169169
}

Agent/HandledException/NRMAHexBackgroundUploader.h

Lines changed: 0 additions & 15 deletions
This file was deleted.

Agent/HandledException/NRMAHexBackgroundUploader.mm

Lines changed: 0 additions & 48 deletions
This file was deleted.

Agent/HandledException/NRMASessionIdentifierManager.h

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)