Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 85 additions & 3 deletions packages/nativescript-appdynamics/common.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { HttpErrorResponse } from '@angular/common/http';

interface SessionFrame {
updateName(name: string): void;
}
Expand All @@ -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 {
Expand All @@ -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;
}
44 changes: 41 additions & 3 deletions packages/nativescript-appdynamics/index.android.ts
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -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);
Expand All @@ -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<string, java.util.List<string>>();

headerKeys.forEach((key) => {
const stringList = new java.util.ArrayList<string>();
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();
}
}
11 changes: 9 additions & 2 deletions packages/nativescript-appdynamics/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AppdynamicsConfiguration } from '@essent/nativescript-appdynamics/common';
import { AppdynamicsConfiguration } from './common';

declare interface SessionFrame {
updateName(name: string): void;
Expand All @@ -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;
}
39 changes: 36 additions & 3 deletions packages/nativescript-appdynamics/index.ios.ts
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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);
}
Expand All @@ -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();
}
}