闪退日志的同步写入文件记录

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

在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


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
7月前
|
SQL 关系型数据库 MySQL
我使用flinkcdc的sql形式进行全量同步,4张表,有两张表数据没进去,看日志,id怎么是null呢?
我使用flinkcdc的sql形式进行全量同步,4张表,有两张表数据没进去,看日志,id怎么是null呢?
164 40
|
6月前
|
SQL 数据采集 DataWorks
DataWorks产品使用合集之pyodps的线程限制是什么意思
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
6月前
|
DataWorks 数据可视化 安全
DataWorks产品使用合集之SLS日志中新增了存在iotId这个字段,同步的时候怎么手动增加
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
2月前
|
SQL 存储 关系型数据库
Mysql主从同步 清理二进制日志的技巧
Mysql主从同步 清理二进制日志的技巧
30 1
|
7月前
|
SQL Oracle 关系型数据库
实时计算 Flink版产品使用合集之从Oracle数据库同步数据时,checkpoint恢复后无法捕获到任务暂停期间的变更日志,如何处理
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStreamAPI、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
7月前
|
开发工具 iOS开发
如何导出iPhone手机中app共享文件夹的文件与闪退日志的收集
如何导出iPhone手机中app共享文件夹的文件与闪退日志的收集
90 1
|
7月前
|
定位技术 调度
自定义同步日志系统
自定义同步日志系统
36 2
|
6月前
|
SQL 分布式计算 DataWorks
DataWorks产品使用合集之如何同步SLS日志到odps上
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
7月前
|
SQL 关系型数据库 数据库
实时计算 Flink版产品使用合集之同步PostgreSQL数据时,WAL 日志无限增长,是什么导致的
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
Web App开发 运维 Java
如何基于mPaaS的闪退日志进行闪退排查
目前 mPaas Android是使用的是Crash SDK对闪退进行的处理,CrashSDK 是 Android 平台上一款功能强大的崩溃日志收集 SDK,有着极高的崩溃收集率和完整、全面的崩溃日志信息,生成的日志内容非常利于问题的跟进和解决。在我们的日常运维中,经常遇到一些闪退,无法直接从闪退堆栈看到原因,尤其是一些非Java的Native的闪退,这里分享下在mPaas框架下怎么使用Crash SDK对闪退进行分析。
324 0