在开发中遇到Crash是很正常的现象,还记得刚入iOS开发这个坑的时候根本不懂什么、错误提示啊、函数调用栈啊、po命令调试啊等等..........。那个时候最长用的就是异常断点,还有就是N个断点一步一步往下走哈哈哈直到遇到异常。😁🤣🤣🤣
可是最近对接SDK比较多有时候遇到偶现的Crash自己手贱又没有把输出Log Copy出来就很尴尬了,于是想着是否可以把APP运行时的Crash Log记录下来这样下次连上电脑就可以直接查看上次的Crash日志进行错误分析了。于是百度了下发现Apple已经为我们提供了一个API在异常抛出之前进行调用,而这个API函数参数就是一个回调函数。下面我们来使用这个API进行一个简单的日志记录上报的单例类实现。
ACCrashManager .h的实现
typedefvoid(^ACReportBlock)(BOOLshouldReport);
@interface ACCrashManager :NSObject
@property (nonatomic, strong, readonly) NSData *crashData;
+ (instancetype)shareManager;
- (void)startCacheCrashWith:(NSString*)APPId
withReportBlock:(ACReportBlock)reportBlock;
@end
.m的实现
#define ACFileManager [NSFileManager defaultManager]
#define ACFileHandleWith(filePath) [NSFileHandle fileHandleForWritingAtPath:filePath]
@interface ACCrashManager ()
@property (nonatomic, strong) NSData *crashData;
@property (nonatomic, copy) NSString *crashAPPId;
@property (nonatomic, copy) ACReportBlock reportBlock;
@end
@implementation ACCrashManager
#pragma mark - Create Manager
static ACCrashManager*crashManager =nil;
+ (instancetype)shareManager {
staticdispatch_once_tonceToken;
dispatch_once(&onceToken, ^{
crashManager = [[ACCrashManager alloc] init];
});
return crashManager;
}
#pragma mark - Setter && getter
- (NSData*)crashData
{
NSString*filePath =ACCrashFilePath();
if([ACFileManagerfileExistsAtPath:filePath])
{
return [NSData dataWithContentsOfFile:filePath];
}else
{
returnnil;
}
}
#pragma mark - Public Method
- (void)startCacheCrashWith:(NSString*)APPId
withReportBlock:(ACReportBlock)reportBlock
{
self.reportBlock= reportBlock;
NSSetUncaughtExceptionHandler(&ACUncaughtExceptionHandler);//Apple异常调用API
NSLog(@"AC : Crach Start Cache");
self.crashAPPId= APPId.length>0? APPId :@"AC.ErrorDir";
if(self.reportBlock)
{
self.reportBlock(YES);
}
}
#pragma mark - Private Method
/*捕获错误异常的回调函数*/
void ACUncaughtExceptionHandler(NSException*exception)//异常调用API的回调函数
{
NSString*reportErrorStr = [NSString stringWithFormat:@"\n\n\"ERROR\" :{\n\"AC Crash TIME\" : \"%@\",\n\"AC Crash Name\" : \"%@\", \n\"AC Crash Reason\" : \"%@\", \n\n\"AC Crash CallStackReturnAddresses\" : \n\"%@\", \n\"AC Crash CallStackSymbols\" : \n\"%@\" \n}", ACGetTomeNow(), exception.name, exception.reason, exception.callStackReturnAddresses, exception.callStackSymbols];
NSData *reportData = [reportErrorStr dataUsingEncoding:NSUTF8StringEncoding];
NSString*filePath =ACCrashFilePath();
BOOLisWrite =NO;
if([ACFileManagerfileExistsAtPath:filePath])
{
isWrite =YES;
NSFileHandle*fileHandle =ACFileHandleWith(filePath);
[fileHandleseekToEndOfFile];
[fileHandlewriteData:reportData];
[fileHandlesynchronizeFile];
[fileHandlecloseFile];
}else
{
isWrite = [ACFileManagercreateFileAtPath:filePathcontents:reportDataattributes:nil];
}
if(isWrite)
{
NSLog(@"文件写入成功");
}else
{
NSLog(@"文件写入失败");
}
}
/*创建错误日志文件夹*/
staticNSString*ACReportFileDirectories(void)
{
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *reportPath = [documentPath stringByAppendingString:[NSString stringWithFormat:@"/%@", crashManager.crashAPPId]];
BOOLisDir =YES;
if(![ACFileManagerfileExistsAtPath:reportPathisDirectory:&isDir])
{
NSError*error =nil;
BOOL isCreateDir = [[NSFileManager defaultManager] createDirectoryAtPath:reportPath withIntermediateDirectories:YES attributes:nil error:&error];
if(isCreateDir)
{
NSLog(@"文件夹创建成功");
}else
{
NSLog(@"文件夹创建失败 : %@", error);
}
}
returnreportPath;
}
/* 获取错误文件存放的文件夹 */
staticNSString*ACCrashFilePath(void)
{
NSString *filePath = ACReportFileDirectories();
filePath = [filePathstringByAppendingString:@"/ACErrorReport"];
returnfilePath;
}
/* 获取当前时间*/
staticNSString*ACGetTomeNow()
{
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formattersetDateFormat:@"YYYY-MM-dd HH:mm:ss"];
NSDate*datenow = [NSDatedate];
NSString*currentTimeString = [formatterstringFromDate:datenow];
returncurrentTimeString;
}
这样便可以实现一个简单的Crash日志记录的功能。如果需要上传日志只需要在回调reportBlock中判断是否有上传日志,如果有则将crashData上传服务器。这里日志格式可以根据服务器要求自定义。🤣🤣🤣🤣🤣🤣我这里写的JSON。不过callStackReturnAddresses、callStackSymbols没有json格式化所以好像报错、需要上传服务器的自己再修改下reportErrorStr格式就好。
当然这里只是简单的一个实现思路、我们还是可以使用更好的日志收集分析工具的,如Bugly官方地址,这里不多介绍因为Bugly的使用相对来说很简单,而且日志分析也是可视化的、最最主要的他还提供了一些推荐的解决方案。是不是以后不用改BUG了🤣🤣🤣🤣🤣🤣好啦、到此结束。开启Bugly之旅啦!!!
网友评论