上篇文章Crash監控平臺Sentry的iOS SDK源碼解析(一)咱們已經瞭解到Sentry是如何捕獲各類異常事件的,其中最重要的一張圖以下
那麼這篇文章咱們就要去細讀異常事件是如何被處理和上報的。json
其中CrashMonitors
分類下的Monitor捕獲到的異常最後都會歸於一個函數那就是SentryCrashMonitor
的sentrycrashcm_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