iOS集成Sentry進(jìn)行異常收集


異常捕獲/收集的平臺(tái)有很多,我們選用了Sentry度气;Sentry支持搭建在自己的服務(wù)器上(self-hosted)墨榄,支持多種編程語(yǔ)言,號(hào)稱是有超過(guò)5萬(wàn)家公司的100萬(wàn)名開(kāi)發(fā)人員在使用即横;Sentry提供了3種類型賬號(hào):Developer,Team,Business战授;Developer類型是免費(fèi)的,但功能有限,且異常記錄每個(gè)月最多5K條(這個(gè)數(shù)量栓始,筆者還特意測(cè)試了下臊旭,異常記錄到達(dá)5K后滋戳,記錄列表將會(huì)提示讓你升級(jí)到付費(fèi)版)娄涩;如果免費(fèi)版不能滿足要求孔轴,就需付費(fèi)使用;有條件的最好是自建服務(wù)器晋柱,異常記錄數(shù)是沒(méi)有限制的优构;關(guān)于self-hosted搭建官方文檔有詳細(xì)的教程,本文我們主要只講解iOS端集成Sentry的過(guò)程雁竞;

創(chuàng)建項(xiàng)目

Sentry官網(wǎng)注冊(cè)賬號(hào)钦椭,創(chuàng)建一個(gè)Objective-C或者Swift項(xiàng)目;

然后找到項(xiàng)目設(shè)置選擇Client Keys(DSN)碑诉,復(fù)制DSN后面代碼中需要用到(如果是self-hosted則是Public DSN)彪腔;

代碼實(shí)現(xiàn)

pod導(dǎo)入Sentry庫(kù);
appDelegate的didFinishLaunchingWithOptions方法中啟動(dòng)sentry捕獲进栽;

- (void)startSentry {
    NSError *error = nil;
    // 根據(jù)DSN創(chuàng)建SentryClient
    SentryClient *client = [[SentryClient alloc] initWithDsn:kSentryDSN didFailWithError:&error];
    SentryClient.sharedClient = client;
    [SentryClient.sharedClient startCrashHandlerWithError:&error];
    
    if (nil != error) {
        NSLog(@"%@", error);
    }
}

Sentry提供了一系列屬性德挣,供我們自定義一些信息;

SentryClient.sharedClient.environment = environment; // 環(huán)境 例如:debug
[SentryClient.sharedClient enableAutomaticBreadcrumbTracking]; // 開(kāi)啟面包屑功能
SentryClient.sharedClient.maxBreadcrumbs = 30; // 面包屑最多棧數(shù)

// 用戶信息
SentryUser *user = [[SentryUser alloc] initWithUserId:guid]; // 日志記錄以此區(qū)別快毛、歸類不同用戶
user.username = userName;
user.extra = @{@"cellphone":cellphone}; // 自定義字段用戶信息
SentryClient.sharedClient.user = user;

SentryClient.sharedClient.extra = @{@"other":otherMsg}; // 自定義字段信息

至此就已經(jīng)實(shí)現(xiàn)對(duì)異常的監(jiān)聽(tīng)格嗅、捕獲了;

源碼窺探

sentry比較強(qiáng)大唠帝,監(jiān)聽(tīng)了各種各樣情況的Crash異常屯掖;從源碼中可以大致窺探其支持的Crash異常類型:

這其中包括C++、死鎖襟衰、僵尸對(duì)象等等異常贴铜;我們比較熟悉的可能就是NSException了,它只包括Foundation框架的比如數(shù)組越界右蒲、數(shù)組,字典插入nil對(duì)象等情況阀湿;接下來(lái)我們就看下SentryCrashMonitor_NSException源碼實(shí)現(xiàn),(其他類型暫時(shí)不管,看著頭大);

g_isEnabled = isEnabled;
if(isEnabled)
{
    SentryCrashLOG_DEBUG(@"Backing up original handler.");
    g_previousUncaughtExceptionHandler = NSGetUncaughtExceptionHandler();

    SentryCrashLOG_DEBUG(@"Setting new handler.");
    NSSetUncaughtExceptionHandler(&handleUncaughtException);
    SentryCrash.sharedInstance.uncaughtExceptionHandler = &handleUncaughtException;
    SentryCrash.sharedInstance.currentSnapshotUserReportedExceptionHandler = &handleCurrentSnapshotUserReportedException;
}

如果開(kāi)啟捕獲(調(diào)試階段sentry是不開(kāi)啟捕獲的)瑰妄,則先使用g_previousUncaughtExceptionHandler記錄之前捕獲異常的函數(shù)指針陷嘴;然后通過(guò)NSSetUncaughtExceptionHandler設(shè)置sentry的異常捕獲函數(shù);

異常發(fā)生時(shí)就會(huì)調(diào)用函數(shù)

static void handleException(NSException* exception, BOOL currentSnapshotUserReported) {
    SentryCrashLOG_DEBUG(@"Trapped exception %@", exception);
    if(g_isEnabled)
    {
        ....
        
        SentryCrash_MonitorContext* crashContext = &g_monitorContext;
        memset(crashContext, 0, sizeof(*crashContext));
        crashContext->crashType = SentryCrashMonitorTypeNSException;
        crashContext->eventID = eventID;
        crashContext->offendingMachineContext = machineContext;
        crashContext->registersAreValid = false;
        crashContext->NSException.name = [[exception name] UTF8String];
        crashContext->NSException.userInfo = [[NSString stringWithFormat:@"%@", exception.userInfo] UTF8String];
        
        ...
        
        if (g_previousUncaughtExceptionHandler != NULL)
        {
            SentryCrashLOG_DEBUG(@"Calling original exception handler.");
            g_previousUncaughtExceptionHandler(exception);
        }
    }
}

主要配置一些異常的信息间坐,然后將信息存儲(chǔ)起來(lái)灾挨;以備下次啟動(dòng)應(yīng)用時(shí)再調(diào)用接口上傳這些數(shù)據(jù);最后再調(diào)用之前的捕獲異常函數(shù)竹宋,這里主要的作用就是兼容其他異常捕獲功能劳澄;因?yàn)槠渌a也可能調(diào)用了NSSetUncaughtExceptionHandler設(shè)置捕獲函數(shù);

自定義Events

除了捕獲異常蜈七,sentry還支持發(fā)送自定義的日志信息秒拔,比如網(wǎng)絡(luò)請(qǐng)求失敗就可以將失敗信息上傳;

SentryEvent *event = [[SentryEvent alloc] initWithLevel:kSentrySeverityWarning]; // 指定事件的嚴(yán)重級(jí)別 Fatal/Error/Warnig
event.message = message; // 錯(cuò)誤信息
event.environment = environment;
event.extra =@{@"url":url,@"code":@(code),@"param":param}; // 自定義字段
[SentryClient.sharedClient sendEvent:event withCompletionHandler:^(NSError * _Nullable error) {
    if (nil != error) {
        NSLog(@"%@", error);
    }
}];
測(cè)試

sentry代碼都寫好后飒硅,我們手動(dòng)拋個(gè)異常測(cè)下sentry平臺(tái)是否能正常統(tǒng)計(jì)異常的數(shù)據(jù)砂缩;
一切沒(méi)問(wèn)題的話作谚,后臺(tái)我們就能看到日志記錄了:

  • 面包屑信息
  • 方法調(diào)用棧

因?yàn)槲覀冞€沒(méi)有上傳dSYM符號(hào)文件,sentry不能解析crash日志定位到具體方法庵芭;

上傳dSYM文件

sentry平臺(tái)創(chuàng)建Token:User Setting ----> Auth Tokens ---> Create New Token妹懒;創(chuàng)建時(shí)按需勾選選項(xiàng);

拿到Token后就可以上傳文件了

有兩種上傳方式

  • shell腳本上傳
  1. 先打包双吆,然后拿到dSYM文件眨唬;
Archives列表選擇Show in Finder
顯示包內(nèi)容
  1. 安裝sentry-cli
    brew install getsentry/tools/sentry-cli
  2. 編寫并執(zhí)行以下腳本即可,其中URL如果是sentry服務(wù)器則是https://sentry.io好乐,如果是self-hosted則填寫自己服務(wù)器url匾竿;
sentry-cli --url URL --auth-token YOUR_TOKEN upload-dif --org YOUR_ORG --project  YOUR_PROJECT   dSYM_PATH --log-level=debug
  • 通過(guò)fastlane上傳
  1. 安裝fastlane
    sudo gem install fastlane -NV或是brew cask install fastlane命令安裝;
    安裝完后執(zhí)行命令fastlane --version蔚万,確認(rèn)安裝成功搂橙;
  2. 初始化fastlane
    cd到項(xiàng)目目錄下,執(zhí)行命令fastlane init;

這里有4個(gè)選項(xiàng)笛坦,因?yàn)槲覀冃枰闹皇巧蟼鱠SYM文件選擇4就可以了;如果需要能自動(dòng)打包并提交到AppStore功能則可以選3苔巨;選擇3后續(xù)會(huì)要求配置Apple ID相關(guān)信息版扩;

初始化成功后,項(xiàng)目目錄下會(huì)有一個(gè)fastlane文件侄泽;

  1. 編輯Fastfile文件庸毫,編寫腳本
default_platform(:ios)

platform :ios do
  desc "上傳到sentry"
  lane :upload_symbols do
  sentry_api_host = "http://sentry.io”
  org_slug = “YOUR_ORG”
  project_slug = “YOUR_PROJECT”
  auth_token = “YOUR_TOKEN”
  #download_dsyms
  gym(
      scheme: “YOUR_SCHEME”,
      workspace: “xxxx.xcworkspace",
      include_bitcode: false #根據(jù)項(xiàng)目bitcode設(shè)置情況
      )
  sentry_upload_dsym(
    url: "#{sentry_api_host}",
    auth_token: "#{auth_token}",
    org_slug: "#{org_slug}",
    project_slug: "#{project_slug}"
  )
  end
end
  1. 打包并上傳
    cd到項(xiàng)目中fastlane目錄下沟于,執(zhí)行命令fastlane sentry_upload_dsym;這步將會(huì)自動(dòng)打包并拿到dSYM文件上傳到sentry(省去手動(dòng)打包這個(gè)步驟);

上傳成功后膘流,sentry項(xiàng)目設(shè)置中Debug Files就能看到文件了

之后再次捕獲crash異常,查看堆棧信息就已經(jīng)能解析出具體的方法了:


fastlane更多詳細(xì)使用可參考:
iOS效率神器fastlane自動(dòng)打包

網(wǎng)上沒(méi)有找到關(guān)于sentry原理分析的文章鸟缕,但各種異常收集框架原理大致相同鸭限,這里有篇講解KSCrash的也可作參考;
KSCrash崩潰收集原理淺析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末析桥,一起剝皮案震驚了整個(gè)濱河市司草,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌泡仗,老刑警劉巖埋虹,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異娩怎,居然都是意外死亡搔课,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門截亦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)爬泥,“玉大人柬讨,你說(shuō)我怎么就攤上這事〖泵穑” “怎么了姐浮?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)葬馋。 經(jīng)常有香客問(wèn)我卖鲤,道長(zhǎng),這世上最難降的妖魔是什么畴嘶? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任蛋逾,我火速辦了婚禮,結(jié)果婚禮上窗悯,老公的妹妹穿的比我還像新娘区匣。我一直安慰自己,他們只是感情好蒋院,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布亏钩。 她就那樣靜靜地躺著,像睡著了一般欺旧。 火紅的嫁衣襯著肌膚如雪姑丑。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天辞友,我揣著相機(jī)與錄音栅哀,去河邊找鬼。 笑死称龙,一個(gè)胖子當(dāng)著我的面吹牛留拾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鲫尊,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼痴柔,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了马昨?” 一聲冷哼從身側(cè)響起竞帽,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鸿捧,沒(méi)想到半個(gè)月后屹篓,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡匙奴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年堆巧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谍肤,死狀恐怖啦租,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情荒揣,我是刑警寧澤篷角,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站系任,受9級(jí)特大地震影響恳蹲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜俩滥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一嘉蕾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧霜旧,春花似錦错忱、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至崎逃,卻和暖如春玖媚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背婚脱。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留勺像,地道東北人障贸。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像吟宦,于是被迫代替她去往敵國(guó)和親篮洁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 前序 這玩意我是真的不太喜歡殃姓,但是應(yīng)領(lǐng)導(dǎo)需求袁波,因?yàn)檫@玩意可以架在自己的服務(wù)器上,從某種程度上能夠避免自己項(xiàng)目的信息...
    a_只羊閱讀 5,269評(píng)論 8 5
  • 主要內(nèi)容 閃退捕獲 日志分析 閃退捕獲 內(nèi)核級(jí)異常:Mach異常->Unit信號(hào)(Mach層捕獲到異常通過(guò)發(fā)送信號(hào)...
    mtry閱讀 1,405評(píng)論 0 1
  • 本博客講講App異常監(jiān)控蜗侈,每個(gè)app都要保證使用質(zhì)量篷牌,這樣才能保住用戶量,所以對(duì)于應(yīng)用程序的監(jiān)控顯得尤為重要踏幻。想象...
    Hozan閱讀 9,265評(píng)論 5 12
  • 本文整理下最近對(duì)于crash采集的總結(jié)枷颊,和踩過(guò)的坑。 CrashReporter 首先,iOS有自己的CrashR...
    談Xx閱讀 19,739評(píng)論 15 66
  • 前言 崩潰是讓發(fā)人員比較頭痛的事情夭苗,app崩潰了信卡,說(shuō)明代碼寫的有問(wèn)題,這時(shí)如何快速定位到崩潰的地方很重要题造。調(diào)試階段...
    進(jìn)無(wú)盡閱讀 1,974評(píng)論 0 9