iOS - crash日志抓取保存

  • 前言

曾经我以为我的程序可以一帆风顺,知道我们遇到了你-crash,那是一段令人呕吐的回忆。

  • 背景

  • 在实际项目开发中,我们会遇到很多不同的问题,有写嘛随着编程经验丰富,随手就可以解决了,但是如果你的程序以及发布到商店了,并且已经通过审核,某天某个客户反馈:“你这个是个什么XX%%XX%%,辣鸡,闪退”;又或者把程序给测试人员了,那边反应闪退了,but 没有现场,不知道原因,排查起来那也是一件很忧伤的事情。

  • 结论

  • 综上所述,我们还是有必要写一个抓取奔溃信息的东东的:
    第一:能让你的程序越来越强壮
    第二:能在一定程度上让你快速修复BUG
    第三:能让你知道你踏了哪些坑
    第四:还能和测试妹子多沟通不是(你把手机拿过来哦,我来看看log信息,然后测试妹子就来了……咳~正经一点,我们是讨论技术的)

好在Apple已经提供了方法给我们了,不然又得让吃螃蟹的人死多少脑细胞。

<br />

个人拙见

这个是系统定义的一个方法,让我们传入一个方法地址(不能乱写的),当程序奔溃了就会触发这个方法,不仅仅可以用来获取log信息,还可以保存一下数据。

typedef void NSUncaughtExceptionHandler(NSException *exception);

FOUNDATION_EXPORT NSUncaughtExceptionHandler * _Nullable NSGetUncaughtExceptionHandler(void);
FOUNDATION_EXPORT void NSSetUncaughtExceptionHandler(NSUncaughtExceptionHandler * _Nullable);

<br />
为了能不在AppDelegate中写那么多冗余的代码,于是我把他稍微的封装了一下

1、先导入头文件

#import "PQCatchErrorLog.h"

<br />

2、“注册”抓取Log

这里之所以用双引号因为我也不知道怎么去描述,
但是和通知很像,当发送奔溃时才会通知,所以这里就相当于注册通知,大家仁者见仁智者见智。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    [PQCatchErrorLog catchLog];
    
    [PQCatchErrorLog printErrorLog];
    
    return YES;
}

3、...

然后?然后就没了……
此外还提供了

/**
 得到LOG信息

 @return log信息 - NSString
 */
+ (NSString *)logInfo;


/**
 得到LOG信息,以便于上传

 @return log信息 - Data
 */
+ (NSData *)logData;


/**
 删除error信息

 @return 返回是否删除成功
 */
+ (BOOL)delErrorLogFile;

对应的实现代码

+ (void)catchLog{
    NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        //捕捉崩溃信息
        NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
    }
    return self;
}


+ (void)printErrorLog{
    NSLog(@"path - %@ \nerrorLog - %@",LOGFilePath,[PQCatchErrorLog logInfo]);
}

+ (NSString *)logInfo{
    return [NSString stringWithContentsOfFile:LOGFilePath encoding:NSUTF8StringEncoding error:nil];
}

+ (NSData *)logData{
    return [[PQCatchErrorLog logInfo] dataUsingEncoding:NSUTF8StringEncoding];
}

+ (BOOL)delErrorLogFile{
    NSError * error;
    [[NSFileManager defaultManager] removeItemAtPath:LOGFilePath error:&error];
    
    if (!error) {
        return YES;
    }
    
    NSLog(@"\n删除失败 - %@",error);
    return NO;
}

<br />

并且为了记录时间,我还写了一个类别方法,一起写在了类中

.h
// ----------------   把时间转化为字符串     -----------------
@interface NSDate (PQCatchErrorLog)
/**
 把当前时间转化字符串
 
 @return 当前时间字符串
 */
+ (NSString *)currentDateForDateSeconds;
@end

.m
/**
 把当前时间转化字符串
 
 @return 当前时间字符串
 */
+ (NSString *)currentDateForDateSeconds{
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSString *destDateString1 = [dateFormatter stringFromDate:[NSDate date]];
    NSString *destDateString = [destDateString1 substringFromIndex:2];
    return destDateString;
}

<br />

还有一个地址拼接的

.h
// ----------------   文件地址拼接     -----------------
@interface NSString (PQCatchErrorLog)
/**
 为字符串添加地址
 
 @return 地址
 */
- (NSString *)byAppendToCacheDocument;
@end


.m
/**
 为字符串添加地址
 
 @return 地址
 */
- (NSString *)byAppendToCacheDocument{
    NSString * path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject;
    return [path stringByAppendingPathComponent:self];
}

<br />
<br /><br />

最后在介绍一下:如何保存log信息

//抓取奔溃日志
void UncaughtExceptionHandler(NSException *exception) {
    
    NSArray *arr = [exception callStackSymbols];//得到当前调用栈信息
    
    NSString *reason = [exception reason];//非常重要,就是崩溃的原因
    
    NSString *name = [exception name];//异常类型
    
    NSMutableString * log = [NSMutableString stringWithFormat:@"callStackSymbols - 当前栈信息:\n"];
    for (NSString * str in arr) {
        [log appendFormat:@"%@\n",str];
    }
    [log appendFormat:@"\nreason - 崩溃原因:\n %@",reason];
    [log appendFormat:@"\nname - 异常类型:\n %@",name];
    
    [log insertString:[NSString stringWithFormat:@"*************** %@ *******************\n",[NSDate currentDateForDateSeconds]] atIndex:0];
    
    //创建一个文件 如果是第一次就直接写入,然后返回
    if (![[NSFileManager defaultManager]fileExistsAtPath:LOGFilePath]) {
        [[NSFileManager defaultManager]createFileAtPath:LOGFilePath contents:nil attributes:nil];
        [log insertString:[NSString stringWithFormat:@"\n*************** 奔溃日志 *******************\n"] atIndex:0];
        [log writeToFile:LOGFilePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
        return;
    }
    
    //创建一个fileHandler
    NSFileHandle * fileHandler = [NSFileHandle fileHandleForWritingAtPath:LOGFilePath];
    //跳到文件末尾
    [fileHandler seekToEndOfFile];
    //写入文件
    [fileHandler writeData:[log dataUsingEncoding:NSUTF8StringEncoding]];
    //关闭file Handler
    [fileHandler closeFile];
}

最后故意在ViewController中写了一个数组越界的BUG
大家自行感受一下:

*************** 奔溃日志 *******************
*************** 16-11-28 15:32:47 *******************
callStackSymbols - 当前栈信息:
0   CoreFoundation                      0x000000010c91634b __exceptionPreprocess + 171
1   libobjc.A.dylib                     0x000000010c37721e objc_exception_throw + 48
2   CoreFoundation                      0x000000010c96ebdf -[__NSSingleObjectArrayI objectAtIndex:] + 111
3   CatchErrorLog抓取奔溃信息     0x000000010bda22a3 -[ViewController viewDidLoad] + 163
4   UIKit                               0x000000010cedac99 -[UIViewController loadViewIfRequired] + 1258
5   UIKit                               0x000000010cedb0cc -[UIViewController view] + 27
6   UIKit                               0x000000010cda4c51 -[UIWindow addRootViewControllerViewIfPossible] + 71
7   UIKit                               0x000000010cda53a2 -[UIWindow _setHidden:forced:] + 293
8   UIKit                               0x000000010cdb8cb5 -[UIWindow makeKeyAndVisible] + 42
9   UIKit                               0x000000010cd31c89 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4818
10  UIKit                               0x000000010cd37de9 -[UIApplication _runWithMainScene:transitionContext:completion:] + 1731
11  UIKit                               0x000000010cd34f69 -[UIApplication workspaceDidEndTransaction:] + 188
12  FrontBoardServices                  0x000000010fe87723 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 24
13  FrontBoardServices                  0x000000010fe8759c -[FBSSerialQueue _performNext] + 189
14  FrontBoardServices                  0x000000010fe87925 -[FBSSerialQueue _performNextFromRunLoopSource] + 45
15  CoreFoundation                      0x000000010c8bb311 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
16  CoreFoundation                      0x000000010c8a059c __CFRunLoopDoSources0 + 556
17  CoreFoundation                      0x000000010c89fa86 __CFRunLoopRun + 918
18  CoreFoundation                      0x000000010c89f494 CFRunLoopRunSpecific + 420
19  UIKit                               0x000000010cd337e6 -[UIApplication _run] + 434
20  UIKit                               0x000000010cd39964 UIApplicationMain + 159
21  CatchErrorLog抓取奔溃信息     0x000000010bda280f main + 111
22  libdyld.dylib                       0x000000010f6f668d start + 1

reason - 崩溃原因:
 *** -[__NSSingleObjectArrayI objectAtIndex:]: index 2 beyond bounds [0 .. 0]
name - 异常类型:
 NSRangeException

在第3行:标出来了在哪个方法中

在第2行:标出来了是用数组取值

在崩溃原因中标出来了: 数组越界

最后一行是:异常类型

<br />

最后的最后
DEMO地址 如果有用,烦请star

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容