上篇文章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