日誌文件中提取json--開發小記

以前交接過來的工做裏有個頁面本地調試麻煩,由於它的數據是後端實時上報而來,每次一報bug,都是給一個幾兆的日誌文本(上面記錄了實際出問題的上報數據【json格式】),經過肉眼分析日誌搭配頁面邏輯人工解讀,太難了……後面在頁面加了mock的邏輯,但遇到一個問題,就是把日誌中的數據配置到mock接口那邊 node

=> 因而有了寫腳本從日誌文件裏提取json數據的想法。json

如何識別json字符串

  • 用正則匹配json,json的數據格式不一致且多層嵌套{}。 => 放棄

替換方案:來自Stack Overflow>>後端

原理:經過對字符串進行"{}"識別和截取,利用JSON.parse對截取的字符串進行嘗試轉換,若是成功則返回,失敗(拋出異常)則捕獲異常並繼續縮窄字符串內容範圍。編輯器

該段原理邏輯識別不了全部json,優先匹配第一個出現的json,我改爲了從後往前來識別json,主要是結合需求的日誌重點在後面的jsonpost

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內容來。ui

接下來就是如何合理提取json爲好,日誌文件內容的特徵是每行輸出信息,而基本每行能提取一個關鍵json的數據來,那麼能夠用上nodejs的流來實現按行讀取同時提取jsondebug

參考>>調試

注:只是看中了用stream來處理文件讀取方便,對於較爲大一點的文件(我遇到的日誌最大不超過1g),能夠按參考連接裏的event-stream來處理日誌

該部分的內容就不作重複說明,直接貼代碼。code

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 再複製回來才正常 - -||

相關文章
相關標籤/搜索