美文网首页NSLog崩溃日志相关
iOS 搜集Xcode打印日志及奔溃日志并上传到服务器

iOS 搜集Xcode打印日志及奔溃日志并上传到服务器

作者: 绿绿清欢 | 来源:发表于2017-09-22 20:06 被阅读80次

    将Xcode打印的日志传导服务器上,这样测试人员在测试时发现了问题,比如奔溃,希望能不仅获取当时的奔溃日志,也能获取正常的打印日志信息,有此产生了此需求。(一些第三方奔溃统计产品是能满足部分需求的,但是自定义地加入正常xcode控制台的输出还是有点麻烦。)

    来,愉悦下眼睛.jpg

    如果你有自己的后台团队,或者跟你们服务器团队沟通很是方便,这点是很容易实现哒,要他们给你个专门的接口或地址来存放这个日志就好啦;不过万一你们家服务团队像我们家的很忙,你也不想因为这个小小的需求去进行你不怎么擅长地沟通活动,或者你是接的外包App任务,你可能想要有一个自己的小服务器来进行访问和存储,这里我就要特别推荐下七牛云存储

    使用七牛云服务的公司.png

    重要的是有免费的10GB的存储空间,PUT / DELETE 请求,前10万次免费;GET 请求,前100万次免费。对于我们这个需求完全够用了有木有。

    七牛云免费套餐.png

    那下面就看我们具体怎么操作

    1. 首先是看如何搜集奔溃
      苹果官方的方法是NSSetUncaughtExceptionHandler
      创建一个MyUncaughtExceptionHandler的类,在.h文件中
      #import <Foundation/Foundation.h>
    
     @interface MyUncaughtExceptionHandler : NSObject
    + (void)setDefaultHandler;
    + (NSUncaughtExceptionHandler *)getHandler;
    
     @end
    

    在.m文件中

    #import "MyUncaughtExceptionHandler.h"
    
    // 返回沙盒地址
    NSString * applicationDocumentsDirectory()
     {
        return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    }
    // 出现崩溃时的回调函数
    void UncaughtExceptionHandler(NSException * exception)
    {
     NSString * path = [applicationDocumentsDirectory() stringByAppendingPathComponent:@"Exception.txt"];
    
    
    NSArray * arr = [exception callStackSymbols];
    NSString * reason = [exception reason]; // 崩溃的原因  可以有崩溃的原因(数组越界,字典nil,调用未知方法...) 崩溃的控制器以及方法
    NSString * name = [exception name];
    NSString * url = [NSString stringWithFormat:@"========异常错误报告========\nname:%@\nreason:\n%@\ncallStackSymbols:\n%@",name,reason,[arr componentsJoinedByString:@"\n"]];
    // 将txt文件写入沙盒
    [url writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];
    
    }
    
    
    @implementation MyUncaughtExceptionHandler
    // 返回沙盒地址
    -(NSString *)applicationDocumentsDirectory
    {
    return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    }
    
    + (void)setDefaultHandler
    {
    NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
    }
    
      + (NSUncaughtExceptionHandler *)getHandler
     {
    return NSGetUncaughtExceptionHandler();
     }
    
    @end
    

    抓取到的文件下载下来是这样哒

    屏幕快照 2017-09-22 下午7.16.09.png
    1. 如何搜集Xcode的打印输出
      同样我们造一个GetXcodeLogInfomation的类,然后在.h文件里
    #import <Foundation/Foundation.h>
    
    @interface GetXcodeLogInfomation : NSObject
    + (void)saveXcodeInfomation;
    + (NSString *)getLogFilePath;
    @end
    

    在.m文件里

    #import "GetXcodeLogInfomation.h"
    @implementation GetXcodeLogInfomation
    
    + (void)saveXcodeInfomation{
    NSString * path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"XcodeInfo.txt"];
       //这里删除文件是为了避免在之前的文件上累加这次运行的日志
    NSFileManager *defaultManager = [NSFileManager defaultManager];
    [defaultManager removeItemAtPath:path error:nil];
    
    /*这个函数的作用是把原本应该输出到Xcode console控制台的信息,转存到 XcodeInfo.txt中,所以实际在Xcode调试的时候不要用此方法,"a+"表示“追加写入(区别于"r"表示“只读访问”、"w"表示“只写访问”)*/
    ///此方法适用于打包后给测试人员安装时调用
    freopen([path cStringUsingEncoding:NSASCIIStringEncoding],"a+", stderr);
    }
    + (NSString *)getLogFilePath{
    
    NSString * path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"XcodeInfo.txt"];
    return path;
    }
    @end
    

    这里奔溃后下载下来的文件是这样哒

    屏幕快照 2017-09-22 下午7.17.19.png

    这表示我们成功抓取了Xcode控制台输出的信息,并成功地保存上传了服务器;是不是棒棒哒,好了我们中途休息一下

    relax-for-health.jpg
    1. 本地生成token,上传到七牛云
      这里有个问题值得注意,Exception.txt 是存储奔溃时打印的堆栈信息;XcodeInfo.txt存储的事Xcode console打印出的信息,这表示XcodeInfo.txt是包涵了Exception.txt存储的 奔溃时打印的堆栈信息的,所以我们存储上传一个就好啦。

    注意:由App直接上传到七牛云是需要在App端生成Token信息的,这种做法并不安全,所以推荐仅测试时传堆栈信息使用,要存储别的文件推荐采取官方推荐的方法。

    我们建一个QiniuAuthPolicy类来本地生成Token,在.h文件里

    #import <Foundation/Foundation.h>
    
    @interface QiniuAuthPolicy : NSObject
    + (NSString *)token;
    @end
    

    在.m文件里

    #import "QiniuAuthPolicy.h"
    #import <CommonCrypto/CommonDigest.h>
    #include <CommonCrypto/CommonHMAC.h>
    #import "QNUrlSafeBase64.h"
    #import "QN_GTM_Base64.h"
    
    
    @implementation QiniuAuthPolicy
    + (NSString*)dictionryToJSONString:(NSMutableDictionary *)dictionary
    {
    NSError *parseError = nil;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:&parseError];
    
    return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    }
    
    //AccessKey  以及SecretKey
    + (NSString *)token{
    
    return [QiniuAuthPolicy makeToken:@"6_BiWGE_Jc_Kug1QrNqNJZ4cHtQGzwMUHvBcwPlf" secretKey:@"OJKeDPHXvHfeQBLak42cyDREO8EX66kaU8k2a7dy"];
    }
    
    + (NSString *) hmacSha1Key:(NSString*)key textData:(NSString*)text
    {
    const char *cData  = [text cStringUsingEncoding:NSUTF8StringEncoding];
    const char *cKey = [key cStringUsingEncoding:NSUTF8StringEncoding];
    uint8_t cHMAC[CC_SHA1_DIGEST_LENGTH];
    CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
    NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:CC_SHA1_DIGEST_LENGTH];
    NSString *hash = [QNUrlSafeBase64 encodeData:HMAC];
    return hash;
    }
    //**根据AccessKey和SecretKey生成Token**
    + (NSString *)makeToken:(NSString *)accessKey secretKey:(NSString *)secretKey
    {
    //根据时间scope和时间来设置
    NSString *baseName = [self marshal];
    baseName = [baseName stringByReplacingOccurrencesOfString:@" " withString:@""];
    baseName = [baseName stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    
    NSData   *baseNameData = [baseName dataUsingEncoding:NSUTF8StringEncoding];
    NSString *baseNameBase64 = [QNUrlSafeBase64 encodeData:baseNameData];
    NSString *secretKeyBase64 =  [QiniuAuthPolicy hmacSha1Key:secretKey textData:baseNameBase64];
    NSString *token = [NSString stringWithFormat:@"%@:%@:%@",  accessKey, secretKeyBase64, baseNameBase64];
    
    return token;
    }
    
    + (NSString *)marshal
    {
    time_t deadline;
    time(&deadline);
    NSMutableDictionary *dic = [NSMutableDictionary dictionary];
    [dic setObject:@"littlelydia" forKey:@"scope"];
    //3464706673 是token有效期,即Unix时间戳(Unix timestamp) 转换成北京时间 2079/10/17 2:31:13
    NSNumber *escapeNumber = [NSNumber numberWithLongLong:3464706673];
    [dic setObject:escapeNumber forKey:@"deadline"];
    NSString *json = [QiniuAuthPolicy dictionryToJSONString:dic];
    return json;
    }
    

    这样我们就生成了有效的Token,最后就是组合上传日志啦
    在Appdelegate.m里

    #import "AppDelegate.h"
    // #import "MyUncaughtExceptionHandler.h"//这里是否要引入这个头文件就看你需求啦
    #import "AFNetworking.h"
    #import <HappyDNS/HappyDNS.h>
    #import "QiniuSDK.h"
    #import "QiniuAuthPolicy.h"
    #import "GetXcodeLogInfomation.h"
    
    @interface AppDelegate ()
    
    @end
    
    @implementation AppDelegate
    
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    #pragma mark -- 崩溃日志
    
    
        // 发送崩溃日志
    #if DEBUG
    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *dataPath = [path stringByAppendingPathComponent:@"XcodeInfo.txt"];
    
    NSData *data = [NSData dataWithContentsOfFile:dataPath];
    if (data != nil) {
        [self sendExceptionLogWithData];
    }
    [GetXcodeLogInfomation saveXcodeInfomation];
    #endif
    
    return YES;
    }
     #pragma mark -- 发送崩溃日志
    - (void)sendExceptionLogWithData
    {
    [self upLoadFile:[QiniuAuthPolicy token]];
    
     }
    
    - (QNUploadManager * )QNUploadManager {
    static QNUploadManager *manager;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        QNConfiguration *config = [QNConfiguration build:^(QNConfigurationBuilder *builder) {
            NSMutableArray *array = [[NSMutableArray alloc] init];
            [array addObject:[QNResolver systemResolver]];
            QNDnsManager *dns = [[QNDnsManager alloc] init:array networkInfo:[QNNetworkInfo normal]];
            //是否选择  https  上传
            builder.zone = [[QNAutoZone alloc] initWithHttps:YES dns:dns];
        }];
        manager = [[QNUploadManager alloc] initWithConfiguration:config];
    });
    
    return manager;
    }
    -(void)upLoadFile:(NSString *)token
    {
    QNUploadManager *manager = [self QNUploadManager];
    
    NSString *tokenStr = token;
    
    QNUploadOption *uploadOption = [[QNUploadOption alloc] initWithMime:nil progressHandler:^(NSString *key, float percent) {
        NSLog(@"percent == %.2f", percent);
    }
                                                                 params:nil
                                                               checkCrc:NO
                                                     cancellationSignal:nil];
    
    /** 上传文件*/
    
    NSString *path = [GetXcodeLogInfomation getLogFilePath];
    [manager putFile:path key:nil token:tokenStr complete:^(QNResponseInfo *info, NSString *key, NSDictionary *resp) {
        NSLog(@"info ===== %@", info);
        NSLog(@"resp ===== %@", resp);
    
      }
    option:uploadOption];
    
    
     }
    

    OK,圆满完成任务啦!

    congratulations-on-your-achievements-im-52650-20248.jpg

    最后,关于将七牛云导入到项目中,用cocoapods真地很容易,关于cocoapods的安装,巧神的博客讲得很详细啦;关于使用这里简单提一下;首先是正常地创建一个新的Project,比如我创建地UploadLogToQiniu,然后可以用文本编辑器把创建一个名叫Podfile的文件,或者把别的项目的PodFile拷贝过来修改成针对自己项目哒,改完后是这个样纸

    屏幕快照 2017-09-22 下午7.41.43.png
    然后把这个文件拷贝到之前创建的UploadLogToQiniu项目文件里 ,项目文件就是这样啦 (现在这有这三个文件)
    屏幕快照 2017-09-22 下午7.43.24.png
    然后打开终端,先切换到你项目文件所在的目录,输入 cd 后直接拖拽文件到终端,如图
    屏幕快照 2017-09-22 下午7.46.34.png
    按Enter键后,再输入Pod Install(记住,获取安装一个项目的依赖库,都是使用pod install命令,只有当需要把所有库更新到最新版本,才需要pod update)
    屏幕快照 2017-09-22 下午7.50.40.png
    输入回车后它就自动开始安装依赖库啦,安装成功后是这个样纸
    屏幕快照 2017-09-22 下午7.51.53.png
    然后你的项目文件就自动变成了这个样纸,
    屏幕快照 2017-09-22 下午7.52.41.png
    之后你只需要打开UploadLogToQiniu.xcworkspace这个文件来编辑你的项目就好啦!
    好了,enjoy your time!
    参考文章:
    iOS 崩溃日志 收集与发送服务器
    二探-七牛Token生成

    相关文章

      网友评论

        本文标题:iOS 搜集Xcode打印日志及奔溃日志并上传到服务器

        本文链接:https://www.haomeiwen.com/subject/rfdksxtx.html