Skip to content

Commit

Permalink
feat: adds configurable request header instrumentation to network events
Browse files Browse the repository at this point in the history
The agent will now produce network event attributes for select header values if the headers are
detected on the request. The header names to instrument are passed into the agent when started.

#138,#135
  • Loading branch information
ndesai-newrelic committed Dec 19, 2023
1 parent a74f790 commit da255b2
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 17 deletions.
47 changes: 30 additions & 17 deletions android/src/main/java/com/NewRelic/NRMModularAgentModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReadableType;
import com.newrelic.agent.android.FeatureFlag;
import com.newrelic.agent.android.NewRelic;
import com.newrelic.agent.android.ApplicationFramework;
Expand All @@ -27,6 +29,9 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import javax.annotation.Nonnull;

public class NRMModularAgentModule extends ReactContextBaseJavaModule {

Expand All @@ -39,6 +44,7 @@ public NRMModularAgentModule(ReactApplicationContext reactContext) {
}

@Override
@Nonnull
public String getName() {
return "NRMModularAgent";
}
Expand Down Expand Up @@ -148,7 +154,7 @@ public void startAgent(String appKey, String agentVersion, String reactNativeVer
}

Map<String, Object> mapToAttributes(ReadableMap readableMap) {
Map<String, Object> attributeMap = new HashMap<String, Object>();
Map<String, Object> attributeMap = new HashMap<>();
ReadableMapKeySetIterator iterator = readableMap.keySetIterator();

while (iterator.hasNextKey()) {
Expand Down Expand Up @@ -177,6 +183,19 @@ Map<String, Object> mapToAttributes(ReadableMap readableMap) {
return attributeMap;
}

List<String> mapToList(ReadableArray readableArray) {
List<String> attributeArray = new ArrayList<>();

for (int i = 0; i < readableArray.size(); i++) {
ReadableType type = readableArray.getType(i);

if(type == ReadableType.String) {
attributeArray.add(readableArray.getString(i));
}
}
return attributeArray;
}

@ReactMethod
public void isAgentStarted(String name, Callback callback) {
callback.invoke(false, NewRelic.isStarted());
Expand Down Expand Up @@ -229,6 +248,12 @@ public void recordCustomEvent(String eventType, String eventName, ReadableMap re
NewRelic.recordCustomEvent(eventType, eventName, mapToAttributes(readableMap));
}

@ReactMethod
public void addHTTPHeadersTrackingFor(ReadableArray readableArray) {
NewRelic.addHTTPHeadersTrackingFor(mapToList(readableArray));
}


@ReactMethod
public void crashNow(String message) {
if(message.isEmpty()) {
Expand Down Expand Up @@ -442,22 +467,10 @@ private StackTraceElement[] generateStackTraceElements(Map<String, Object> stack
for(int i = 0; i < stackFrameMap.size(); ++i) {
Map<String, Object> element = (Map<String, Object>) stackFrameMap.get(Integer.toString(i));
String methodName = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
methodName = (String) element.getOrDefault("methodName", "");
} else {
if (element.containsKey("methodName")) {
methodName = (String)element.get("methodName");
}
}
String fileName = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
fileName = (String) element.getOrDefault("file", "");
} else {
if (element.containsKey("file")) {
fileName = (String)element.get("file");
}
}
int lineNumber = element.get("lineNumber") != null ? ((Double) element.get("lineNumber")).intValue() : 1;
methodName = (String) element.getOrDefault("methodName", "");
String fileName;
fileName = (String) element.getOrDefault("file", "");
int lineNumber = element.get("lineNumber") != null ? ((Double) Objects.requireNonNull(element.get("lineNumber"))).intValue() : 1;
StackTraceElement stackTraceElement = new StackTraceElement(" ", methodName, fileName, lineNumber);
stackTraceList.add(stackTraceElement);
}
Expand Down
9 changes: 9 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,15 @@ class NewRelic {
this.NRMAModularAgentWrapper.execute('noticeHttpTransaction', url, httpMethod, statusCode, startTime, endTime, bytesSent, bytesReceived, responseBody);
}

/**
* Add Headers as Attributes in Http Requests, for use in New Relic Insights.
* The method includes a list of headers, specified as a map.
* @param headers {Map<string, any>} A map that includes a list of headers.
*/
addHTTPHeadersTrackingFor(headers) {
this.NRMAModularAgentWrapper.execute('addHTTPHeadersTrackingFor', headers);
}

/**
* Records network failures.
* If a network request fails, use this method to record details about the failure.
Expand Down
5 changes: 5 additions & 0 deletions ios/bridge/NRMModularAgent.m
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,11 @@ - (dispatch_queue_t)methodQueue{
[NewRelic recordCustomEvent:eventType name:eventName attributes:attributes];
}

RCT_EXPORT_METHOD(addHTTPHeadersTrackingFor:(NSArray<NSString*> _Nonnull) headers) {
// todo: Not sure if we need to check the validity of these arguments at all..
[NewRelic addHTTPHeadersTrackingFor:headers];
}

/**
* Track a method as an interaction
*/
Expand Down

0 comments on commit da255b2

Please sign in to comment.