iOS開啟錯誤日志人生

????????在開發(fā)中遇到Crash是很正常的現(xiàn)象,還記得剛?cè)雐OS開發(fā)這個坑的時候根本不懂什么、錯誤提示啊、函數(shù)調(diào)用棧啊、po命令調(diào)試啊等等..........。那個時候最長用的就是異常斷點,還有就是N個斷點一步一步往下走哈哈哈直到遇到異常。????????

????????可是最近對接SDK比較多有時候遇到偶現(xiàn)的Crash自己手賤又沒有把輸出Log Copy出來就很尷尬了,于是想著是否可以把APP運行時的Crash Log記錄下來這樣下次連上電腦就可以直接查看上次的Crash日志進行錯誤分析了。于是百度了下發(fā)現(xiàn)Apple已經(jīng)為我們提供了一個API在異常拋出之前進行調(diào)用,而這個API函數(shù)參數(shù)就是一個回調(diào)函數(shù)。下面我們來使用這個API進行一個簡單的日志記錄上報的單例類實現(xiàn)。

ACCrashManager?.h的實現(xiàn)


typedefvoid(^ACReportBlock)(BOOLshouldReport);

@interface ACCrashManager :NSObject

@property (nonatomic, strong, readonly) NSData *crashData;

+ (instancetype)shareManager;

- (void)startCacheCrashWith:(NSString*)APPId

? ? ? ? ? ? withReportBlock:(ACReportBlock)reportBlock;

@end


.m的實現(xiàn)


#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異常調(diào)用API

? ? NSLog(@"AC : Crach Start Cache");

? ? self.crashAPPId= APPId.length>0? APPId :@"AC.ErrorDir";

? ? ? ? if(self.reportBlock)

? ? ? ? ? ? {

? ? ? ? ? ? ? ? self.reportBlock(YES);

? ? ? ? ? ? }

}

#pragma mark - Private Method

/*捕獲錯誤異常的回調(diào)函數(shù)*/

void ACUncaughtExceptionHandler(NSException*exception)//異常調(diào)用API的回調(diào)函數(shù)

{

? ? 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(@"文件寫入失敗");

? ? }

}

/*創(chuàng)建錯誤日志文件夾*/

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(@"文件夾創(chuàng)建成功");

? ? ? ? }else

? ? ? ? {

? ? ? ? ? ? NSLog(@"文件夾創(chuàng)建失敗 : %@", 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;

}


這樣便可以實現(xiàn)一個簡單的Crash日志記錄的功能。如果需要上傳日志只需要在回調(diào)reportBlock中判斷是否有上傳日志,如果有則將crashData上傳服務(wù)器。這里日志格式可以根據(jù)服務(wù)器要求自定義。????????????我這里寫的JSON。不過callStackReturnAddresses、callStackSymbols沒有json格式化所以好像報錯、需要上傳服務(wù)器的自己再修改下reportErrorStr格式就好。

當然這里只是簡單的一個實現(xiàn)思路、我們還是可以使用更好的日志收集分析工具的,如Bugly官方地址,這里不多介紹因為Bugly的使用相對來說很簡單,而且日志分析也是可視化的、最最主要的他還提供了一些推薦的解決方案。是不是以后不用改BUG了????????????好啦、到此結(jié)束。開啟Bugly之旅啦!??!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容