crash日志的本地记录

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: crash日志的本地记录

app大都会crash,而第三方平台截获的崩溃日志,需要文件描述符才能解析。手机上的崩溃日志有时候也定位不到具体行。

我的建议是为了方便,测试版本可以本地记录崩溃日志。线上版本可以使用fir或bugly.

本地记录的好处是实时获取,不需要本地文件描述符。

本地记录崩溃日志的方法是增加如下代码:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [self configDDLog];
    return YES;
}


- (void)configDDLog {
#ifdef DEBUG
    //把崩溃日志写入文件
    [GBLogWrite redirectNSLogToDocumentFolder];
#endif

}

打印本地日志的方法是由以前我的同步日志改进过来的。我的同步日志连续快速打印有性能问题,但是应用崩溃前打印一次不在话下,没有任何问题:

GBLogWrite.h文件

//
//  GBLogWrite.h
//  GreenBusiness
//
//  Created by 郏国上 on 2017/3/31.
//  Copyright © 2017年 UXing. All rights reserved.
//

#import <Foundation/Foundation.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 GBLogWrite : NSObject
+ (void)redirectNSLogToDocumentFolder;
@end

GBLogWrite.m文件

//
//  GBLogWrite.m
//  GreenBusiness
//
//  Created by 郏国上 on 2017/3/31.
//  Copyright © 2017年 UXing. All rights reserved.
//

#import "GBLogWrite.h"

static const long g_logFilesCount = 3;
static const long g_logFileLines = 65535;

@implementation GBLogWrite


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, ...)
{
    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_logFilesCount)
        {
            NSMutableArray *newFilesMutableArr = [NSMutableArray array];
            for(i =  m - g_logFilesCount ; i < m; i++)
            {
                filePath = filesMutableArr[i];
                [newFilesMutableArr addObject:filePath];
            }
            //若想删除超过指定日志文件个数的最老的文件
            for(i = 0; i < m - g_logFilesCount ; i++)
            {
                filePath = filesMutableArr[i];
                [fm removeItemAtPath:filePath error:nil];
            }
            filesMutableArr = newFilesMutableArr;
            m = filesMutableArr.count;

            //            for(i = 0 ; (m - i > 0) && (i  < m - g_logFilesCount); i++)
            //            {
            //                filePath = filesMutableArr[m - i];
            //                [fm removeItemAtPath:filePath error:nil];
            //            }
        }

        if(g_logFilesCount == m)
        {
            filePath = filesMutableArr[m - 1];
            lines = [[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]
                     componentsSeparatedByString:@"\n"];
            if(lines.count < g_logFileLines)
            {
                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_logFileLines)
            {
                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

打印的崩溃日志:

ModalName: 文件日志, ErrorLevel: Error, Function: UncaughtExceptionHandler, Line: 215, Format: <- 2017-03-31 11:02:48 ->[ Uncaught Exception ]
Name: NSInvalidArgumentException, Reason: *** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]
[ Fe Symbols Start ]
0   CoreFoundation                      0x000000018894eff0 <redacted> + 148
1   libobjc.A.dylib                     0x00000001873b0538 objc_exception_throw + 56
2   CoreFoundation                      0x00000001888302c8 <redacted> + 308
3   CoreFoundation                      0x0000000188942c38 <redacted> + 36
4   CoreFoundation                      0x0000000188832cbc <redacted> + 36
5   OutdoorClub                         0x0000000100133d58 -[ODCCurrentPathLocationDataSingleObject saveCoordateWithFileName:lastLocationOperateModel:] + 308
6   OutdoorClub                         0x0000000100132c90 -[ODCCurrentPathLocationDataSingleObject readmis] + 1672
7   OutdoorClub                         0x0000000100132324 __50-[ODCCurrentPathLocationDataSingleObject initData]_block_invoke + 100
8   OutdoorClub                         0x00000001003c2a10 +[NSTimer(YYAdd) _yy_ExecBlock:] + 148
9   Foundation                          0x0000000189443760 __NSFireTimer + 88
10  CoreFoundation                      0x00000001888fda9c <redacted> + 28
11  CoreFoundation                      0x00000001888fd7a0 <redacted> + 856
12  CoreFoundation                      0x00000001888fd060 <redacted> + 244
13  CoreFoundation                      0x00000001888fac84 <redacted> + 1484
14  CoreFoundation                      0x000000018882ad94 CFRunLoopRunSpecific + 424
15  GraphicsServices                    0x000000018a294074 GSEventRunModal + 100
16  UIKit                               0x000000018eae3130 UIApplicationMain + 208
17  OutdoorClub                         0x000000010014853c main + 124
18  libdyld.dylib                       0x000000018783959c <redacted> + 4
[ Fe Symbols End ]

.
ModalName: 文件日志, ErrorLevel: Error, Function: UncaughtExceptionHandler, Line: 215, Format: <- 2017-03-31 19:34:33 ->[ Uncaught Exception ]
Name: NSInvalidArgumentException, Reason: *** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]
[ Fe Symbols Start ]
0   CoreFoundation                      0x000000018894eff0 <redacted> + 148
1   libobjc.A.dylib                     0x00000001873b0538 objc_exception_throw + 56
2   CoreFoundation                      0x00000001888302c8 <redacted> + 308
3   CoreFoundation                      0x0000000188942c38 <redacted> + 36
4   CoreFoundation                      0x0000000188832cbc <redacted> + 36
5   OutdoorClub                         0x00000001000c7d38 -[ODCCurrentPathLocationDataSingleObject saveCoordateWithFileName:lastLocationOperateModel:] + 448
6   OutdoorClub                         0x00000001000c6be4 -[ODCCurrentPathLocationDataSingleObject readmis] + 1672
7   OutdoorClub                         0x00000001000c6254 __50-[ODCCurrentPathLocationDataSingleObject initData]_block_invoke + 100
8   OutdoorClub                         0x0000000100356a70 +[NSTimer(YYAdd) _yy_ExecBlock:] + 148
9   Foundation                          0x0000000189443760 __NSFireTimer + 88
10  CoreFoundation                      0x00000001888fda9c <redacted> + 28
11  CoreFoundation                      0x00000001888fd7a0 <redacted> + 856
12  CoreFoundation                      0x00000001888fd060 <redacted> + 244
13  CoreFoundation                      0x00000001888fac84 <redacted> + 1484
14  CoreFoundation                      0x000000018882ad94 CFRunLoopRunSpecific + 424
15  GraphicsServices                    0x000000018a294074 GSEventRunModal + 100
16  UIKit                               0x000000018eae3130 UIApplicationMain + 208
17  OutdoorClub                         0x00000001000dc52c main + 124
18  libdyld.dylib                       0x000000018783959c <redacted> + 4
[ Fe Symbols End ]

.
ModalName: 文件日志, ErrorLevel: Error, Function: UncaughtExceptionHandler, Line: 215, Format: <- 2017-04-01 09:35:10 ->[ Uncaught Exception ]
Name: NSInvalidArgumentException, Reason: *** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]
[ Fe Symbols Start ]
0   CoreFoundation                      0x000000018894eff0 <redacted> + 148
1   libobjc.A.dylib                     0x00000001873b0538 objc_exception_throw + 56
2   CoreFoundation                      0x00000001888302c8 <redacted> + 308
3   CoreFoundation                      0x0000000188942c38 <redacted> + 36
4   CoreFoundation                      0x0000000188832cbc <redacted> + 36
5   OutdoorClub                         0x00000001000a3d38 -[ODCCurrentPathLocationDataSingleObject saveCoordateWithFileName:lastLocationOperateModel:] + 448
6   OutdoorClub                         0x00000001000a2be4 -[ODCCurrentPathLocationDataSingleObject readmis] + 1672
7   OutdoorClub                         0x00000001000a2254 __50-[ODCCurrentPathLocationDataSingleObject initData]_block_invoke + 100
8   OutdoorClub                         0x0000000100332a70 +[NSTimer(YYAdd) _yy_ExecBlock:] + 148
9   Foundation                          0x0000000189443760 __NSFireTimer + 88
10  CoreFoundation                      0x00000001888fda9c <redacted> + 28
11  CoreFoundation                      0x00000001888fd7a0 <redacted> + 856
12  CoreFoundation                      0x00000001888fd060 <redacted> + 244
13  CoreFoundation                      0x00000001888fac84 <redacted> + 1484
14  CoreFoundation                      0x000000018882ad94 CFRunLoopRunSpecific + 424
15  GraphicsServices                    0x000000018a294074 GSEventRunModal + 100
16  UIKit                               0x000000018eae3130 UIApplicationMain + 208
17  OutdoorClub                         0x00000001000b852c main + 124
18  libdyld.dylib                       0x000000018783959c <redacted> + 4
[ Fe Symbols End ]
.

注意:我们的同步日志系统在didFinishLaunchingWithOptions之前的崩溃无能为力,毕竟只有同步日志初始化以后才能记录,估计第三方平台也有这样的问题。


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
4月前
crash命令 —— log
crash命令 —— log
|
iOS开发 开发者 C#
|
iOS开发 MacOS 编译器
[Mac OS/iOS]反汇编工具Hopper分析Crash Log
   在Mac OS下分析Crash Log有很多种方法,这里不是要说明如何分析的Crash Log, 主要是展示下Hopper的使用。 强大的IDA大家可能已经知道,但它的Mac OS版本又让人回到了DOS时代。
2319 0
|
2月前
|
XML 安全 Java
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
本文介绍了Java日志框架的基本概念和使用方法,重点讨论了SLF4J、Log4j、Logback和Log4j2之间的关系及其性能对比。SLF4J作为一个日志抽象层,允许开发者使用统一的日志接口,而Log4j、Logback和Log4j2则是具体的日志实现框架。Log4j2在性能上优于Logback,推荐在新项目中使用。文章还详细说明了如何在Spring Boot项目中配置Log4j2和Logback,以及如何使用Lombok简化日志记录。最后,提供了一些日志配置的最佳实践,包括滚动日志、统一日志格式和提高日志性能的方法。
500 30
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
|
26天前
|
监控 安全 Apache
什么是Apache日志?为什么Apache日志分析很重要?
Apache是全球广泛使用的Web服务器软件,支持超过30%的活跃网站。它通过接收和处理HTTP请求,与后端服务器通信,返回响应并记录日志,确保网页请求的快速准确处理。Apache日志分为访问日志和错误日志,对提升用户体验、保障安全及优化性能至关重要。EventLog Analyzer等工具可有效管理和分析这些日志,增强Web服务的安全性和可靠性。
|
3月前
|
XML JSON Java
Logback 与 log4j2 性能对比:谁才是日志框架的性能王者?
【10月更文挑战第5天】在Java开发中,日志框架是不可或缺的工具,它们帮助我们记录系统运行时的信息、警告和错误,对于开发人员来说至关重要。在众多日志框架中,Logback和log4j2以其卓越的性能和丰富的功能脱颖而出,成为开发者们的首选。本文将深入探讨Logback与log4j2在性能方面的对比,通过详细的分析和实例,帮助大家理解两者之间的性能差异,以便在实际项目中做出更明智的选择。
370 3
|
4天前
|
SQL 关系型数据库 MySQL
MySQL事务日志-Undo Log工作原理分析
事务的持久性是交由Redo Log来保证,原子性则是交由Undo Log来保证。如果事务中的SQL执行到一半出现错误,需要把前面已经执行过的SQL撤销以达到原子性的目的,这个过程也叫做"回滚",所以Undo Log也叫回滚日志。
MySQL事务日志-Undo Log工作原理分析
|
1月前
|
存储 监控 安全
什么是事件日志管理系统?事件日志管理系统有哪些用处?
事件日志管理系统是IT安全的重要工具,用于集中收集、分析和解释来自组织IT基础设施各组件的事件日志,如防火墙、路由器、交换机等,帮助提升网络安全、实现主动威胁检测和促进合规性。系统支持多种日志类型,包括Windows事件日志、Syslog日志和应用程序日志,通过实时监测、告警及可视化分析,为企业提供强大的安全保障。然而,实施过程中也面临数据量大、日志管理和分析复杂等挑战。EventLog Analyzer作为一款高效工具,不仅提供实时监测与告警、可视化分析和报告功能,还支持多种合规性报告,帮助企业克服挑战,提升网络安全水平。
|
3月前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1755 14
MySQL事务日志-Redo Log工作原理分析