1、场景说明
- 为何需要hook日志:
- 控制台输出的日志太多太杂了,导致看不见关键信息.
- 或者有些输出的日志search不到源码,但是需要知道从哪儿调用的?(比如想知道哪个第三方库调用的)
2、解决办法
-
2.2 设置xcode环境变量
- OS_ACTIVITY_MODE 为 Disable,将会所有log不可见,不论系统log还是自行输出log
- 效果说明:屏蔽的只是对NSLog及NSLog的封装打印对象。而系统中好多的自动打印是使用了NSLog,使用该方法可以屏蔽系统打印。
- 代码中操作不规范造成的系统自动打印
-
2.3 自定义宏代替NSLog宏
// DEBUG 模式下打印日志, 当前行
#ifdef DEBUG
// 02 输出格式:第868行: 要打印的信息
#define CHLog(...) fprintf(stderr,"第%d行:%s
",/* 行号 */__LINE__, /* 动态参数 */[[NSString stringWithFormat:__VA_ARGS__] UTF8String]);
// 发布状态
#else
#define CHLog(...)
#endif
-
2.4 第三方的库里面的打印
- 还有一些Log可能是在第三方的库里面打印的,所以我们无法采用第三种方式。我们可以采用method_swizzle来进行方法替换,但是NSLog和printf是函数,这时候就需要用到fishhook来进行函数实现替换。
- ① 原理:
- 简单说就是修改系统函数地址(自定义函数不可hook),达到函数实现交换的目的
- ② 替换对象
// 函数指针,用来保存原始的函数地址 (C 语言语法,函数指针类型变量)
static void (*sys_nslog)(NSString *format, ...);
// 定义一个新的函数
void my_nslog(NSString *format, ...) {
va_list vl;
va_start(vl, format);
NSString *str = [[NSString alloc] initWithFormat:format arguments:vl];
va_end(vl);
// 调用原始的:此处类似method_swizzle
//sys_nslog([NSString stringWithFormat:@"hook => %@", str]);
}
- ⓑ 替换NSLogv,有些极少的情况,可能还需要hook NSLogv()
// 函数指针,用来保存原始的函数地址 (C 语言语法,函数指针类型变量)
static void (*orig_nslogv)(NSString *format,va_list args);
// 定义一个新的函数
void my_nslogv(NSString *format,va_list args) {
//orig_nslogv(format,args);
}
// C语言原有printf
static void (*orig_printf)(const char *path, ...);
// 定义一个新的函数
void my_printf(const char *format,...) {
// 什么都不写就是屏蔽打印
//orig_printf(format);
}
- ③ 具体用法:
- 给自己的工具类开个分类,添加以下代码
- CHTool+Log.m
#import "CHTool+Log.h"
#include "fishhook.h"
@implementation CHTool (Log)
// 函数指针,用来保存原始的函数地址 (C 语言语法,函数指针类型变量)
static void (*sys_nslog)(NSString *format, ...);
// 定义一个新的函数
void my_nslog(NSString *format, ...) {
va_list vl;
va_start(vl, format);
NSString *str = [[NSString alloc] initWithFormat:format arguments:vl];
va_end(vl);
// 调用原始的:此处类似method_swizzle
//sys_nslog([NSString stringWithFormat:@"hook => %@", str]);
}
// 函数指针,用来保存原始的函数地址 (C 语言语法,函数指针类型变量)
static void (*orig_nslogv)(NSString *format,va_list args);
// 定义一个新的函数
void my_nslogv(NSString *format,va_list args) {
//orig_nslogv(format,args);
}
// C语言原有printf
static void (*orig_printf)(const char *path, ...);
// 定义一个新的函数
void my_printf(const char *format,...) {
// 什么都不写就是屏蔽打印
//orig_printf(format);
}
// 关闭以下对象控制的日志打印
+ (void)stop_AllLog {
// 启动后执行下面这句话即可实现NSLog() hook
struct rebinding nslog_rebinding = {"NSLog", my_nslog, (void*)&sys_nslog};
// 1代表后面有一个函数实现需要重新绑定
rebind_symbols((struct rebinding[1]){nslog_rebinding}, 1);
// 启动后执行下面这句话即可实现NSLogv() hook
struct rebinding nslogv_rebinding = {"NSLogv",my_nslogv,(void*)&orig_nslogv};
// 1代表后面有一个函数实现需要重新绑定
rebind_symbols((struct rebinding[1]){nslogv_rebinding}, 1);
// 启动后执行下面这句话即可实现printf() hook
struct rebinding printf_rebinding = {"printf", my_printf, (void*)&orig_printf};
// 1代表后面有一个函数实现需要重新绑定
rebind_symbols((struct rebinding[1]){printf_rebinding}, 1);
}
@end
#import "CHTool.h"
NS_ASSUME_NONNULL_BEGIN
@interface CHTool (Log)
// DEBUG 模式下打印日志, 当前行
#ifdef DEBUG
// 02 输出格式:第868行: 要打印的信息
#define CHLog(...) fprintf(stderr,"第%d行:%s
",/* 行号 */__LINE__, /* 动态参数 */[[NSString stringWithFormat:__VA_ARGS__] UTF8String]);
// 发布状态
#else
#define CHLog(...)
#endif
// 关闭所有的日志打印
+ (void)stop_AllLog;
@end
- 据我自己的观察,fprintf 方法第三方用的比较少,所以保留成我自己的打印方法。
- ④ 应用启动入口调用该方法就可以了
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
// 关闭所有的日志打印
[CHTool stop_AllLog];
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
- 效果: