之前交接过来的工作里有个页面本地调试麻烦,因为它的数据是后端实时上报而来,每次一报bug,都是给一个几兆的日志文本(上面记录了实际出问题的上报数据【json格式】),通过肉眼分析日志搭配页面逻辑人工解读,太难了……后面在页面加了mock的逻辑,但遇到一个问题,就是把日志中的数据配置到mock接口那边
=> 于是有了写脚本从日志文件里提取json数据的想法。
如何识别json字符串
- 用正则匹配json,json的数据格式不一致且多层嵌套{}。 => 放弃
替换方案:来自Stack Overflow>>
原理:通过对字符串进行"{}"识别和截取,利用JSON.parse对截取的字符串进行尝试转换,如果成功则返回,失败(抛出异常)则捕获异常并继续缩窄字符串内容范围。
该段原理逻辑识别不了所有json,优先匹配第一个出现的json,我改成了从后往前来识别json,主要是结合需求的日志重点在后面的json
function extractJSON(str) {
var firstOpen, firstClose = str.length - 1, candidate;
firstClose = str.lastIndexOf('}');
do {
// debugger
firstOpen = str.indexOf('{');
// console.log('firstOpen: ' + firstOpen, 'firstClose: ' + firstClose);
if(firstClose <= firstOpen) {
return null;
}
do {
candidate = str.substring(firstOpen, firstClose + 1);
// console.log('candidate: ' + candidate);
try {
var res = JSON.parse(candidate);
return [res, firstOpen, firstClose + 1];
}
catch(e) {
// console.log('...failed');
}
firstOpen = str.indexOf('{', firstOpen+1);
} while(firstClose > firstOpen && firstOpen !== -1);
firstClose = str.lastIndexOf('}', firstClose - 1);
} while(firstClose != -1);
return null;
}
如此,就可以解决如何从一段文本中提取出json内容来。
接下来就是如何合理提取json为好,日志文件内容的特征是每行输出信息,而基本每行能提取一个关键json的数据来,那么可以用上nodejs的流来实现按行读取同时提取json
注:只是看中了用stream来处理文件读取方便,对于较为大一点的文件(我遇到的日志最大不超过1g),可以按参考链接里的event-stream来处理
该部分的内容就不做重复说明,直接贴代码。
const fs = require('fs');
const readline = require('readline');
const stream = require('stream');
const instream = fs.createReadStream('test.log.txt');
const outstream = new stream();
const rl = readline.createInterface(instream, outstream);
function extractJSON(str) {
var firstOpen, firstClose = str.length - 1, candidate;
firstClose = str.lastIndexOf('}');
do {
// debugger
firstOpen = str.indexOf('{');
// console.log('firstOpen: ' + firstOpen, 'firstClose: ' + firstClose);
if(firstClose <= firstOpen) {
return null;
}
do {
candidate = str.substring(firstOpen, firstClose + 1);
// console.log('candidate: ' + candidate);
try {
var res = JSON.parse(candidate);
// console.log('...found');
return [res, firstOpen, firstClose + 1];
}
catch(e) {
// console.log('...failed');
}
firstOpen = str.indexOf('{', firstOpen+1);
} while(firstClose > firstOpen && firstOpen !== -1);
firstClose = str.lastIndexOf('}', firstClose - 1);
} while(firstClose != -1);
return null;
}
// 记录行数
let lineCount = 0;
// 收集json数据
const results = [];
rl.on("line", lineStr => {
lineCount++;
const extractRes = extractJSON(lineStr);
if (extractRes) {
results.push({
line: lineCount,
content: extractRes[0]
})
}
});
rl.on('close', () => {
// 对收集来的json数据再做一下过滤,提取目标json
const fileContents = results.filter(res => res.content.msg_content);
// 一般结果都不少,直接写入输出文件中
fs.writeFileSync('res.txt', fileContents.map(obj => {
return ' 行:' + obj.line + '\n' + JSON.stringify(obj.content);
}).join('\n\n'));
});
如此一来,写好脚本以后就方便过滤日志文件提取必要信息用于分析了,也可以拿来mock用。
发现从code上复制的代码贴到sf编辑器,会加入\
的转义符,还得复制到sublime 再复制回来才正常 - -||