Crash监控平台Sentry的iOS SDK源码解析(二)

回顾

上篇文章Crash监控平台Sentry的iOS SDK源码解析(一)咱们已经了解到Sentry是如何捕获各类异常事件的,其中最重要的一张图以下

那么这篇文章咱们就要去细读异常事件是如何被处理和上报的。json

源码解析

异常事件归一

其中CrashMonitors分类下的Monitor捕获到的异常最后都会归于一个函数那就是SentryCrashMonitorsentrycrashcm_handleException函数,代码解读以下缓存

void sentrycrashcm_handleException(struct SentryCrash_MonitorContext* context) {
    context->requiresAsyncSafety = g_requiresAsyncSafety;
    //若是在处理异常的过程当中发生了第二个异常
    if(g_crashedDuringExceptionHandling)
    {
        context->crashedDuringCrashHandling = true;
    }
    //遍历全部Monitors,若是Monitor是打开的,就给异常事件添加上下文环境
    for(int i = 0; i < g_monitorsCount; i++)
    {
        Monitor* monitor = &g_monitors[i];
        if(isMonitorEnabled(monitor))
        {
            addContextualInfoToEvent(monitor, context);
        }
    }

    //处理异常事件
    g_onExceptionEvent(context);

    if (context->currentSnapshotUserReported) {
        g_handlingFatalException = false;
    } else {
        //若是不是用户自主上报的异常将关闭全部Monitor
        if(g_handlingFatalException && !g_crashedDuringExceptionHandling) {
            SentryCrashLOG_DEBUG("Exception is fatal. Restoring original handlers.");
            sentrycrashcm_setActiveMonitors(SentryCrashMonitorTypeNone);
        }
    }
}
复制代码

其中添加事件上下文的函数addContextualInfoToEvent主要用于记录异常现场数据,例如SentryCrashMonitor_AppState记录APP状态信息的代码session

static void addContextualInfoToEvent(SentryCrash_MonitorContext* eventContext) {
    if(g_isEnabled)
    {
        eventContext->AppState.activeDurationSinceLastCrash = g_state.activeDurationSinceLastCrash;
        eventContext->AppState.activeDurationSinceLaunch = g_state.activeDurationSinceLaunch;
        eventContext->AppState.applicationIsActive = g_state.applicationIsActive;
        eventContext->AppState.applicationIsInForeground = g_state.applicationIsInForeground;
        eventContext->AppState.appStateTransitionTime = g_state.appStateTransitionTime;
        eventContext->AppState.backgroundDurationSinceLastCrash = g_state.backgroundDurationSinceLastCrash;
        eventContext->AppState.backgroundDurationSinceLaunch = g_state.backgroundDurationSinceLaunch;
        eventContext->AppState.crashedLastLaunch = g_state.crashedLastLaunch;
        eventContext->AppState.crashedThisLaunch = g_state.crashedThisLaunch;
        eventContext->AppState.launchesSinceLastCrash = g_state.launchesSinceLastCrash;
        eventContext->AppState.sessionsSinceLastCrash = g_state.sessionsSinceLastCrash;
        eventContext->AppState.sessionsSinceLaunch = g_state.sessionsSinceLaunch;
    }
}
复制代码

其中g_onExceptionEvent是一个函数指针,用于处理异常事件的。该函数指针在初始化的时候已经赋值,具体以下app

SentryCrashMonitorType sentrycrash_install(const char* appName, const char* const installPath) {
    ...
    //设置各类数据的缓存地址
    char path[SentryCrashFU_MAX_PATH_LENGTH];
    snprintf(path, sizeof(path), "%s/Reports", installPath);
    sentrycrashfu_makePath(path);
    sentrycrashcrs_initialize(appName, path);

    snprintf(path, sizeof(path), "%s/Data", installPath);
    sentrycrashfu_makePath(path);
    snprintf(path, sizeof(path), "%s/Data/CrashState.json", installPath);
    sentrycrashstate_initialize(path);

    snprintf(g_consoleLogPath, sizeof(g_consoleLogPath), "%s/Data/ConsoleLog.txt", installPath);
    if(g_shouldPrintPreviousLog)
    {
        printPreviousLog(g_consoleLogPath);
    }
    sentrycrashlog_setLogFilename(g_consoleLogPath, true);

    //设置日志发送的时间周期为60秒
    sentrycrashccd_init(60);

    //设置异常事件处理回调函数
    sentrycrashcm_setEventCallback(onCrash);
    //打开各类异常捕获Monitors
    SentryCrashMonitorType monitors = sentrycrash_setMonitoring(g_monitoring);

    SentryCrashLOG_DEBUG("Installation complete.");
    return monitors;
}
复制代码

事件持久化

咱们来看看上文设置的事件处理回调函数onCrash函数

static void onCrash(struct SentryCrash_MonitorContext* monitorContext) {
    //若是不是用户主动上报异常,更新并记录APP crash状态
    if (monitorContext->currentSnapshotUserReported == false) {
        SentryCrashLOG_DEBUG("Updating application state to note crash.");
        sentrycrashstate_notifyAppCrash();
    }
    monitorContext->consoleLogPath = g_shouldAddConsoleLogToReport ? g_consoleLogPath : NULL;

    if(monitorContext->crashedDuringCrashHandling)
    {
        //若是在处理异常事件的过程当中出现了二次异常,就记录最后的一次异常
        sentrycrashreport_writeRecrashReport(monitorContext, g_lastCrashReportFilePath);
    }
    else
    {
        char crashReportFilePath[SentryCrashFU_MAX_PATH_LENGTH];
        //获取异常事件记录的日志路径
        sentrycrashcrs_getNextCrashReportPath(crashReportFilePath);
        strncpy(g_lastCrashReportFilePath, crashReportFilePath, sizeof(g_lastCrashReportFilePath));
        //将异常事件写入文件
        sentrycrashreport_writeStandardReport(monitorContext, crashReportFilePath);
    }
}
复制代码

事件发送

在APP Crash的时候记录异常,那什么时机去发送异常日志呢?细心的同窗可能在上一篇文章的时候已经发现了,就是在APP下一次启动Sentry初始化的时候啦!在Sentry初始化的地方咱们能够看到post

- (BOOL)startCrashHandlerWithError:(NSError *_Nullable *_Nullable)error {
    [SentryLog logWithMessage:@"SentryCrashHandler started" andLevel:kSentryLogLevelDebug];
    static dispatch_once_t onceToken = 0;
    dispatch_once(&onceToken, ^{
        installation = [[SentryInstallation alloc] init];
        [installation install];
        //发送全部缓存的日志
        [installation sendAllReports];
    });
    return YES;
}
复制代码

主要调用的函数时序图以下 ui

发送的核心代码以下spa

- (void) sendAllReportsWithCompletion:(SentryCrashReportFilterCompletion) onCompletion
{
    //获取全部异常日志
    NSArray* reports = [self allReports];

    SentryCrashLOG_INFO(@"Sending %d crash reports", [reports count]);

    //发送全部日志
    [self sendReports:reports
         onCompletion:^(NSArray* filteredReports, BOOL completed, NSError* error)
     {
         SentryCrashLOG_DEBUG(@"Process finished with completion: %d", completed);
         if(error != nil)
         {
             SentryCrashLOG_ERROR(@"Failed to send reports: %@", error);
         }
         if((self.deleteBehaviorAfterSendAll == SentryCrashCDeleteOnSucess && completed) ||
            self.deleteBehaviorAfterSendAll == SentryCrashCDeleteAlways)
         {
             //发送成功后删除全部日志
             sentrycrash_deleteAllReports();
         }
         //执行回调
         sentrycrash_callCompletion(onCompletion, filteredReports, completed, error);
     }];
}

复制代码

总结

总的看下来,Sentry的Crash日志采集的逻辑仍是比较简单的,就是拦截Crash事件,记录并上报。看上去很简单,可是实际门槛仍是比较高的,好比涉及到内核的方法都不是很常见,因此仍是须要不少时间去消化。3d

相关文章
相关标签/搜索