分享

ios Crash闪退日志获取和上传至服务器(NSSetUncaughtExceptionHandler)

 没原创_去搜索 2015-08-31

最近客户有个要求:人家谁谁有crash日志捕获和上传,我们是不是也要做一个...  人家谁谁.....还有什么什么功能........

  正好最近也在研究这方面东东,所以整理一下分享给大家:如何用程序获取Crash日志 并 可以上传Crash日志。


   首先我们整理经常会闪退的异常哪些:数组越界、空引用、引用未定义方法、内存空间不足等等。

   友盟分享后台是可以看到crash的日志,如下图:

   

        开始研究的时候,我有两个疑问:

        1.如何获取crash闪退日志(工具和程序两种方法);

        2.解析crash;

       

        说明:这里说的crash日志不是在联调的情况下(是生产环境,通俗的说就是发布了的产品)。


        如何获取crash闪退日志 -- 工具查看

        先看第一个问题如何查看,我搜索的方法有以下几个:

        第一个方法:XCode  的菜单Window->Organizer    选择Devices  ->  选中的手机 -> 点击手机名称左边的箭头 会等到如下图

        

       在右边竖蓝色矩形框中 Type里面出现两种类型:Unknown和Crash 这两种类型分别是 内存不够回收内存kill应用程序导致Crash和程序异常Crash的日志。


       上图是我在刚打开日志(立马、马上)截的图,否则过了5秒中,会变成这样(自动解析):

       

      注意对比一下红色框框内容,这个日志也基本上上告诉你crash的原因了。

      

       第二种方法 打开手机 - > 设置 -> 通用 - > 关于本机 - > 诊断与用量 - > 诊断与用量数据  这里面就是所有应用的Crash日志。

       第三种方法 通过iTunes Connect(Manage Your Applications - View Details - Crash Reports)获取用户的crash日志。方法很多这里不多列了。


      解析crash

      参见:http:///questions/1460892/symbolicating-iphone-app-crash-reports )

       

      用程序获取crash日志

      但是这里都是工具,没有用到程序获取,经过千方百计的查询(思路是:先找到存放crash的iphone系统路径:var/mobile/Library/Logs/CrashReporter)找到了crash存放的路径,唉,苦于无法读取(用程序读出来都是nil),当然如果是越狱手机就不一样是可以读取的。这个思路断掉了。


       换个思路:自己用程序捕获crash,保存到本地可以吗?这么一试,果然........

       第一步:新建一个继承自NSObject的类(Xcode新建一个空项目过程略),取名字CatchCrash,在h和m文件中写下:

       .h文件

       

  1. #import <Foundation/Foundation.h>  
  2.   
  3. @interface CatchCrash : NSObject  
  4.   
  5. void uncaughtExceptionHandler(NSException *exception);  
  6.   
  7. @end  

       .m(m)文件



  1. #import "CatchCrash.h"  
  2.   
  3. @implementation CatchCrash  
  4.   
  5. void uncaughtExceptionHandler(NSException *exception)  
  6. {  
  7.     // 异常的堆栈信息  
  8.     NSArray *stackArray = [exception callStackSymbols];  
  9.     // 出现异常的原因  
  10.     NSString *reason = [exception reason];  
  11.     // 异常名称  
  12.     NSString *name = [exception name];  
  13.     NSString *exceptionInfo = [NSString stringWithFormat:@"Exception reason:%@\nException name:%@\nException stack:%@",name, reason, stackArray];  
  14.     NSLog(@"%@", exceptionInfo);  
  15.       
  16.     NSMutableArray *tmpArr = [NSMutableArray arrayWithArray:stackArray];  
  17.     [tmpArr insertObject:reason atIndex:0];  
  18.       
  19.     //保存到本地  --  当然你可以在下次启动的时候,上传这个log  
  20.     [exceptionInfo writeToFile:[NSString stringWithFormat:@"%@/Documents/error.log",NSHomeDirectory()]  atomically:YES encoding:NSUTF8StringEncoding error:nil];  
  21. }  
  22.   
  23. @end  

       第二步:添加一个继承自UIViewcontroller的类,取名字为TestViewController。


       第三步:注册CatchCrash异常处理方法,在Appdelegate写下如下代码:


  1. #import "AppDelegate.h"  
  2. #import "CatchCrash.h"  
  3. #import "TestViewController.h"  
  4.   
  5. @implementation AppDelegate  
  6.   
  7. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions  
  8. {  
  9.     self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];  
  10.     // Override point for customization after application launch.  
  11.   
  12.     //注册消息处理函数的处理方法  
  13.     NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);  
  14.       
  15.     TestViewController *testVc = [[TestViewController alloc] init];  
  16.     self.window.rootViewController = testVc;  
  17.       
  18.     self.window.backgroundColor = [UIColor whiteColor];  
  19.     [self.window makeKeyAndVisible];  
  20.     return YES;  
  21. }  
  22.   
  23. - (void)applicationWillResignActive:(UIApplication *)application  
  24. {}  
  25.   
  26. - (void)applicationDidEnterBackground:(UIApplication *)application  
  27. {}  
  28.   
  29. - (void)applicationWillEnterForeground:(UIApplication *)application  
  30. {}  
  31.   
  32. - (void)applicationDidBecomeActive:(UIApplication *)application  
  33. {}  
  34.   
  35. - (void)applicationWillTerminate:(UIApplication *)application  
  36. {}  

       第四部:在TestViewController的Xib上面添加一个按钮并给其添加一个单击事件,TestViewController.m文件中有如下代码:
  1. #import "TestViewController.h"  
  2.   
  3. @interface TestViewController ()  
  4.   
  5. @end  
  6.   
  7. @implementation TestViewController  
  8.   
  9. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil  
  10. {  
  11.     self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];  
  12.     if (self) {  
  13.         // Custom initialization  
  14.     }  
  15.     return self;  
  16. }  
  17.   
  18. - (void)viewDidLoad  
  19. {  
  20.     [super viewDidLoad];  
  21.     // Do any additional setup after loading the view from its nib.  
  22. }  
  23.   
  24. - (void)didReceiveMemoryWarning  
  25. {  
  26.     [super didReceiveMemoryWarning];  
  27.     // Dispose of any resources that can be recreated.  
  28. }  
  29.   
  30. #pragma mark - 单击事件  
  31. - (IBAction)crashTapped:(id)sender  
  32. {  
  33.     //常见异常1---不存在方法引用  
  34. //    [self performSelector:@selector(thisMthodDoesNotExist) withObject:nil];  
  35.       
  36.     //常见异常2---键值对引用nil  
  37. //    [[NSMutableDictionary dictionary] setObject:nil forKey:@"nil"];  
  38.       
  39.     //常见异常3---数组越界  
  40.     [[NSArray array] objectAtIndex:1];  
  41.       
  42.     //常见异常4---memory warning 级别3以上  
  43. //    [self performSelector:@selector(killMemory) withObject:nil];  
  44.       
  45.     //其他大家去想吧  
  46. }  
  47.   
  48. #pragma mark - custom method  
  49. - (void) killMemory  
  50. {  
  51.     for (int i = 0; i < 300; i ++)  
  52.     {  
  53.         UILabel *tmpLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 200)];  
  54.         tmpLabel.layer.masksToBounds = YES;  
  55.         tmpLabel.layer.cornerRadius = 10;  
  56.         tmpLabel.backgroundColor = [UIColor redColor];  
  57.         [self.view addSubview:tmpLabel];  
  58.     }  
  59. }  
  60.   
  61. @end  



         运行代码:可以看到闪退,我们用iExplorer打开:

         

        导出error日志,我们可以看到:


  1. Exception reason:NSRangeException  
  2. <span style="color:#FF0000;">Exception name:*** -[__NSArrayI objectAtIndex:]: index 1 beyond bounds for empty arrayspan>  
  3. Exception stack:(  
  4.     0   CoreFoundation                      0x2f2edfeb <redacted> + 154  
  5.     1   libobjc.A.dylib                     0x39b66ccf objc_exception_throw + 38  
  6.     2   CoreFoundation                      0x2f224a89 <redacted> + 176  
  7. <span style="color:#FF0000;"> 3   TestCrash                           0x000e8077 -[TestViewController crashTapped:] + 126span>  
  8.     4   UIKit                               0x31b3f057 <redacted> + 90  
  9.     5   UIKit                               0x31b3eff7 <redacted> + 30  
  10.     6   UIKit                               0x31b3efd1 <redacted> + 44  
  11.     7   UIKit                               0x31b2a737 <redacted> + 374  
  12.     8   UIKit                               0x31b3ea4f <redacted> + 590  
  13.     9   UIKit                               0x31b3e721 <redacted> + 528  
  14.     10  UIKit                               0x31b396eb <redacted> + 758  
  15.     11  UIKit                               0x31b0e8ed <redacted> + 196  
  16.     12  UIKit                               0x31b0cf97 <redacted> + 7102  
  17.     13  CoreFoundation                      0x2f2b925b <redacted> + 14  
  18.     14  CoreFoundation                      0x2f2b872b <redacted> + 206  
  19.     15  CoreFoundation                      0x2f2b6f1f <redacted> + 622  
  20.     16  CoreFoundation                      0x2f221f0f CFRunLoopRunSpecific + 522  
  21.     17  CoreFoundation                      0x2f221cf3 CFRunLoopRunInMode + 106  
  22.     18  GraphicsServices                    0x3417a663 GSEventRunModal + 138  
  23.     19  UIKit                               0x31b6d16d UIApplicationMain + 1136  
  24.     20  TestCrash                           0x000e810d main + 116  
  25.     21  libdyld.dylib                       0x3a073ab7 <redacted> + 2  
  26. )  

太清楚了,对吧,下次启动应用程序的时候可以把这个error.log(这个名字我临时写的,用什么日期也可以的)上传。可以在日志中加上类似与友盟的  iphone 的UUID、Bundle ID 等等。


现在猜测一下,友盟是不是这么做的呢?

进入一个引用友盟sdk项目的根目录,打开mac终端输入命令 grep -r   NSSetUncaughtExceptionHandler .(这个点不能少或者绝对路径替换这个点),得到如下图:


真的就找到了,什么什么  matches。当然这只是猜测。


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多