Data Collection Custom Rules¶
View¶
Requires enabling the configuration FTRUMConfig.enableTraceUserView = YES.
rumConfig.viewTrackingHandler = [CustomViewTracker new];
#import "FTDefaultUIKitViewTrackingHandler.h"
// Example of protocol implementation
@interface CustomViewTracker : NSObject <FTUIKitViewTrackingHandler>
// Add only when the SDK's default view collection rules are needed
@property (nonatomic, strong) FTDefaultUIKitViewTrackingHandler defaultHandler;
@end
@implementation CustomViewTracker
// Add only when the SDK's default view collection rules are needed
-(FTDefaultUIKitViewTrackingHandler *)defaultHandler{
if (!_defaultHandler) {
_defaultHandler = [FTDefaultUIKitViewTrackingHandler new];
}
return _defaultHandler;
}
- (FTRUMView *)rumViewForViewController:(UIViewController *)viewController {
// Exact match by class name
if ([viewController isKindOfClass:[HomeViewController class]]) {
return [[FTRUMView alloc] initWithViewName:@"main_home" property:@{@"page_type": @"home"}];
}
// Filter by prefix
else if ([NSStringFromClass([viewController class]) hasPrefix:@"FT"]) {
return [[FTRUMView alloc] initWithViewName:[NSString stringWithFormat:@"ft_%@", NSStringFromClass([viewController class])] property:nil];
}
// Set via accessibilityLabel
else if (viewController.view.accessibilityLabel) {
return [[FTRUMView alloc] initWithViewName:viewController.view.accessibilityLabel property:nil];
}
// After customizing specific pages, use the SDK's default collection rules for the remaining pages (return the default handler's result)
return [self.defaultHandler rumViewForViewController:viewController];
// To skip tracking, simply return nil
return nil;
}
@end
rumConfig.viewTrackingHandler = CustomViewTracker()
class CustomViewTracker: NSObject, FTUIKitViewTrackingHandler {
// Keep this property only when the SDK's default view collection rules are needed
lazy var defaultHandler: FTDefaultUIKitViewTrackingHandler = {
FTDefaultUIKitViewTrackingHandler()
}()
func rumView(for viewController: UIViewController) -> FTRUMView? {
// Exact match by class name
if viewController is HomeViewController {
let properties: [String: Any] = ["page_type": "home"]
return FTRUMView(viewName: "main_home", property: properties)
}
// Filter by class name prefix
let vcClassName = String(describing: type(of: viewController))
if vcClassName.hasPrefix("FT") {
let viewName = "ft_\(vcClassName)"
return FTRUMView(viewName: viewName, property: nil)
}
// Set via accessibilityLabel
if let accessibilityLabel = viewController.view.accessibilityLabel, !accessibilityLabel.isEmpty {
return FTRUMView(viewName: accessibilityLabel, property: nil)
}
// After customizing specific pages, use the SDK's default collection rules for the remaining pages (return the default handler's result)
return defaultHandler.rumView(for: viewController)
// To skip tracking, simply return nil
return nil
}
}
Action¶
Requires enabling the configuration FTRUMConfig.enableTraceUserAction = YES.
rumConfig.actionTrackingHandler = [CustomActionTracker new];
#import "FTDefaultActionTrackingHandler.h"
// Example of protocol implementation
// In the iOS environment, conform to the `FTUIPressRUMActionsHandler` protocol.
// In the tvOS environment, conform to the `FTUITouchRUMActionsHandler` protocol.
@interface CustomActionTracker : NSObject <FTUIPressRUMActionsHandler,FTUITouchRUMActionsHandler>
// Add only when the SDK's default Action collection rules are needed
@property (nonatomic, strong) FTDefaultActionTrackingHandler defaultHandler;
@end
@implementation CustomActionTracker
// Add only when the SDK's default Action collection rules are needed
-(FTDefaultActionTrackingHandler *)defaultHandler{
if (!_defaultHandler) {
_defaultHandler = [FTDefaultActionTrackingHandler new];
}
return _defaultHandler;
}
// Protocol method required for both iOS and tvOS
- (nullable FTRUMAction *)rumLaunchActionWithLaunchType:(FTLaunchType)type {
if(type == FTLaunchCold){
return [[FTRUMAction alloc]initWithActionName:@"cold"];
}
// Return nil to skip tracking
return nil;
}
// Protocol method required for the iOS environment
-(nullable FTRUMAction *)rumActionWithTargetView:(UIView *)targetView{
if (view.accessibilityIdentifier){
return [[FTRUMAction alloc] initWithActionName:view.accessibilityIdentifier];
}
// After customizing specific Actions, use the SDK's default collection rules for the rest (return the default handler's result)
return [self.defaultHandler rumActionWithTargetView:targetView];
// Return nil to skip tracking
return nil;
}
// Protocol method required for the tvOS environment
- (nullable FTRUMAction *)rumActionWithPressType:(UIPressType)type targetView:(UIView *)targetView{
if (type == UIPressTypeSelect && view.accessibilityIdentifier){
return [[FTRUMAction alloc] initWithActionName:view.accessibilityIdentifier];
}
// After customizing specific Actions, use the SDK's default collection rules for the rest (return the default handler's result)
return [self.defaultHandler rumActionWithPressType:type targetView:targetView];
// Return nil to skip tracking
return nil;
}
@end
rumConfig.actionTrackingHandler = CustomActionTracker()
// Example of protocol implementation
// In the iOS environment, conform to the `FTUIPressRUMActionsHandler` protocol.
// In the tvOS environment, conform to the `FTUITouchRUMActionsHandler` protocol.
class CustomActionTracker: NSObject, FTUIPressRUMActionsHandler, FTUITouchRUMActionsHandler {
// Add only when the SDK's default Action collection rules are needed
lazy var defaultHandler: FTDefaultActionTrackingHandler = {
FTDefaultActionTrackingHandler()
}()
// Protocol method required for both iOS and tvOS
func rumLaunchAction(with type: FTLaunchType) -> FTRUMAction? {
if type == .cold {
return FTRUMAction(actionName: "cold")
}
// Return nil to skip tracking
return nil
}
// Protocol method required for the iOS environment
func rumAction(withTargetView targetView: UIView) -> FTRUMAction? {
if let identifier = targetView.accessibilityIdentifier {
return FTRUMAction(actionName: identifier)
}
// After customizing specific Actions, use the SDK's default collection rules for the rest (return the default handler's result)
return defaultHandler.rumAction(withTargetView: targetView)
// Return nil to skip tracking
return nil
}
// Protocol method required for the tvOS environment
func rumAction(with pressType: UIPress.PressType, targetView: UIView) -> FTRUMAction? {
if pressType == .select, let identifier = targetView.accessibilityIdentifier {
return FTRUMAction(actionName: identifier)
}
// After customizing specific Actions, use the SDK's default collection rules for the rest (return the default handler's result)
return defaultHandler.rumAction(with: pressType, targetView: targetView)
// Return nil to skip tracking
return nil
}
}
Resource¶
Requires enabling the configuration FTRUMConfig.enableTraceUserResource = YES or custom collection via forwarding URLSession Delegate.
Filter Collection Based on URL¶
Add Custom Attributes¶
By setting an attribute provider closure, you can return additional attributes to be attached to the RUM Resource.
For example, you might want to add the HTTP request body to the RUM Resource:
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};
}
Filter Network Errors¶
When a network request encounters an error, RUM generates an Error data entry of type network_error. Some URLSession local errors, such as task.cancel, are part of the normal program logic and are not erroneous data. In such cases, you can intercept and filter them using the sessionTaskErrorFilter callback. Return YES to confirm interception, NO to not intercept. After interception, RUM-Error will not collect this error.
Custom TraceHeader¶
Can be set globally via FTTraceConfig.traceInterceptor or URLSession-level custom Trace. The following example uses w3c-traceContext.
FTTraceConfig *traceConfig = [[FTTraceConfig alloc]init];
traceConfig.traceInterceptor = ^FTTraceContext * _Nullable(NSURLRequest *request) {
// 1. Get the business-customized traceId
NSString *replaceTrace = [request.allHTTPHeaderFields valueForKey:CUSTOM_TRACE_HEADER];
// 2. Get the SDK's standard W3C traceparent request header
NSDictionary *traceHeaders = [[FTExternalDataManager sharedManager] getTraceHeaderWithUrl:request.URL];
NSString *traceParentStr = traceHeaders[FT_NETWORK_TRACEPARENT_KEY];
// 3. Parse the W3C traceparent format and replace the traceId at index 1
NSArray *traceComponents = [traceParentStr componentsSeparatedByString:@"-"];
if (traceComponents.count != 4) {
return nil;
}
NSMutableArray *newComponents = [traceComponents mutableCopy];
newComponents[1] = replaceTrace;
NSString *newTraceParent = [newComponents componentsJoinedByString:@"-"];
// 4. Assemble and return the custom trace context
FTTraceContext *context = [FTTraceContext new];
context.traceHeader = @{FT_NETWORK_TRACEPARENT_KEY:newTraceParent};
context.traceId = replaceTrace;
// Preserve the SDK-generated spanId (fixed position at index 2)
context.spanId = newComponents[2];
return context;
};
let traceConfig = FTTraceConfig()
traceConfig.traceInterceptor = { (request: URLRequest) -> FTTraceContext? in
// 1. Get the business-customized traceId
guard let replaceTrace = request.allHTTPHeaderFields?[CUSTOM_TRACE_HEADER] else {
return nil
}
// 2. Get the SDK's standard W3C traceparent request header
guard let traceHeaders = FTExternalDataManager.shared().getTraceHeader(with: request.url!), let traceParentStr = traceHeaders[FT_NETWORK_TRACEPARENT_KEY] as? String else {
return nil
}
// 3. Parse the W3C traceparent format and replace the traceId at index 1
let traceComponents = traceParentStr.components(separatedBy: "-")
guard traceComponents.count == 4 else {
return nil
}
var newComponents = traceComponents
newComponents[1] = replaceTrace
let newTraceParent = newComponents.joined(separator: "-")
// 4. Assemble and return the custom trace context
let context = FTTraceContext()
context.traceHeader = [FT_NETWORK_TRACEPARENT_KEY: newTraceParent]
context.traceId = replaceTrace
// Preserve the SDK-generated spanId (fixed position at index 2)
context.spanId = newComponents[2]
return context
}