ios NSLog内容,崩溃日志,重定向到本地

主要参考的原文 : http://www.jianshu.com/p/8222a8b24129

最简单的方式就是用iOS自带的首先是日志输出,分为c的printf和标准的NSLog输出,printf会向标准输出(sedout)打印,而NSLog则是向标准出错(stderr),我们需要同时让他们都将日志打印到一个文件中。下面2句是最核心的内容 . 

//写入
 freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);
 freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);

那么就看看这个核心是个什么东西 , 

  • freopen是被包含于C标准库头文件<stdio.h>中的一个函数,用于重定向输入输出流。该函数可以在不改变代码原貌的情况下改变输入输出环境,但使用时应当保证流是可靠的。

  • FILE *freopen(const char * restrict filename, const char * restrict mode, FILE * restrict stream); 

    形参说明:

    filename:需要重定向到的文件名或文件路径。

    mode:代表文件访问权限的字符串。例如,"r"表示“只读访问”、"w"表示“只写访问”、"a"表示“追加写入”,a+ 就是追加写入, 并且有读的权限, (r+,w+) 都是有读写权限。

    stream:需要被重定向的文件流。  sedout对应printf ,而stderr 对应 NSLog,sedin对应用户输入

    返回值:如果成功,则返回该指向该输出流的文件指针,否则返回为NULL。

我的方式

在AppDelegate.m文件中的 didFinishLaunchingWithOptions:方法中写入如下代码:

//判断是否连接Xcode
/* if(isatty(STDOUT_FILENO)) {
  return;
}
//判断设备是否是模拟器
UIDevice *device = [UIDevice currentDevice];
if ([device.model hasSuffix:@"Simulator"]){
  return;
}*/
//打印log日志logDic
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
//文件夹路径
NSString *logDic = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"DBLOG"];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL fileExists = [fileManager fileExistsAtPath:logDic];
if (!fileExists)
{
  //创建文件夹logDic
  [fileManager createDirectoryAtPath:logDic withIntermediateDirectories:YES attributes:nil error:nil];
}

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]];
[dateFormatter setDateFormat:@"yyyy-MM-dd"];
NSString *dateStr = [dateFormatter stringFromDate:[NSDate date]];
//创建文件
NSString *logFilePath = [logDic stringByAppendingFormat:@"/%@.log",dateStr];
//写入
freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);
freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);

----------------------解释下

[logFilePath cStringUsingEncoding:NSASCIIStringEncoding]是将字符串转出ASC2编码, 所以路径中的中文是不可以的.

这段代码的主要作用是:使用真机运行应用程序时将所需要的信息日志通过NSLOG写入文件夹。当然写入文件时还可以将错误日志记录并写入文件,如果你不需要记录错误日志将不需要看下面的代码,操作和写入信息的代码类似。

  捕获的Objective-C异常日志, 信号量的异常也可以模拟,但是如果设备连着xcode是不会进入SignalHandler的,非调试模式下可以进去,并且日志也是有的

- (void)addCrashLog {
    
    //捕获的Objective-C异常日志
    NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
    //捕获信号量导致的异常
    signal(SIGABRT, SignalHandler);
    signal(SIGILL, SignalHandler);
    signal(SIGSEGV, SignalHandler);
    signal(SIGFPE, SignalHandler);
    signal(SIGBUS, SignalHandler);
    signal(SIGPIPE, SignalHandler);
    
}


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" ];
}

//将crash日志保存到Document目录下的Log文件夹下
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *logDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"DBLOG"];

NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:logDirectory]) {
  [fileManager createDirectoryAtPath:logDirectory  withIntermediateDirectories:YES attributes:nil error:nil];
}

NSString *logFilePath = [logDirectory stringByAppendingPathComponent:@"UncaughtException.log"];
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]];

NSString *crashString = [NSString stringWithFormat:@"<- %@ ->[ Uncaught Exception ]\r\nName: %@, Reason: %@\r\n[ Fe Symbols Start ]\r\n%@[ Fe Symbols End ]\r\n\r\n", dateStr, name, reason, strSymbols];
//把错误日志写到文件中
if (![fileManager fileExistsAtPath:logFilePath]) {
  [crashString writeToFile:logFilePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
}else{
  NSFileHandle *outFile = [NSFileHandle fileHandleForWritingAtPath:logFilePath];
  [outFile seekToEndOfFile];
  [outFile writeData:[crashString dataUsingEncoding:NSUTF8StringEncoding]];
  [outFile closeFile];
}
}


void SignalHandler(int signal) {
    //拦截signal
    NSString * signalStr = @"未知信号量异常";
    switch (signal) {
        case SIGABRT:
            signalStr = @"SIGABRT : abort()";
            break;
        case SIGILL:
            signalStr = @"SIGILL : illegal instruction (not reset when caught)";
            break;
        case SIGSEGV:
            signalStr = @"SIGSEGV : segmentation violation";
            break;
        case SIGFPE:
            signalStr = @"SIGFPE : floating point exception";
            break;
        case SIGBUS:
            signalStr = @"SIGBUS : bus error";
            break;
        case SIGPIPE:
            signalStr = @"SIGPIPE : write on a pipe with no one to read it";
            break;
        case SEGV_ACCERR:
            signalStr = @"SEGV_ACCERR : [XSI] invalid permission for mapped object";
            break;
    }
    
    NSLog(@"捕获到信号量导致的异常 %@",signalStr);

    // 下面的需要 #include <execinfo.h>, 
    NSMutableString *mstr = [[NSMutableString alloc] init];
    [mstr appendString:@"Stack:\n"];
    void* callstack[128];
    int i, frames = backtrace(callstack, 128);
    char** strs = backtrace_symbols(callstack, frames);
    for (i = 0; i <frames; ++i) {
        [mstr appendFormat:@"%s\n", strs[i]];
    }
    NSLog(@"%@",mstr);

    CFRunLoopRef runLoop = CFRunLoopGetCurrent();
    CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
    
    // 做一个小延迟,保证日志能写入成功
    for (int i=0; i<100; i++) {
        for (NSString *mode in (__bridge NSArray *)allModes){
            //为阻止线程退出,使用 CFRunLoopRunInMode(model, 0.001, false)等待系统消息,false表示RunLoop没有超时时间
            CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);
        }
    }
    
}
    //模拟SIGBUS异常,内存地址未对齐
    //EXC_BAD_ACCESS(code=1,address=0x1000dba58)
    char *s = "hello world";
    *s = 'H';

利用模拟器检测是否在“DBLOG”的文件夹下写入了后缀为.log的日志文件。在任意的模拟机上运行,获取到文件的路径,譬如:

前往可以看到如下图:

此时已经在此设备的Documents目录下生成了一个名叫“DBLOG”的文件夹来存放log日志。由于我们的日志命名方式我们可以看到今天生成的日志:“2016-09-26.log”,双击可以查看日志的内容。当然日志可以不加后缀,本来就是流的操作,所以无论是传给服务器还是在本地都是可以的,在读取的时候再注意即可。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值