iOS奔潰問題處理經(jīng)驗

面對形形色色的奔潰問題鸣哀,作為一個老碼農(nóng),從最初的不知所措吞彤,慢慢也學會了和其共存共生诺舔。畢竟奔潰抓不完,但如何更好地抓奔潰卻是個永恒的話題备畦。從iOS發(fā)展的這數(shù)年來,關于奔潰的處理早有成熟與完整的解決方案许昨,而此次實踐懂盐,莫如說是給這個方案再增添一些小小的裝飾罷了。

  1. 收集奔潰
    收集崩潰大致有以下幾種方式:
    A. 蘋果自帶奔潰收集系統(tǒng)糕档。通過iTunes Connect(Manage Your Applications - View Details - Crash Reports)打開奔潰控制開關莉恼,用戶同意隱私控制后即可收集奔潰。由于需要用戶主動認可速那,此方式能收集的奔潰并不太多俐银。
    B. 第三方奔潰收集平臺。本人常用Fabric的Crashlytics端仰,這個平臺的優(yōu)點在于捶惜,除收集奔潰信息外,能多維度產(chǎn)生日活荔烧,奔潰數(shù)據(jù)的日吱七,周苞轿,月等圖線仿畸,有助于開發(fā)乃至產(chǎn)品分析。
    C.自己開發(fā)的奔潰收集平臺。在NSException類提供的NSSetUncaughtExceptionHandler函數(shù)設置奔潰截獲代碼微饥,即可在奔潰發(fā)生時執(zhí)行自定義的奔潰處理,常見的奔潰處理信息可以包含奔潰現(xiàn)場的call stack茬斧,界面信息威蕉,用戶信息,業(yè)務信息等窜管,可視各產(chǎn)品的需要來自己定制散劫。
  2. 奔潰分析
    以下是Crashlytics中一段常見的奔潰日志:

常見的奔潰信息

奔潰信息包括發(fā)生時間,奔潰類型微峰,最后停留的代碼位置及奔潰原因舷丹,以及奔潰代碼的call stack信息。
有一般經(jīng)驗的開發(fā)人員蜓肆,對上面的奔潰處理應該會比較得心應手颜凯。這就是一個函數(shù)名無效的錯誤,原因是數(shù)據(jù)類型不是期待的NSNumber型而變成了NSNull仗扬,這類錯誤的處理應該是比較簡單的症概。
那下面這個呢?

完全不知道怎么回事早芭,有沒有彼城?
僅有的線索:1. iOS7專享crash 2. 某一個UITextField輸入框的自動布局沒有觸發(fā) 。怎么查退个。如同大海撈針募壕。
有沒有更進一步的線索呢?其實可以有的语盈。
當我們做應用埋點統(tǒng)計的時候舱馅,常常想埋得越全越好,因為產(chǎn)品總會不停得增加埋點刀荒,最后還不如一次性全覆蓋到代嗤。那奔潰日志是不是也可以參考這種模式?打印出奔潰當時的ViewController名字怎么樣缠借?
方式也非常簡單干毅。ViewController的名字,可以直接通過取它的類名泼返。獲取的時機硝逢,比較適合的是viewWillAppear,并且也可以用swizzling的方式全局獲得。當然趴捅,如果頁面共用很多垫毙,繼承關系復雜的情況下,還是建議到每個頁面自己去獲取吧拱绑。比如:

- (void) viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
//設置主線程名字综芥,crash時記錄此name,可提高crash發(fā)現(xiàn)的幾率
  NSString*className = NSStringFromClass([self class]);
  if(className){
    [[NSThread mainThread] setName:className];
  }
}

非常簡單的代碼猎拨,就把主線程的名字替換成了當前ViewContronller的名字膀藐。再上線抓奔潰,結果就是這樣:

是不是大大縮小了范圍红省。一個小小的技巧能給查奔潰帶來多大的效益呢额各。

  1. 自定義加強版的內測奔潰收集
    內部測試時,用Crashlytics當然也是可以的吧恃。但第三方奔潰收集在和用戶交互方面是一個短板虾啦。當你老板在用你的應用突然奔潰時,他的怒不可遏是可以想象的痕寓。然后他耐心的打來電話要報告這個奔潰傲醉,你卻告訴他你只能看到一堆奔潰日志,看不到他在哪個界面呻率,操作哪個按鈕硬毕,發(fā)送的哪個請求,輸入了什么文字礼仗,反正是什么都不知道吐咳,你覺得老板年底能放過你嗎?
    對于內測用戶元践,稍許復雜的反饋機制是可以接受的韭脊,因為大家的目的都是為了改良產(chǎn)品。所以可以適當增加一些反饋的信息单旁,我們比較推薦的是在奔潰時乾蓬,除常規(guī)的奔潰日志,可以增加log日志慎恒,屏幕抓圖這兩項內容。
    A. log日志的保存及獲得:
    采用CocoaLumberjack這類第三方庫打印log是比較合適的方案撵渡,根據(jù)需要融柬,CocoaLumberjack可以打印log到文件,在奔潰的時候趋距,取log文件直接發(fā)送即可:
    NSArray *loggers = [DDLog allLoggers];
    for (id logger in loggers){
        if ([logger isKindOfClass:[DDFileLogger class]]){
            NSString *logPath = ((DDFileLogger *)logger).logFileManager.logsDirectory;
            NSData *logData = [NSData dataWithContentsOfFile:logPath];
//ToDo粒氧,增加代碼發(fā)送log文件到奔潰平臺
        }
    }

B.屏幕抓圖是還原奔潰現(xiàn)場的一個有效的信息,一般奔潰平臺限于圖片文件過大节腐,以及泄漏隱私的問題外盯,很少提供屏幕抓圖功能摘盆。內測環(huán)境建議自行加上奔潰時的抓圖,方便開發(fā)定位界面:

UIGraphicsBeginImageContext([UIScreen mainScreen].bounds.size);
UIGraphicsBeginImageContextWithOptions([UIScreen mainScreen].bounds.size, NO, 0.0);
[self.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *libraryPath = [paths objectAtIndex:0];
NSString *path = [libraryPath stringByAppendingPathComponent:@"crashSnap.jpg"];
[UIImageJPEGRepresentation(image, 1.0) writeToFile:path atomically:YES];

C.奔潰現(xiàn)場抓缺ス丁:奔潰日志可以采用NSException類孩擂,設置奔潰處理函數(shù):

NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
void uncaughtExceptionHandler(NSException *exception) {
     NSLog(@"%@", [NSString stringWithFormat:@"MainThread Name: %@\n%@ \n %@", [NSThread mainThread].name, exception, exception.callStackSymbols]);
}

D.發(fā)送到收集奔潰渠道
收集奔潰的渠道很多,除去那些商用的以及免費的不說箱熬,常見的可以由應用服務器開一個接口來接收奔潰數(shù)據(jù)类垦。這里介紹一種更適合iOS開發(fā)者以及個人的低成本的接受渠道,就是傳統(tǒng)的郵件城须。
通過郵件收集奔潰有不少好處蚤认,首先你不要集成那些龐大的sdk,也不用給后端提需求糕伐,只要自己默默地注冊一個郵箱砰琢。而且郵件能傳送的數(shù)據(jù)也比一般的后臺接口廣泛,文本良瞧,圖片陪汽,二進制文件都可以。展示上也可以根據(jù)需要自由選擇頁面或者客戶端莺褒。
發(fā)送郵件通常采用SMTP協(xié)議掩缓,遺憾的是現(xiàn)在許多免費郵箱都加強了SMTP的驗證碼機制,因此網(wǎng)易遵岩,騰訊你辣,新浪等主流郵箱已經(jīng)不能用,谷歌等被墻的更不必說尘执,搜狐的似乎還是可以舍哄。
發(fā)送郵件我們參考了SKPSMTPMessage這個項目,并改寫了一些不能使用的方法誊锭。整個流程并不復雜表悬,根據(jù)SMTP協(xié)議的要求,發(fā)起握手丧靡,傳輸標題蟆沫、地址等,繼續(xù)傳輸正文温治,附件饭庞,然后結束。

一個SMTP傳輸示例:

S: 220 www.example.com ESMTP Postfix
C: HELO mydomain.com
S: 250 Hello mydomain.com
C: MAIL FROM: <sender@mydomain.com>
S: 250 Ok
C: RCPT TO: <friend@example.com>
S: 250 Ok
C: DATA
S: 354 End data with <CR><LF>.<CR><LF>
C: Subject: test message
C: From:""< sender@mydomain.com>
C: To:""< friend@example.com>
C:
C: Hello,
C: This is a test.
C: Goodbye.
C: .
S: 250 Ok: queued as 12345
C: quit
S: 221 Bye

郵件發(fā)送的代碼:

#import "MailSender.h"


@interface PBCrashReporter () <MailSenderDelegate>
@end

@implementation PBCrashReporter
- (void)sendFeedbackEmail
{
    MailSender *mailSender = [[MailSender alloc] init];
    mailSender.fromEmail = @"xxx@sohu.com";
    mailSender.toEmail = @"xxx@sohu.com";
    mailSender.relayHost = @"smtp.sohu.com";
    mailSender.requiresAuth = YES;
    mailSender.login = @"xxx@sohu.com";
    mailSender.pass = @"xxxxxx";
    mailSender.wantsSecure = NO;
    
    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
    NSString *userId =  [defaults stringForKey:kUserId];
    if (userId){
        mailSender.fromName = userId;
    }
    
    mailSender.subject = @"奔潰收集郵件";
    mailSender.delegate = self;
    
    
    NSDictionary *plainPart = [NSDictionary dictionaryWithObjectsAndKeys:@"text/plain; charset=UTF-8",smtpPartContentTypeKey,
                               @"crash日志熬荆,詳情見附件",smtpPartMessageKey,@"8bit",smtpPartContentTransferEncodingKey,nil];
    NSString *vcf1Path = [PBCrashReporter pathOfReportFile];
    NSData *vcf1Data = [NSData dataWithContentsOfFile:vcf1Path];
    

    NSDictionary *vcf1Part = [NSDictionary dictionaryWithObjectsAndKeys:@"text/directory;\r\n\tx-unix-mode=0644;\r\n\tname=\"crash.txt\"",smtpPartContentTypeKey,
                             @"attachment;\r\n\tfilename=\"crash.txt\"",smtpPartContentDispositionKey,[vcf1Data base64EncodedStringWithOptions:0],smtpPartMessageKey,@"base64",smtpPartContentTransferEncodingKey,nil];

    
    mailSender.parts = [NSArray arrayWithObjects:plainPart,vcf1Part,vcf2Part,nil];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [mailSender sendMail];
    });
}

- (void)mailSent:(JFMailSender *)message
{
    //if something must run in main thread,please use dispatch_get_main_queue();
    NSLog(@"Yay! Message was sent!");
    [[NSFileManager defaultManager] removeItemAtPath:[PBCrashReporter pathOfReportFile] error:nil];
    [[NSFileManager defaultManager] removeItemAtPath:[PBCrashReporter pathOfSnapFile] error:nil];
}

- (void)mailFailed:(JFMailSender *)message error:(NSError *)error
{
    //if something must run in main thread,please use dispatch_get_main_queue();
    NSLog(@"%@", [NSString stringWithFormat:@"Darn! Error!\n%li: %@\n%@", (long)[error code], [error localizedDescription], [error localizedRecoverySuggestion]]);
}
@end

crash符號表解析
通過上面方法舟山,自己收集到的奔潰日志,都是沒有經(jīng)過解析的地址堆棧。需要轉換為函數(shù)名的堆棧信息累盗,才能方便地找出問題所在寒矿。最方便使用的符號表解析工具是Xcode自帶的symbolicatecrash。
這個工具的使用方法已經(jīng)有很多教程若债,這里我們給出一個最容易記憶的方法符相,就是兩個素材,一個工具拆座,一條命令主巍。
素材1:奔潰日志文件,可以是我們自己生成的crash日志文件
素材2: dSYM文件挪凑,打包時產(chǎn)生的符號地址映射文件
工具:symbolicatecrash
命令:


/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash  ./*.crash ./*.app.dSYM > symbol.crash

產(chǎn)生一個新的crash日志文件孕索,就已經(jīng)是完成符號轉換后的了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末躏碳,一起剝皮案震驚了整個濱河市搞旭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌菇绵,老刑警劉巖肄渗,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異咬最,居然都是意外死亡翎嫡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門永乌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惑申,“玉大人,你說我怎么就攤上這事翅雏∪ν眨” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵望几,是天一觀的道長绩脆。 經(jīng)常有香客問我,道長橄抹,這世上最難降的妖魔是什么靴迫? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮楼誓,結果婚禮上矢劲,老公的妹妹穿的比我還像新娘。我一直安慰自己慌随,他們只是感情好,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著阁猜,像睡著了一般丸逸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上剃袍,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天黄刚,我揣著相機與錄音,去河邊找鬼民效。 笑死憔维,一個胖子當著我的面吹牛,可吹牛的內容都是我干的畏邢。 我是一名探鬼主播业扒,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼舒萎!你這毒婦竟也來了程储?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤臂寝,失蹤者是張志新(化名)和其女友劉穎章鲤,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咆贬,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡败徊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了掏缎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片皱蹦。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖御毅,靈堂內的尸體忽然破棺而出根欧,到底是詐尸還是另有隱情,我是刑警寧澤端蛆,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布凤粗,位于F島的核電站,受9級特大地震影響今豆,放射性物質發(fā)生泄漏嫌拣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一呆躲、第九天 我趴在偏房一處隱蔽的房頂上張望异逐。 院中可真熱鬧,春花似錦插掂、人聲如沸灰瞻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酝润。三九已至燎竖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間要销,已是汗流浹背构回。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留疏咐,地道東北人纤掸。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像浑塞,于是被迫代替她去往敵國和親借跪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理缩举,服務發(fā)現(xiàn)垦梆,斷路器,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 前言 iOS崩潰是讓iOS開發(fā)人員比較頭痛的事情仅孩,app崩潰了托猩,說明代碼寫的有問題,這時如何快速定位到崩潰的地方很...
    齊滇大圣閱讀 65,326評論 29 443
  • 前言 崩潰是讓發(fā)人員比較頭痛的事情辽慕,app崩潰了京腥,說明代碼寫的有問題,這時如何快速定位到崩潰的地方很重要溅蛉。調試階段...
    進無盡閱讀 2,018評論 0 9
  • 從三月份找實習到現(xiàn)在公浪,面了一些公司,掛了不少船侧,但最終還是拿到小米欠气、百度、阿里镜撩、京東预柒、新浪、CVTE袁梗、樂視家的研發(fā)崗...
    時芥藍閱讀 42,243評論 11 349
  • 1宜鸯、第八章 Samba服務器2、第八章 NFS服務器3遮怜、第十章 Linux下DNS服務器配站點淋袖,域名解析概念命令:...
    哈熝少主閱讀 3,732評論 0 10