DKSocketLog 实现终端输出设备日志
# 前言
程序员调试程序无非就两种,打印日志和断点调试。大部分情况下,这是足够的,但有些特殊情况,比如 iPhone 接着外接设备比如 OTG,这个时候就没办法用数据线连电脑了,自然也就无法断点调试,只能靠日志。而日志也没办法直接显示在 Xcode 上,只能用保存文件的方式将日志文件保存到手机,再去找到日志去看。非常麻烦,于是想了一下用 webSocket 与服务器通讯,就能以一种更直接方便的方式来看日志。
# 准备工作
# Node.JS
到 Node.js 官网 安装 nodejs 环境,安装完成后敲node -v
,如果 log 输出了版本号,说明 node 环境已经配好了。
$ node -v
v4.4.1
2
# socket.io
先贴上 Socket.io 官网,Socket.io GitHub
# 安装 socket.io
$ sudo npm install 'socket.io'@0.9
注意,要指定版本号为0.9,用1.0以上的1.x版本有问题,因为 Client(iOS)端的 socket.io 是0.9版本,不支持1.x,如果使用0.9版本的客户端给1.x版本的服务端发消息,会出现400报错:
{
"code":0,
"message":"Transport unknown"
}
2
3
4
解决办法就是 fall back,卸载重装
$ sudo npm uninstall 'socket.io'
$ sudo npm install 'socket.io'@0.9
2
# app.js
var io = require('socket.io').listen(8888);
io.sockets.on('connection', function (socket) {
socket.on('message', function (data) {
console.info(data);
});
});
io.configure('development', function(){
io.enable('browser client etag');
io.set('log level', 1);
});
io.configure('release', function(){
io.set('transports', ['websocket']);
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
其中 8888 为 node 监听的端口,可根据需求更改。 log level 设为 1 是为了不输出 debug 的 log。
保存这个 js 文件并用 node 命令执行,(建议保存到项目目录下,后期可能会做一些自动化脚本来执行。)
$ node app.js
info - socket.io started
2
如果看到info - socket.io started
就说明,服务端已经启动完毕。当然,可能没有那么一帆风顺,可能有几个报错:
Error: Cannot find module 'express'
: 缺少插件,将 express
和 ejs
都安装即可。
$ sudo npm install 'express'
$ sudo npm install 'ejs'
2
warn - error raised: Error: listen EADDRINUSE :::8888
: 端口占用,要么监听其它端口,要么将端口占用的程序杀掉。
查看 PID
$ sudo lsof -i tcp:8888
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 1371 bingo 11u IPv6 0xdaf792eabddb3d4d 0t0 TCP *:ddi-tcp-1 (LISTEN)
2
3
杀死进程
$ sudo kill -9 1371
再试着执行 nodejs 即可
$ node app.js
info - socket.io started
2
如果还有其它问题,自行网上查找资料解决,后面我会考虑将端口占用的问题也用脚本自动化解决。
以上都是准备工作,其实也不多,只需要准备一次环境,以后就不需要再搞服务端了。
# 使用方法
将源代码中的 DKSocketLog
文件夹整个添加到 iOS 项目中,并在 PCH 文件中引入头文件
#import "DKSocketLog.h"
项目需要在 Build Phases 添加 libicucore.tbd
,否则会报如下错误。
Undefined symbols for architecture arm64:
"_utf8_nextCharSafeBody", referenced from:
_validate_dispatch_data_partial_string in SRWebSocket.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
2
3
4
5
找一个地方写 DKSocketLog 的配置,可以写在 AppDelegate 或者其它地方,根据项目架构自行安排。Demo 中我新建了一个类 DKLogConfig,并在 load 方法中进行配置。
+ (void)load
{
// [[DKSocketLog sharedInstance] setDebugMode:YES];
// [[DKSocketLog sharedInstance] setSecureEnable:YES];
[[DKSocketLog sharedInstance] setupHostname:@"192.168.1.89" port:8888];
}
2
3
4
5
6
方法 | 说明 |
---|---|
setDebugMode | 是否开启DEBUG模式,默认关闭 |
setSecureEnable | 是否使用安全传输协议(HTTPS) |
setupHostname:port | 设置主机名和端口,模拟器可以不设置,默认是 localhost;真机要确保与电脑在同一个网段下,局域网下或使用 WIFI |
使用 DKLog(...) 代替 NSLog(...)
DKLog(@"打印100内的随机数:%d", arc4random_uniform(100));
如果没问题,就可以看到 Xcode 的控制台与终端同步输出 log
2017-03-02 15:36:40.705 -[ViewController logBtnClick] 第25行: 打印100内的随机数:14
# 补充
或许会有疑问,真实环境下会不会浪费带宽,每次都尝试链接会不会消耗性能?
可以放心,这种 log 只在 DEBUG 包下有效,发布 Release 包什么操作也没有,详情可以看 DKLog 宏的定义。
#ifdef DEBUG
#define DKLog(...) \
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; \
[dateFormatter setDateFormat:@"yyyy-MM-dd hh:mm:ss.SSS"]; \
NSString *dateStr = [dateFormatter stringFromDate:[NSDate date]]; \
NSString *logStr = [NSString stringWithFormat:@"%@ %s 第%d行: %@",dateStr, __func__, __LINE__, [NSString stringWithFormat:__VA_ARGS__]]; \
printf("%s\n", [logStr UTF8String]);\
[[DKSocketLog sharedInstance].socketIO sendMessage:logStr];
#else
#define DKLog(...)
#endif
2
3
4
5
6
7
8
9
10
11
# 后话
一个简单的终端监听日志并输出的功能就这样集成实现了,当然还是显得有些简陋,可扩展的功能还有很多,比如很多东西都可以封成一个 Shell 脚本,除了上面说到的执行 node 命令,杀死占用端口的进程外,还可以将日志写到电脑上,在项目根目录下建一个 log 文件夹并将日志都保存到 log 文件夹下,并按日期分文件,好像挺好玩的样子,还有同事说既然都用到了 webSocket,为什么不用网页来展示日志?可以更美观,好像也有道理,这样就不用开着终端了,node 在后台跑着进程就行,再写一个守护进程的保证它不死掉等等,后面有时间慢慢扩展。