Skip to content

macOS Application Integration


Guance User Access Monitoring can collect various macOS application Metrics data and analyze the performance of each macOS application endpoint in a visualized manner.

Prerequisites

Note

If the RUM Headless service has been activated, the prerequisites have been automatically configured, and you can directly integrate the application.

Application Integration

  1. Navigate to User Access Monitoring > Create > macOS;
  2. Input the application name;
  3. Input the application ID;
  4. Choose the application integration method:

    • Public DataWay: Directly receives RUM data without installing the DataKit collector.
    • Local Environment Deployment: Receives RUM data after meeting the prerequisites.

Installation

Source Code Address: https://github.com/GuanceCloud/datakit-macos

Demo: https://github.com/GuanceCloud/datakit-macos/Example

  1. Configure the Podfile.
   target 'yourProjectName' do

   # Pods for your project
   pod 'FTMacOSSDK', '~>[latest_version]'

   end
  1. Execute pod install in the Podfile directory to install the SDK.
  1. Select PROJECT -> Package Dependency, click + under the Packages section.

  2. In the search box that appears, input https://github.com/GuanceCloud/datakit-macos. This is the repository location for the code.

  3. After Xcode successfully retrieves the package, it will display the SDK configuration page.

Dependency Rule: It's recommended to choose Up to Next Major Version.

Add To Project: Select the supported project.

After filling out the configurations, click the Add Package button and wait until loading completes.

  1. In the pop-up window Choose Package Products for datakit-macos, select the Target where the SDK needs to be added, then click the Add Package button. At this point, the SDK has been successfully added.

If your project is managed by SPM, add FTMacOSSDK as a dependency by adding dependencies to Package.swift.

  dependencies: [
    .package(url: "https://github.com/GuanceCloud/datakit-macos.git", .upToNextMajor(from: "[latest_version]"))
]

Add Header Files

#import "FTMacOSSDK.h"
import FTMacOSSDK

SDK Initialization

Basic Configuration

Since the viewDidLoad method of the first displayed view NSViewController and the windowDidLoad method of NSWindowController are called earlier than the applicationDidFinishLaunching method of AppDelegate, to avoid abnormal lifecycle collection of the first view, it is recommended to initialize the SDK in the main.m file.

// main.m file
#import "FTMacOSSDK.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // Local environment deployment, Datakit deployment
        FTSDKConfig *config = [[FTSDKConfig alloc]initWithDatakitUrl:datakitUrl];
        // Use public DataWay deployment
        //FTSDKConfig *config = [[FTSDKConfig alloc]initWithDatawayUrl:datawayUrl clientToken:clientToken];
        config.enableSDKDebugLog = YES;
        [FTSDKAgent startWithConfigOptions:config];
    }
    return NSApplicationMain(argc, argv);
}

Create an mian.swift file and remove @main or @NSApplicationMain from AppDelegate.swift.

import Cocoa
import FTMacOSSDK
// Create AppDelegate and set it as delegate
let delegate = AppDelegate()
NSApplication.shared.delegate = delegate
// Initialize SDK 
let config = FTSDKConfig.init(datakitUrl: datakitUrl)
// Use public DataWay deployment
//let config = FTSDKConfig(datawayUrl: datawayUrl, clientToken: clientToken)
config.enableSDKDebugLog = true
FTSDKAgent.start(withConfigOptions: config)

_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

Property Type Required Meaning
datakitUrl NSString Yes Datakit access address, example: http://10.0.0.1:9529, default port 9529, the device with installed SDK must be able to access this address. Note: Either datakit or dataway configuration must be chosen
datawayUrl NSString Yes Public Dataway access address, example: http://10.0.0.1:9528, default port 9528, the device with installed SDK must be able to access this address. Note: Either datakit or dataway configuration must be chosen
clientToken NSString Yes Authentication token, must be used together with datawayUrl
enableSDKDebugLog BOOL No Set whether debug logs should be printed. Default NO
env NSString No Set the collection environment. Default prod, supports customization, can also be set via the provided FTEnv enumeration using -setEnvWithType: method
service NSString No Set the name of the associated business or service. Affects the service field data in Logs and RUM. Default: df_rum_ios
globalContext NSDictionary No Add custom tags. Refer to here for rules

RUM Configuration

   FTRumConfig *rumConfig = [[FTRumConfig alloc]initWithAppid:appid];
   rumConfig.enableTrackAppCrash = YES;
   rumConfig.enableTrackAppANR = YES;
   rumConfig.enableTrackAppFreeze = YES;
   rumConfig.enableTraceUserAction = YES;
   rumConfig.enableTraceUserVIew = YES;
   rumConfig.enableTraceUserResource = YES;
   rumConfig.errorMonitorType = FTErrorMonitorAll;
   rumConfig.deviceMetricsMonitorType = FTDeviceMetricsMonitorAll;
   [[FTSDKAgent sharedInstance] startRumWithConfigOptions:rumConfig];
   let rumConfig = FTRumConfig(appid: appid)
   rumConfig.enableTraceUserAction = true
   rumConfig.enableTrackAppANR = true
   rumConfig.enableTraceUserView = true
   rumConfig.enableTraceUserResource = true
   rumConfig.enableTrackAppCrash = true
   rumConfig.enableTrackAppFreeze = true
   rumConfig.errorMonitorType = .all
   rumConfig.deviceMetricsMonitorType = .all
   rumConfig.monitorFrequency = .rare
   FTSDKAgent.sharedInstance().startRum(withConfigOptions: rumConfig)
Property Type Required Meaning
appid NSString Yes Unique identifier for the User Access Monitoring application ID. Corresponds to setting the RUM appid, enabling RUM collection functionality, get appid method
sampleRate int No Sampling rate. Range [0,100], 0 means no collection, 100 means full collection, default value is 100. Scope applies to all View, Action, LongTask, Error data under the same session_id
enableTrackAppCrash BOOL No Set whether crash logs need to be collected. Default NO
enableTrackAppANR BOOL No Collect ANR unresponsive events. Default NO
enableTrackAppFreeze BOOL No Collect UI freeze events. Default NO
enableTraceUserView BOOL No Set whether user View operations need to be tracked. Default NO
enableTraceUserAction BOOL No Set whether user Action operations need to be tracked. Default NO
enableTraceUserResource BOOL No Set whether user network requests need to be tracked. DefaultNO, only applicable to native http
resourceUrlHandler FTResourceUrlHandler No Custom resource collection rules. Default does not filter. Returns: NO indicates to collect, YES indicates not to collect.
errorMonitorType FTErrorMonitorType No Supplementary error event monitoring types. Adds monitoring information in collected crash data. FTErrorMonitorBattery for battery level, FTErrorMonitorMemory for memory usage, FTErrorMonitorCpu for CPU occupancy.
monitorFrequency FTMonitorFrequency No Sampling cycle for view performance monitoring
deviceMetricsMonitorType FTDeviceMetricsMonitorType No View performance monitoring type. Adds corresponding monitoring items in the collected View data. FTDeviceMetricsMonitorMemory monitors current application memory usage, FTDeviceMetricsMonitorCpu monitors CPU jumps, FTDeviceMetricsMonitorFps monitors screen frame rate.
globalContext NSDictionary No Add custom tags for distinguishing data sources in user monitoring. If tracking functionality is required, parameter key is track_id, value is any number. Refer to here for notes on adding rules.

Log Configuration

    // Enable logger
    FTLoggerConfig *loggerConfig = [[FTLoggerConfig alloc]init];
    loggerConfig.enableCustomLog = YES;
    loggerConfig.printCustomLogToConsole = YES;
    loggerConfig.enableLinkRumData = YES;
    loggerConfig.logLevelFilter = @[@(FTStatusError),@(FTStatusCritical)];
    loggerConfig.discardType = FTDiscardOldest;
    [[FTSDKAgent sharedInstance] startLoggerWithConfigOptions:loggerConfig];
    let loggerConfig = FTLoggerConfig()
    loggerConfig.enableCustomLog = true
    loggerConfig.enableLinkRumData = true
    loggerConfig.printCustomLogToConsole = true
    loggerConfig.logLevelFilter = [NSNumber(value: FTLogStatus.statusError.rawValue),NSNumber(value: FTLogStatus.statusCritical.rawValue)] // loggerConfig.logLevelFilter = [2,3]
    loggerConfig.discardType = .discardOldest
    FTSDKAgent.sharedInstance().startLogger(withConfigOptions: loggerConfig)
Property Type Required Meaning
sampleRate int No Sampling rate. Range [0,100], 0 means no collection, 100 means full collection, default value is 100.
enableCustomLog BOOL No Whether to upload custom logs. DefaultNO
logLevelFilter NSArray No Set the status array of custom logs to be collected. Default collects all
enableLinkRumData BOOL No Whether to associate with RUM data. DefaultNO
discardType FTLogCacheDiscard No Set the log discard rule when the limit is reached. Default FTDiscard
FTDiscard discards appended data when log data exceeds the maximum (5000). FTDiscardOldest discards old data when log data exceeds the maximum.
printCustomLogToConsole BOOL No Set whether custom logs should be output to the console. DefaultNO, refer to output format for custom logs
globalContext NSDictionary No Add custom log tags. Refer to here for rules

Trace Configuration

   FTTraceConfig *traceConfig = [[FTTraceConfig alloc]init];
   traceConfig.enableLinkRumData = YES;
     traceConfig.enableAutoTrace = YES;
   traceConfig.networkTraceType = FTNetworkTraceTypeDDtrace;
   [[FTSDKAgent sharedInstance] startTraceWithConfigOptions:traceConfig];
   let traceConfig = FTTraceConfig.init()
   traceConfig.enableLinkRumData = true
   traceConfig.enableAutoTrace = true
   FTSDKAgent.sharedInstance().startTrace(withConfigOptions: traceConfig)
Property Type Required Meaning
sampleRate int No Sampling rate. Range [0,100], 0 means no collection, 100 means full collection, default value is 100.
networkTraceType NS_ENUM No Set the type of link tracing. Default is DDTrace, currently supports Zipkin , Jaeger, DDTrace, Skywalking (8.0+), TraceParent (W3C). If connecting OpenTelemetry and choosing the corresponding link type, please refer to the supported types and agent related configurations
enableLinkRumData BOOL No Whether to associate with RUM data. DefaultNO
enableAutoTrace BOOL No Set whether automatic http trace is enabled. DefaultNO, currently only supports NSURLSession

RUM User Data Tracking

You can enable automatic mode through FTRUMConfig configuration, or manually add it. Rum-related data is passed through the FTGlobalRumManager singleton. Relevant APIs are as follows:

View

If enableTraceUserView = YES is set to enable automatic collection, the SDK will automatically collect the Window lifecycle. The lifecycle of the window -becomeKeyWindow defines the View start, and -resignKeyWindow defines the View end.

Page names are set in the priority order of NSStringFromClass(window.contentViewController.class) > NSStringFromClass(window.windowController.class) > NSStringFromClass(window).

If the views inside the window are very complex, you can use the following API for custom collection.

Usage Method

/// Create a page
///
/// Call before the `-startViewWithName` method. This method records the page loading time. If unable to obtain the load time, this method can be skipped.
/// - Parameters:
///  - viewName: Page name
///  - loadTime: Page load time (nanoseconds)
-(void)onCreateView:(NSString *)viewName loadTime:(NSNumber *)loadTime;

/// Enter the page
/// - Parameters:
///  - viewName: Page name
///  - property: Event custom attributes (optional)
-(void)startViewWithName:(NSString *)viewName property:(nullable NSDictionary *)property;

/// Leave the page
/// - Parameter property: Event custom attributes (optional)
-(void)stopViewWithProperty:(nullable NSDictionary *)property;
/// Create a page
///
/// Call before the `-startViewWithName` method. This method records the page loading time. If unable to obtain the load time, this method can be skipped.
/// - Parameters:
///  - viewName: Page name
///  - loadTime: Page load time (ns)
open func onCreateView(_ viewName: String, loadTime: NSNumber)

/// Enter the page
/// - Parameters:
///  - viewName: Page name
///  - property: Event custom attributes (optional)
open func startView(withName viewName: String, property: [AnyHashable : Any]?)

/// Leave the page
/// - Parameter property: Event custom attributes (optional)
open func stopView(withProperty property: [AnyHashable : Any]?)

Code Example

- (void)viewDidAppear{
  [super viewDidAppear];
  // Scenario 1:
  [[FTGlobalRumManager sharedManager] startViewWithName:@"TestVC"];  

  // Scenario 2: Dynamic parameters
  [[FTGlobalRumManager sharedManager] startViewWithName:@"TestVC" property:@{@"custom_key":@"custom_value"}];  
}
-(void)viewDidDisappear{
  [super viewDidDisappear];
  // Scenario 1:
  [[FTGlobalRumManager sharedManager] stopView];  

  // Scenario 2: Dynamic parameters
  [[FTGlobalRumManager sharedManager] stopViewWithProperty:@{@"custom_key":@"custom_value"}];
}
override func viewDidAppear() {
    super.viewDidAppear()
    // Scenario 1:
    FTExternalDataManager.shared().startView(withName: "TestVC")
    // Scenario 2: Dynamic parameters
    FTExternalDataManager.shared().startView(withName: "TestVC",property: ["custom_key":"custom_value"])
}
override func viewDidDisappear() {
    super.viewDidDisappear()
    // Scenario 1:
    FTGlobalRumManager.shared().stopView()
    // Scenario 2: Dynamic parameters
    FTGlobalRumManager.shared().stopView(withProperty: ["custom_key":"custom_value"])
}

Action

Usage Method

/// Add Action event
/// - Parameters:
///   - actionName: Event name
///   - actionType: Event type
///   - property: Event custom attributes (optional)
- (void)addActionName:(NSString *)actionName actionType:(NSString *)actionType property:(nullable NSDictionary *)property;
/// Add Action event
/// - Parameters:
///   - actionName: Event name
///   - actionType: Event type
///   - property: Event custom attributes (optional)
func addActionName(String, actionType: String, property: [AnyHashable : Any]?)

Code Example

// Scenario 1
[[FTGlobalRumManager sharedManager] addActionName:@"UITableViewCell click" actionType:@"click"];
// Scenario 2: Dynamic parameters
[[FTGlobalRumManager sharedManager]  addActionName:@"UITableViewCell click" actionType:@"click" property:@{@"custom_key":@"custom_value"}];
// Scenario 1
FTGlobalRumManager.shared().addActionName("custom_action", actionType: "click")
// Scenario 2: Dynamic parameters
FTGlobalRumManager.shared().addActionName("custom_action", actionType: "click",property: ["custom_key":"custom_value"])

Error

Usage Method

/// Add Error event
/// - Parameters:
///   - type: Error type
///   - message: Error message
///   - stack: Stack trace
///   - property: Event custom attributes (optional)
- (void)addErrorWithType:(NSString *)type message:(NSString *)message stack:(NSString *)stack property:(nullable NSDictionary *)property;

/// Add Error event
/// - Parameters:
///   - type: Error type
///   - state: Program running state
///   - message: Error message
///   - stack: Stack trace
///   - property: Event custom attributes (optional)
- (void)addErrorWithType:(NSString *)type state:(FTAppState)state  message:(NSString *)message stack:(NSString *)stack property:(nullable NSDictionary *)property;
/// Add Error event
/// - Parameters:
///   - type: Error type
///   - message: Error message
///   - stack: Stack trace
///   - property: Event custom attributes (optional)
open func addError(withType: String, message: String, stack: String, property: [AnyHashable : Any]?)

/// Add Error event
/// - Parameters:
///   - type: Error type
///   - state: Program running state
///   - message: Error message
///   - stack: Stack trace
///   - property: Event custom attributes (optional)
open func addError(withType type: String, state: FTAppState, message: String, stack: String, property: [AnyHashable : Any]?)

Code Example

// Scenario 1
[[FTGlobalRumManager sharedManager] addErrorWithType:@"type" message:@"message" stack:@"stack"];
// Scenario 2: Dynamic parameters
[[FTGlobalRumManager sharedManager] addErrorWithType:@"ios_crash" message:@"crash_message" stack:@"crash_stack" property:@{@"custom_key":@"custom_value"}];
// Scenario 3: Dynamic parameters
[[FTGlobalRumManager sharedManager] addErrorWithType:@"ios_crash" state:FTAppStateUnknown message:@"crash_message" stack:@"crash_stack" property:@{@"custom_key":@"custom_value"}];
// Scenario 1
FTGlobalRumManager.shared().addError(withType: "custom_type", message: "custom_message", stack: "custom_stack")
// Scenario 2: Dynamic parameters
FTGlobalRumManager.shared().addError(withType: "custom_type", message: "custom_message", stack: "custom_stack",property: ["custom_key":"custom_value"])
// Scenario 3: Dynamic parameters       
FTGlobalRumManager.shared().addError(withType: "custom_type", state: .unknown, message: "custom_message", stack: "custom_stack", property: ["custom_key":"custom_value"])

LongTask

Usage Method

/// Add freeze event
/// - Parameters:
///   - stack: Freeze stack trace
///   - duration: Freeze duration (nanoseconds)
///   - property: Event custom attributes (optional)
- (void)addLongTaskWithStack:(NSString *)stack duration:(NSNumber *)duration property:(nullable NSDictionary *)property;
/// Add freeze event
/// - Parameters:
///   - stack: Freeze stack trace
///   - duration: Freeze duration (nanoseconds)
///   - property: Event custom attributes (optional)
func addLongTask(withStack: String, duration: NSNumber, property: [AnyHashable : Any]?)

Code Example

// Scenario 1
[[FTGlobalRumManager sharedManager] addLongTaskWithStack:@"stack string" duration:@1000000000];
// Scenario 2: Dynamic parameters
[[FTGlobalRumManager sharedManager] addLongTaskWithStack:@"stack string" duration:@1000000000 property:@{@"custom_key":@"custom_value"}];
// Scenario 1
FTGlobalRumManager.shared().addLongTask(withStack: "stack string", duration: 1000000000)
// Scenario 2: Dynamic parameters
FTGlobalRumManager.shared().addLongTask(withStack: "stack string", duration: 1000000000 ,property: [["custom_key":"custom_value"]])

Resource

Usage Method

/// HTTP request starts
/// - Parameters:
///   - key: Request identifier
///   - property: Event custom attributes (optional)
- (void)startResourceWithKey:(NSString *)key property:(nullable NSDictionary *)property;

/// HTTP adds request data
///
/// - Parameters:
///   - key: Request identifier
///   - metrics: Request related performance attributes
///   - content: Request related data
- (void)addResourceWithKey:(NSString *)key metrics:(nullable FTResourceMetricsModel *)metrics content:(FTResourceContentModel *)content;

/// HTTP request ends
/// - Parameters:
///   - key: Request identifier
///   - property: Event custom attributes (optional)
- (void)stopResourceWithKey:(NSString *)key property:(nullable NSDictionary *)property;
/// HTTP request starts
/// - Parameters:
///   - key: Request identifier
///   - property: Event custom attributes (optional)
open func startResource(withKey key: String, property: [AnyHashable : Any]?)

/// HTTP request ends
/// - Parameters:
///   - key: Request identifier
///   - property: Event custom attributes (optional)
open func stopResource(withKey key: String, property: [AnyHashable : Any]?)

/// HTTP adds request data
///
/// - Parameters:
///   - key: Request identifier
///   - metrics: Request related performance attributes
///   - content: Request related data
open func addResource(withKey key: String, metrics: FTResourceMetricsModel?, content: FTResourceContentModel)

Code Example

#import "FTMacOSSDK.h"

// Step 1: Before the request begins
[[FTGlobalRumManager sharedManager] startResourceWithKey:key];

// Step 2: When the request completes
[[FTGlobalRumManager sharedManager] stopResourceWithKey:key];

// Step 3: Concatenate Resource data
//FTResourceContentModel data
FTResourceContentModel *content = [[FTResourceContentModel alloc]init];
content.httpMethod = request.HTTPMethod;
content.requestHeader = request.allHTTPHeaderFields;
content.responseHeader = httpResponse.allHeaderFields;
content.httpStatusCode = httpResponse.statusCode;
content.responseBody = responseBody;
//ios native
content.error = error;

//If stage times can be obtained
//FTResourceMetricsModel
//Obtain NSURLSessionTaskMetrics data directly using FTResourceMetricsModel initialization method
FTResourceMetricsModel *metricsModel = [[FTResourceMetricsModel alloc]initWithTaskMetrics:metrics];

//For other platforms, all time data is in nanoseconds
FTResourceMetricsModel *metricsModel = [[FTResourceMetricsModel alloc]init];

//Step 4: Add resource if there's no time data, pass nil for metrics
[[FTGlobalRumManager sharedManager] addResourceWithKey:key metrics:metricsModel content:content];
import FTMacOSSDK

// Step 1: Before the request begins
FTGlobalRumManager.shared().startResource(withKey: key)

// Step 2: When the request completes
FTGlobalRumManager.shared().stopResource(withKey: resource.key)

// Step 3:① Concatenate Resource data
let contentModel = FTResourceContentModel(request: task.currentRequest!, response: task.response as? HTTPURLResponse, data: resource.data, error: error)

//② If stage times can be obtained
//FTResourceMetricsModel
//Obtain NSURLSessionTaskMetrics data directly using FTResourceMetricsModel initialization method
var metricsModel:FTResourceMetricsModel?
if let metrics = resource.metrics {
   metricsModel = FTResourceMetricsModel(taskMetrics:metrics)
}
//For other platforms, all time data is in nanoseconds
metricsModel = FTResourceMetricsModel()
...

//Step 4: Add resource if there's no time data, pass nil for metrics
FTGlobalRumManager.shared().addResource(withKey: resource.key, metrics: metricsModel, content: contentModel)

Logger Log Printing

Currently, log content is limited to 30 KB, exceeding characters will be truncated.

Usage Method

//
//  FTLogger.h
//  FTMacOSSDK

/// Add info type custom log
/// - Parameters:
///   - content: Log content
///   - property: Custom attributes (optional)
-(void)info:(NSString *)content property:(nullable NSDictionary *)property;

/// Add warning type custom log
/// - Parameters:
///   - content: Log content
///   - property: Custom attributes (optional)
-(void)warning:(NSString *)content property:(nullable NSDictionary *)property;

/// Add error type custom log
/// - Parameters:
///   - content: Log content
///   - property: Custom attributes (optional)
-(void)error:(NSString *)content  property:(nullable NSDictionary *)property;

/// Add critical type custom log
/// - Parameters:
///   - content: Log content
///   - property: Custom attributes (optional)
-(void)critical:(NSString *)content property:(nullable NSDictionary *)property;

/// Add ok type custom log
/// - Parameters:
///   - content: Log content
///   - property: Custom attributes (optional)
-(void)ok:(NSString *)content property:(nullable NSDictionary *)property;
open class FTLogger : NSObject, FTLoggerProtocol {}
public protocol FTLoggerProtocol : NSObjectProtocol {
/// Add info type custom log
/// - Parameters:
///   - content: Log content
///   - property: Custom attributes (optional)
optional func info(_ content: String, property: [AnyHashable : Any]?)

/// Add warning type custom log
/// - Parameters:
///   - content: Log content
///   - property: Custom attributes (optional)
optional func warning(_ content: String, property: [AnyHashable : Any]?)

/// Add error type custom log
/// - Parameters:
///   - content: Log content
///   - property: Custom attributes (optional)
optional func error(_ content: String, property: [AnyHashable : Any]?)

/// Add critical type custom log
/// - Parameters:
///   - content: Log content
///   - property: Custom attributes (optional)
optional func critical(_ content: String, property: [AnyHashable : Any]?)

/// Add ok type custom log
/// - Parameters:
///   - content: Log content
///   - property: Custom attributes (optional)
optional func ok(_ content: String, property: [AnyHashable : Any]?)
}

Log Levels

///Event levels and statuses, default: FTStatusInfo
typedef NS_ENUM(NSInteger, FTLogStatus) {
    /// Information
    FTStatusInfo         = 0,
    /// Warning
    FTStatusWarning,
    /// Error
    FTStatusError,
    /// Critical
    FTStatusCritical,
    /// OK
    FTStatusOk,
};
///Event levels and statuses, default: FTStatusInfo
public enum FTLogStatus : Int, @unchecked Sendable {
    /// Information
    case statusInfo = 0
    /// Warning
    case statusWarning = 1
    /// Error
    case statusError = 2
    /// Critical
    case statusCritical = 3
    /// OK
    case statusOk = 4
}

Code Example

// Method one: Through FTSDKAgent
// Note: Ensure the SDK is initialized successfully when used, otherwise assertion failure may occur and cause a crash in the test environment.
[[FTSDKAgent sharedInstance] logging:@"test_custom" status:FTStatusInfo];

// Method two: Through FTLogger (recommended)
// If the SDK is not initialized successfully, calling methods in FTLogger to add custom logs will fail but won't cause assertion failure or crashes.
[[FTLogger sharedInstance] info:@"test" property:@{@"custom_key":@"custom_value"}];
// Method one: Through FTSDKAgent
// Note: Ensure the SDK is initialized successfully when used, otherwise assertion failure may occur and cause a crash in the test environment.
FTSDKAgent.sharedInstance().logging("contentStr", status: .statusInfo, property:["custom_key":"custom_value"])

// Method two: Through FTLogger (recommended)
// If the SDK is not initialized successfully, calling methods in FTLogger to add custom logs will fail but won't cause assertion failure or crashes.
FTLogger.shared().info("contentStr", property: ["custom_key":"custom_value"])

Custom Logs Output to Console

Set printCustomLogToConsole = YES to enable custom log output to the console. You will see the following formatted logs in the xcode debugging console:

2023-06-29 13:47:56.960021+0800 App[64731:44595791] [MACOS APP] [INFO] content ,{K=V,...,Kn=Vn}

2023-06-29 13:47:56.960021+0800 App[64731:44595791]: Standard prefix for os_log log output;

[MACOS APP]: Prefix to distinguish SDK output custom logs;

[INFO]: Level of custom logs;

content: Content of custom logs;

{K=V,...,Kn=Vn}: Custom attributes.

You can enable automatic mode through FTTraceConfig configuration, or manually add it. Trace-related data is passed through the FTTraceManager singleton. Relevant APIs are as follows:

Usage Method

// FTTraceManager.h

/// Get trace request header parameters
/// - Parameters:
///   - key: Unique identifier that can determine a specific request
///   - url: Request URL
/// - Returns: Dictionary of trace request header parameters
- (NSDictionary *)getTraceHeaderWithKey:(NSString *)key url:(NSURL *)url;
/// Get trace request header parameters
/// - Parameters:
///   - key: Unique identifier that can determine a specific request
///   - url: Request URL
/// - Returns: Dictionary of trace request header parameters
open func getTraceHeader(withKey key: String, url: URL) -> [AnyHashable : Any]

Code Example

```objectivec NSString *key = [[NSUUID UUID]UUIDString]; NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"]; //Manual operation needed: Obtain traceHeader before the request and add it to the request header NSDictionary *traceHeader = [[FTTraceManager sharedInstance] getTraceHeaderWithKey:key url:url]; NSMutableURLRequest *request =[previous content continued...]

```objectivec [NSMutableURLRequest requestWithURL:url]; if (traceHeader && traceHeader.allKeys.count>0) { [traceHeader enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) { [request setValue:value forHTTPHeaderField:field]; }]; } NSURLSession *session=[NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];

NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { //Your code }];

[task resume];

=== "Swift"

    ```swift
    let url:URL = NSURL.init(string: "https://www.baidu.com")! as URL
    if let traceHeader = FTTraceManager.sharedInstance().getTraceHeader(withKey: NSUUID().uuidString, url: url) {
         let request = NSMutableURLRequest(url: url)
         //Manual operation needed: Obtain traceHeader before the request and add it to the request header
         for (a,b) in traceHeader {
             request.setValue(b as? String, forHTTPHeaderField: a as! String)
         }
         let task = URLSession.shared.dataTask(with: request as URLRequest) {  data,  response,  error in
            //Your code
         }
         task.resume()
    }
    ```

## User Binding and Logout
### Usage Method

=== "Objective-C"

    ```objectivec
    /// Bind user information
    ///
    /// - Parameters:
    ///   - Id:  User ID
    ///   - userName: User name (optional)
    ///   - userEmail: User email (optional)
    ///   - extra: Additional user information (optional)
    - (void)bindUserWithUserID:(NSString *)Id userName:(nullable NSString *)userName userEmail:(nullable NSString *)userEmail extra:(nullable NSDictionary *)extra;

    /// Unbind current user
    - (void)unbindUser;
    ```

=== "Swift"

    ```swift
    /// Bind user information
    ///
    /// - Parameters:
    ///   - Id:  User ID
    ///   - userName: User name (optional)
    ///   - userEmail: User email (optional)
    ///   - extra: Additional user information (optional)
    open func bindUser(withUserID Id: String, userName: String?, userEmail: String?, extra: [AnyHashable : Any]?)

    /// Unbind current user
    open func unbindUser()
    ```

### Code Example

=== "Objective-C"

    ```objectivec
    // You can call this method after the user logs in successfully to bind user information
    [[FTSDKAgent sharedInstance] bindUserWithUserID:USERID];
    // or
    [[FTSDKAgent sharedInstance] bindUserWithUserID:USERID userName:USERNAME userEmail:USEREMAIL];
    // or
    [[FTSDKAgent sharedInstance] bindUserWithUserID:USERID userName:USERNAME userEmail:USEREMAIL extra:@{EXTRA_KEY:EXTRA_VALUE}];

    // You can call this method after the user logs out to unbind user information
    [[FTSDKAgent sharedInstance] unbindUser];
    ```
=== "Swift"

    ```swift
    // You can call this method after the user logs in successfully to bind user information
    FTSDKAgent.sharedInstance().bindUser(withUserID: USERID)
    // or
    FTSDKAgent.sharedInstance().bindUser(withUserID: USERID, userName: USERNAME, userEmail: USEREMAIL)
    // or
    FTSDKAgent.sharedInstance().bindUser(withUserID: USERID, userName: USERNAME, userEmail: USEREMAIL,extra:[EXTRA_KEY:EXTRA_VALUE])

    // You can call this method after the user logs out to unbind user information
    FTSDKAgent.sharedInstance().unbindUser()
    ```

## Close SDK

Use `FTSDKAgent` to close the SDK.

### Usage Method

=== "Objective-C"

    ```objective-c
    /// Close running objects within the SDK
    - (void)shutDown;
    ```

=== "Swift"

    ```swift
    /// Close running objects within the SDK
    func shutDown()
    ```
### Code Example

=== "Objective-C"

    ```objective-c
    // If dynamically changing SDK configurations, need to close first to avoid incorrect data generation
    [[FTSDKAgent sharedInstance] shutDown];
    ```  

=== "Swift"

    ```swift
    // If dynamically changing SDK configurations, need to close first to avoid incorrect data generation
    FTSDKAgent.sharedInstance().shutDown()
    ```
## Add Custom Tags {#user-global-context}

### Static Usage

You can create multiple Configurations and use preprocessor directives to set values:

1. Create multiple Configurations:

![](../img/image_9.png)

2. Set predefined attributes to distinguish different Configurations:

![](../img/image_10.png)

3. Use preprocessor directives:

```objectivec
//Target -> Build Settings -> GCC_PREPROCESSOR_DEFINITIONS configure predefined definitions
#if PRE
#define Track_id       @"0000000001"
#define STATIC_TAG     @"preprod"
#elif  DEVELOP
#define Track_id       @"0000000002"
#define STATIC_TAG     @"common"
#else
#define Track_id       @"0000000003"
#define STATIC_TAG     @"prod"
#endif

FTRumConfig *rumConfig = [[FTRumConfig alloc]init]; 
rumConfig.globalContext = @{@"track_id":Track_id,@"static_tag":STATIC_TAG};
... //Other setting operations
[[FTSDKAgent sharedInstance] startRumWithConfigOptions:rumConfig];

Dynamic Usage

Since the globalContext set after RUM starts won't take effect, users can save locally and set during the next app launch.

  1. Save files locally, such as using NSUserDefaults, configure and add code to get tag data when configuring the SDK.
NSString *dynamicTag = [[NSUserDefaults standardUserDefaults] valueForKey:@"DYNAMIC_TAG"]?:@"NO_VALUE";

FTRumConfig *rumConfig = [[FTRumConfig alloc]init];
rumConfig.globalContext = @{@"dynamic_tag":dynamicTag};
... //Other setting operations
[[FTSDKAgent sharedInstance] startRumWithConfigOptions:rumConfig];
  1. Add methods to change file data at any location.
 [[NSUserDefaults standardUserDefaults] setValue:@"dynamic_tags" forKey:@"DYNAMIC_TAG"];
  1. Finally, restart the application for changes to take effect.

Notes

  1. Special key: track_id (configured in RUM, used for tracking functionality)

  2. When users add custom tags via globalContext that overlap with SDK-owned tags, SDK tags will override user settings. It's recommended to prefix tag names with project abbreviations, such as df_tag_name.

  3. Set globalContext before calling -startRumWithConfigOptions to make it effective.

  4. Custom tags configured in FTSDKConfig will be added to all types of data.

For more detailed information, refer to the SDK Demo.

Frequently Asked Questions

About Crash Log Analysis

Non-modular Header Included Inside Framework Module Error Appears

Because the SDK's .h files include dependent library .h files, you need to set

Target -> Build Settings -> CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES to YES.

Feedback

Is this page helpful? ×