Logitor:一个实时日志文件的实时查看器

喜闻乐见又造了一个轮子233

前言

Unity默认的日志系统感觉不好用,为了方便观察调试信息,还为了更好地利用多显示器,花了一天时间撸了一个看实时日志的工具

注意:现在这个版本只是适用于我之前弄的适用于Unity的日志增强模块

本文所介绍的工具源码在GitHub上托管

现在有做了这些功能

1. 实时显示

Unity在运行的时候会用Debug.Log()之类的方法输出日志,此时工具会实时显示结果

2. 高亮显示

如果出现了警告或者错误,工具会高亮显示异常的一行,规则按照国际标准,警告黄色,错误红色

3. 自动刷新

每当游戏终止并重新运行时,游戏会重新生成一个新的日志文件,此时工具会自动检测到新文件的存在,并清空页面内容同时开始监听读取新文件的内容

实现方法

先用Python写了一个命令行下监听日志文件的工具,所以之后封装到Electron的时候就懒得移植了,直接用node的child_process模块运行了一个Python的子进程。

工作流程是这样的:首先Python脚本实时读取最新的日志文件并通过标准输出stdout输出信息,被node获取到,使用js对原始信息进行处理排版,显示到前端页面上。如果获取到新文件的消息,则清空页面,重新开始记录。

工作流程

看上去好像也不复杂,但是还是遇到了不少坑,有几个重点

1. 两个进程同时读写同一个文件会导致冲突

至少在Windows下是这样的,一旦Python打开了日志文件,Unity在继续写入的时候就会产生异常。所以我在Python端就采取了轮询的方法,每0.1秒打开一次日志文件读取内容并马上关闭,如果内容变多了就把多出来的行print出来。当然Unity那边在输出的时候也要try一下,如果冲突的话就等到下一次写入周期再尝试。

2. 动态监测最新的日志文件

每次轮询的时候都把日志文件的目录列出来,根据文件名找到最新的日志文件,如果和当前正在监听的不一样了,那么就说明新的一次运行已经开始了,此时应当开始监听新的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def check_new():
latest = 0
global g_folder_name
global g_log_file_prefix # "GameLog"
global g_log_count # 用于统计日志文件的总数
files = os.listdir(g_folder_name)
g_log_count = 0
for filename in files:
try:
if not filename.startswith(g_log_file_prefix):
continue
g_log_count += 1
date = int(filename[7:len(filename) - 4])
if date > latest:
latest = date
except ValueError:
pass
return "%s%d.txt" % (g_log_file_prefix, latest)
pass # end of check_new

3. 实时获取Python脚本的输出内容

node.js的child_process模块spawn出来的进程不能“即时”地获取到Python脚本所输出的内容,Google后找到解决方案。造成这种情况的原因是Python里print之后会把数据存在缓存中而并不会立即输出,所以解决方法很简单,每次print完之后调用一次stdout的flush就行了。

1
2
3
def output(msg):
print(msg.encode('unicode-escape'))
sys.stdout.flush()

4. Electron对Unicode字符的支持有问题

可能是我没找到正确的设置编码的方法,总之中文字符在被node获取到之后怎么搞都是乱码,最后决定用稍微麻烦点但是最稳妥的方案。就是在Python脚本中输出文本前先把要输出的字符串转成Unicode编码,就是\u1234这样的形式,这样一来就都是ASCII中的简单字符了,传给js后由js进行解码,还原成原本的中文字符。

1
2
3
4
5
6
7
const onGetOutput = function(msg) {
// msg: \u4e2d\u6587
msg = unescape(msg.replace(/\\u/g, '%u'));
// msg: 中文
console.log(msg);
// ...
}

5. 前端页面的一些实现细节

我毕竟不是前端程序员,弄网页啥的还是不熟练,把这些点归到同一条吧

  • div尺寸动态适应页面高度

直接用js实现的,监听resize事件,变化的时候就重新计算尺寸

1
2
3
4
5
$(window).resize(function() {
const mainHeight = window.innerHeight - 80; // container的高度,80是其margin和padding
const upperHeight = $('#upper').height() + 20; // 20是h1的margin
$('.doc-table').height(mainHeight - upperHeight);
});
  • 冻结表格的标题行

采取的方式是弄两个表格,第一个只有一行标题,第二个存储内容,然后只让第二个有滚动条就行了