From 8ff6684029604c6bcfc8b8323d74709cfa3c96a0 Mon Sep 17 00:00:00 2001 From: Kenny Date: Wed, 31 May 2023 16:24:36 -0700 Subject: [PATCH 1/7] feat: add agent start configuration options --- README.md | 26 +++++++++- package.json | 2 +- plugin.xml | 25 +++++++++- src/android/NewRelicCordovaPlugin.java | 62 ++++++++++++++++++++--- src/ios/NewRelicCordovaPlugin.m | 69 ++++++++++++++++++++++++-- 5 files changed, 171 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index a50b3e6..8aa903c 100644 --- a/README.md +++ b/README.md @@ -28,13 +28,37 @@ If you don't have a New Relic account, [create a free trial](https://newrelic.co Finally, copy the application tokens from your New Relic applications page, and have them ready for the next step. You only need to copy the application tokens of the platforms you are building on. -### Adding the plugin +## Adding the plugin Change to your Cordova project directory and add the plugin to your project using the Cordova command line tool. The `--variable` argument is used to pass application tokens to the plugin. ``` # Install from github repository cordova plugin add https://github.com/newrelic/newrelic-cordova-plugin.git --variable IOS_APP_TOKEN="{ios-app-token}" --variable ANDROID_APP_TOKEN="{android-app-token}" ``` +### Agent Configuration Options +The `--variable` argument can also be used to add optional configuration options on agent start to the plugin. +``` +# Disable Crash Reporting +cordova plugin add https://github.com/newrelic/newrelic-cordova-plugin.git --variable IOS_APP_TOKEN="{ios-app-token}" --variable ANDROID_APP_TOKEN="{android-app-token}" --variable CRASH_REPORTING_ENABLED="false" +``` + +Currently, the plugin supports the following agent configuration options: +* `CRASH_REPORTING_ENABLED`: Enable or disable crash reporting. + * Possible values are `true` and `false`. Defaults to `true`. +* `DISTRIBUTED_TRACING_ENABLED`: Enable or disable the adding of distributed tracing headers to network requests. + * Possible values are `true` and `false`. Defaults to `true`. +* `INTERACTION_TRACING_ENABLED`: Enable or disable interaction tracing. Trace instrumentation still occurs, but no traces are harvested. This will disable default and custom interactions. + * Possible values are `true` and `false`. Defaults to `true`. +* `DEFAULT_INTERACTIONS_ENABLED`: Enable or disable default interactions. Trace instrumentation still occurs, but no traces are harvested. This will enable or disable default interactions only while custom interactions remain enabled. + * Possible values are `true` and `false`. Defaults to `true`. +* `LOGGING_ENABLED`: Enable or disable agent logging. + * Possible values are `true` and `false`. Defaults to `true`. +* `LOG_LEVEL`: Specifies the log level. + * Possible values are `ERROR` (least verbose), `WARNING` `INFO`, `VERBOSE`, `DEBUG`, `AUDIT` (most verbose). + * Defaults to `INFO` on Android and `WARNING` on iOS. +* `COLLECTOR_ADDRESS`: Specifies the URI authority component of the harvest data upload endpoint. +* `CRASH_COLLECTOR_ADDRESS`: Specifies the authority component of the crash data upload URI. + # Updating the plugin Update the New Relic Cordova plugin to the latest released version easily via the following command: ``` diff --git a/package.json b/package.json index 7c3bea9..c48652e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "newrelic-cordova-plugin", - "version": "6.1.0", + "version": "6.2.0", "description": "New Relic Cordova Plugin for iOS and Android", "repo": "https://github.com/newrelic/newrelic-cordova-plugin/", "scripts": { diff --git a/plugin.xml b/plugin.xml index 6fe339a..17c6932 100644 --- a/plugin.xml +++ b/plugin.xml @@ -19,7 +19,14 @@ - + + + + + + + + @@ -32,6 +39,14 @@ + + + + + + + + @@ -77,6 +92,14 @@ + + + + + + + + strToLogLevel = new HashMap<>(); + strToLogLevel.put("ERROR", AgentLog.ERROR); + strToLogLevel.put("WARNING", AgentLog.WARN); + strToLogLevel.put("INFO", AgentLog.INFO); + strToLogLevel.put("VERBOSE", AgentLog.VERBOSE); + strToLogLevel.put("AUDIT", AgentLog.AUDIT); + + int logLevel = AgentLog.INFO; + String configLogLevel = preferences.getString("LOG_LEVEL", "INFO").toUpperCase(); + if (strToLogLevel.containsKey(configLogLevel)) { + logLevel = strToLogLevel.get(configLogLevel); + } + + String collectorAddress = preferences.getString("COLLECTOR_ADDRESS", null); + String crashCollectorAddress = preferences.getString("CRASH_COLLECTOR_ADDRESS", null); + + NewRelic newRelic = NewRelic.withApplicationToken(appToken) + .withApplicationFramework(ApplicationFramework.Cordova, pluginVersion) + .withLoggingEnabled(preferences.getString("LOGGING_ENABLED", "true").toLowerCase().equals("true")) + .withLogLevel(logLevel); + + if (isEmptyConfigParameter(collectorAddress) && isEmptyConfigParameter(crashCollectorAddress)) { + newRelic.start(this.cordova.getActivity().getApplication()); + } else { + // Set missing collector addresses (if any) + if (collectorAddress == null) { + collectorAddress = "mobile-collector.newrelic.com"; + } + if (crashCollectorAddress == null) { + crashCollectorAddress = "mobile-crash.newrelic.com"; + } + newRelic.usingCollectorAddress(collectorAddress); + newRelic.usingCrashCollectorAddress(crashCollectorAddress); + newRelic.start(this.cordova.getActivity().getApplication()); + } + + newRelic.start(this.cordova.getActivity().getApplication()); NewRelic.setAttribute(AnalyticsAttribute.APPLICATION_PLATFORM_VERSION_ATTRIBUTE, pluginVersion); } - } + public boolean isEmptyConfigParameter(String parameter) { + return parameter == null || parameter.isEmpty() || parameter.equals("x"); + } + public StackTraceElement[] parseStackTrace(String stack) { String[] lines = stack.split("\n"); ArrayList stackTraceList = new ArrayList<>(); diff --git a/src/ios/NewRelicCordovaPlugin.m b/src/ios/NewRelicCordovaPlugin.m index 42ff17a..6e39975 100644 --- a/src/ios/NewRelicCordovaPlugin.m +++ b/src/ios/NewRelicCordovaPlugin.m @@ -19,7 +19,8 @@ @implementation NewRelicCordovaPlugin { - (void)pluginInitialize { NSString* applicationToken = [self.commandDelegate.settings objectForKey:@"ios_app_token"]; - NSString* platformVersion = [self.commandDelegate.settings objectForKey:@"plugin_version"]; + + NSDictionary* config = self.commandDelegate.settings; jscRegex = [NSRegularExpression regularExpressionWithPattern:@"^\\s*(?:([^@]*)(?:\\((.*?)\\))?@)?(\\S.*?):(\\d+)(?::(\\d+))?\\s*$" options:NSRegularExpressionCaseInsensitive @@ -31,16 +32,76 @@ - (void)pluginInitialize options:NSRegularExpressionCaseInsensitive error:nil]; - if (applicationToken == nil || ([applicationToken isEqualToString:@""] || [applicationToken isEqualToString:@"x"])) { + if ([self isEmptyConfigParameter:applicationToken]) { NRLOG_ERROR(@"Failed to load application token! The iOS agent is not configured for Cordova."); } else { + + if ([self shouldDisableFeature:config[@"crash_reporting_enabled"]]) { + [NewRelic disableFeatures:NRFeatureFlag_CrashReporting]; + } + if ([self shouldDisableFeature:config[@"distributed_tracing_enabled"]]) { + [NewRelic disableFeatures:NRFeatureFlag_DistributedTracing]; + } + if ([self shouldDisableFeature:config[@"interaction_tracing_enabled"]]) { + [NewRelic disableFeatures:NRFeatureFlag_InteractionTracing]; + } + if ([self shouldDisableFeature:config[@"default_interactions_enabled"]]) { + [NewRelic disableFeatures:NRFeatureFlag_DefaultInteractions]; + } + + // Set log level depending on loggingEnabled and logLevel + NRLogLevels logLevel = NRLogLevelWarning; + NSDictionary *logDict = @{ + @"ERROR": [NSNumber numberWithInt:NRLogLevelError], + @"WARNING": [NSNumber numberWithInt:NRLogLevelWarning], + @"INFO": [NSNumber numberWithInt:NRLogLevelInfo], + @"VERBOSE": [NSNumber numberWithInt:NRLogLevelVerbose], + @"AUDIT": [NSNumber numberWithInt:NRLogLevelAudit], + }; + if ([logDict objectForKey:[config[@"log_level"] uppercaseString]]) { + NSString* configLogLevel = [config[@"log_level"] uppercaseString]; + NSNumber* newLogLevel = [logDict valueForKey:configLogLevel]; + logLevel = [newLogLevel intValue]; + } + if ([self shouldDisableFeature:config[@"logging_enabled"]]) { + logLevel = NRLogLevelNone; + } + [NRLogger setLogLevels:logLevel]; + + + NSString* collectorAddress = config[@"collector_address"]; + NSString* crashCollectorAddress = config[@"crash_collector_address"]; + [NewRelic setPlatform:NRMAPlatform_Cordova]; - [NewRelic setPlatformVersion:platformVersion]; - [NewRelic startWithApplicationToken:applicationToken]; + [NewRelic setPlatformVersion:config[@"platform_version"]]; + + if ([self isEmptyConfigParameter:collectorAddress] && [self isEmptyConfigParameter:crashCollectorAddress]) { + [NewRelic startWithApplicationToken:applicationToken]; + } else { + // Set to default collector endpoints if only one of two addresses was set + if ([self isEmptyConfigParameter:collectorAddress]) { + collectorAddress = @"mobile-collector.newrelic.com"; + } + if ([self isEmptyConfigParameter:crashCollectorAddress]) { + crashCollectorAddress = @"mobile-crash.newrelic.com"; + } + + [NewRelic startWithApplicationToken:applicationToken + andCollectorAddress:collectorAddress + andCrashCollectorAddress:crashCollectorAddress]; + } } } +- (BOOL)isEmptyConfigParameter:(NSString *)param { + return param == nil || ([param isEqualToString:@""] || [param isEqualToString:@"x"]); +} + +- (BOOL)shouldDisableFeature:(NSString *)flag { + return [[flag lowercaseString] isEqualToString:@"false"]; +} + - (void)recordBreadCrumb:(CDVInvokedUrlCommand *)command { NSString* name = [command.arguments objectAtIndex:0]; NSDictionary *attributes = [command.arguments objectAtIndex:1]; From 1c54a22e6692720316cb94b05f19a06e8aeafaa1 Mon Sep 17 00:00:00 2001 From: Kenny Date: Fri, 2 Jun 2023 13:06:54 -0700 Subject: [PATCH 2/7] fix: add empty check for regex groups --- src/ios/NewRelicCordovaPlugin.m | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/ios/NewRelicCordovaPlugin.m b/src/ios/NewRelicCordovaPlugin.m index 6e39975..2b3b12d 100644 --- a/src/ios/NewRelicCordovaPlugin.m +++ b/src/ios/NewRelicCordovaPlugin.m @@ -172,10 +172,13 @@ - (NSMutableArray*)parseStackTrace:(NSString*)stackString { } NSMutableDictionary* stackTraceElement = [NSMutableDictionary new]; - stackTraceElement[@"method"] = [line substringWithRange:[result[0] rangeAtIndex:1]]; - stackTraceElement[@"file"] = [line substringWithRange:[result[0] rangeAtIndex:3]]; - NSNumber* lineNum = @([[line substringWithRange:[result[0] rangeAtIndex:4]] intValue]); - stackTraceElement[@"line"] = lineNum; + NSRange methodRange = [result[0] rangeAtIndex:1]; + NSRange fileRange = [result[0] rangeAtIndex:3]; + NSRange lineNumRange = [result[0] rangeAtIndex:4]; + + stackTraceElement[@"method"] = methodRange.length == 0 ? @" " : [line substringWithRange:methodRange]; + stackTraceElement[@"file"] = fileRange.length == 0 ? @" " : [line substringWithRange:fileRange]; + stackTraceElement[@"line"] = lineNumRange.length == 0 ? [NSNumber numberWithInt:1] : @([[line substringWithRange:lineNumRange] intValue]); [stackFramesArr addObject:stackTraceElement]; } From cafd922952b0b3344a225378dd5896a0ad96c519 Mon Sep 17 00:00:00 2001 From: Kenny Date: Fri, 2 Jun 2023 13:33:30 -0700 Subject: [PATCH 3/7] tests: add example error with missing fields --- tests/tests.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/tests.js b/tests/tests.js index fc6aeda..8d41e6d 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -121,10 +121,12 @@ exports.defineAutoTests = () => { spyOn(cordova, "exec").and.callThrough(); spyOn(window.console, "warn").and.callThrough(); + // should parse errors with missing fields let exampleError = new Error(); exampleError.name = 'fakeErrorName'; exampleError.message = 'fakeMsg'; - exampleError.stack = 'fakeStack'; + exampleError.stack = 'ionic://com.example.app.poc:8100/plugins/newrelic-cordova-plugin/www/js/missingmethod.js:123:45\nmissingfile@:1:2345\nmissinglinenum@ionic://com.example.app.poc:8100/example.js::\nregularstackline@ionic://com.example.app.poc:8100/example.js:1:23456'; + // will crash if unable to execute window.NewRelic.recordError(exampleError); window.NewRelic.recordError(new TypeError); window.NewRelic.recordError(new EvalError); @@ -143,6 +145,10 @@ exports.defineAutoTests = () => { expect(window.NewRelic.recordError).toHaveBeenCalledTimes(10); }); + it('should parse JS error with missing fields', () => { + + }); + it('should have currentSessionId', () => { expect(window.NewRelic.currentSessionId() instanceof Promise).toBe(true); spyOn(window.NewRelic, "currentSessionId").and.callThrough(); From 71be846fcf17441d63fe33989d4e58f2f9ad396a Mon Sep 17 00:00:00 2001 From: Kenny Date: Fri, 2 Jun 2023 15:32:31 -0700 Subject: [PATCH 4/7] feat: Add fedramp flag --- README.md | 4 ++++ plugin.xml | 5 ++++- src/android/NewRelicCordovaPlugin.java | 3 +++ src/ios/NewRelicCordovaPlugin.m | 3 +++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8aa903c..656a836 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,8 @@ cordova plugin add https://github.com/newrelic/newrelic-cordova-plugin.git --var ``` ### Agent Configuration Options +***These options are only available on Cordova plugin v6.2.1 and above.*** + The `--variable` argument can also be used to add optional configuration options on agent start to the plugin. ``` # Disable Crash Reporting @@ -58,6 +60,8 @@ Currently, the plugin supports the following agent configuration options: * Defaults to `INFO` on Android and `WARNING` on iOS. * `COLLECTOR_ADDRESS`: Specifies the URI authority component of the harvest data upload endpoint. * `CRASH_COLLECTOR_ADDRESS`: Specifies the authority component of the crash data upload URI. +* `FEDRAMP_ENABLED`: Enable or disable reporting data using different endpoints for US government clients. + * Possible values are `true` and `false`. Defaults to `false`. # Updating the plugin Update the New Relic Cordova plugin to the latest released version easily via the following command: diff --git a/plugin.xml b/plugin.xml index 17c6932..fd68cc5 100644 --- a/plugin.xml +++ b/plugin.xml @@ -27,6 +27,7 @@ + @@ -47,6 +48,7 @@ + @@ -64,7 +66,7 @@ - + @@ -100,6 +102,7 @@ + strToLogLevel = new HashMap<>(); strToLogLevel.put("ERROR", AgentLog.ERROR); diff --git a/src/ios/NewRelicCordovaPlugin.m b/src/ios/NewRelicCordovaPlugin.m index 2b3b12d..1c8913a 100644 --- a/src/ios/NewRelicCordovaPlugin.m +++ b/src/ios/NewRelicCordovaPlugin.m @@ -49,6 +49,9 @@ - (void)pluginInitialize if ([self shouldDisableFeature:config[@"default_interactions_enabled"]]) { [NewRelic disableFeatures:NRFeatureFlag_DefaultInteractions]; } + if (![self shouldDisableFeature:config[@"fedramp_enabled"]]) { + [NewRelic enableFeatures:NRFeatureFlag_FedRampEnabled]; + } // Set log level depending on loggingEnabled and logLevel NRLogLevels logLevel = NRLogLevelWarning; From ba9c252dd8f554c2576f152975236d776cd904aa Mon Sep 17 00:00:00 2001 From: Kenny Date: Wed, 7 Jun 2023 09:40:48 -0700 Subject: [PATCH 5/7] feat: add web view instrumentation flag --- README.md | 2 ++ plugin.xml | 3 +++ src/ios/NewRelicCordovaPlugin.m | 3 +++ 3 files changed, 8 insertions(+) diff --git a/README.md b/README.md index 656a836..b842ea0 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,8 @@ Currently, the plugin supports the following agent configuration options: * `LOG_LEVEL`: Specifies the log level. * Possible values are `ERROR` (least verbose), `WARNING` `INFO`, `VERBOSE`, `DEBUG`, `AUDIT` (most verbose). * Defaults to `INFO` on Android and `WARNING` on iOS. +* `WEB_VIEW_INSTRUMENTATION` (iOS ONLY): Enable (default) or disable automatic WKWebView instrumentation. + * Possible values are `true` and `false`. Defaults to `true`. * `COLLECTOR_ADDRESS`: Specifies the URI authority component of the harvest data upload endpoint. * `CRASH_COLLECTOR_ADDRESS`: Specifies the authority component of the crash data upload URI. * `FEDRAMP_ENABLED`: Enable or disable reporting data using different endpoints for US government clients. diff --git a/plugin.xml b/plugin.xml index fd68cc5..46572b2 100644 --- a/plugin.xml +++ b/plugin.xml @@ -25,6 +25,7 @@ + @@ -46,6 +47,7 @@ + @@ -100,6 +102,7 @@ + diff --git a/src/ios/NewRelicCordovaPlugin.m b/src/ios/NewRelicCordovaPlugin.m index 1c8913a..98a8d72 100644 --- a/src/ios/NewRelicCordovaPlugin.m +++ b/src/ios/NewRelicCordovaPlugin.m @@ -49,6 +49,9 @@ - (void)pluginInitialize if ([self shouldDisableFeature:config[@"default_interactions_enabled"]]) { [NewRelic disableFeatures:NRFeatureFlag_DefaultInteractions]; } + if ([self shouldDisableFeature:config[@"web_view_instrumentation"]]) { + [NewRelic disableFeatures:NRFeatureFlag_WebViewInstrumentation]; + } if (![self shouldDisableFeature:config[@"fedramp_enabled"]]) { [NewRelic enableFeatures:NRFeatureFlag_FedRampEnabled]; } From 3e56cb54f4f06f6e46ad8ca290e56d2e835c0521 Mon Sep 17 00:00:00 2001 From: Kenny Date: Thu, 8 Jun 2023 15:46:26 -0700 Subject: [PATCH 6/7] feat: release 6.2.1 --- CHANGELOG.md | 9 +++++++++ package.json | 2 +- plugin.xml | 4 ++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24b789d..1ba5acf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +# 6.2.1 + +### New in this release +* Upgrade native iOS agent to v7.4.5 +* Added agent configuration options when adding the plugin to your project. + +### Fixed in this release +* Fixed an issue where certain JS errors were improperly parsed on iOS. + # 6.2.0 ### New in this release diff --git a/package.json b/package.json index c48652e..170b5cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "newrelic-cordova-plugin", - "version": "6.2.0", + "version": "6.2.1", "description": "New Relic Cordova Plugin for iOS and Android", "repo": "https://github.com/newrelic/newrelic-cordova-plugin/", "scripts": { diff --git a/plugin.xml b/plugin.xml index 46572b2..201fc4a 100644 --- a/plugin.xml +++ b/plugin.xml @@ -6,7 +6,7 @@ + id="newrelic-cordova-plugin" version="6.2.1"> NewRelic New Relic Cordova Plugin for iOS and Android New Relic @@ -18,7 +18,7 @@ - + From 049fd9c5e18ce4acfb996dd835a543d380107d50 Mon Sep 17 00:00:00 2001 From: Kenny Date: Fri, 9 Jun 2023 11:13:12 -0700 Subject: [PATCH 7/7] fix: use correct plugin version string on ios --- src/ios/NewRelicCordovaPlugin.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ios/NewRelicCordovaPlugin.m b/src/ios/NewRelicCordovaPlugin.m index 98a8d72..f0623d3 100644 --- a/src/ios/NewRelicCordovaPlugin.m +++ b/src/ios/NewRelicCordovaPlugin.m @@ -80,7 +80,7 @@ - (void)pluginInitialize NSString* crashCollectorAddress = config[@"crash_collector_address"]; [NewRelic setPlatform:NRMAPlatform_Cordova]; - [NewRelic setPlatformVersion:config[@"platform_version"]]; + [NewRelic setPlatformVersion:config[@"plugin_version"]]; if ([self isEmptyConfigParameter:collectorAddress] && [self isEmptyConfigParameter:crashCollectorAddress]) { [NewRelic startWithApplicationToken:applicationToken];