New Relic React Native Module
This module utilizes native New Relic agents to expose the Javascript environment. The New Relic SDKs collect crashes, network traffic, and other information for hybrid apps using native components.
Features
- Capture JavaScript errors
- Capture interactions and the sequence they were created
- Pass user information to New Relic to track user sessions
Requirements
Installation
yarn add @bibabovn/react-native-newrelic
- React native autolink package
- Don't forget to run:
npx pod-install
Android Setup
-
Install the New Relic native Android agent (instructions here)
-
Update build.gradle:
buildscript {
...
repositories {
...
mavenCentral()
}
dependencies {
...
classpath "com.newrelic.agent.android:agent-gradle-plugin:5.+"
}
}
-
Update app/build.gradle
apply plugin: "com.android.application"
apply plugin: 'newrelic' // <-- add this
...
dependencies {
...
implementation "com.newrelic.agent.android:android-agent:5.+"
}
-
Update the app's MainApplication.java
file (./android/app/src/main/java/'{package}/MainApplication.java')
-
Set app permissions
- Ensure that your app requests INTERNET and ACCESS_NETWORK_STATE permissions by adding these lines to your AndroidManifest.xml.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
iOS Setup
NewRelic doesn't officially support React Native yet, so some RN features may not work well with NewRelic like Flipper on iOS. So if you want to use RNNewRelic for development you'll need to disable it
Usage
See new relic IOS sdk doc or android sdk for more detail
nrInit()
Call this to initialize the NRNewRelic module. capture all unhandle js exception
crashNow(message?: string): void;
Test with a native exception
startInteraction(interactionName: string): Promise;
Track a method as an interaction
setInteractionName(interactionName: string): void;
Name or rename interaction
endInteraction(id: InteractionId): void;
End an interaction
Required. The string ID for the interaction you want to end.
This string is returned when you use startInteraction().
nrRecordMetric(name: string, category: MetricCategory | string, args?: MetricAttributes): void;
Create custom metrics
enum Category {
NONE = 'None',
VIEW_LOADING = 'View Loading',
VIEW_LAYOUT = 'Layout',
DATABASE = 'Database',
IMAGE = 'Images',
JSON = 'JSON',
NETWORK = 'Network',
}
type MetricAttributes = {
count: number;
} | {
totalValue: number;
} | {
count: number;
totalValue: number;
exclusiveValue: number;
} | {
count: number;
totalValue: number;
exclusiveValue: number;
countUnit: MetricUnit;
valueUnit: MetricUnit;
};
reportJSExceptionHandler(e?: any, isFatal?: boolean): void;
Call this to record js handled exception.
setAttribute(name: string, value: boolean | number | string): void;
Create or update an attribute
removeAttribute(name: string): void;
This method removes the attribute specified by the name string
setUserId(userId: string): void;
Set custom user ID for associating sessions with events and attributes
recordBreadcrumb(name: string, attributes?: {[key: string]: boolean | number | string}): void;
Track app activity/screen that may be helpful for troubleshooting crashes
recordCustomEvent(eventType: string, eventName?: string, attributes?: {[key: string]: boolean | number | string}): void;
Creates and records a custom event, for use in New Relic Insights
* IMPORTANT considerations and best practices include:
*
* - You should limit the total number of event types to approximately five.
* eventType is meant to be used for high-level categories.
* For example, you might create an event type Gestures.
*
* - Do not use eventType to name your custom events.
* Create an attribute to name an event or use the optional name parameter.
* You can create many custom events; it is only event types that you should limit.
*
* - Using the optional name parameter has the same effect as adding a name key in the attributes dictionary.
* name is a keyword used for displaying your events in the New Relic UI.
* To create a useful name, you might combine several attributes.
noticeNetworkRequest(url: string, options: RequestOptions): void;
Record HTTP transactions at varying levels of detail
noticeNetworkRequest(url: string, options: RequestOptions): void;
Record HTTP error transactions at varying levels of detail
interface RequestOptions {
httpMethod: 'GET' | 'POST' | 'PUT' | 'HEAD' | 'DELETE' | 'PATCH' | 'OPTIONS';
statusCode: number;
startTime?: number;
endTime?: number;
bytesSent?: number;
bytesReceived?: number;
responseHeader?: any;
responseBody?: string;
params?: {
[key: string]: any;
};
}
Example
import * as React from 'react';
import {
StyleSheet,
View,
Text,
FlatList,
Button,
SafeAreaView,
} from 'react-native';
import {
crashNow,
endInteraction,
noticeNetworkRequest,
nrInit,
recordBreadcrumb,
setAttribute,
setUserId,
startInteraction,
} from 'react-native-newrelic';
export default function App() {
const [dataSource, setResult] = React.useState<any>([]);
const [isLoading, setLoading] = React.useState<boolean>(true);
React.useEffect(() => {
nrInit();
recordBreadcrumb('User open first screen', { stack: 'feed-stack' });
setUserId('test-id');
}, []);
React.useEffect(() => {
const url = 'https://reactnative.dev/movies.json';
const startTime = new Date().getTime();
fetch(url)
.then((response) => response.json())
.then((response) => {
const endTime = new Date().getTime();
noticeNetworkRequest(url, {
httpMethod: 'GET',
startTime,
endTime,
responseBody: JSON.stringify(response),
statusCode: 200,
responseHeader: response.headers,
});
console.log(response);
setLoading(false);
setResult(response.movies);
})
.catch((error) => {
console.error(error);
});
}, []);
React.useEffect(() => {
setAttribute('name', 'User name');
setAttribute('isActive', true);
setAttribute('age', 23);
}, []);
const badApiLoad = async () => {
setLoading(true);
const interactionId = await startInteraction('StartLoadBadApiCall');
const url = 'https://facebook.github.io/react-native/moviessssssssss.json';
fetch(url)
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
setLoading(false);
endInteraction(interactionId);
setResult(responseJson.movies);
})
.catch((error) => {
setLoading(false);
endInteraction(interactionId);
console.error(error);
});
};
const testNativeCrash = () => {
crashNow('Test crash message');
};
const jsErrorHandle = () => {
throw new Error('test js error handle');
};
return (
<SafeAreaView style={styles.container}>
<Button title={'Bad API'} onPress={badApiLoad} color={'#3365f3'} />
<Button
title={'Test JS error handle'}
onPress={jsErrorHandle}
color={'#3365f3'}
/>
<Button
title={'Test native crash'}
onPress={testNativeCrash}
color={'#3365f3'}
/>
<FlatList
data={dataSource}
renderItem={({ item }) => (
<Text>
{item.title}, {item.releaseYear}
</Text>
)}
keyExtractor={({ id }) => id}
ListEmptyComponent={
<View>{isLoading ? <Text>Loading...</Text> : null}</View>
}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 16,
},
box: {
width: 60,
height: 60,
marginVertical: 20,
},
});
License
MIT