Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NR-199912: Background Instrumentation #192

Merged
merged 14 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -265,7 +265,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 @@ -506,7 +506,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 @@ -518,7 +518,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 @@ -531,7 +531,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 @@ -915,6 +915,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
89 changes: 87 additions & 2 deletions Agent/General/NewRelicAgentInternal.m
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,17 @@ - (id) initWithCollectorAddress:(NSString*)collectorHost

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

if ([NRMAFlags shouldEnableBackgroundReporting]) {
if (@available(iOS 13.0, *)) {
[[BGTaskScheduler sharedScheduler] registerForTaskWithIdentifier:[[NSBundle mainBundle] bundleIdentifier] usingQueue:nil launchHandler:^(__kindof BGTask * _Nonnull task) {
[self handleAppRefreshTask: task];
}];
} else {
// Fallback on earlier versions
}
}

self.appWillTerminate = NO;
[NRMACPUVitals setAppStartCPUTime];
if ([UIApplication sharedApplication].applicationState != UIApplicationStateBackground) {
Expand Down Expand Up @@ -187,6 +198,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 +547,8 @@ - (void) applicationWillEnterForeground {
return;
}

_currentApplicationState = UIApplicationStateActive;

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

_currentApplicationState = UIApplicationStateBackground;

[[NRMAHarvestController harvestController].harvestTimer stop];

Expand Down Expand Up @@ -652,7 +671,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 +719,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 +729,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 +744,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 +782,59 @@ - (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");

}
} else {
// Fallback on earlier versions
}

}

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

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

[task setExpirationHandler:^{
__weak BGTask *weakTask = task;
if (weakTask) {
[weakTask setTaskCompletedWithSuccess:false];
}
//TODO: Invalidate and cancel the harvest request
// weakTask.invalidateAndCancel()

}];

[[[NRMAHarvestController harvestController] harvester] execute];

// TODO: Make sure this is the right place to call this.
[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
14 changes: 7 additions & 7 deletions Agent/HandledException/NRMAHexBackgroundUploader.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
#import <Foundation/Foundation.h>
#import "NRMAConnection.h"

@interface NRMAHexBackgroundUploader : NRMAConnection<NSURLSessionDelegate>
@property(strong) NSString* hexHost;

- (instancetype) initWithHexHost:(NSString*)hexHost;

- (void) sendData:(NSData*)data;
@end
//@interface NRMAHexBackgroundUploader : NRMAConnection<NSURLSessionDelegate>
//@property(strong) NSString* hexHost;
//
//- (instancetype) initWithHexHost:(NSString*)hexHost;
//
//- (void) sendData:(NSData*)data;
//@end
Loading
Loading