iOS インストルメンテーションのカスタマイズ
モバイル iOS SDK を使用して iOS アプリケーションをインストゥルメント化すると、SDK が公開する API を使用して、コントローラ UI に表示されるアプリケーションのデータをカスタマイズすることもできます。
以下のセクションでは、iOS SDK を使用してインストルメンテーションをカスタマイズする方法について説明します。
- データの追加タイプの収集
- ユーザインタラクションのキャプチャ
- プログラムによるスクリーンショットの取得
- スクリーンショットのブロックとブロック解除
- コレクタへのユーザデータの送信を停止する場合のエージェントの無効化
- クラッシュ レポート コールバックの追加
- クラッシュレポートの無効化
- エラーと例外のレポート
- Application-Not-Responding(ANR)検出の構成
- ハイブリッド アプリケーション サポートの構成
- プログラムによるセッションの制御
- セッションフレームの開始と終了
- カスタムアプリケーション名のエージェントの構成
- 一部の HTTP リクエストを無視するためのエージェントの構成
- カスタム HTTP ライブラリでのエージェントの使用
- ネットワークリクエストに対応した URL の変換
- ロギングの有効化とロギングレベルの設定
- iOS SDK のドキュメント
データの追加タイプの収集
ADEUMInstrumentation クラスで使用可能なメソッドを使用して、6 つの追加タイプのデータを収集できます。
| データのタイプ | 説明 | 仕様 | データが表示される場所 |
|---|---|---|---|
| 情報ポイント | メソッドが呼び出される頻度と実行される時間。 |
| |
| カスタムタイマー | コード内の任意のイベントシーケンスが、複数のメソッドにまたがる場合でも、時間を計測。 |
| |
| カスタムメトリック | 収集する整数ベースのデータ。 |
| |
| ユーザデータ | 有用と思われる任意の文字列キーと値のペアこのデータは、消去されるまで、リストされるすべてのインストルメンテーション タイプに含まれます。 |
| |
| トピック パス(パンくずリスト) | クラッシュのコンテキスト。 |
| |
| ユーザの操作 | ユーザがボタンを押したとき、リストをクリックしたとき、およびテキストを選択したときにキャプチャ。 |
|
情報ポイント
- Objective-C
-
- (void)myMethod { id tracker = [ADEumInstrumentation beginCall:self selector:_cmd]; // Implementation of method here ... [ADEumInstrumentation endCall:tracker]; } - Swift
-
func myMethod() { let tracker = ADEumInstrumentation.beginCall(self, selector: #function) // Implementation of method here ... ADEumInstrumentation.endCall(tracker) }
カスタムタイマー
startTimer と stopTimer を使用して、コード内の任意のイベントシーケンスの時間を測定できます。たとえば、ユーザーが画面を表示するのにかかった時間を追跡する場合、インストルメンテーションは次のようになります。 - Objective-C
-
- (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [ADEumInstrumentation startTimerWithName:@"View Lifetime"]; } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; [ADEumInstrumentation stopTimerWithName:@"View Lifetime"]; } - Swift
-
func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) ADEumInstrumentation.startTimer(withName: "View Lifetime") } func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) ADEumInstrumentation.stopTimer(withName: "View Lifetime") }
この情報は、コントローラ UI の [Custom Data] ビューに表示されます。
startTimerWithName を再度呼び出すと、名前付きタイマーがリセットされます。Custom Metrics
任意の整数ベースのデータをエージェントに渡すことができます。report.MetricWithName コールの最初のパラメータは、メトリックをコントローラ UI に表示する場合の名前です。メトリック名には、英数字とスペースのみを使用します。不正な文字は、ASCII 16 進値に置き換えられます。
たとえば、「My custom metric」というメトリックを報告する場合は、次のようになります。
[ADEumInstrumentation reportMetricWithName:@"My custom metric" value:<#VALUE HERE#>];
この情報は、コントローラ UI の [Custom Data] ビューに表示されます。
Custom User Data
次のメソッドで使用する任意の文字列キーおよび値のペアを設定し、後で削除することができます。
-
setUserData(key, value, success, error) -
removeUserData(key)
これが設定されると、ユーザーデータは、ネットワークリクエスト、セッション、またはクラッシュのインストルメンテーションとともに引き続き送信されます。以前に設定されたユーザーデータはキーごとに削除できます。clearAllUserData()または、 を使用して、すべてのキーの以前に設定されたユーザーデータを削除できます。
パラメータ
次の表で、パラメータについて説明します。
| Name | タイプ | 説明 |
|---|---|---|
| キー | 文字列 | キーと値のペアを識別するキー。 |
| 値 | 文字列 | キーに関連付けられている値。 |
| success | function | ユーザ定義のコールバックの成功例。 |
| error | function | ユーザ定義のコールバックの失敗例。 |
例
- Objective-C
-
- (void) onUserLoggedIn:(NSString *)userid { [ADEumInstrumentation setUserData:@"User ID" value:userid]; ... } - Swift
-
func onUserLogged(in userid: String?) { ADEumInstrumentation.setUserData("User ID", value: userid) }
- Objective-C
-
- (void) onUserLoggedIn:(NSString *)userid { [ADEumInstrumentation removeUserData:@"User ID"]; ... } - Swift
-
func onUserLogged(in userid: String?) { ADEumInstrumentation.removeUserData("User ID") ... }
clearAllUserData() について
clearAllUserData() API は、一度にすべてのユーザーデータ(上に列挙されたすべてのタイプのデータ)を消去します。上記の API の Splunk AppDynamics setUserData() リストで設定された項目の範囲外にある他のデータは消去されません。また、以前に送信用にキューに入れられていた既存のインストルメンテーション ビーコンにすでに付加されているユーザーデータは削除されず、ネットワーク リクエスト インストルメンテーションにリクエストごとに付加されたユーザーデータには影響しません。
例
clearAllUserData SDK API を使用する方法を示しています。 - Objective-C
-
- (void) onUserLoggedOut { [ADEumInstrumentation clearAllUserData]; ... } - Swift
-
func onUserLoggedOut() { ADEumInstrumentation.clearAllUserData() ... }
トピックパス(パンくずリスト)
トピックパスを使用すると、ユーザエクスペリエンスのコンテキストでクラッシュの場所を特定できます。問題が発生したときに、トピックパスを設定します。その後のある時点でアプリケーションがクラッシュした場合、トピックパスはクラッシュレポートとともに表示されます。
トピックパスを残すには、次の 2 つの方法があります。
次のメソッドを使用すると、トピックパスがクラッシュレポートのみで報告されます。
+ (void)leaveBreadcrumb:(NSString *)breadcrumb
次のメソッドを使用すると、トピックパスが報告される場所を微調整(クラッシュレポートのみ、またはクラッシュレポートとセッションを選択)できます。
+ (void)leaveBreadcrumb:(NSString *)breadcrumb mode:(ADEumBreadcrumbVisibility)mode
mode は次のいずれかです。
-
ADEumBreadcrumbVisibilityCrashesOnly -
ADEumBreadcrumbVisibilityCrashesAndSessions
ユーザインタラクションのキャプチャ
iOSエージェントを有効にして、ユーザーインタラクションによってトリガーされた特定のUIイベントを追跡できます。ユーザインタラクションがキャプチャされると、UI イベントでセッションをソートし、セッション ウォーターフォールのタイムラインで UI イベントを表示できます。
ユーザが次のいずれかまたはすべてを実行するときにキャプチャできます。
- ボタンのクリック
- テーブルセルの選択
- テキストフィールドの選択
- テキストビューの選択
インタラクション キャプチャ モードは、セキュリティとプライバシー上の理由でデフォルトでは無効になっています。これは、ユーザインタラクションに機密情報が含まれている可能性があるためです。さらに、UI インタラクションとスクリーンショットのキャプチャの両方を有効にすると、このような潜在的なセキュリティとプライバシー上の問題が複合化する場合があります。
ユーザ インタラクション キャプチャ モードの有効化
ユーザ ^インタラクション キャプチャ モードを有効にするには、ADEumAgentConfiguration オブジェクトの interactionCaptureMode プロパティにキャプチャモードを割り当てます。次のインストルメンテーション コードの例では、サポートされているすべてのタイプのユーザーインタラクションをキャプチャするように iOS エージェントを構成します。
ADEumAgentConfiguration *config = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>];
config.interactionCaptureMode = ADEumInteractionCaptureModeAll;
[ADEumInstrumentation initWithConfiguration:config];
また、1 つのタイプのユーザインタラクションのみをキャプチャするように iOS エージェントを構成することもできます。
ADEumAgentConfiguration *config = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>];
config.interactionCaptureMode = ADEumInteractionCaptureModeButtonPressed;
[ADEumInstrumentation initWithConfiguration:config];
プログラムによるスクリーンショットの取得
- Objective-C
-
[ADEumInstrumentation takeScreenshot]; - Swift
-
ADEumInstrumentation.takeScreenshot()
スクリーンショットの無効化
- Objective-C
-
ADEumAgentConfiguration *config = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>]; config.screenshotsEnabled = NO; [ADEumInstrumentation initWithConfiguration:config]; - Swift
-
let config = ADEumAgentConfiguration(appKey: <#EUM_APP_KEY#>); config.screenshotsEnabled = false; ADEumInstrumentation.initWith(config);
スクリーンショットのブロックとブロック解除
また、iOS SDK を使用して、コードブロックの実行中にスクリーンショットの実行をブロックできます。これにより、スクリーンショットのブロックを解除するまで、スクリーンショットの作成が一時的にブロックされます。これにより、ユーザがログインやアカウント画面などで個人データを入力する状況でのスクリーンショットの作成を停止できます。
ADEumInstrumentation クラスでは、スクリーンショットをブロックおよびブロック解除するためのメソッド(blockScreenshots と unblockScreenshots)が使用できます。ADEumAgentConfiguration オブジェクトの screenshotsEnabled プロパティまたはコントローラ UI によってスクリーンショットが無効になっている場合、これらのメソッドは無効になります。また、screenshotsBlocked を呼び出して、スクリーンショットがブロックされているか確認できます。
- Objective-C
-
#import "ADEumInstrumentation.h" ... - (IBAction)loginUser:(id)sender { if(![ADEumInstrumentation screenshotsBlocked]) { [ADEumInstrumentation blockScreenshots]; } LoginCredentials creds = [UserLogin getUserCreds]; if(creds.authorized) { [LoginUser redirectToProfile:creds.user] [ADEumInstrumentation unblockScreenshots]; } } ... - Swift
-
import ADEumInstrumentation ... @IBAction func loginUser(_ sender: UIButton) { if(!ADEumInstrumentation.screenshotsBlocked()) { ADEumInstrumentation.blockScreenshots() } let creds = UserLogin.getUserCreds() if(creds.authorized) { LoginUser.redirectToProfile(credits.user) ADEumInstrumentation.unblockScreenshots() } } ...
コレクタへのユーザデータの送信を停止する場合のエージェントの無効化
エージェントの初期化中および実行中に、エージェントを無効にしてコレクタへのすべてのデータの送信を停止できます。たとえば、プライバシー上の理由でユーザがモニタリングをオプトアウトするオプションがアプリにある場合は、エージェントを無効にできます。
shutdownAgent
- Objective-C
-
[ADEumInstrumentation shutdownAgent]; - Swift
-
ADEumInstrumentation.shutdownAgent()
- このコールは、エージェントからのトラフィックのみを停止します。
- エージェントが初期化されると、コールは削除できず、ライセンスが消費されます。
- この状態をデバイスで永続的にする場合は、UserDefaults にコードを追加して状態を保存し、そのフラグを使用してコード内のエージェントを条件付きで初期化します。
restartAgent
- Objective-C
-
[ADEumInstrumentation restartAgent]; - Swift
-
ADEumInstrumentation.restartAgent();
- このコールは、同様にリモートでエージェントをシャットダウンできるサーバ側のコールにも対応します。
- コールは、アプリケーションの実行中にのみ有効です。
- エージェントがリモートで無効になっている場合、コールは無視されます。
- コールがメモリから削除され、アプリケーションが再起動されるか、デバイスが再起動されると、エージェントは通常どおり初期化されます。
クラッシュ レポート コールバックの追加
モバイル RUM を使用している場合、コードの他の部分(Google Analytics など)がモバイル RUM が収集するクラッシュレポート情報を使用できるようにすることがあります。サマリークラッシュ情報を渡せるようにするには、クラッシュレポートのランタイムコールバックを設定します。iOS エージェントがクラッシュを検出して報告するときにコールバックを取得するには、コードに次のプロトコルを実装する必要があります。
@protocol ADEumCrashReportCallback <NSObject>
- (void)onCrashesReported:(NSArray<ADEumCrashReportSummary *> *)crashReportSummaries;
@end渡される各 ADEumCrashReportSummary には、次のプロパティがあります。
@interface ADEumCrashReportSummary : NSObject
/** Uniquely defines the crash, can be used as key to find full crash report. */
@property (nonatomic, readonly) NSString *crashId;
/** The exception name, may be `nil` if no `NSException` occured. */
@property (nonatomic, readonly) NSString * ADEUM_NULLABLE exceptionName;
/** The exception reason, may be `nil` if no `NSException` occured. */
@property (nonatomic, readonly) NSString * ADEUM_NULLABLE exceptionReason;
/** The Mach exception signal name */
@property (nonatomic, readonly) NSString *signalName;
/** The Mach exception signal code */
@property (nonatomic, readonly) NSString *signalCode;
@endGoogle Analytics などの別の分析ツールに情報を送信する場合は、次の 5 つのプロパティすべてを含めることを推奨します。
- exceptionNameと exceptionReason はオプションであり、クラッシュの原因をすばやく特定するのに役立ちます。これらは、例外レポートランタイム(Objective-C など)内でクラッシュ原因が発生した場合にのみ表示されます。
- signalName と signalCode は、クラッシュをすばやく特定するのに役立ちます。これらはシステムから取得するものであり、ランタイムとは独立しています。
-
その他の情報については、crashId を使用すると、コントローラ UI でクラッシュを検索できます。
たとえば、クラッシュ情報を iOS ロガーに出力するために、次のような ADEumCrashReportCallback クラスを実装できます。
// assumes the containing object has "adopted" the protocol
- (void)onCrashesReported:(NSArray<ADEumCrashReportSummary *> *)summaries {
for (ADEumCrashReportSummary *summary in summaries) {
NSLog(@"Crash ID: %@", summary.crashId);
NSLog(@"Signal: %@ (%@)", summary.signalName, summary.signalCode);
NSLog(@"Exception Name:\n%@", summary.exceptionName);
NSLog(@"Exception Reason:\n%@", summary.exceptionReason);
}
}エージェントの構成時に、ADEumCrashReportCallback プロトコルを実装するオブジェクトを設定します。
ADEumAgentConfiguration *config = [ADEumAgentConfiguration new];
config.crashReportCallback = myCrashReportCallback;前回の実行からクラッシュの検出と収集が行われた場合、メイン/UI スレッドでコールバックが呼び出されます。最新の iOS SDK ドキュメントを参照してください。
クラッシュレポートの無効化
クラッシュレポートはデフォルトで有効になっていますが、インストルメンテーション構成を使用して手動でクラッシュレポートを無効にできます。他のクラッシュレポートツールを使用している場合、競合を最小限に抑え、クラッシュレポートの結果を最適化するために、クラッシュレポートを無効にする場合があります。
crashReportingEnabled プロパティを使用してインストルメンテーションを設定することにより、クラッシュレポートを無効にできます。 - Objective-C
ADEumAgentConfiguration *config = [[ADEumAgentConfiguration alloc] initWithAppKey:appKey]; config.crashReportingEnabled = No [ADEumInstrumentation initWithConfiguration:config];
- Swift
let config = ADEumAgentConfiguration(appKey: <#EUM_APP_KEY#>); config.crashReportingEnabled = false; ADEumInstrumentation.initWith(config);
エラーと例外のレポート
に使用される標準 オブジェクトの初期化に使用される domain: String では、ハッシュ、StackTraceID、または ThreadID などの一意の文字列を使用しないでください。これらの文字列を含めると、Error Group が無数に作成され、パフォーマンスに影響を与えます。
一意のエラー文字列と値を追加するには、次の 2 つのデータ収集オプションがあります。
- トピックパスとして文字列値を追加します。報告するイベント/エラーに単一の値が添付されている場合は、その値を leaveBreadcrumb に追加します。「トピックパス(パンくずリスト)」を参照してください
- 文字列名と整数値を追加します。報告するイベント/エラーに名前と整数値の両方が添付されている場合は、その値を
reportMetricWithNameに追加します。「カスタムメトリック」を参照してください。
クラスの ADEumInstrumentation メソッドを使用して例外を報告できます。報告された例外は、セッション詳細に表示されます。
このメソッドには、次の 2 つの署名があります。
| Objective-C 関数の署名 | 説明 |
|---|---|
(void)reportError:(NSError *)error withSeverity (ADEumErrorSeverityLevel)severity; |
この署名を使用してエラーを報告し、問題の重大度レベルを設定し、スタックトレースを送信します。 この関数の署名は、デフォルトでスタックトレースを送信します。スタックトレースを送信しない場合は、次の関数の署名を追加の引数 とともに使用し、その値を に設定します。 警告: に使用される標準 オブジェクトの初期化に使用される
domain: String では、ハッシュ、StackTraceID、または ThreadID などの一意の文字列を使用しないでください。これらの文字列を含めると、Error Group が無数に作成され、パフォーマンスに影響を与えます。一意のエラー文字列と値を追加するには、次の 2 つのデータ収集オプションがあります。
|
(void)reportError:(NSError *)error withSeverity:(ADEumErrorSeverityLevel)severity andStackTrace:(BOOL)stacktrace; |
この署名を使用してエラーを報告し、問題の重大度レベルを設定し、スタックトレースを含めるかどうかを明示的に指定します。 stacktrace を YES に設定して、報告されるエラーにスタックトレースを含めると、[Code Issues Details] ダイアログにスタックトレースを表示できます。 スタックトレースなしでエラーを報告するには、 を に設定します。 警告: reportError に使用される新しい throwable の構成に使用される文字列メッセージでは、ハッシュ、StackTraceID、または ThreadID などの一意の文字列を使用しないでください。これらの文字列を含めると、Error Group が無数に作成され、パフォーマンスに影響を与えます。
一意のエラー文字列と値を追加するには、次の 2 つのデータ収集オプションがあります。
|
重要度レベル
問題に次の重大度レベルのいずれかを設定することもできます。シビラティ(重大度)レベルを使用すると、[Code Issues Dashboard] または [Code Issues Analyze] でエラーをフィルタリングできます。
-
ADEumErrorSeverityLevelInfo -
ADEumErrorSeverityLevelWarning -
ADEumErrorSeverityLevelCritical
エラーレポートの例
ADEumErrorSeverityLevelCritical に設定します。 - Objective-C
-
NSError *err = nil; [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"pathToFile" error:&err]; if (err) { [ADEumInstrumentation reportError:err withSeverity:ADEumErrorSeverityLevelCritical, andStackTrace: NO]; } else { ... } - Swift
-
var err: Error? = nil try? FileManager.default.contentsOfDirectory(atPath: "pathToFile") if err != nil { ADEumInstrumentation.reportError(err, withSeverity: ADEumErrorSeverityLevelCritical, andStackTrace: false) } else { ... }
domain: String では、ハッシュ、StackTraceID、または ThreadID などの一意の文字列を使用しないでください。これらの文字列を含めると、Error Group が無数に作成され、パフォーマンスに影響を与えます。一意のエラー文字列と値を追加するには、次の 2 つのデータ収集オプションがあります。
- トピックパスとして文字列値を追加します。報告するイベント/エラーに単一の値が添付されている場合は、その値を leaveBreadcrumb に追加します。「トピックパス(パンくずリスト)」を参照してください。
- 文字列名と整数値を追加します。報告するイベント/エラーに名前と整数値の両方が添付されている場合は、その値を reportMetric に追加します。「カスタムメトリック」を参照してください。
- Objective-C
-
NSString *domain = @"com.YourCompany.AddUsers.ErrorDomain"; NSString *desc = NSLocalizedString(@"Unable to add user.", @""); NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc }; NSError *error = [NSError errorWithDomain:domain code:-101 userInfo:userInfo]; [ADEumInstrumentation reportError:error withSeverity: ADEumErrorSeverityLevelWarning]; - Swift
-
var domain = "com.YourCompany.AddUsers.ErrorDomain" var desc = NSLocalizedString("Unable to add user.", comment: "") var userInfo = [NSLocalizedDescriptionKey: desc] var error = NSError(domain: domain, code: -101, userInfo: userInfo) ADEumInstrumentation.reportError(error, withSeverity: ADEumErrorSeverityLevelWarning)
Application-Not-Responding(ANR)検出の構成
デフォルトでは、iOS エージェントは ANR 問題を検出しません。また、ANR 検出が有効になっている場合、ANR 問題はスタックトレースなしで報告されます。手動で ANR 検出を有効にし、iOS エージェント構成を使用してスタックトレースを含めるようにフラグを設定する必要があります。ANR モニタリングの詳細については、「コードの問題」を参照してください。ANR の問題のしきい値を指定するには、「アプリケーションが応答しないしきい値の設定」を参照してください。
ANR 検出の有効化
anrDetectionEnabled プロパティを使用してインストルメンテーションを構成することによって、ANR の問題の検出を有効にします。 - Objective-C
-
ADEumAgentConfiguration *adeumAgentConfig = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>]; // Enable ANR detection adeumAgentConfig.anrDetectionEnabled = YES; [ADEumInstrumentation initWithConfiguration:adeumAgentConfig]; - Swift
-
let config = ADEumAgentConfiguration(appKey: <#EUM_APP_KEY#>); // Enable ANR detection config.anrDetectionEnabled = true; ADEumInstrumentation.initWith(config);
ANR を使用したスタックトレースのレポート
anrStackTraceEnabled を [はい(Yes)](Objective-C)または [true](Swift)に設定して、ANR でスタックトレースを報告します。 - Objective-C
-
ADEumAgentConfiguration *adeumAgentConfig = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>]; // Enable ANR detection adeumAgentConfig.anrDetectionEnabled = YES; // Set the flag to include stack traces with ANRs adeumAgentConfig.anrStackTraceEnabled = YES; [ADEumInstrumentation initWithConfiguration:adeumAgentConfig]; - Swift
-
let config = ADEumAgentConfiguration(appKey: <#EUM_APP_KEY#>) // Enable ANR detection config.anrDetectionEnabled = true // Set the flag to include stack traces with ANRs config.anrStackTraceEnabled = true ADEumInstrumentation.initWith(config)
ハイブリッド アプリケーション サポートの構成
デフォルトでは、iOS エージェントは iOS WKWebView をインストルメント化しますが、Ajax コールを収集して報告することはありません。この機能の概要と説明については、「ハイブリッド アプリケーションのサポート」を参照してください。
ハイブリッド アプリケーション サポートを無効にしたり、動作を変更したりするには、静的またはランタイム構成を設定します。次のセクションでは、ランタイム構成または静的構成のいずれかを使用して、ハイブリッドサポートのデフォルト設定を変更する方法を示します。
ハイブリッド アプリケーション サポートのランタイム構成
次のコード例では、JavaScript エージェントのインジェクションを無効にしています。インジェクションを無効にすると、アプリケーション内の WKWebView はインストルメント化されず、Ajax コールは報告されません。
ADEumAgentConfiguration *adeumAgentConfig = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>];
// Disable the JavaScript Agent Injection
adeumAgentConfig.jsAgentEnabled = NO;
[ADEumInstrumentation initWithConfiguration:adeumAgentConfig];
JavaScript エージェントのインジェクションはデフォルトで有効になっています。Ajax コールの収集とレポートも有効にするには、次のようにします。
ADEumAgentConfiguration *adeumAgentConfig = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>];
// Enable the collection and reporting of Ajax calls
adeumAgentConfig.jsAgentAjaxEnabled = YES;
[ADEumInstrumentation initWithConfiguration:adeumAgentConfig];
ハイブリッド アプリケーション サポートの静的構成
次の場合には、静的構成を使用する必要があります。
- 強制的に WKWebView や Ajax コールをインストルメント化する(ランタイム構成をオーバーライドする)
- ハイブリッドサポートを無効にし、ランタイム構成をオーバーライドする
- 自己ホストされた JavaScript 拡張ファイルに URL を設定する
次の表は、info.plist ファイルでサポートされているプロパティとデフォルト値を示しています。
| プロパティ | デフォルト値 | 説明 |
|---|---|---|
| serverJsAgentEnabled | true |
クライアントがこのフラグに false を受け取ると、JavaScript エージェントは無効になります。したがって、WKWebView と Ajax リクエストはモニタされません。 インジェクションは、新しい WKWebView の作成時に発生します。このため、このフラグが false に設定されているときに WKWebView が作成された場合、その特定の WKWebView は、その後フラグが true に設定されてもインストルメント化されません。 |
| ForceWebviewInstrumentation | false | true に設定すると、iOS エージェントはランタイム構成に関係なく、JavaScript エージェントを WKWebView に挿入します。 |
| ForceAjaxInstrumentation | true | true に設定すると、ランタイム構成に関係なく、Ajax オペレーションは常に収集および報告されます。 |
| ADRUMExtUrlHttp |
JavaScript エージェントは、基本 JavaScript エージェントと JavaScript エージェントの拡張という 2 つのコンポーネントで構成されています。基本 JavaScript エージェントは、モバイルエージェントのバイナリに組み込まれ、上記のルールに従って挿入されます。 初期化後、JavaScript エージェントは、これらのプロパティによって指定された URL から JavaScript エージェント拡張を取得します。 | |
| ADRUMExtUrlHttps | https://cdn.appdynamics.com |
構成例
次の info.plist の例では、強制的に WKWebView をインストルメント化(ランタイム構成をオーバーライド)しますが、Ajax リクエストの収集とレポートは強制されません。この構成では、JavaScript 拡張ファイルが取得される URL も設定されます。
<plist>
<dict>
...
<key>ADEUM_Settings</key>
<dict>
<key>ForceWebviewInstrumentation</key>
<true/>
<key>ForceAjaxInstrumentation</key>
<false/>
<key>ADRUMExtUrlHttp</key>
<string>http://<your-domain>/adrum.cdn</string>
<key>ADRUMExtUrlHttps</key>
<string>https://<your-domain>/adrum.cdn</string>
</dict>
...
</dict>
</plist>
プログラムによるセッションの制御
デフォルトでは、ユーザが非アクティブになってからモバイルセッションが終了します。たとえば、ユーザがアプリケーションを開くと、セッションは開始され、ユーザが設定した期間にアプリケーションを使用しなくなった後にのみ終了します。ユーザがアプリケーションの再使用を開始すると、新しいセッションが開始されます。
ただし、セッションの期間を定義するのに非アクティブな期間を設定する代わりに、次の API を使用して、セッションの開始と終了をプログラムで制御できます。
- (void)startNextSessionインストルメンテーション クラスからメソッド startNextSession を呼び出すと、現在のセッションが終了し、新しいセッションが開始されます。API を使用すると、セッションを定義してフレーム化することができます。これにより、ビジネス目標と予想されるユーザフローをより厳密に合わせることができます。たとえば、API を使用して、製品の購入を追跡するセッションを定義したり、新しいユーザを登録したりすることができます。
この API を過剰に使用すると、セッションが調整されます(過剰使用は iOS エージェントごとに 1 分あたり 10 コールを超えた場合になりますが、変更される可能性があります)。API を使用しない場合、セッションは、ユーザが非アクティブになった後、デフォルトの終了にフォールバックします。
プログラムによって制御されるセッションの例
- Objective-C
-
-(void) checkout { AppDelegate *appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate]; NSString *checkoutUrl = [appDelegate.url stringByAppendingString:@"rest/cart/co/"]; NSURL *url = [NSURL URLWithString:checkoutUrl]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; NSURLResponse *response = nil; NSError *error = nil; NSData *body = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; const char *responseBytes = [body bytes]; if (responseBytes == nil) checkoutResponse = [NSString stringWithUTF8String:"Could not connect to the server"]; else { checkoutResponse = [NSString stringWithUTF8String:responseBytes]; [ADEumInstrumentation startNextSession]; } } - Swift
-
func checkout() { let appDelegate = UIApplication.shared.delegate as? AppDelegate let checkoutUrl = appDelegate?.url ?? "" + ("rest/cart/co/") let url = URL(string: checkoutUrl) var request: NSMutableURLRequest? = nil if let url = url { request = NSMutableURLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 60.0) } var response: URLResponse? = nil var error: Error? = nil var body: Data? = nil if let request = request { body = try? NSURLConnection.sendSynchronousRequest(request, returning: &response) } let responseBytes = Int8(body?.bytes ?? 0) if responseBytes == nil { checkoutResponse = String(utf8String: "Could not connect to the server") } else { checkoutResponse = String(utf8String: &responseBytes) ADEumInstrumentation.startNextSession() } }
セッションフレームの開始と終了
SessionFrame API を使用して、セッションアクティビティに表示されるセッションフレームを作成できます。セッションフレームは、セッション中にユーザが実行している内容のコンテキストを提供します。この API を使用すると、ユーザ画面の命名方法が向上し、ビジネスコンテキスト内のユーザフローを記録できます。
使用例
次に、SessionFrame API を使用する一般的な使用例を示します。
- 1 つの では複数の関数が実行されていて、個々の関数をより詳細にトラッキングする必要があります。
- ユーザフローは、複数の ViewController またはユーザの操作におよびます。たとえば、API を使用してセッションフレーム「Login」、「Product Selection」、および「Purchase」を作成して、ユーザが購入のためにフローを記録することができます。
- ユーザの操作に基づいて動的情報をキャプチャし、オーダー ID などのセッションフレームに名前を付けることができます。
SessionFrame API
- Objective-C
-
クラス メソッド 説明 ADEumInstrumentation + (ADEumSessionFrame *)startSessionFrame:(NSString *)nameセッションフレームを開始して名前を付けるには、これを使用します。セッションフレームに名前を付けると、[Sessions Dialog] でフレームを簡単に識別して追跡できます。
ADEumSessionFrame - (void)updateName:(NSString *)name セッションフレーム名の名前を変更します。startSessionFrame から返された ADEumSessionFrame オブジェクトからこのメソッドを呼び出します。
ADEumSessionFrame - (void)end セッションフレームを終了します。startSessionFrame から返された ADEumSessionFrame オブジェクトからこのメソッドを呼び出します。
- Swift
-
クラス メソッド 説明 ADEumInstrumentation startSessionFrame(_ name: String?) -> ADEumSessionFrameセッションフレームを開始して名前を付けるには、これを使用します。セッションフレームに名前を付けると、[Sessions Dialog.] でフレームを簡単に識別して追跡できます。 ADEumSessionFrame
updateName(_ name: String?)セッションフレーム名の名前を変更します。startSessionFrame から返された ADEumSessionFrame オブジェクトからこのメソッドを呼び出します。 ADEumSessionFrame end() セッションフレームを終了します。startSessionFrame から返された ADEumSessionFrame オブジェクトからこのメソッドを呼び出します。
セッションフレームの例
- Objective-C
-
#import "ADEumSessionFrame.h" ... @property (nonatomic, strong) ADEumSessionFrame *checkoutSessionFrame; - (IBAction)checkoutCartButtonClicked:(id)sender { // The user starting to check out starts when the user clicks the checkout button // this may be after they have updated quantities of items in their cart, etc. checkoutSessionFrame = [ADEumInstrumentation startSessionFrame:@"Checkout"]; } - (IBAction)confirmOrderButtonClicked:(id)sender { // Once they have confirmed payment info and shipping information, and they // are clicking the "Confirm" button to start the backend process of checking out // we may know more information about the order itself, such as an Order ID. NSString *newSessionName = [NSString stringWithFormat:@"Checkout: Order ID %@",orderId]; [checkoutSessionFrame updateName:newSessionName]; } - (void)processOrderCompleted { // Once the order is processed, the user is done "checking out" so we end // the session frame [checkoutSessionFrame end]; checkoutSessionFrame = nil; } - (void)checkoutCancelled { // If they cancel or go back, you'll want to end the session frame also, or else // it will be left open and appear to have never ended. [checkoutSessionFrame end]; checkoutSessionFrame = nil; } - Swift
-
import ADEumSessionFrame ... var checkoutSessionFrame: ADEumSessionFrame? @IBAction func checkoutCartButtonClicked(_ sender: UIButton) { // The check out starts when the user clicks the checkout button. // This may be after they have updated quantities of items in their cart, etc. checkoutSessionFrame = ADEumInstrumentation.startSessionFrame("Checkout") } @IBAction func confirmOrderButtonClicked(_ sender: UIButton) { // Once users have confirmed payment info and shipping information, and they // are clicking the "Confirm" button to start the backend process of checking out, // we may know more information about the order itself, such as an order ID. let newSessionName = "Checkout: Order ID \(orderId)" checkoutSessionFrame.updateName(newSessionName) } func processOrderCompleted() { // Once the order is processed, the user is done "checking out", so we end the session frame. checkoutSessionFrame.end() checkoutSessionFrame = nil } func checkoutCancelled() { // If they cancel or go back, you'll want to end the session frame also, or else it will be // left open and appear to have never ended. checkoutSessionFrame.end() checkoutSessionFrame = nil }
カスタムアプリケーション名のエージェントの構成
デフォルトでは、Splunk AppDynamics はバンドル ID から最後のセグメントを抽出してアプリケーション名を検出します。ただし、さまざまな地域のアプリケーションストアに異なるバンドル ID を付けたアプリケーションバイナリを展開することがあります。バンドル ID が異なる場合でも、1 つのアプリケーションに属するすべてのデータがまとめて収集され表示されるようにするには、アプリケーションにカスタム名を指定して共通名を設定します。設定するには、ADEumInstrumentation の設定に使用する ADEumAgentConfiguration インスタンスでアプリケーション名プロパティを設定します。詳細については、最新の iOS SDK ドキュメントを参照してください。
com.example.appdynamics.HelloWorld の場合、UI のアプリケーション名は「HelloWorld」と表示されます。これは、デフォルトのアプリ名とカスタムのアプリ名の両方に適用されます。@property (nonatomic, strong) NSString *applicationName; 一部の HTTP リクエストを無視するためのエージェントの構成
NSURL を使用した HTTP リクエストは、アプリケーションの内部で使用され、実際のネットワークリクエストを表すものではない場合があります。これらのリクエストに基づいて作成されたメトリックは、一般的に問題の追跡には役立たないため、そのデータが収集されないようにする場合があります。特定の NSURL リクエストを無視するには、ADEumInstrumentation の設定に使用する ADEumAgentConfiguration インスタンスに除外 URL パターンプロパティを設定します。できるだけ簡潔な正規表現を使用します。最新の iOS SDK ドキュメントを参照してください。
@property (nonatomic, strong) NSSet * excludedUrlPatterns;カスタム HTTP ライブラリでのエージェントの使用
iOS エージェントは、 または クラスのいずれかによって基盤となる実装が処理されたときに、自動的にネットワークリクエストを検出します。これにより、ほとんどの iOS ネットワークリクエストが対象になります。ただし、モバイルアプリケーションでは、カスタム HTTP ライブラリが使用される場合があります。
- iOS エージェントがカスタムライブラリからのリクエストを検出するには、
ADEumHTTPRequestTrackerクラスを使用してアプリケーションに手動でリクエスト トラッキング コードを追加します。 - サーバ側の処理との相関を許可するようにヘッダーを設定するには、
ADEumServerCorrelationHeadersクラスを使用します。 - カスタムライブラリを使用して、HTTP 経由でビーコンを送信するようにエージェントを設定するには、
ADEumCollectorChannelプロトコルとADEumAgentConfigurationクラスを使用します。
リクエストトラッキングの追加
リクエストトラッキングを手動で追加するには、リクエストの開始と終了のタイミングをエージェントに通知します。また、応答のステータスをエージェントに通知するようにプロパティを設定します。
要求のトラッキングの開始
HTTP リクエストのトラッキングを開始するには、リクエストを送信する直前に次のメソッドを呼び出します。
ADEumInstrumentation の initWithKey メソッドのいずれかを使用してエージェントを初期化する必要があります。@interface ADEumHTTPRequestTracker : NSObject
...
+ (ADEumHTTPRequestTracker *)requestTrackerWithURL:(NSURL *)url;
url は要求される URL です。このパラメータを nil にすることはできません。
HTTP リクエストのトラッキングを完了するには、応答またはエラーを受信した直後に、トラッカーオブジェクトに適切な を設定し、次のメソッドを呼び出してリクエストの結果をエージェントに返します。リクエスト トラッカー プロパティの設定このメソッドを呼び出した後は、このオブジェクトを使用し続けることはできません。別のリクエストを追跡するには、requestTrackerWithURL を再度呼び出します。
- (void)reportDone;
リクエスト トラッカー プロパティの設定
エージェントにコールの結果を返すには、requestTrackerWithURL オブジェクトに次のプロパティを設定する必要があります。
@property (copy, nonatomic) NSError *error;
応答の受信に失敗した場合を示します。リクエストが成功した場合は、nil である必要があります。
@property (copy, nonatomic) NSNumber *statusCode;
応答の HTTP ステータスコード(受信した場合)を示します。
-
応答を受信した場合は、整数である必要があります。
-
エラーが発生し、応答が受信されなかった場合、これは にする必要があります。
@property (copy, nonatomic) NSDictionary *allHeaderFields;
サーバーのレスポンスヘッダーのキーと値を表すディクショナリを指定します。このディクショナリの形式は、 の allHTTPHeadersFields プロパティと同じである必要があります。ディクショナリエレメントは、キーと値のペアで構成されます。キーはヘッダーキー名で、値はヘッダー値です。
エラーが発生し、応答が受信されなかった場合、これは にする必要があります。
例
次のようなリクエストスニペットがあるとします。
- (NSData *)sendRequest:(NSURL *) url error:(NSError **)error {
// implementation omitted
NSData *result = nil;
if (errorOccurred) {
*error = theError;
} else {
result = responseBody;
}
return result;
}
トラッカーを追加すると、次のようになります。
- (NSData *)sendRequest:(NSURL *)url error:(NSError **)error {
ADEumHTTPRequestTracker *tracker = [ADEumHTTPRequestTracker requestTrackerWithURL:url];
// implementation omitted
NSData *result = nil;
if (errorOccurred) {
*error = theError;
tracker.error = theError;
} else {
tracker.statusCode = theStatusCode;
tracker.allHeaderFields = theResponseHeaders;
result = responseBody;
}
[tracker reportDone];
return result;
}
サーバー側の相関を有効にする
リクエストとサーバ側の処理の相関を有効にするには、サーバ側エージェントが検出できる発信リクエストに特定のヘッダーを追加し、応答でサーバ側エージェントから取得したヘッダーを返して、iOS エージェントが使用できるようにします。
これは、標準 HTTP ライブラリに対して自動的に実行されます。
@interface ADEumServerCorrelationHeaders : NSObject
+ (NSDictionary *)generate;
@end
次の作業が必要です。
-
バックエンドにリクエストを送信する前に、generate メソッドを呼び出し、生成されたヘッダーを設定します。
-
上記の allHeaderFields プロパティを使用して、レスポンスヘッダーを返します。
ネットワークリクエストへのカスタムデータの追加
次のメソッドを 1 つ(または複数)呼び出して属性を ADEumHTTPRequestTracker に追加することで、カスタムデータをネットワークリクエストに追加できます。
- (NSData *)sendRequest:(NSURL *)url error:(NSError **)error {
ADEumHTTPRequestTracker *tracker = [ADEumHTTPRequestTracker requestTrackerWithURL:url];
// implementation omitted
NSData *result = nil;
if (errorOccurred) {
*error = theError;
tracker.error = theError;
} else {
tracker.statusCode = theStatusCode;
tracker.allHeaderFields = theResponseHeaders;
result = responseBody;
}
// Custom data can be added to this one request.
// Different types can be used.
// The data added will only appear for this network request and will not persist.
[tracker setUserData:@"trackerStringKey" value:@"Test String Value"];
[tracker setUserDataLong:@"trackerLongKey" value:66004024];
[tracker setUserDataBoolean:@"trackerBooleanKey" value:1];
[tracker setUserDataDouble:@"trackerDoubleKey" value:5905400.6];
[tracker setUserDataDate:@"trackerDateKey" value:[NSDate date]];
[tracker reportDone];
return result;
}カスタム HTTP ライブラリを使用するようにビーコンチャネルを設定
iOS エージェントは、ビーコンを送信するために HTTP を使用します。この目的で、エージェントがカスタム HTTP ライブラリを使用する構成を行うには、次の手順を実行します。
-
このプロトコルに準拠したクラスを実装します。
/** * Protocol for customizing the connection between the agent SDK and the collector. */ @protocol ADEumCollectorChannel <NSObject> /** * Sends a request synchronously and returns the response received, or an error. * * The semantics of this method are exactly equivalent to NSURLConnection's * sendSynchronousRequest:returningResponse:error: method. * * @param request The URL request to load. * @param response Out parameter for the URL response returned by the server. * @param error Out parameter used if an error occurs while processing the request. May be NULL. */ - (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error; @end -
ADEumCollectorChannelを初期化する前にADEumInstrumentationの ADEumAgentConfiguration プロパティを設定します。これはを実装するクラスのインスタンスを渡します。最新の iOS SDK ドキュメントを参照してください。@property (nonatomic, strong) id<ADEumCollectorChannel> collectorChannel;
ネットワークリクエストに対応した URL の変換
アプリケーションがネットワークリクエストを行う場合、機密情報が含まれている URL を EUM サーバに報告したくない場合があります。その場合は、ネットワークリクエスト URL を報告する前に変換するか、すべて無視します。
次の手順を実行します。
- 特定の URL を変更または無視するネットワーク リクエスト コールバックを実装します。
- 初期化コードにネットワーク リクエスト コールバックを登録します。
ネットワーク要求のコールバックの実装
特定の URL を変更または無視するコールバックでは、次のようにプロトコルを実装します。コールバックメソッド networkRequestCallback は同期されているため、関数からすばやく戻ることをお勧めします。
- (BOOL)networkRequestCallback:(ADEumHTTPRequestTracker *)networkRequestURL の変換
通常 、networkRequestCallback メソッドは次の手順に従って URL を変換する必要があります。
- 正規表現やパターンマッチングなどの手法を使用して、特定の URL を識別します。
ADEumHTTPRequestTrackerオブジェクトの url プロパティを変更します。ADEumHTTPRequestTrackerオブジェクトのその他のプロパティに対する変更は無視されます。- url プロパティに有効な URL を割り当てます。
- リクエストヘッダーとレスポンスヘッダーが必要な場合は、次のフィールドを使用します。
-
allHeaderFields:レスポンスヘッダーを返します。 -
allRequestHeaderFields:リクエストヘッダーを返します。
-
- YES (Objective-C)または true (Swift)を返します。
すべてのネットワークリクエストの URL を変換することもできるので、最初の手順はオプションです。
- Objective-C
-
- (BOOL)networkRequestCallback:(ADEumHTTPRequestTracker *)networkRequest { NSString *maskURL = @"http://networkrequest-mask.com"; NSURL *url = [NSURL URLWithString:maskURL]; networkRequest.url = url; return YES; } - Swift
-
func networkRequestCallback(_ networkRequest: ADEumHTTPRequestTracker?) -> Bool { let maskURL = "http://networkrequest-mask.com" let url = URL(string: maskURL) networkRequest?.url = url return true }
- Objective-C
-
- (BOOL)networkRequestCallback:(ADEumHTTPRequestTracker *)networkRequest { NSString *urlString = networkRequest.url.absoluteString; BOOL returnBeacon = YES; NSString *maskURL = @"http://customer-account.com"; if (!([urlString rangeOfString:@"accountInfo"].location == NSNotFound)) { networkRequest.url = [NSURL URLWithString:maskURL]; } return returnBeacon; } - Swift
-
func networkRequestCallback(_ networkRequest: ADEumHTTPRequestTracker?) -> Bool { let urlString = networkRequest?.url.absoluteString returnBeacon = true let maskURL = "http://customer-account.com" if !(Int((urlString as NSString?)?.range(of: "accountInfo").location ?? 0) == NSNotFound) { networkRequest?.url = URL(string: maskURL) } return returnBeacon }
URL の無視
networkRequestCallback メソッドが false を返した場合、ビーコンはドロップされます。ビーコンを無視する一般的なプロセスは次のとおりです。
-
正規表現やパターンマッチングなどの手法を使用して、特定の URL を識別します。
- false を返します。
コールバック networkRequestCallback が常に NO(Objective-C)または false(Swift)を返すようにすれば、理論的にはすべてのネットワークリクエストを無視できます。
- Objective-C
-
- (BOOL)networkRequestCallback:(ADEumHTTPRequestTracker *)networkRequest { return NO; } - Swift
-
func networkRequestCallback(_ networkRequest: ADEumHTTPRequestTracker?) -> Bool { return false }
- Objective-C
-
- (BOOL)networkRequestCallback:(ADEumHTTPRequestTracker *)networkRequest { NSString *urlString = networkRequest.url.absoluteString; BOOL returnBeacon = YES; if (!([urlString rangeOfString:@"avatar"].location == NSNotFound)) { returnBeacon = NO; } return returnBeacon; } - Swift
-
func networkRequestCallback(_ networkRequest: ADEumHTTPRequestTracker?) -> Bool { let urlString = networkRequest?.url.absoluteString var returnBeacon = true if !(Int((urlString as NSString?)?.range(of: "avatar").location ?? 0) == NSNotFound) { returnBeacon = false } return returnBeacon }
コールバックの登録
ADEumHTTPRequestTracker オブジェクトを使用してコールバックを呼び出します。 - Objective-C
-
ADEumAgentConfiguration *config = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>]; config.networkRequestCallback = self; [ADEumInstrumentation initWithConfiguration:config]; - Swift
-
let config = ADEumAgentConfiguration(appKey: <#EUM_APP_KEY#>) config.networkRequestCallback = self ADEumInstrumentation.initWith(config)
ロギングの有効化とロギングレベルの設定
ロギングレベルを有効にして設定するには、メソッド を使用します。ロギングは、次のいずれかのレベルに設定できます。
- ADEumLoggingLevelOff
- ADEumLoggingLevelAll
- ADEumLoggingLevelVerbose
- ADEumLoggingLevelDebug
- ADEumLoggingLevelInfo
- ADEumLoggingLevelWarn
- ADEumLoggingLevelError
例
- Objective-C
-
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // appKey should be assigned your EUM app key ADEumAgentConfiguration *config = [[ADEumAgentConfiguration alloc] initWithAppKey: <#EUM_APP_KEY#>]; config.loggingLevel = ADEumLoggingLevelAll; [ADEumInstrumentation initWithConfiguration:config]; ... } - Swift
-
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { { // appKey should be assigned your EUM app key let config = ADEumAgentConfiguration(appKey: <#EUM_APP_KEY#>) config.loggingLevel = .all ADEumInstrumentation.initWithConfiguration(config) ... return true }
iOS SDK のドキュメント
最新の iOS SDK ドキュメント、または以下に記載されている以前のバージョンを参照してください。
- https://sdkdocs.appdynamics.com/ios-sdk/22.12/html/
- https://sdkdocs.appdynamics.com/ios-sdk/22.5/html/
- https://sdkdocs.appdynamics.com/ios-sdk/22.3/html/
- https://sdkdocs.appdynamics.com/ios-sdk/22.2/html/
- https://sdkdocs.appdynamics.com/ios-sdk/22.1/html/
- https://sdkdocs.appdynamics.com/ios-sdk/21.12/html/
- https://sdkdocs.appdynamics.com/ios-sdk/21.8/html/
- https://sdkdocs.appdynamics.com/ios-sdk/21.6/html/
- https://sdkdocs.appdynamics.com/ios-sdk/21.5/html/
- https://sdkdocs.appdynamics.com/ios-sdk/21.2/html/