美文网首页
iOS开启错误日志人生

iOS开启错误日志人生

作者: AlexCorleone | 来源:发表于2018-08-09 18:52 被阅读25次

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

    相关文章

      网友评论

          本文标题:iOS开启错误日志人生

          本文链接:https://www.haomeiwen.com/subject/jpvcbftx.html