Skip to content

Commit

Permalink
NR-199912: Background Instrumentation (#192)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
cdillard-NewRelic authored Apr 10, 2024
1 parent de07b1c commit 9fbff5d
Show file tree
Hide file tree
Showing 28 changed files with 202 additions and 224 deletions.
30 changes: 0 additions & 30 deletions Agent.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Agent/Analytics/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ extern NSString *const kNRMA_Attrib_dtId;
extern NSString *const kNRMA_Attrib_dtTraceId;
extern NSString *const kNRMA_Attrib_name;
extern NSString *const kNRMA_Attrib_offline;
extern NSString *const kNRMA_Attrib_background;

extern NSString *const kNRMA_Val_errorType_HTTP;
extern NSString *const kNRMA_Val_errorType_Network;
Expand Down
1 change: 1 addition & 0 deletions Agent/Analytics/Constants.m
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
NSString * const kNRMA_Attrib_dtTraceId = @"trace.id";
NSString * const kNRMA_Attrib_name = @"name";
NSString * const kNRMA_Attrib_offline = @"offline";
NSString * const kNRMA_Attrib_background = @"background";

NSString * const kNRMA_Val_errorType_HTTP = @"HTTPError";
NSString * const kNRMA_Val_errorType_Network = @"NetworkFailure";
Expand Down
9 changes: 9 additions & 0 deletions Agent/Analytics/Events/NRMAMobileEvent.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
// Copyright © 2023 New Relic. All rights reserved.
//

#import <UIKit/UIKit.h>

#import "NRMAMobileEvent.h"
#import "Constants.h"
#import "NRMAFlags.h"
#import "NewRelicInternalUtils.h"
#import "NewRelicAgentInternal.h"

static NSString* const kTimestampKey = @"Timestamp";
static NSString* const kSessionElapsedTimeKey = @"SessionElapsedTime";
Expand Down Expand Up @@ -38,6 +41,12 @@ - (nonnull instancetype) initWithTimestamp:(NSTimeInterval)timestamp
}
}
}
// Handle Background attribute addition.
if([NRMAFlags shouldEnableBackgroundReporting]) {
if ([NewRelicAgentInternal sharedInstance].currentApplicationState == UIApplicationStateBackground) {
[self addAttribute:kNRMA_Attrib_background value:@YES];
}
}
}

return self;
Expand Down
2 changes: 1 addition & 1 deletion Agent/Analytics/NRMAAnalytics.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
- (void) clearLastSessionsAnalytics;

- (BOOL) checkOfflineStatus;

- (BOOL) checkBackgroundStatus;
//this utilizes setSessionAttribute:value: which validates the user input 'name'.
- (BOOL) setLastInteraction:(NSString*)name;

Expand Down
15 changes: 11 additions & 4 deletions Agent/Analytics/NRMAAnalytics.mm
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ - (BOOL) addInteractionEvent:(NSString*)name

return [_eventManager addEvent:[event autorelease]];
} else {
return _analyticsController->addInteractionEvent([name UTF8String], duration_secs, [self checkOfflineStatus]);
return _analyticsController->addInteractionEvent([name UTF8String], duration_secs, [self checkOfflineStatus], [self checkBackgroundStatus]);
}
}

Expand Down Expand Up @@ -504,7 +504,7 @@ - (BOOL)addNetworkRequestEvent:(NRMANetworkRequestData *)requestData
if ([NRMAFlags shouldEnableNetworkRequestEvents]) {
NewRelic::NetworkRequestData* networkRequestData = [requestData getNetworkRequestData];
NewRelic::NetworkResponseData* networkResponseData = [responseData getNetworkResponseData];
return _analyticsController->addRequestEvent(*networkRequestData, *networkResponseData, std::move(payload), [self checkOfflineStatus]);
return _analyticsController->addRequestEvent(*networkRequestData, *networkResponseData, std::move(payload), [self checkOfflineStatus], [self checkBackgroundStatus]);
}
return NO;
}
Expand All @@ -516,7 +516,7 @@ - (BOOL)addNetworkErrorEvent:(NRMANetworkRequestData *)requestData
NewRelic::NetworkRequestData* networkRequestData = [requestData getNetworkRequestData];
NewRelic::NetworkResponseData* networkResponseData = [responseData getNetworkResponseData];

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

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

return _analyticsController->addHTTPErrorEvent(*networkRequestData, *networkResponseData, std::move(payload), [self checkOfflineStatus]);
return _analyticsController->addHTTPErrorEvent(*networkRequestData, *networkResponseData, std::move(payload), [self checkOfflineStatus], [self checkBackgroundStatus]);
}
return NO;
}
Expand Down Expand Up @@ -913,6 +913,13 @@ - (BOOL) checkOfflineStatus {
return false;
}

- (BOOL) checkBackgroundStatus {
if([NRMAFlags shouldEnableBackgroundReporting]) {
return ([NewRelicAgentInternal sharedInstance].currentApplicationState == UIApplicationStateBackground);
}
return false;
}

- (BOOL)recordUserAction:(NRMAUserAction *)userAction {
if (userAction == nil) { return NO; };

Expand Down
2 changes: 2 additions & 0 deletions Agent/FeatureFlags/NRMAFlags.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@

+ (BOOL) shouldEnableNewEventSystem;

+ (BOOL) shouldEnableBackgroundReporting;

+ (NSArray<NSString*>*) namesForFlags:(NRMAFeatureFlags)flags;

// Private Setting
Expand Down
9 changes: 9 additions & 0 deletions Agent/FeatureFlags/NRMAFlags.m
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ + (BOOL) shouldEnableLogReporting {
+ (BOOL) shouldEnableNewEventSystem {
return ([NRMAFlags featureFlags] & NRFeatureFlag_NewEventSystem) != 0;
}

+ (BOOL) shouldEnableBackgroundReporting {
return ([NRMAFlags featureFlags] & NRFeatureFlag_BackgroundReporting) != 0;
}

+ (NSArray<NSString*>*) namesForFlags:(NRMAFeatureFlags)flags {
NSMutableArray *retArray = [NSMutableArray array];
if ((flags & NRFeatureFlag_InteractionTracing) == NRFeatureFlag_InteractionTracing) {
Expand Down Expand Up @@ -234,6 +239,10 @@ + (BOOL) shouldEnableNewEventSystem {
if ((flags & NRFeatureFlag_NewEventSystem) == NRFeatureFlag_NewEventSystem) {
[retArray addObject:@"NewEventSystem"];
}
if ((flags & NRFeatureFlag_BackgroundReporting) == NRFeatureFlag_BackgroundReporting) {
[retArray addObject:@"BackgroundReporting"];
}

return retArray;
}

Expand Down
4 changes: 4 additions & 0 deletions Agent/General/NewRelicAgentInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#import "NRMAUserActionFacade.h"
#import "NRMAURLTransformer.h"

#import <BackgroundTasks/BackgroundTasks.h>

// Keys used for harvester data request.
#define NEW_RELIC_APP_VERSION_HEADER_KEY @"X-NewRelic-App-Version"
#define NEW_RELIC_OS_NAME_HEADER_KEY @"X-NewRelic-OS-Name"
Expand Down Expand Up @@ -44,6 +46,8 @@

@property (nonatomic, assign) BOOL isShutdown;

@property (nonatomic, readonly, assign) UIApplicationState currentApplicationState;

+ (void)shutdown;

+ (void)startWithApplicationToken:(NSString*)appToken
Expand Down
82 changes: 80 additions & 2 deletions Agent/General/NewRelicAgentInternal.m
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,16 @@ - (id) initWithCollectorAddress:(NSString*)collectorHost

self = [super init];
if (self) {

// NOTE: BackgroundReporting is only enabled for iOS 13+.
if ([NRMAFlags shouldEnableBackgroundReporting]) {
if (@available(iOS 13.0, *)) {
[[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:[[NSBundle mainBundle] bundleIdentifier] usingQueue:nil launchHandler:^(__kindof BGTask * _Nonnull task) {
[self handleAppRefreshTask: task];
}];
}
}

self.appWillTerminate = NO;
[NRMACPUVitals setAppStartCPUTime];
if ([UIApplication sharedApplication].applicationState != UIApplicationStateBackground) {
Expand Down Expand Up @@ -187,6 +197,10 @@ - (id) initWithCollectorAddress:(NSString*)collectorHost
name:UIApplicationWillTerminateNotification
object:[UIApplication sharedApplication]];

if ([NRMAFlags shouldEnableBackgroundReporting]) {
[[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
}

NRLOG_INFO(@"Agent enabled");

// Store Data For Crash Reporter
Expand Down Expand Up @@ -532,6 +546,8 @@ - (void) applicationWillEnterForeground {
return;
}

_currentApplicationState = UIApplicationStateActive;

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
0),
^{
Expand Down Expand Up @@ -621,6 +637,8 @@ - (void) applicationDidEnterBackground {
}
// We are leaving the background.
didFireEnterForeground = NO;

_currentApplicationState = UIApplicationStateBackground;

[[NRMAHarvestController harvestController].harvestTimer stop];

Expand Down Expand Up @@ -652,7 +670,7 @@ - (void) applicationDidEnterBackground {
if ([[UIDevice currentDevice] respondsToSelector:@selector(isMultitaskingSupported)] &&
[[UIDevice currentDevice] isMultitaskingSupported]) {


// Current One time background task is dispatched upon going to the background.
UIApplication* application = [UIApplication sharedApplication];

// Mark the start of our background task.
Expand Down Expand Up @@ -700,6 +718,8 @@ - (void) applicationDidEnterBackground {
if (self.appWillTerminate) {
return;
}

// Currently this is where the actual harvest occurs when we go to background
NRLOG_VERBOSE(@"Harvesting data in background");
[[[NRMAHarvestController harvestController] harvester] execute];
#ifndef DISABLE_NRMA_EXCEPTION_WRAPPER
Expand All @@ -708,7 +728,13 @@ - (void) applicationDidEnterBackground {
class:NSStringFromClass([NRMAHarvester class])
selector:@"execute"];
} @finally {
[self agentShutdown];

if ([NRMAFlags shouldEnableBackgroundReporting]) {
NRLOG_VERBOSE(@"used to agentShutdown. Continuing since BackgroundInstrumentation enabled.");
}
else {
[self agentShutdown];
}
}
#endif

Expand All @@ -717,6 +743,11 @@ - (void) applicationDidEnterBackground {
[application endBackgroundTask:background_task];
// Invalidate the background_task.
background_task = UIBackgroundTaskInvalid;

if ([NRMAFlags shouldEnableBackgroundReporting]) {
// Schedule the next background harvest.
[self scheduleHeartbeatTask];
}
}
});
} else {
Expand Down Expand Up @@ -750,6 +781,53 @@ - (void) agentShutdown {
[NRMALastActivityTraceController clearLastActivityStamp];
}

// We only support background fetch in iOS 13+
- (void) scheduleHeartbeatTask {
if (@available(iOS 13.0, *)) {
BGAppRefreshTaskRequest *request = [[BGAppRefreshTaskRequest alloc] initWithIdentifier:[[NSBundle mainBundle] bundleIdentifier]];
request.earliestBeginDate = nil;//[NSDate dateWithTimeIntervalSinceNow:5 * 60];

NSError *error = nil;

[[BGTaskScheduler sharedScheduler] submitTaskRequest:request error:&error];

if (error) {
NRLOG_ERROR(@"Error schedule heartbeat: %@", error);
}
else {
NRLOG_VERBOSE(@"Scheduled heartbeat");

}
}
}

- (void) handleAppRefreshTask:(BGAppRefreshTask *)task {
NRLOG_VERBOSE(@"handleAppRefreshTask BGAppRefreshTask");
if (@available(iOS 13.0, *)) {

// We always reschedule the heartbeat task.
[self scheduleHeartbeatTask];

__weak BGAppRefreshTask *weakTask = task;

[task setExpirationHandler:^{
__strong BGAppRefreshTask *strongTask = weakTask;
if (strongTask) {
[strongTask setTaskCompletedWithSuccess:false];
}
}];

[[[NRMAHarvestController harvestController] harvester] execute];

[task setTaskCompletedWithSuccess:true];
}
else {
NRLOG_VERBOSE(@"No background tasks pre iOS 13");

}

}

+ (BOOL) harvestNow {
return [NRMAHarvestController harvestNow];
}
Expand Down
2 changes: 1 addition & 1 deletion Agent/HandledException/NRMAHandledExceptions.mm
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ - (void) checkOffline:(std::shared_ptr<NewRelic::Hex::Report::HexReport>) report
@synchronized(r) {
NRMANetworkStatus status = [r currentReachabilityStatus];
if (status == NotReachable) {
report->setAttributeNoValidation("offline", true);
report->setAttributeNoValidation(__kNRMA_Attrib_offline, true);
}
}
}
Expand Down
15 changes: 0 additions & 15 deletions Agent/HandledException/NRMAHexBackgroundUploader.h

This file was deleted.

48 changes: 0 additions & 48 deletions Agent/HandledException/NRMAHexBackgroundUploader.mm

This file was deleted.

10 changes: 0 additions & 10 deletions Agent/HandledException/NRMASessionIdentifierManager.h

This file was deleted.

Loading

0 comments on commit 9fbff5d

Please sign in to comment.