将Xcode打印的日志传导服务器上,这样测试人员在测试时发现了问题,比如奔溃,希望能不仅获取当时的奔溃日志,也能获取正常的打印日志信息,有此产生了此需求。(一些第三方奔溃统计产品是能满足部分需求的,但是自定义地加入正常xcode控制台的输出还是有点麻烦。)
来,愉悦下眼睛.jpg如果你有自己的后台团队,或者跟你们服务器团队沟通很是方便,这点是很容易实现哒,要他们给你个专门的接口或地址来存放这个日志就好啦;不过万一你们家服务团队像我们家的很忙,你也不想因为这个小小的需求去进行你不怎么擅长地沟通活动,或者你是接的外包App任务,你可能想要有一个自己的小服务器来进行访问和存储,这里我就要特别推荐下七牛云存储
使用七牛云服务的公司.png重要的是有免费的10GB的存储空间,PUT / DELETE 请求,前10万次免费;GET 请求,前100万次免费。对于我们这个需求完全够用了有木有。
七牛云免费套餐.png那下面就看我们具体怎么操作
- 首先是看如何搜集奔溃
苹果官方的方法是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- 如何搜集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- 本地生成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生成
网友评论