在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