iOS开启错误日志人生

2018-08-09  本文已影响25人  AlexCorleone

        在开发中遇到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之旅啦!!!

上一篇下一篇

猜你喜欢

热点阅读