1

之前交接过来的工作里有个页面本地调试麻烦,因为它的数据是后端实时上报而来,每次一报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 再复制回来才正常 - -||


Dont
7k 声望144 粉丝

学如逆水行舟不进则退