Skip to content

Mobile instrumentation plugin for the Capacitor hybrid mobile platform by Ionic

License

Notifications You must be signed in to change notification settings

kennyt276/newrelic-capacitor-plugin

 
 

Repository files navigation

Community Plus header

Android Test Suite iOS Test Suite

newrelic-capacitor-plugin

NewRelic Plugin for ionic Capacitor. This plugin uses native New Relic Android and iOS agents to instrument the Ionic Capacitor environment. The New Relic SDKs collect crashes, network traffic, and other information for hybrid apps using native components.

Features

  • Capture JavaScript errors
  • Network Instrumentation
  • Distributed Tracing
  • Tracking console log, warn and error
  • Promise rejection tracking
  • Capture interactions and the sequence in which they were created
  • Pass user information to New Relic to track user sessions

Current Support:

  • Android API 24+
  • iOS 10
  • Depends on New Relic iOS/XCFramework and Android agents

Install

npm install @newrelic/newrelic-capacitor-plugin
npx cap sync

Ionic Capacitor Setup

You can start the New Relic agent in the initialization of your app in main.ts (Angular or Vue) or index.tsx (React). Add the following code to launch NewRelic (don't forget to put proper application tokens):

import { NewRelicCapacitorPlugin, NREnums, AgentConfiguration } from '@newrelic/newrelic-capacitor-plugin';
import { Capacitor } from '@capacitor/core';

var appToken;

if(Capacitor.getPlatform() === 'ios') {
    appToken = '<IOS-APP-TOKEN>';
} else {
    appToken = '<ANDROID-APP-TOKEN>';
}

let agentConfig : AgentConfiguration = {
  //Android Specific
  // Optional:Enable or disable collection of event data.
  analyticsEventEnabled: true,

  // Optional:Enable or disable crash reporting.
  crashReportingEnabled: true,

  // Optional:Enable or disable interaction tracing. Trace instrumentation still occurs, but no traces are harvested. This will disable default and custom interactions.
  interactionTracingEnabled: true,

  // Optional:Enable or disable reporting successful HTTP requests to the MobileRequest event type.
  networkRequestEnabled: true,

  // Optional:Enable or disable reporting network and HTTP request errors to the MobileRequestError event type.
  networkErrorRequestEnabled: true,

  // Optional:Enable or disable capture of HTTP response bodies for HTTP error traces, and MobileRequestError events.
  httpResponseBodyCaptureEnabled: true,

  // Optional:Enable or disable agent logging.
  loggingEnabled: true,

  // Optional:Specifies the log level. Omit this field for the default log level.
  // Options include: ERROR (least verbose), WARNING, INFO, VERBOSE, AUDIT (most verbose).
  logLevel: NREnums.LogLevel.INFO,

  // iOS Specific
  // Optional:Enable/Disable automatic instrumentation of WebViews
  webViewInstrumentation: true,

  // Optional:Set a specific collector address for sending data. Omit this field for default address.
  collectorAddress: "",

  // Optional:Set a specific crash collector address for sending crashes. Omit this field for default address.
  crashCollectorAddress: ""

  // Optional:Enable or disable sending JS console logs to New Relic.
  sendConsoleEvents: true
}

NewRelicCapacitorPlugin.start({appKey:appToken, agentConfiguration:agentConfig})

AppToken is platform-specific. You need to generate separate tokens for Android and iOS apps.

Android Setup

  1. Install the New Relic native Android agent (instructions here).
  2. Update build.gradle:
  buildscript {
    ...
    repositories {
      ...
      mavenCentral()
    }
    dependencies {
      ...
      classpath "com.newrelic.agent.android:agent-gradle-plugin:6.10.0"
    }
  }
  1. Update app/build.gradle:
  apply plugin: "com.android.application"
  apply plugin: 'newrelic' // <-- add this
  1. Make sure 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" />

To automatically link the package, rebuild your project:

# Android apps
ionic capacitor run android

# iOS apps
ionic capacitor run ios

API

Set a custom user identifier value to associate user sessions with analytics events and attributes.

setUserId(options: { userId: string; }) => void
Param Type
options { userId: string; }

Usage:

    NewRelicCapacitorPlugin.setUserId({ userId: "CapacitorUserId" });

Creates a session-level attribute shared by multiple mobile event types. Overwrites its previous value and type each time it is called.

setAttribute(options: { name: string; value: string; }) => void
Param Type
options { name: string; value: string; }

Usage:

    NewRelicCapacitorPlugin.setAttribute({ name: "CapacitorAttribute", value: "123" });

This method removes the attribute specified by the name string.

removeAttribute(options: { name: string; }) => void
Param Type
options { name: string; }

Usage:

    NewRelicCapacitorPlugin.removeAttribute({ name: "CapacitorAttribute" });

Track app activity/screen that may be helpful for troubleshooting crashes.

recordBreadcrumb(options: { name: string; eventAttributes: object; }) => void
Param Type
options { name: string; eventAttributes: object; }

Usage:

    NewRelicCapacitorPlugin.recordBreadcrumb({ name: "shoe", eventAttributes: {"shoeColor": "blue","shoesize": 9,"shoeLaces": true} });

Creates and records a custom event for use in New Relic Insights.

recordCustomEvent(options: { eventType: string; eventName: string; attributes: object; }) => void
Param Type
options { eventType: string; eventName: string; attributes: object; }

Usage:

    NewRelicCapacitorPlugin.recordCustomEvent({ eventType: "mobileClothes", eventName: "pants", attributes:{"pantsColor": "blue","pantssize": 32,"belt": true} });

Track a method as an interaction.

startInteraction(options: { value: string; }) => Promise<{ value: string; }>
Param Type
options { value: string; }

Returns: Promise<{ value: string; }>


End an interaction (Required). This uses the string ID for the interaction you want to end. This string is returned when you use startInteraction().

endInteraction(options: { interactionId: string; }) => void
Param Type
options { interactionId: string; }

Usage:

 const badApiLoad = async () => {
   const id = await NewRelicCapacitorPlugin.startInteraction({ value: 'StartLoadBadApiCall' });
   console.log(id);
   const url = 'https://fakewebsite.com/moviessssssssss.json';
   fetch(url)
     .then((response) => response.json())
     .then((responseJson) => {
       console.log(responseJson);
       NewRelicCapacitorPlugin.endInteraction({ interactionId: id.value });
     }) .catch((error) => {
       NewRelicCapacitorPlugin.endInteraction({ interactionId: id.value });
       console.error(error);
     });
 };

Throws a demo run-time exception to test New Relic crash reporting.

crashNow(options?: { message: string; } | undefined) => void
Param Type
options { message: string; }

Usage:

    NewRelicCapacitorPlugin.crashNow();
    NewRelicCapacitorPlugin.crashNow({ message: "A demo crash message" });

Returns the current session ID. This method is useful for consolidating monitoring of app data (not just New Relic data) based on a single session definition and identifier.

currentSessionId(options?: {} | undefined) => Promise<{ sessionId: string; }>
Param Type
options {}

Returns: Promise<{ sessionId: string; }>

Usage:

    let { sessionId } = await NewRelicCapacitorPlugin.currentSessionId();

Increments the count of an attribute with a specified name. Overwrites its previous value and type each time it is called. If the attribute does not exists, it creates a new attribute. If no value is given, it increments the value by 1.

incrementAttribute(options: { name: string; value?: number; }) => void
Param Type
options { name: string; value?: number; }

Usage:

    NewRelicCapacitorPlugin.incrementAttribute({ name: 'CapacitorAttribute', value: 15 })

Manually records HTTP transactions, with an option to also send a response body.

noticeHttpTransaction(options: { url: string; method: string; status: number; startTime: number; endTime: number; bytesSent: number; bytesReceived: number; body: string; }) => void
Param Type
options { url: string; method: string; status: number; startTime: number; endTime: number; bytesSent: number; bytesReceived: number; body: string; }

Usage:

    NewRelicCapacitorPlugin.noticeHttpTransaction({
      url: "https://fakewebsite.com",
      method: "GET",
      status: 200,
      startTime: Date.now(),
      endTime: Date.now(),
      bytesSent: 10,
      bytesReceived: 2500,
      body: "fake http response body 200",
    });

Records custom metrics (arbitrary numerical data), where countUnit is the measurement unit of the metric count and valueUnit is the measurement unit for the metric value. If using countUnit or valueUnit, then all of value, countUnit, and valueUnit must all be set. Supported measurements for countUnit and valueUnit are: PERCENT, BYTES, SECONDS, BYTES_PER_SECOND, OPERATIONS

recordMetric(options: { name: string; category: string; value?: number; countUnit?: string; valueUnit?: string; }) => void
Param Type
options { name: string; category: string; value?: number; countUnit?: string; valueUnit?: string; }

Usage:

    NewRelicCapacitorPlugin.recordMetric({
      name: "CapacitorMetricName",
      category: "CapacitorMetricCategory",
    });
    NewRelicCapacitorPlugin.recordMetric({
      name: "CapacitorMetricName2",
      category: "CapacitorMetricCategory2",
      value: 25,
    });
    NewRelicCapacitorPlugin.recordMetric({
      name: "CapacitorMetricName3",
      category: "CapacitorMetricCategory3",
      value: 30,
      countUnit: NREnums.MetricUnit.SECONDS,
      valueUnit: NREnums.MetricUnit.OPERATIONS,
    });

Removes all attributes from the session

removeAllAttributes(options?: {} | undefined) => void
Param Type
options {}

Usage:

    NewRelicCapacitorPlugin.removeAllAttributes();

Sets the event harvest cycle length. Default is 600 seconds (10 minutes). Minimum value can not be less than 60 seconds. Maximum value should not be greater than 600 seconds.

setMaxEventBufferTime(options: { maxBufferTimeInSeconds: number; }) => void
Param Type
options { maxBufferTimeInSeconds: number; }

Usage:

    NewRelicCapacitorPlugin.setMaxEventBufferTime({ maxBufferTimeInSeconds: 60 });

Sets the maximum size of the event pool stored in memory until the next harvest cycle. Default is a maximum of 1000 events per event harvest cycle. When the pool size limit is reached, the agent will start sampling events, discarding some new and old, until the pool of events is sent in the next harvest cycle.

setMaxEventPoolSize(options: { maxPoolSize: number; }) => void
Param Type
options { maxPoolSize: number; }

Usage:

    NewRelicCapacitorPlugin.setMaxEventPoolSize({ maxPoolSize: 2000 })

FOR ANDROID ONLY. Enable or disable the collecton of event data. This is set to true by default.

analyticsEventEnabled(options: { enabled: boolean; }) => void
Param Type
options { enabled: boolean; }

Usage:

    NewRelicCapacitorPlugin.analyticsEventEnabled({ enabled: true })

Enable or disable reporting successful HTTP requests to the MobileRequest event type. This is set to true by default.

networkRequestEnabled(options: { enabled: boolean; }) => void
Param Type
options { enabled: boolean; }

Usage:

    NewRelicCapacitorPlugin.networkRequestEnabled({ enabled: true })

Enable or disable reporting network and HTTP request errors to the MobileRequestError event type.

networkErrorRequestEnabled(options: { enabled: boolean; }) => void
Param Type
options { enabled: boolean; }

Usage:

    NewRelicCapacitorPlugin.networkErrorRequestEnabled({ enabled: true })

Enable or disable capture of HTTP response bodies for HTTP error traces, and MobileRequestError events.

httpResponseBodyCaptureEnabled(options: { enabled: boolean; }) => void
Param Type
options { enabled: boolean; }

Usage:

    NewRelicCapacitorPlugin.httpResponseBodyCaptureEnabled({ enabled: true })

getAgentConfiguration(...)

Returns the current agent configuration settings. This method allows you to see the current state of the agent configuration.

getAgentConfiguration(options?: {} | undefined) => Promise<AgentConfiguration>
Param Type
options {}

Returns: Promise<AgentConfiguration>

Usage:

    import { NewRelicCapacitorPlugin, AgentConfiguration} from '@newrelic/newrelic-capacitor-agent';

    let agentConfig : AgentConfiguration = await NewRelicCapacitorPlugin.getAgentConfiguration();
    let sendConsoleEvents = agentConfig.sendConsoleEvents;

Error Reporting

recordError(...)

Records JavaScript/TypeScript errors for Ionic Capacitor. You should add this method to your framework's global error handler.

recordError(options: { name: string; message: string; stack: string; isFatal: boolean; }) => void
Param Type
options { name: string; message: string; stack: string; isFatal: boolean; }

Usage:

    try {
      throw new Error('Example error message');
    } catch (e: any) {
      NewRelicCapacitorPlugin.recordError({
        name: e.name,
        message: e.message,
        stack: e.stack,
        isFatal: false,
      });
    }

Angular

Angular 2+ exposes an ErrorHandler class to handle errors. You can implement New Relic by extending this class as follows:

import { ErrorHandler, Injectable } from '@angular/core';
import { NewRelicCapacitorPlugin } from "@newrelic/newrelic-capacitor-plugin";

@Injectable()
export class GlobalErrorHandler extends ErrorHandler {
  constructor() {
    super();
  }
  handleError(error: any): void {
    NewRelicCapacitorPlugin.recordError({
      name: error.name,
      message: error.message,
      stack: error.stack ? error.stack : "No stack available",
      isFatal: false,
    });
    super.handleError(error);
  }
}

Then, you'll need to let Angular 2 know about this new error handler by listing overrides for the provider in app.module.ts:

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule,HttpClientModule],
  providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },{provide: ErrorHandler, useClass: GlobalErrorHandler}],
  bootstrap: [AppComponent],
})

React

React 16+ has added error boundary components that catch errors that bubble up from child components. These are very useful for tracking and reporting errors to New Relic.

import React, { Component, ErrorInfo, ReactNode } from "react";
import { NewRelicCapacitorPlugin } from "@newrelic/newrelic-capacitor-plugin";

interface Props {
  children?: ReactNode;
}

interface State {
  hasError: boolean;
}

class ErrorBoundary extends Component<Props, State> {
  public state: State = {
    hasError: false,
  };

  public static getDerivedStateFromError(_: Error): State {
    // Update state so the next render will show the fallback UI.
    return { hasError: true };
  }

  public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    console.error("Uncaught error:", error, errorInfo);
    NewRelicCapacitorPlugin.recordError({
      name: error.name,
      message: error.message,
      stack: error.stack ? error.stack : "No stack available",
      isFatal: false,
    });
  }

  public render() {
    if (this.state.hasError) {
      // Render error messages or other components here.
      return;
    }

    return this.props.children;
  }
}

export default ErrorBoundary;

Vue

Vue has a global error handler that reports native JavaScript errors and passes in the Vue instance. This handler will be useful for reporting errors to New Relic.

import { NewRelicCapacitorPlugin } from "@newrelic/newrelic-capacitor-plugin";

Vue.config.errorHandler = (err, vm, info) => {

    // Record properties passed to the component if there are any
    if(vm.$options.propsData) {
        NewRelicCapacitorPlugin.recordBreadcrumb({
          name: "Props passed to component in error handler",
          eventAttributes: vm.$options.propsData
        });
    }

    // Get the lifecycle hook, if present
    let lifecycleHookInfo = 'none';
    if (info){
        lifecycleHookInfo = info;
    }

    // Record a breadcrumb with more details such as component name and lifecycle hook
    NewRelicCapacitorPlugin.recordBreadcrumb({
      name: "Vue Error",
      eventAttributes: {
        componentName: vm.$options.name,
        lifeCycleHook: lifecycleHookInfo
      }
    });

    // Record the JS error to New Relic
    NewRelicCapacitorPlugin.recordError({
      name: err.name,
      message: err.message,
      stack: err.stack ? err.stack : "No stack available",
      isFatal: false,
    });
}

How to see JSErrors(Fatal/Non Fatal) in NewRelic One?

There is no section for JavaScript errors, but you can see JavaScript errors in custom events and also query them in NRQL explorer.

Screen Shot 2022-02-10 at 12 41 11 PM

You can also build dashboard for errors using this query:

SELECT jsAppVersion,name,Message,errorStack,isFatal FROM `JS Errors` SINCE 24 hours ago

Contribute

We encourage your contributions to improve newrelic-capacitor-plugin! Keep in mind that when you submit your pull request, you'll need to sign the CLA via the click-through using CLA-Assistant. You only have to sign the CLA one time per project.

If you have any questions, or to execute our corporate CLA (which is required if your contribution is on behalf of a company), drop us an email at [email protected].

A note about vulnerabilities

As noted in our security policy, New Relic is committed to the privacy and security of our customers and their data. We believe that providing coordinated disclosure by security researchers and engaging with the security community are important means to achieve our security goals.

If you believe you have found a security vulnerability in this project or any of New Relic's products or websites, we welcome and greatly appreciate you reporting it to New Relic through HackerOne.

If you would like to contribute to this project, review these guidelines.

Support

New Relic hosts and moderates an online forum where customers can interact with New Relic employees as well as other customers to get help and share best practices. Like all official New Relic open source projects, there's a related Community topic in the New Relic Explorers Hub. You can find this project's topic/threads here:

https://discuss.newrelic.com/tags/mobile

License

This project is distributed under the Apache 2 license.

newrelic-capacitor-plugin also uses source code from third-party libraries. You can find full details on which libraries are used and the terms under which they are licensed in the third-party notices document.

About

Mobile instrumentation plugin for the Capacitor hybrid mobile platform by Ionic

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Swift 36.6%
  • Java 30.0%
  • TypeScript 24.1%
  • CSS 5.1%
  • Objective-C 1.7%
  • Ruby 1.4%
  • Other 1.1%