异常上报
SDK 集成
- 通过CocoaPods集成
新建项目
cd 项目目录
vim Podfile
#
source 'https://github.com/CocoaPods/Specs.git'
inhibit_all_warnings!
platform :ios, '7.0'
target 'BuglyDemo' do
pod 'ReactiveCocoa','2.5'
pod 'AFNetworking', '2.5.4'
pod 'MJExtension', '2.0.4'
pod 'MJRefresh','3.1.9'
pod 'SDWebImage','3.7.5'
pod 'UITableView+FDTemplateLayoutCell'
pod 'Bugly'
end
保存并执行pod install
,然后用后缀为.xcworkspace
的文件打开工程。
初始化SDK
导入头文件
在工程的AppDelegate.m文件导入头文件
#import <Bugly/Bugly.h>
//如果是Swift工程,请在对应bridging-header.h中导入
默认Debug模式,是不会生成dSYM文件,需要开启.重新编译CMD+B,修改配置如下图
Bugly后台显示异常数据
dSYM文件
iOS平台中,dSYM文件是指具有调试信息的目标文件,文件名通常为:xxx.app.dSYM。
- 为了方便找回Crash对应的dSYM文件和还原堆栈,建议每次构建或者发布APP版本的时候,备份好dSYM文件。
生成后,在哪里可以找到dSYM文件?
保存log到本地,并上传到Bugly管理后台
1.遵守代理协议
@interface AppDelegate ()<BuglyDelegate>
2.设置代理对象
BuglyConfig *config = [[BuglyConfig alloc] init];
config.delegate = self;
[Bugly startWithAppId:@"你的AppId" config:config];
3.实现代理方法attachmentForException
#pragma mark - Bugly代理 - 捕获异常,回调(@return 返回需上报记录,随 异常上报一起上报)
- (NSString *)attachmentForException:(NSException *)exception {
#ifdef DEBUG // 调试
return [NSString stringWithFormat:@"我是携带信息:%@",[self redirectNSLogToDocumentFolder]];
#endif
return nil;
}
#pragma mark - 保存日志文件
- (NSString *)redirectNSLogToDocumentFolder{
//如果已经连接Xcode调试则不输出到文件
if(isatty(STDOUT_FILENO)) {
return nil;
}
UIDevice *device = [UIDevice currentDevice];
if([[device model] hasSuffix:@"Simulator"]){
//在模拟器不保存到文件中
return nil;
}
//获取Document目录下的Log文件夹,若没有则新建
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *logDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Log"];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL fileExists = [fileManager fileExistsAtPath:logDirectory];
if (!fileExists) {
[fileManager createDirectoryAtPath:logDirectory withIntermediateDirectories:YES attributes:nil error:nil];
}
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 *logFilePath = [logDirectory stringByAppendingFormat:@"/%@.txt",dateStr];
// freopen 重定向输出输出流,将log输入到文件
freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);
freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
return [[NSString alloc] initWithContentsOfFile:logFilePath encoding:NSUTF8StringEncoding error:nil];
}
Bugly iOS 符号表配置
什么是符号表?
符号表是内存地址与函数名、文件名、行号的映射表。符号表元素如下所示:
<起始地址> <结束地址> <函数> [<文件名:行号>]
为什么要配置符号表?
为了能快速并准确地定位用户APP发生Crash的代码位置,Bugly使用符号表对APP发生Crash的程序堆栈进行解析和还原。
自动配置:XCode + sh脚本
到官方文档下载自动配置符号表工具包,解压如下:
使用文档中的方式一进行配置(默认方式)
配置Xcode编译执行脚本
- 在Xcode工程对应Target的
Build Phases
中新增Run Scrpit Phase
打开工具包中的dSYM_upload.sh
,复制所有内容,在新增的Run Scrpit Phase
中粘贴
修改新增的Run Scrpit中的 <YOUR_APP_ID>
为您的App ID
,<YOUR_APP_KEY>
为您的App Key
,<YOUR_BUNDLE_ID>
为App的Bundle Id
脚本默认在Debug模式及模拟器编译情况下不会上传符号表,在需要上传的时候,请修改下列选项
- Debug模式编译是否上传,1=上传 0=不上传,默认不上传
UPLOAD_DEBUG_SYMBOLS=0
- 模拟器编译是否上传,1=上传 0=不上传,默认不上传
UPLOAD_SIMULATOR_SYMBOLS=0
至此,自动上传符号表脚本配置完毕,Bugly 会在每次 Xcode 工程编译后自动完成符号表配置工作。
应用升级&热更新
介绍:
Bugly 应用升级服务为您的应用版本配置升级提醒,并可对用户范围及数量进行精准控制,多纬度数据监控,实时了解版本转化率。
不过看文档介绍没有iOS端的应用升级。
注意Bugly使用的是JSpatch来做热更新的,可能导致上线审核时被拒绝。
因为苹果公司对热更新的强烈抵制,所以Bugly才把异常上报功能和热更新的SDK分开集成。
所以有迫切迭代上线应用的小伙伴慎用!!!
所以有迫切迭代上线应用的小伙伴慎用!!!
所以有迫切迭代上线应用的小伙伴慎用!!!
因为BuglyHotfix SDK v2.0 及以上版本不包含 JSPatch,所以JSPatch要单独集成
SDK 集成
pod 'BuglyHotfix'
- 注意点
BuglyHotfix iOS SDK 最低兼容 iOS 系统版本 iOS 7.0
BuglyHotfix 已经包含 Bugly 的所有功能,如果已经集成 Bugly SDK,请移除 Bugly 并替换成 BuglyHotfix 。
JSPatch 集成
JSPatch下载
把下列文件拖入 Xcode 工程内(请勾选Copy items if needed选项)
JPEngine.h
JPEngine.m
JSPatch.js
在工程内的Prefix Header(后缀为.pch)的文件内 #include “BuglyHotfixConfuse_pch.h” ,如工程内没有 pch 文件请参见这篇文章配置。
初始化SDK
#import "AppDelegate.h"
#import <BuglyHotfix/Bugly.h>
#import <BuglyHotfix/BuglyMender.h>
#import "JPEngine.h"
@interface AppDelegate ()<BuglyDelegate>
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self configBugly];
return YES;
}
- (void)configBugly {
//初始化 Bugly 异常上报
BuglyConfig *config = [[BuglyConfig alloc] init];
config.delegate = self;
config.debugMode = YES;
config.reportLogLevel = BuglyLogLevelInfo;
[Bugly startWithAppId:@"b950788e02"
#if DEBUG
developmentDevice:YES
#endif
config:config];
//捕获 JSPatch 异常并上报
[JPEngine handleException:^(NSString *msg) {
NSException *jspatchException = [NSException exceptionWithName:@"Hotfix Exception" reason:msg userInfo:nil];
[Bugly reportException:jspatchException];
}];
//检测补丁策略
[[BuglyMender sharedMender] checkRemoteConfigWithEventHandler:^(BuglyHotfixEvent event, NSDictionary *patchInfo) {
//有新补丁或本地补丁状态正常
if (event == BuglyHotfixEventPatchValid || event == BuglyHotfixEventNewPatch) {
//获取本地补丁路径
NSString *patchDirectory = [[BuglyMender sharedMender] patchDirectory];
if (patchDirectory) {
//指定执行的 js 脚本文件名
NSString *patchFileName = @"main.js";
NSString *patchFile = [patchDirectory stringByAppendingPathComponent:patchFileName];
//执行补丁加载并上报激活状态
if ([[NSFileManager defaultManager] fileExistsAtPath:patchFile] &&
[JPEngine evaluateScriptWithPath:patchFile] != nil) {
BLYLogInfo(@"evaluateScript success");
[[BuglyMender sharedMender] reportPatchStatus:BuglyHotfixPatchStatusActiveSucess];
}else {
BLYLogInfo(@"evaluateScript failed");
[[BuglyMender sharedMender] reportPatchStatus:BuglyHotfixPatchStatusActiveFail];
}
}
}
}];
}
#pragma mark - Bugly代理 - 捕获异常,回调(@return 返回需上报记录,随 异常上报一起上报)
- (NSString *)attachmentForException:(NSException *)exception {
#ifdef DEBUG // 调试
return [NSString stringWithFormat:@"我是携带信息:%@",[self redirectNSLogToDocumentFolder]];
#endif
return nil;
}
#pragma mark - 保存日志文件
- (NSString *)redirectNSLogToDocumentFolder{
//如果已经连接Xcode调试则不输出到文件
if(isatty(STDOUT_FILENO)) {
return nil;
}
UIDevice *device = [UIDevice currentDevice];
if([[device model] hasSuffix:@"Simulator"]){
//在模拟器不保存到文件中
return nil;
}
//获取Document目录下的Log文件夹,若没有则新建
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *logDirectory = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Log"];
NSLog(@"%@",paths);
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL fileExists = [fileManager fileExistsAtPath:logDirectory];
if (!fileExists) {
[fileManager createDirectoryAtPath:logDirectory withIntermediateDirectories:YES attributes:nil error:nil];
}
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 *logFilePath = [logDirectory stringByAppendingFormat:@"/%@.txt",dateStr];
// freopen 重定向输出输出流,将log输入到文件
freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);
freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
return [[NSString alloc] initWithContentsOfFile:logFilePath encoding:NSUTF8StringEncoding error:nil];
}