http://www.cnblogs.com/alario/archive/2012/03/28/2421574.html#2343515

如果你去4S店修车,给小工说你的车哪天怎么样怎么样了,小工有可能会立即搬出一台电脑,插上行车电脑把日志打出来,然后告诉你你的车发生过什么故障。汽车尚且如此,何况移动互联网应用呢。

本文第一篇:经营你的iOS应用日志(一):开始编写日志组件

言归正传。开发iOS应用,解决Crash问题始终是一个难题。Crash分为两 种,一种是由EXC_BAD_ACCESS引起的,原因是访问了不属于本进程的内存地址,有可能是访问已被释放的内存;另一种是未被捕获的 Objective-C异常(NSException),导致程序向自身发送了SIGABRT信号而崩溃。其实对于未捕获的Objective-C异常, 我们是有办法将它记录下来的,如果日志记录得当,能够解决绝大部分崩溃的问题。这里对于UI线程与后台线程分别说明。

先看UI线程。iOS SDK提供了NSSetUncaughtExceptionHandler函数,用法如:

NSSetUncaughtExceptionHandler( handleRootException );

这样在UI线程发生未捕获异常后,进程崩溃之前,handleRootException会被执行。这个函数实现如下

static void handleRootException( NSException* exception ){    NSString* name = [ exception name ];    NSString* reason = [ exception reason ];    NSArray* symbols = [ exception callStackSymbols ]; // 异常发生时的调用栈    NSMutableString* strSymbols = [ [ NSMutableString alloc ] init ]; // 将调用栈拼成输出日志的字符串    for ( NSString* item in symbols )    {        [ strSymbols appendString: item ];        [ strSymbols appendString: @"\r\n" ];    }

// 写日志,级别为ERROR    writeCinLog( __FUNCTION__, CinLogLevelError, @"[ Uncaught Exception ]\r\nName: %@, Reason: %@\r\n[ Fe Symbols Start ]\r\n%@[ Fe Symbols End ]", name, reason, strSymbols );    [ strSymbols release ];

// 这儿必须Hold住当前线程,等待日志线程将日志成功输出,当前线程再继续运行    blockingFlushLogs( __FUNCTION__ );

// 写一个文件,记录此时此刻发生了异常。这个挺有用的哦    NSDictionary* dict = [ NSDictionary dictionaryWithObjectsAndKeys:            currentCinLogFileName(), @"LogFile",                // 当前日志文件名称            currentCinLogFileFullPath(), @"LogFileFullPath",    // 当前日志文件全路径            [ NSDate date ], @"TimeStamp",                        // 异常发生的时刻            nil ];    NSString* path = [ NSString stringWithFormat: @"%@/Documents/", NSHomeDirectory() ];    NSString* lastExceptionLog = [ NSString stringWithFormat: @"%@LastExceptionLog.txt", path ];    [ dict writeToFile: lastExceptionLog atomically: YES ];

}
复制代码

而我们的日志组件必须实现blockingFlushLogs函数,确保进程在日志完全写入文件后再退出。这个实现应该很简单吧。

当应用下次启动时,我们可以检查,如果有 LastExceptionLog.txt,则弹窗引导测试人员将日志发过来。如果iPhone上面配置了EMail帐户,可以很简单的调用 MFMailComposeViewController将日志文件作为附件发送,当然也可以想其它办法。

记得正式发布的版本要将它条件编译去掉哦。

其中文件中的最后一条ERROR即为导致崩溃的异常,而从ERROR之前的日志可以看出当前程序的运行情况。ERROR如下:

<- 03-20 17:21:43 ERROR -> [UI] -[CinUIRunLoopActionManager(Protected) handleRootException:][ Uncaught Exception ]Name: NSDestinationInvalidException, Reason: *** -[CinThreadRunLoopActionManager performSelector:onThread:withObject:waitUntilDone:modes:]: target thread exited while waiting for the perform[ Fe Symbols Start ]0   CoreFoundation                      0x340c88d7 __exceptionPreprocess + 1861   libobjc.A.dylib                     0x343181e5 objc_exception_throw + 322   CoreFoundation                      0x340c87b9 +[NSException raise:format:] + 03   CoreFoundation                      0x340c87db +[NSException raise:format:] + 344   Foundation                          0x35a12493 -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] + 9985   Foundation                          0x35a3afb5 -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:] + 1086   MyiOSapplication                    0x0022b7e9 -[CinThreadRunLoopActionManager(Protected) performAction:] + 14413  UIKit                               0x374b36b5 -[UIViewController _setViewAppearState:isAnimating:] + 14414  UIKit                               0x374b38c1 -[UINavigationController viewWillAppear:] + 28815  UIKit                               0x374b36b5 -[UIViewController _setViewAppearState:isAnimating:] + 14416  UIKit                               0x3750e61b -[UIViewController beginAppearanceTransition:animated:] + 19017  UIKit                               0x3750b415 -[UITabBarController transitionFromViewController:toViewController:transition:shouldSetSelected:] + 18418  UIKit                               0x3750b357 -[UITabBarController transitionFromViewController:toViewController:] + 3019  UIKit                               0x3750ac91 -[UITabBarController _setSelectedViewController:] + 30020  UIKit                               0x3750a9c5 -[UITabBarController setSelectedIndex:] + 24021  MyiOSapplication                    0x0007ef1d +[Utility ResetCurrentTabIndex] + 17222  MyiOSapplication                    0x001a87bd -[UIViewController(statusBar) dismissModalViewControllerAnimatedEx:] + 41623  MyiOSapplication                    0x001793fb -[ImageProcessingViewController save:] + 69024  CoreFoundation                      0x34022435 -[NSObject performSelector:withObject:withObject:] + 5225  UIKit                               0x3748c9eb -[UIApplication sendAction:to:from:forEvent:] + 6226  UIKit                               0x3748c9a7 -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 3027  UIKit                               0x3748c985 -[UIControl sendAction:to:forEvent:] + 4428  UIKit                               0x3748c6f5 -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 49229  UIKit                               0x3748d02d -[UIControl touchesEnded:withEvent:] + 47630  UIKit                               0x3748b50f -[UIWindow _sendTouchesForEvent:] + 31831  UIKit                               0x3748af01 -[UIWindow sendEvent:] + 38032  UIKit                               0x374714ed -[UIApplication sendEvent:] + 35633  UIKit                               0x37470d2d _UIApplicationHandleEvent + 580834  GraphicsServices                    0x308a3df3 PurpleEventCallback + 88235  CoreFoundation                      0x3409c553 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 3836  CoreFoundation                      0x3409c4f5 __CFRunLoopDoSource1 + 14037  CoreFoundation                      0x3409b343 __CFRunLoopRun + 137038  CoreFoundation                      0x3401e4dd CFRunLoopRunSpecific + 30039  CoreFoundation                      0x3401e3a5 CFRunLoopRunInMode + 10440  GraphicsServices                    0x308a2fcd GSEventRunModal + 15641  UIKit                               0x3749f743 UIApplicationMain + 109042  MyiOSapplication                    0x000d4ccb main + 17443  MyiOSapplication                    0x000039c8 start + 40[ Fe Symbols End ]
复制代码

可以看到,即使我们没有编译时生成的符号文件,也能够打印出调用栈上的每个函数的名称,只是没有文件名和行号。

那么,除了UI线程之外,自己创建的后台线程呢?运行NSRunLoop的后台线程的线程函数应该如下:

- ( void ) threadProc: ( NSString* )threadName{    NSThread* current = [ NSThread currentThread ];    [ current setName: threadName ];    NSAutoreleasePool *pool = [ [ NSAutoreleasePool alloc ] init ];

// 一个没有实际作用的NSTimer,确保NSRunLoop不退出。不知道有没有更好的办法啊    _dummyTimer = [ [ NSTimer timerWithTimeInterval: 10.0                                             target: self                                           selector: @selector( dummyTimerProc: )                                           userInfo: nil                                            repeats: YES ] retain ];

    NSRunLoop *r = [ NSRunLoop currentRunLoop ];    [ r addTimer: _dummyTimer forMode: NSDefaultRunLoopMode ];@try {// 启动后台线程的NSRunLoop        [ r run ];    }@catch ( NSException *exception ) {        [ self handleRootException: exception ];// 一旦在线程根上捕捉到未知异常,记录异常后本线程退出    }@finally {        [ _dummyTimer invalidate ];        [ _dummyTimer release ];        [ pool release ];    }}
复制代码

后台线程的handleRootException与UI线程基本一致。不过为了测试人员更加方便,其实只要不是UI线程发生未捕获异常,都可以先引导用户发送日志,再把进程崩溃掉。

明天继续探讨异常日志的进一步改造

转载于:https://www.cnblogs.com/ligun123/archive/2012/03/31/2426419.html

经营你的iOS应用日志(二):异常日志相关推荐

  1. 如何使用SpringBoot AOP 记录操作日志、异常日志?

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 作者:咫尺的梦想_w cnblogs.com/wm-dv/ ...

  2. SpringBoot AOP 记录操作日志、异常日志

    使用SpringBoot AOP 记录操作日志.异常日志 我们在做项目时经常需要对一些重要功能操作记录日志,方便以后跟踪是谁在操作此功能.在操作某些功能时也有可能会发生异常,但是每次发生异常要定位原因 ...

  3. ssm 项目记录用户操作日志和异常日志

    ssm 项目记录用户操作日志和异常日志 参考文章: (1)ssm 项目记录用户操作日志和异常日志 (2)https://www.cnblogs.com/mei-m/p/10231792.html (3 ...

  4. 使用SpringBoot AOP 记录操作日志、异常日志

    https://www.cnblogs.com/wm-dv/p/11735828.html

  5. iOS应用日志:开始编写日志组件与异常日志

    应用日志(一):开始编写日志组件 对于那些做后端开发的工程师来说,看 LOG解Bug应该是理所当然的事,但我接触到的移动应用开发的工程师里面,很多人并没有这个意识,查Bug时总是一遍一遍的试图重现,试 ...

  6. 熟读《阿里巴巴java开发手册》(二、异常日志)

    目录 (一) 异常处理 (二) 日志规约 (一) 异常处理 1. [强制] Java 类库中定义的可以通过预检查方式规避的 RuntimeException 异常不应该通过 catch 的方式来处理, ...

  7. 无异常日志,就不能排查问题了???

    众所周知,日志是排查问题的重要手段.关于日志设计,以及怎么根据从[用户报障]环节开始到秒级定位问题这个我们下一期说(绝非套路),这一期,主要讲一下,在没有异常日志的情况下,如何定位问题.没有日志当真能 ...

  8. angular代码分析之异常日志设计

    angular代码分析之异常日志设计 错误异常是面向对象开发中的记录提示程序执行问题的一种重要机制,在程序执行发生问题的条件下,异常会在中断程序执行,同时会沿着代码的执行路径一步一步的向上抛出异常,最 ...

  9. AOP基本概念、AOP底层实现原理、AOP经典应用【事务管理、异常日志处理、方法审计】...

    1 什么是AOP AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件 ...

最新文章

  1. uva 610(割边)
  2. centos6.5下安装python3安装、python3虚拟环境创建venv
  3. 2021-07-15
  4. C语言嵌入式系统编程修炼之道——背景篇
  5. 玻璃体浑浊的分子原理
  6. java xmpp_Java XMPP负载测试工具
  7. java world_Java World中的GraphQL简介
  8. 自己用java实现飞鸽传书 2 - 实现文件传输
  9. 第14章 任务和特权级保护
  10. 常用sql操作语句实战演示
  11. linux 有趣的命令
  12. centos jupyter 安装_centos6.4安装 jupyter-notebook
  13. Unity Animator动画状态机 深入理解(一)
  14. Android的数据库(SQLite)学习
  15. html静态网页制作代码
  16. coap python_一步步搭建物联网系统——RESTful的CoAP协议
  17. python结束函数_python函数结束
  18. 到底有多二:一个整数“犯二的程度”定义为该数字中包含2的个数与其位数的比值。
  19. echart 动画 饼图_巧用EChart画动态饼图
  20. 第三方软件MOOS-IvP扩展

热门文章

  1. 使用StarUML生成live555类图
  2. php 类 和 函数,PHP函数和类
  3. 【分布式ID】理解Snowflake算法的实现原理
  4. 【Elasticsearch】 Kibana 里程碑插件的使用
  5. 【ElasticSearch】Es 源码之 ClusterService 源码解读
  6. 【kafka】kafka 消费报错 Failed to add leader for partitions
  7. 【clickhouse】clickhouse时区
  8. 【高并发】JUC中等待多线程完成的工具类CountDownLatch
  9. 【Flink】Flink 上海会议 【视频笔记】
  10. 【guava】大数据量下的集合过滤—Bloom Filter