在AppDelegate.m(有的app含有C++代码是AppDelegate.mm)文件的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions增加异常捕获。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { MainViewController * root = [[MainViewController alloc]init]; MainNavController * nav = [[MainNavController alloc]initWithRootViewController:root]; self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds]; self.window.backgroundColor = [UIColor whiteColor]; self.window.rootViewController = nav; [self.window makeKeyAndVisible]; //在register之前打开log, 后续可以根据log排查问题 //把崩溃日志写入文件 [BITLogWrite redirectNSLogToDocumentFolder]; return YES; }
注意:
捕获日志一定要在[self.window makeKeyAndVisible];
之后,不然可能你捕获不到异常。
要包含同步写日志类的头文件:#import "BITLogWrite.h"
。
BITLogWrite头文件如下:
#import <Foundation/Foundation.h> //#import "BITNSObject.h" #define ERR_LOG 1 /* 应用程序无法正常完成操作,比如网络断开,内存分配失败等 */ #define WARN_LOG 2 /* 进入一个异常分支,但并不会引起程序错误 */ #define INFO_LOG 3 /* 日常运行提示信息,比如登录、退出日志 */ #define DEBUG_LOG 4 /* 调试信息,打印比较频繁,打印内容较多的日志 */ #ifndef LOGERR #define LOGERR(format,...) WriteLog(ERR_LOG,__FUNCTION__,__LINE__,format,##__VA_ARGS__) #endif #ifndef LOGWARN #define LOGWARN(format,...) WriteLog(WARN_LOG,__FUNCTION__,__LINE__,format,##__VA_ARGS__) #endif #ifndef LOGINFO #define LOGINFO(format,...) WriteLog(INFO_LOG,__FUNCTION__,__LINE__,format,##__VA_ARGS__) #endif #ifndef LOGDEBUG #define LOGDEBUG(format,...) WriteLog(DEBUG_LOG,__FUNCTION__,__LINE__,format,##__VA_ARGS__) #endif @interface BITLogWrite : NSObject + (void)redirectNSLogToDocumentFolder; @end
BITLogWrite.m代码如下:
#import "BITLogWrite.h" static const long g_synchronousLogFilesCount = 3; static const long g_synchronousLogFileLines = 65535; @implementation BITLogWrite NSString *getTime(void) { NSDateFormatter * formatter = [[NSDateFormatter alloc]init]; [formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss:SSS"]; NSString* dateTime = [formatter stringFromDate:[NSDate date]]; return dateTime; } void WriteFileLog(NSString *format, ...) { // if([BITSingleObject sharedInstance].crashBlock) // { // [BITSingleObject sharedInstance].crashBlock(); // } va_list args; va_start(args, format); NSString *string = [[NSString alloc] initWithFormat:format arguments:args]; va_end(args); if(!string) { return; } NSFileManager* fm = [NSFileManager defaultManager]; NSString *str = nil; BOOL flag = NO; NSArray* myPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString* myDocPath = [myPaths objectAtIndex:0]; NSString* path = [myDocPath stringByAppendingPathComponent:@"crash"]; [fm fileExistsAtPath:path isDirectory:&flag]; NSArray *dirarray = nil; NSString* filePath = nil; NSArray *lines = nil; NSError *error = nil; NSData* data = nil; NSInteger n = 0, i = 0, m = 0; NSMutableArray *filesMutableArr = [NSMutableArray array]; NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; NSString *app_Name = [infoDictionary objectForKey:@"CFBundleName"]; if(flag) { // dirarray = [fm contentsOfDirectoryAtPath:filePath]; // FLDDLogDebug(@"%@ ",dirarray); dirarray = [fm contentsOfDirectoryAtPath:path error:nil]; NSLog(@"%@ ",dirarray); n = dirarray.count; for(i = 0; i < n; i++) { filePath = [path stringByAppendingPathComponent:dirarray[i]]; if ([fm fileExistsAtPath:filePath]) { [filesMutableArr addObject:filePath]; } } m = filesMutableArr.count; if(m > g_synchronousLogFilesCount) { NSMutableArray *newFilesMutableArr = [NSMutableArray array]; for(i = m - g_synchronousLogFilesCount ; i < m; i++) { filePath = filesMutableArr[i]; [newFilesMutableArr addObject:filePath]; } //若想删除超过指定日志文件个数的最老的文件 for(i = 0; i < m - g_synchronousLogFilesCount ; i++) { filePath = filesMutableArr[i]; [fm removeItemAtPath:filePath error:nil]; } filesMutableArr = newFilesMutableArr; m = filesMutableArr.count; // for(i = 0 ; (m - i > 0) && (i < m - g_synchronousLogFilesCount); i++) // { // filePath = filesMutableArr[m - i]; // [fm removeItemAtPath:filePath error:nil]; // } } if(g_synchronousLogFilesCount == m) { filePath = filesMutableArr[m - 1]; lines = [[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil] componentsSeparatedByString:@"\n"]; if(lines.count < g_synchronousLogFileLines) { data = [NSData dataWithContentsOfFile:filePath]; str =[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; str = [NSString stringWithFormat:@"%@\n%@",str,string]; [str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil]; } else { [fm removeItemAtPath:filePath error:nil]; str = [NSString stringWithFormat:@"%@ %@.text", app_Name, getTime()]; filePath = [path stringByAppendingPathComponent:str]; str = string; [str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil]; } } else if(m > 0) { filePath = filesMutableArr[m - 1]; // str =[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error]; // str = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; // NSLog(@"str :%@", str); lines = [[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil] componentsSeparatedByString:@"\n"]; // if(lines.count < 65535) if(lines.count < g_synchronousLogFileLines) { data = [NSData dataWithContentsOfFile:filePath]; str =[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; str = [NSString stringWithFormat:@"%@\n%@",str,string]; [str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil]; } else { str = [NSString stringWithFormat:@"%@ %@.text", app_Name, getTime()]; filePath = [path stringByAppendingPathComponent:str]; str = string; [str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil]; } } else { str = [NSString stringWithFormat:@"%@ %@.text", app_Name, getTime()]; filePath = [path stringByAppendingPathComponent:str]; str = string; [str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil]; } } else { BOOL res = [fm createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil]; if (res) { // NSLog(@"文件夹创建成功"); str = [NSString stringWithFormat:@"%@ %@.text", app_Name, getTime()]; filePath = [path stringByAppendingPathComponent:str]; str = string; // NSLog(@"filePath :%@", filePath); [str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error]; // NSLog(@"error :%@", error); } else { NSLog(@"文件夹创建失败"); } } } void WriteLog(int ulErrorLevel, const char *func, int lineNumber, NSString *format, ...) { va_list args; va_start(args, format); NSString *string = [[NSString alloc] initWithFormat:format arguments:args]; va_end(args); NSString *strFormat = [NSString stringWithFormat:@"%@%s, %@%i, %@%@",@"Function: ",func,@"Line: ",lineNumber, @"Format: ",string]; NSString * strModelName = @"文件日志"; //模块名 NSString *strErrorLevel = [[NSString alloc] init]; // switch (ulErrorLevel) { // case ERR_LOG: // strErrorLevel = @"Error"; // break; // case WARN_LOG: // strErrorLevel = @"Warning"; // break; // case INFO_LOG: // strErrorLevel = @"Notice"; // break; // case DEBUG_LOG: // strErrorLevel = @"Debug"; // break; // default: // break; // } NSLog(@"ModalName: %@, ErrorLevel: %@, %@.",strModelName, strErrorLevel, strFormat); NSString *str = [NSString stringWithFormat:@"ModalName: %@, ErrorLevel: %@, %@.",strModelName, strErrorLevel, strFormat]; NSLog(@"%@", str); // WriteLog; WriteFileLog(str); } void UncaughtExceptionHandler(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" ]; } NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]]; [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; NSString *dateStr = [formatter stringFromDate:[NSDate date]]; LOGERR(@"<- %@ ->[ Uncaught Exception ]\r\nName: %@, Reason: %@\r\n[ Fe Symbols Start ]\r\n%@[ Fe Symbols End ]\r\n\r\n", dateStr, name, reason, strSymbols); } + (void)redirectNSLogToDocumentFolder { //未捕获的Objective-C异常日志 NSSetUncaughtExceptionHandler (&UncaughtExceptionHandler); } @end