Skip to content

Commit

Permalink
Add optional attributes for recordError (#69)
Browse files Browse the repository at this point in the history
* feat: add optional attributes parameter for javascript code

* feat: handle optional attributes for record error in native code

* test: add unit tests for optional attributes parameter in record error

* feat: add boolean type for value in optional attributes for record error

* test: modify test for record error to test boolean and number values in optional attributes

* feat: update README documentation for record error to include optional attributes
  • Loading branch information
mchavez-newrelic authored Jul 14, 2023
1 parent a48382f commit 29e593d
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 5 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ By default, these configurations are already set to true on agent start.
```
## Error Reporting
### recordError(err: [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)) : void;
### recordError(err: [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error), attributes?: {[key: string]: boolean | number | string}) : void;
Records JavaScript errors for Cordova. It is useful to add this method by adding it to the error handler of the framework that you are using. Here are some examples below:
### Angular
Expand Down
8 changes: 8 additions & 0 deletions src/android/NewRelicCordovaPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -208,12 +208,20 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo
final String errorMessage = args.getString(1);
final String errorStack = args.getString(2);
final Boolean isFatal = args.getBoolean(3);
final JSONObject attributesAsJson = args.getJSONObject(4);

HashMap<String, Object> exceptionMap = new HashMap<>();
try {
exceptionMap.put("name", errorName);
exceptionMap.put("message", errorMessage);
exceptionMap.put("isFatal", isFatal);
if (attributesAsJson != null) {
final Map<String, Object> attributes = new Gson().fromJson(String.valueOf(attributesAsJson),
Map.class);
for (String key : attributes.keySet()) {
exceptionMap.put(key, attributes.get(key));
}
}
} catch (IllegalArgumentException e) {
Log.w("NRMA", e.getMessage());
}
Expand Down
6 changes: 6 additions & 0 deletions src/ios/NewRelicCordovaPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ - (void)recordError:(CDVInvokedUrlCommand *)command {
NSString* errorMessage = [command.arguments objectAtIndex:1];
NSString* errorStack = [command.arguments objectAtIndex:2];
NSString* isFatal = @"false";
NSDictionary* errorAttributes = [command.arguments objectAtIndex:4];

if ([[command.arguments objectAtIndex:3] boolValue] == YES) {
isFatal = @"true";
Expand All @@ -207,6 +208,11 @@ - (void)recordError:(CDVInvokedUrlCommand *)command {
attributes[@"cause"] = errorMessage;
attributes[@"reason"] = errorMessage;
attributes[@"fatal"] = isFatal;
if (errorAttributes != nil && ![errorAttributes isKindOfClass:[NSNull class]]) {
for(id key in errorAttributes) {
attributes[key] = errorAttributes[key];
}
}

NSMutableArray* stackTraceArr = [self parseStackTrace:errorStack];
attributes[@"stackTraceElements"] = stackTraceArr;
Expand Down
18 changes: 16 additions & 2 deletions tests/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,29 @@ exports.defineAutoTests = () => {
window.NewRelic.recordError(new ReferenceError);
window.NewRelic.recordError(new Error);

// should parse errors with non-empty custom attributes
let errorAttributes = new Map([
["errorKey1", "errorValue1"],
["errorKey2", 2],
["errorKey3", true]
]);
window.NewRelic.recordError(exampleError, errorAttributes);

// should parse errors with null custom attributes
window.NewRelic.recordError(exampleError, null);

// should parse errors with empty custom attributes
window.NewRelic.recordError(exampleError, {});

// Bad arguments
window.NewRelic.recordError(undefined);
window.NewRelic.recordError(null);
window.NewRelic.recordError(123);
window.NewRelic.recordError(true);

let numOfNativeCalls = cordova.exec.calls.count() - window.console.warn.calls.count();
expect(numOfNativeCalls).toBe(6);
expect(window.NewRelic.recordError).toHaveBeenCalledTimes(10);
expect(numOfNativeCalls).toBe(9);
expect(window.NewRelic.recordError).toHaveBeenCalledTimes(13);
});

it('should parse JS error with missing fields', () => {
Expand Down
9 changes: 7 additions & 2 deletions www/js/newrelic.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,13 @@ var NewRelic = {
/**
* Records JavaScript errors for Cordova.
* @param {Error} err The error to record.
* @param {Map<string, boolean|number|string>} attributes Optional attributes that will be appended to the handled exception event created in insights.
*/
recordError: function(err, cb, fail) {
recordError: function(err, attributes={}, cb, fail) {
let errorAttributes = attributes instanceof Map ? Object.fromEntries(attributes) : attributes;
if (attributes === null) {
errorAttributes = {};
}
if (err) {
var error;

Expand All @@ -139,7 +144,7 @@ var NewRelic = {
}

if(error !== undefined) {
cordova.exec(cb, fail, "NewRelicCordovaPlugin", "recordError", [error.name, error.message, error.stack, false]);
cordova.exec(cb, fail, "NewRelicCordovaPlugin", "recordError", [error.name, error.message, error.stack, false, errorAttributes]);
} else {
window.console.warn('Undefined error in NewRelic.recordError');
}
Expand Down

0 comments on commit 29e593d

Please sign in to comment.