diff --git a/packages/nativescript-appdynamics/common.ts b/packages/nativescript-appdynamics/common.ts index 92623cc..f611258 100644 --- a/packages/nativescript-appdynamics/common.ts +++ b/packages/nativescript-appdynamics/common.ts @@ -1,3 +1,5 @@ +import { HttpErrorResponse } from '@angular/common/http'; + interface SessionFrame { updateName(name: string): void; } @@ -16,10 +18,83 @@ export enum LoggingLevel { // the app key can be created from the AppDynamics control panel when adding a new app. // The app key should be of format "AD-AAA-BBB", AppDynamics will error if it's malformed. export interface AppdynamicsConfiguration { + /** + * Sets the application key used by the SDK. This is a required property. + */ appKey: string; - collectorURL: string; // should be the 'EUM Cloud' / 'EUM Collector' address - screenshotURL: string; // should be the 'Screenshot Service' address - loggingLevel?: LoggingLevel; // defaults to 'Error' + + /** + * Sets the name of this mobile application. If not set, the application name is the + * package name from the Android Manifest. Most users will not need this. + * + * The `applicationName` may contain uppercase or lowercase letters ('A' through 'Z'), numbers, + * and underscores ('_'). + * + * **NOTE:** If this property is set, all data reported from this application is associated with `applicationName`, + * and appears together in dashboards. + */ + applicationName?: string; + + /** + * Boolean value that indicates if automatic instrumentation is enabled. + * + * Setting this to `false` will disable all automatic instrumentation regardless of any code injection. + * The following features will also be effected: + * + * 1. {@link interactionCaptureMode} will be set to {@link InteractionCaptureMode.None} + * 2. Automatic Network requests from supported libraries will not be tracked. + * 3. Any UI transition events will not be tracked. + * 4. Screenshots (both automatic and manual) will be disabled. + * + * Set to `false` to disable automatic instrumentation, `true` to enable (default is enabled) + */ + autoInstrument?: boolean; + + /** + * Sets the URL of the collector to which the agent will report. + * + * This is NOT your controller URL. You likely do not need to call this method + * unless you have an on-premise EUM Processor. + */ + collectorURL?: string; // should be the 'EUM Cloud' / 'EUM Collector' address + /** + * Sets the URL of the screenshot service to which the agent will upload screenshots. + * + * This is NOT your controller URL. You likely do not need to call this method + * unless you have an AppDynamics managed private cloud (very rare). + * + * **NOTE:** If you have an on-premise EUM Processor + * and set the collector URL in {@link collectorURL}, + * then you do not need to call this method + * because the two URLs are the same, + * and the agent assumes that is the case. + */ + screenshotURL?: string; // should be the 'Screenshot Service' address + /** + * Sets the logging level of the agent. Default is {@link LoggingLevel.Off}. + * + * **WARNING:** Not recommended for production use. + */ + loggingLevel?: LoggingLevel; // defaults to 'Off' + + /** + * Enables or disables JavaScript agent AJAX call reporting. + * If enabled, JavaScript agent will capture AJAX calls in WebViews. + * + * Default is disabled. + * JS Agent injection needs to be enabled for this to have an effect + * See {@link jsAgentInjectionEnabled} + * @param enable true to enable JS AJAX reporting, false to disable + */ + jsAgentAjaxEnabled?: boolean; + + /** + * Enables or disables JavaScript agent injection into WebViews. + * If enabled, JavaScript agent will capture page loads in WebViews. + * + * Default is enabled. + */ + jsAgentInjectionEnabled?: boolean; } export interface IAppdynamics { @@ -31,3 +106,10 @@ export interface IAppdynamics { setUserData(key: string, value: string): void; requestTracker(value: string): void; } + +export interface IRequestTracker { + setError(error: HttpErrorResponse, domain: string): void; + setStatusCode(statusCode: number); + setHeaders(headers: { [key: string]: string[] | null }); + reportDone(): void; +} diff --git a/packages/nativescript-appdynamics/index.android.ts b/packages/nativescript-appdynamics/index.android.ts index da3de9e..1e872d5 100644 --- a/packages/nativescript-appdynamics/index.android.ts +++ b/packages/nativescript-appdynamics/index.android.ts @@ -1,5 +1,6 @@ +import { HttpErrorResponse } from '@angular/common/http'; import { Utils } from '@nativescript/core'; -import { AppdynamicsConfiguration, IAppdynamics, LoggingLevel } from './common'; +import { AppdynamicsConfiguration, IAppdynamics, LoggingLevel, IRequestTracker } from './common'; import lazy from '@nativescript/core/utils/lazy'; const Instrumentation = lazy(() => com.appdynamics.eumagent.runtime.Instrumentation); @@ -13,7 +14,11 @@ export class Appdynamics implements IAppdynamics { .withContext(Utils.android.getApplicationContext()) .withCollectorURL(config.collectorURL) .withScreenshotURL(config.screenshotURL) - .withLoggingLevel(config.loggingLevel || LoggingLevel.Error) + .withLoggingLevel(config.loggingLevel || LoggingLevel.Off) + .withApplicationName(config.applicationName) + .withJSAgentAjaxEnabled(config.jsAgentAjaxEnabled) + .withJSAgentInjectionEnabled(config.jsAgentInjectionEnabled) + .withAutoInstrument(config.autoInstrument) .build(); Instrumentation().start(instrumentationConfig); @@ -40,6 +45,39 @@ export class Appdynamics implements IAppdynamics { } public requestTracker(url: string) { - return Instrumentation().beginHttpRequest(new java.net.URL(url)); + return new RequestTracker(url); + } +} + +export class RequestTracker implements IRequestTracker { + private _tracker; + + constructor(url) { + this._tracker = Instrumentation().beginHttpRequest(new java.net.URL(url)); + } + + setError(error: HttpErrorResponse, domain: string) { + this._tracker = this._tracker.withError(error.message); + } + + setHeaders(httpHeaders: { [key: string]: string[] | null }) { + const headerKeys = Object.keys(httpHeaders); + const values = new java.util.HashMap>(); + + headerKeys.forEach((key) => { + const stringList = new java.util.ArrayList(); + httpHeaders[key].forEach((headerValue) => stringList.add(headerValue)); + values.put(key, stringList); + }); + + this._tracker = this._tracker.withResponseHeaderFields(values); + } + + setStatusCode(statusCode) { + this._tracker = this._tracker.withResponseCode(statusCode); + } + + reportDone(): void { + this._tracker.reportDone(); } } diff --git a/packages/nativescript-appdynamics/index.d.ts b/packages/nativescript-appdynamics/index.d.ts index 42889a5..a1b6253 100644 --- a/packages/nativescript-appdynamics/index.d.ts +++ b/packages/nativescript-appdynamics/index.d.ts @@ -1,4 +1,4 @@ -import { AppdynamicsConfiguration } from '@essent/nativescript-appdynamics/common'; +import { AppdynamicsConfiguration } from './common'; declare interface SessionFrame { updateName(name: string): void; @@ -12,5 +12,12 @@ export declare class Appdynamics { startTimer(name: string): void; stopTimer(name: string): void; setUserData(key: string, value: string): void; - requestTracker(value: string): void; + requestTracker(value: string): RequestTracker; +} + +export declare class RequestTracker { + setError(error: HttpErrorResponse, domain: string): void; + setStatusCode(statusCode: number); + setHeaders(headers: { [key: string]: string[] | null }); + reportDone(): void; } diff --git a/packages/nativescript-appdynamics/index.ios.ts b/packages/nativescript-appdynamics/index.ios.ts index e6623c6..7dee48c 100644 --- a/packages/nativescript-appdynamics/index.ios.ts +++ b/packages/nativescript-appdynamics/index.ios.ts @@ -1,4 +1,5 @@ -import { AppdynamicsConfiguration, IAppdynamics, LoggingLevel } from './common'; +import { HttpErrorResponse } from '@angular/common/http'; +import { AppdynamicsConfiguration, IAppdynamics, LoggingLevel, IRequestTracker } from './common'; // https://docs.appdynamics.com/appd/21.x/21.9/en/end-user-monitoring/mobile-real-user-monitoring/instrument-ios-applications/customize-the-ios-instrumentation @@ -7,7 +8,11 @@ export class Appdynamics implements IAppdynamics { const adeumConfig = ADEumAgentConfiguration.alloc().initWithAppKey(config.appKey); adeumConfig.collectorURL = config.collectorURL; adeumConfig.screenshotURL = config.screenshotURL; - adeumConfig.loggingLevel = (config.loggingLevel || LoggingLevel.Error) as unknown as ADEumLoggingLevel; + adeumConfig.loggingLevel = (config.loggingLevel || LoggingLevel.Off) as unknown as ADEumLoggingLevel; + adeumConfig.applicationName = config.applicationName; + adeumConfig.jsAgentAjaxEnabled = config.jsAgentAjaxEnabled; + adeumConfig.jsAgentEnabled = config.jsAgentInjectionEnabled; + adeumConfig.enableAutoInstrument = config.autoInstrument; ADEumInstrumentation.initWithConfiguration(adeumConfig); } @@ -33,6 +38,34 @@ export class Appdynamics implements IAppdynamics { } public requestTracker(value: string) { - return ADEumHTTPRequestTracker.requestTrackerWithURL(NSURL.URLWithString(value)); + return new RequestTracker(value); + } +} + +export class RequestTracker implements IRequestTracker { + private _tracker; + + constructor(url) { + this._tracker = ADEumHTTPRequestTracker.requestTrackerWithURL(NSURL.URLWithString(url)); + } + + setError(error: HttpErrorResponse, domain: string) { + this._tracker.error = NSError.new().initWithDomainCodeUserInfo(domain, error.status, NSDictionary.dictionaryWithObjectForKey('Error reason', error.message)); + } + + setHeaders(headers: { [key: string]: string[] | null }) { + const values: string[][] = []; + const headerKeys = Object.keys(headers); + headerKeys.forEach((value, index) => (values[index] = headers[value])); + + this._tracker.allHeaderFields = NSDictionary.dictionaryWithObjectsForKeys(values, headerKeys); + } + + setStatusCode(statusCode) { + this._tracker.statusCode = statusCode; + } + + reportDone(): void { + this._tracker.reportDone(); } }