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

背景

开发过程中,为了便于调试,会输出很多打印日志,而只有当电脑连着真机进行调试的时候,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、通过fishhookhook 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方法。但需保证Swiftprint方法重载类在项目目录下,不能在组件下。

代码实现

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];
}

Swiftprint方法捕获(方法重载方案)

核心代码如下:

// 重载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的小工具即可使用日志捕获功能。

原文地址:https://blog.csdn.net/qq_25303213/article/details/128701425

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_19257.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注