データ収集カスタムルール¶
View¶
設定 FTRUMConfig.enableTraceUserView = YES を有効にする必要があります。
rumConfig.viewTrackingHandler = [CustomViewTracker new];
#import "FTDefaultUIKitViewTrackingHandler.h"
// プロトコル実装例
@interface CustomViewTracker : NSObject <FTUIKitViewTrackingHandler>
// SDK デフォルトの view 収集ルールが必要な場合のみ追加
@property (nonatomic, strong) FTDefaultUIKitViewTrackingHandler defaultHandler;
@end
@implementation CustomViewTracker
// SDK デフォルトの view 収集ルールが必要な場合のみ追加
-(FTDefaultUIKitViewTrackingHandler *)defaultHandler{
if (!_defaultHandler) {
_defaultHandler = [FTDefaultUIKitViewTrackingHandler new];
}
return _defaultHandler;
}
- (FTRUMView *)rumViewForViewController:(UIViewController *)viewController {
// クラス名で完全一致
if ([viewController isKindOfClass:[HomeViewController class]]) {
return [[FTRUMView alloc] initWithViewName:@"main_home" property:@{@"page_type": @"home"}];
}
// プレフィックスでフィルタリング
else if ([NSStringFromClass([viewController class]) hasPrefix:@"FT"]) {
return [[FTRUMView alloc] initWithViewName:[NSString stringWithFormat:@"ft_%@", NSStringFromClass([viewController class])] property:nil];
}
// accessibilityLabel で設定
else if (viewController.view.accessibilityLabel) {
return [[FTRUMView alloc] initWithViewName:viewController.view.accessibilityLabel property:nil];
}
// 一部の画面をカスタマイズした後、残りの画面は SDK デフォルト収集ルールを使用(デフォルトハンドラの処理結果を返す)
return [self.defaultHandler rumViewForViewController:viewController];
// トラッキングをスキップする場合は、nil を直接返す
return nil;
}
@end
rumConfig.viewTrackingHandler = CustomViewTracker()
class CustomViewTracker: NSObject, FTUIKitViewTrackingHandler {
// SDK デフォルト view 収集ルールが必要な場合のみこのプロパティを保持
lazy var defaultHandler: FTDefaultUIKitViewTrackingHandler = {
FTDefaultUIKitViewTrackingHandler()
}()
func rumView(for viewController: UIViewController) -> FTRUMView? {
// クラス名で完全一致
if viewController is HomeViewController {
let properties: [String: Any] = ["page_type": "home"]
return FTRUMView(viewName: "main_home", property: properties)
}
// クラス名プレフィックスでフィルタリング
let vcClassName = String(describing: type(of: viewController))
if vcClassName.hasPrefix("FT") {
let viewName = "ft_\(vcClassName)"
return FTRUMView(viewName: viewName, property: nil)
}
// accessibilityLabel で設定
if let accessibilityLabel = viewController.view.accessibilityLabel, !accessibilityLabel.isEmpty {
return FTRUMView(viewName: accessibilityLabel, property: nil)
}
// 一部の画面をカスタマイズした後、残りの画面は SDK デフォルト収集ルールを使用(デフォルトハンドラの処理結果を返す)
return defaultHandler.rumView(for: viewController)
// トラッキングをスキップする場合は、nil を直接返す
return nil
}
}
SwiftUI View 自動収集(実験的)¶
SwiftUI View 自動収集では、まず SDK が画面名を抽出し、次に FTRumConfig.swiftUIViewTrackingHandler を使用して RUM View を生成するかどうかを決定します。デフォルトハンドラを使用して直接有効にするか、FTSwiftUIViewTrackingHandler を実装して、抽出された SwiftUI View Name をフィルタリングし、報告する View Name とプロパティをカスタマイズできます。
特定の SwiftUI View でページ名とライフサイクルを明示的に制御する必要がある場合は、SwiftUI View 手動収集 を参照してください。
注意: SwiftUI View 自動収集は現在実験的機能であり、関連する API と収集動作は今後のバージョンで変更される可能性があります。
使用前に FTRumConfig.enableTraceUserView を有効にし、FTRumConfig.swiftUIViewTrackingHandler を設定する必要があります。
SwiftUI View 収集ルールをカスタマイズするには、FTSwiftUIViewTrackingHandler を実装します。FTRUMView を返すとその SwiftUI View を収集し、nil を返すとスキップします。
rumConfig.swiftUIViewTrackingHandler = [CustomSwiftUIViewTracker new];
@interface CustomSwiftUIViewTracker : NSObject <FTSwiftUIViewTrackingHandler>
@end
@implementation CustomSwiftUIViewTracker
- (nullable FTRUMView *)rumViewForExtractedViewName:(NSString *)extractedViewName {
if ([extractedViewName isEqualToString:@"HomeView"]) {
return [[FTRUMView alloc] initWithViewName:@"main_home" property:@{@"page_type": @"home"}];
}
return nil;
}
@end
rumConfig.swiftUIViewTrackingHandler = CustomSwiftUIViewTracker()
class CustomSwiftUIViewTracker: NSObject, FTSwiftUIViewTrackingHandler {
func rumView(forExtractedViewName extractedViewName: String) -> FTRUMView? {
if extractedViewName == "HomeView" {
return FTRUMView(
viewName: "main_home",
property: ["page_type": "home"]
)
}
return nil
}
}
Action¶
設定 FTRUMConfig.enableTraceUserAction = YES を有効にする必要があります。
rumConfig.actionTrackingHandler = [CustomActionTracker new];
#import "FTDefaultActionTrackingHandler.h"
// プロトコル実装例
// iOS 環境では `FTUIPressRUMActionsHandler` プロトコルに準拠する必要があります
// tvOS 環境では `FTUITouchRUMActionsHandler` プロトコルに準拠する必要があります。
@interface CustomActionTracker : NSObject <FTUIPressRUMActionsHandler,FTUITouchRUMActionsHandler>
// SDK デフォルトの Action 収集ルールが必要な場合のみ追加
@property (nonatomic, strong) FTDefaultActionTrackingHandler defaultHandler;
@end
@implementation CustomActionTracker
// SDK デフォルトの Action 収集ルールが必要な場合のみ追加
-(FTDefaultActionTrackingHandler *)defaultHandler{
if (!_defaultHandler) {
_defaultHandler = [FTDefaultActionTrackingHandler new];
}
return _defaultHandler;
}
// iOS と tvOS の両方で実装が必要なプロトコルメソッド
- (nullable FTRUMAction *)rumLaunchActionWithLaunchType:(FTLaunchType)type {
if(type == FTLaunchCold){
return [[FTRUMAction alloc]initWithActionName:@"cold"];
}
// nil を返してトラッキングをスキップ
return nil;
}
// iOS 環境で実装が必要なプロトコルメソッド
-(nullable FTRUMAction *)rumActionWithTargetView:(UIView *)targetView{
if (view.accessibilityIdentifier){
return [[FTRUMAction alloc] initWithActionName:view.accessibilityIdentifier];
}
// 一部の Action をカスタマイズした後、残りは SDK デフォルト収集ルールを使用(デフォルトハンドラの処理結果を返す)
return [self.defaultHandler rumActionWithTargetView:targetView];
// nil を返してトラッキングをスキップ
return nil;
}
// tvOS 環境で実装が必要なプロトコルメソッド
- (nullable FTRUMAction *)rumActionWithPressType:(UIPressType)type targetView:(UIView *)targetView{
if (type == UIPressTypeSelect && view.accessibilityIdentifier){
return [[FTRUMAction alloc] initWithActionName:view.accessibilityIdentifier];
}
// 一部の Action をカスタマイズした後、残りは SDK デフォルト収集ルールを使用(デフォルトハンドラの処理結果を返す)
return [self.defaultHandler rumActionWithPressType:type targetView:targetView];
// nil を返してトラッキングをスキップ
return nil;
}
@end
rumConfig.actionTrackingHandler = CustomActionTracker()
// プロトコル実装例
// iOS 環境では `FTUIPressRUMActionsHandler` プロトコルに準拠する必要があります
// tvOS 環境では `FTUITouchRUMActionsHandler` プロトコルに準拠する必要があります。
class CustomActionTracker: NSObject, FTUIPressRUMActionsHandler, FTUITouchRUMActionsHandler {
// SDK デフォルトの Action 収集ルールが必要な場合のみ追加
lazy var defaultHandler: FTDefaultActionTrackingHandler = {
FTDefaultActionTrackingHandler()
}()
// iOS と tvOS の両方で実装が必要なプロトコルメソッド
func rumLaunchAction(with type: FTLaunchType) -> FTRUMAction? {
if type == .cold {
return FTRUMAction(actionName: "cold")
}
// nil を返してトラッキングをスキップ
return nil
}
// iOS 環境で実装が必要なプロトコルメソッド
func rumAction(withTargetView targetView: UIView) -> FTRUMAction? {
if let identifier = targetView.accessibilityIdentifier {
return FTRUMAction(actionName: identifier)
}
// 一部の Action をカスタマイズした後、残りは SDK デフォルト収集ルールを使用(デフォルトハンドラの処理結果を返す)
return defaultHandler.rumAction(withTargetView: targetView)
// nil を返してトラッキングをスキップ
return nil
}
// tvOS 環境で実装が必要なプロトコルメソッド
func rumAction(with pressType: UIPress.PressType, targetView: UIView) -> FTRUMAction? {
if pressType == .select, let identifier = targetView.accessibilityIdentifier {
return FTRUMAction(actionName: identifier)
}
// 一部の Action をカスタマイズした後、残りは SDK デフォルト収集ルールを使用(デフォルトハンドラの処理結果を返す)
return defaultHandler.rumAction(with: pressType, targetView: targetView)
// nil を返してトラッキングをスキップ
return nil
}
}
Resource¶
設定 FTRUMConfig.enableTraceUserResource = YES を有効にするか、URLSession Delegate を介したカスタム Network 収集 を有効にする必要があります。
URL による収集フィルタリング¶
カスタムプロパティの追加¶
プロパティプロバイダークロージャを設定することで、RUM Resource に追加するプロパティを返すことができます。
例えば、RUM Resource に HTTP リクエストボディを追加する場合:
rumConfig.resourcePropertyProvider = ^NSDictionary *_Nullable(NSURLRequest *request, NSURLResponse *response,NSData *data, NSError *error) {
NSString *body = @"";
if (request.HTTPBody) {
body = [[NSString alloc] initWithData:httpBody encoding:NSUTF8StringEncoding] ?: @"";
}
return @{@"request_body": body};
}
ネットワークエラーのフィルタリング¶
ネットワークリクエストでエラーが発生した場合、RUM に network_error タイプの Error データが生成されます。一部の URLSession のローカルエラー(例:task.cancel)はユーザープログラムの正常なロジックであり、エラーデータではありません。そのような場合は sessionTaskErrorFilter コールバックでインターセプトしてフィルタリングできます。インターセプトする場合は YES を返し、インターセプトしない場合は NO を返します。インターセプト後、RUM-Error はそのエラーを収集しません。
Error¶
FTRumConfig.issueDataProvider を使用すると、SDK が Crash または ANR を自動収集する際に、その例外情報に基づいて対応する RUM Error にビジネスフィールド(例:ビジネスシナリオ、機能モジュール、実験グループ)を追加できます。この機能は SDK 1.6.7 以降でサポートされています。
issueDataProvider は自動収集された Crash と ANR にのみフィールドを追加し、Crash または ANR の監視自体を有効にするものではありません。使用前に、収集対象に応じて enableTrackAppCrash または enableTrackAppANR を有効にする必要があります。
設定¶
RUM を起動する前に issueDataProvider を設定してください。RUM 起動後に元の FTRumConfig を変更しても、実行中の Provider は変更されません。
FTRumConfig *rumConfig = [[FTRumConfig alloc] initWithAppid:appid];
rumConfig.enableTrackAppCrash = YES;
rumConfig.enableTrackAppANR = YES;
// RUM 起動前に読み取り専用のビジネススナップショットを準備し、コールバック内で時間のかかる処理を避ける
NSDictionary<NSString *, id> *businessContext = @{
@"business_scene": @"checkout",
@"release_channel": @"app_store"
};
rumConfig.issueDataProvider = ^NSDictionary<NSString *, id> * _Nullable(FTIssueInfo *issue) {
NSMutableDictionary<NSString *, id> *fields = [businessContext mutableCopy];
fields[@"issue_category"] =
issue.category == FTIssueCategoryCrash ? @"crash" : @"anr";
fields[@"historical_issue"] = @(issue.isHistorical);
if (issue.threadName.length > 0) {
fields[@"issue_thread_name"] = issue.threadName;
}
return fields;
};
[[FTMobileAgent sharedInstance] startRumWithConfigOptions:rumConfig];
let rumConfig = FTRumConfig(appid: appid)
rumConfig.enableTrackAppCrash = true
rumConfig.enableTrackAppANR = true
// RUM 起動前に読み取り専用のビジネススナップショットを準備し、コールバック内で時間のかかる処理を避ける
let businessContext: [String: Any] = [
"business_scene": "checkout",
"release_channel": "app_store"
]
rumConfig.issueDataProvider = { issue in
var fields = businessContext
fields["issue_category"] =
issue.category == .crash ? "crash" : "anr"
fields["historical_issue"] = issue.isHistorical
if let threadName = issue.threadName, !threadName.isEmpty {
fields["issue_thread_name"] = threadName
}
return fields
}
FTMobileAgent.sharedInstance().startRum(withConfigOptions: rumConfig)
Provider は、条件に一致する自動収集 Error ごとに同期的に 1 回呼び出されます。nil または空の辞書を返すと、その Error にはカスタムフィールドを追加しません。
FTIssueInfo¶
コールバックパラメータ FTIssueInfo は読み取り専用オブジェクトで、現在処理中の Crash または ANR を説明します。
| プロパティ | タイプ | 説明 |
|---|---|---|
| category | FTIssueCategory | 例外カテゴリ:FTIssueCategoryCrash / Swift .crash は Crash、FTIssueCategoryANR / Swift .anr は ANR |
| errorType | NSString | 対応する RUM Error のタイプ。Crash は ios_crash、ANR は anr_error |
| message | NSString | Error メッセージ。取得できない場合は nil |
| stack | NSString | 例外スタック |
| occurredAtNanoseconds | long long | 例外発生時刻、Unix タイムスタンプ、単位はナノ秒 |
| appState | NSString | 対応する RUM Error で使用されるアプリケーション状態 |
| threadName | NSString | 例外スレッド名。取得できない場合は nil |
| historical | BOOL | 永続化データから復元された例外かどうか。Objective-C の getter は isHistorical、Swift は isHistorical |
自動収集シナリオにおける category、errorType、historical の対応関係は以下の通りです。
| シナリオ | category | errorType | historical |
|---|---|---|---|
| Crash レポートがアプリ次回起動時に復元 | Crash | ios_crash |
YES |
| 現在のプロセス復旧後の ANR | ANR | anr_error |
NO |
| 前のプロセスの Watchdog ANR がアプリ次回起動時に復元 | ANR | anr_error |
YES |
historical が YES の場合、FTIssueInfo 内の例外時刻、スタック、アプリ状態は永続化された例外データから取得されますが、Provider はアプリが復旧してレポートを処理する際に実行されます。そのため、Provider 実行時に直接読み取られるビジネス状態は現在のプロセスのものであり、例外発生時の状態とは限りません。例外発生時のビジネス情報を関連付ける必要がある場合は、事前に永続化し、RUM 起動前にスレッドセーフなメモリスナップショットとしてロードしてください。Provider 内でディスク読み取りを行わないでください。
カスタムフィールドルール¶
Provider が返すフィールドは、対応する RUM Error の fields に書き込まれ、tags には書き込まれません。フィールドは以下のルールを満たす必要があります。
- Key は空でない文字列であり、UTF-8 エンコード長が 100 バイトを超えてはなりません。
- Value は、文字列、ブール値、整数、および有限浮動小数点数のみをサポートします。配列、辞書、
NSNull、カスタムオブジェクトはサポートしません。 - 文字列 Value の UTF-8 エンコード長は 4096 バイトを超えてはなりません。
- SDK は、返された辞書内の最大 50 項目までスキャンします。無効なフィールドもスキャン数にカウントされます。辞書の走査順序は固定されていないため、50 項目以内で返すことを推奨します。
- 受け入れられたすべてのフィールドの推定合計サイズは 25 KiB を超えてはなりません。
error.またはerror_で始まる Key は無視されます。- カスタムフィールドが SDK の既存の tag または field と名前が重複する場合、SDK のフィールドが優先されます。
ルールに違反するフィールドは無視されますが、元の Crash または ANR Error の収集と報告は妨げられません。カスタムフィールドは RUM データとともにアップロードされるため、パスワードやトークンなどの機密情報を含めないでください。
コールバック実行の要件¶
issueDataProvider は、メインスレッド以外で並行または再入可能に実行される可能性があります。コールバックは以下の要件を満たす必要があります。
- スレッドセーフな実装を保証し、事前に準備された不変データまたはメモリスナップショットの読み取りを優先してください。
- 10 ms 以内に返すことを推奨します。
- UI を操作したり、ネットワークやディスク I/O を実行したりしないでください。
- スレッド切り替え、同期ディスパッチ、長時間のロック待機などのブロッキング操作を行わないでください。
実行時間が 50 ms を超えた場合、SDK は低速コールバックのデバッグログを出力する可能性がありますが、元の RUM Error の処理は継続されます。
適用範囲¶
issueDataProvider は、SDK が自動収集する Crash、現在のプロセス復旧後の ANR、および前のプロセスの Watchdog ANR に適用されます。以下のデータはこの Provider をトリガーしません。
addErrorなどのインターフェースを介して手動で追加された Error- Resource / Network Error
- WebView Error
- Long Task
- データアップロードまたは再試行プロセス
カスタム TraceHeader¶
FTTraceConfig.traceInterceptor を使用してグローバルに設定するか、URLSession レベルのカスタム Trace を使用できます。以下は w3c-traceContext の例です。
FTTraceConfig *traceConfig = [[FTTraceConfig alloc]init];
traceConfig.traceInterceptor = ^FTTraceContext * _Nullable(NSURLRequest *request) {
// 1. ビジネスカスタムの traceId を取得
NSString *replaceTrace = [request.allHTTPHeaderFields valueForKey:CUSTOM_TRACE_HEADER];
// 2. SDK 標準の W3C traceparent リクエストヘッダーを取得
NSDictionary *traceHeaders = [[FTExternalDataManager sharedManager] getTraceHeaderWithUrl:request.URL];
NSString *traceParentStr = traceHeaders[FT_NETWORK_TRACEPARENT_KEY];
// 3. W3C traceparent 形式を解析し、インデックス 1 の traceId を置き換え
NSArray *traceComponents = [traceParentStr componentsSeparatedByString:@"-"];
if (traceComponents.count != 4) {
return nil;
}
NSMutableArray *newComponents = [traceComponents mutableCopy];
newComponents[1] = replaceTrace;
NSString *newTraceParent = [newComponents componentsJoinedByString:@"-"];
// 4. カスタムトレーシングコンテキストを組み立てて返す
FTTraceContext *context = [FTTraceContext new];
context.traceHeader = @{FT_NETWORK_TRACEPARENT_KEY:newTraceParent};
context.traceId = replaceTrace;
// SDK 生成の spanId を保持(インデックス 2 の固定位置)
context.spanId = newComponents[2];
return context;
};
let traceConfig = FTTraceConfig()
traceConfig.traceInterceptor = { (request: URLRequest) -> FTTraceContext? in
// 1. ビジネスカスタムの traceId を取得
guard let replaceTrace = request.allHTTPHeaderFields?[CUSTOM_TRACE_HEADER] else {
return nil
}
// 2. SDK 標準の W3C traceparent リクエストヘッダーを取得
guard let traceHeaders = FTExternalDataManager.shared().getTraceHeader(with: request.url!), let traceParentStr = traceHeaders[FT_NETWORK_TRACEPARENT_KEY] as? String else {
return nil
}
// 3. W3C traceparent 形式を解析し、インデックス 1 の traceId を置き換え
let traceComponents = traceParentStr.components(separatedBy: "-")
guard traceComponents.count == 4 else {
return nil
}
var newComponents = traceComponents
newComponents[1] = replaceTrace
let newTraceParent = newComponents.joined(separator: "-")
// 4. カスタムトレーシングコンテキストを組み立てて返す
let context = FTTraceContext()
context.traceHeader = [FT_NETWORK_TRACEPARENT_KEY: newTraceParent]
context.traceId = replaceTrace
// SDK 生成の spanId を保持(インデックス 2 の固定位置)
context.spanId = newComponents[2]
return context
}