日志捕获小工具

背景

开发过程中,为了便于调试,会输出很多打印日志,而只有当电脑连着真机进行调试的时候,Xcode控制台才会有日志输出。这也就意味着如果未处于调试状态时,是看不到Xcode控制台的日志输出的,那么如果还想看到日志输出,那么这个功能就尤为重要了。

实现方案的调研与思考

iOS 开发语言有Objective-C和Swift,经销商项目中均有使用。Objective-C中的打印方法为NSLog,Swift中打印方法为print。这就意味着如果要实现此功能,就需要处理两个方法。

NSLog

NSLog是一个全局的C函数,函数声明如下:

FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2) NS_NO_TAIL_CALL;

这意味着用swizzle方法交换是行不通的。

经过调研,捕获NSLog日志有三种方式 

 1、iOS 10以前可以通过ASL接口来获取

      有成熟的第三方库CocoaLumberjack。但是 需要使用他们公开的API,这于我们来说侵入性太大,完全没有必要。

 2、通过fishhook库hook NSLog方法重定向NSLog函数

       fishhook是Facebook提供的一个动态修改链接Mach-O文件的工具,能够hook C函数。

 3、使用dup2函数和STDERR句柄重定向NSLog函数

      NSLog最后重定向的句柄是STDERR,NSLog输出的日志内容,最终都通过STDERR句柄来记录,而dup2函数式专门进行文件重定向的;可以使用dup2重定向STDERR句柄,将内容重定向指定的位置。但是重定向之后,控制台无法正常输出

经考虑后选择更容易驾驭的第二种方案。

print

print是Swift的全局方法,函数声明如下:

func print(_ items: Any..., separator: String = " ", terminator: String = "\n")

Swift语言具备重载特性,我们可以在项目中重载该print方法。这样在项目中调用print方法后,会直接调用已重载后的print方法。但需保证Swift的print方法重载类在项目目录下,不能在组件下。

代码实现

Objective-C中NSLog的方法重定向(选用优化后的fishhook方案)

核心代码如下:

//函数指针,用来保存原始的函数的地址
static void(*old_nslog)(NSString *format, ...);
 
//新的NSLog
void myNSLog(NSString *format, ...){
     
    va_list vl;
    va_start(vl, format);
    NSString* str = [[NSString alloc] initWithFormat:format arguments:vl];
    va_end(vl);
     
    [[DoraemonNSLogManager sharedInstance] addNSLog:str];
    //再调用原来的nslog
    //old_nslog(str);
    old_nslog(@"%@",str);
}
 
@implementation DoraemonNSLogManager
 
+ (instancetype)sharedInstance {
    static id instance = nil;
    static dispatch_once_t onceToken;
     
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}
 
// 开启日志监听
- (void)startNSLogMonitor{
    doraemon_rebind_symbols((struct doraemon_rebinding[1]){"NSLog", (void *)myNSLog, (void **)&old_nslog},1);
}
// 关闭日志监听
- (void)stopNSLogMonitor{
    doraemon_rebind_symbols((struct doraemon_rebinding[1]){"NSLog", (void *)old_nslog, NULL},1);
}
 
// 日志写入缓存
- (void)addNSLog:(NSString *)log{
    DoraemonNSLogModel *model = [[DoraemonNSLogModel alloc] init];
    model.content = log;
    model.timeInterval = [[NSDate date] timeIntervalSince1970];
     
    if (!_dataArray) {
        _dataArray = [[NSMutableArray alloc] init];
    }
    [_dataArray addObject:model];
}

Swift的print方法捕获(方法重载方案)

核心代码如下:

// 重载print方法
public func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
#if DEBUG
    // 调用原系统的打印方法 保持控制台的正常输出
    Swift.print(items, separator: separator, terminator: terminator)
    
    // 添加swift日志打印
    let isOn = DoraemonCacheManager.sharedInstance().nsLogSwitch()
    guard isOn else { return }
    let str =  items.map { "\($0)" }.joined(separator: separator)
    // 日志写入缓存
    DoraemonNSLogManager.sharedInstance().addNSLog(str)
#endif
}

捕获这两个方法后,即可将输出的日志,写入到缓存中,然后再呈现到UI页面上,即可实现在设备上实时查看日志的功能。

如何使用

该方案无侵入,仅需要植入对应的代码即可。

在app的小工具下即可使用日志捕获功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值