1. 背景與痛點

在開發分佈式系統時,日誌分散在多個服務節點中,傳統輪詢查詢方式存在延遲高、資源浪費的問題。某次線上故障中,因未能實時發現錯誤日誌,導致問題排查時間延長2小時。因此,決定自研一套低成本、實時性高的日誌監控系統。


2. 技術選型

  • 數據存儲:Elasticsearch(高效檢索與聚合)
  • 實時推送:WebSocket(全雙工通信,避免HTTP輪詢)
  • 後端服務:Node.js + Express(輕量級,適合快速開發)
  • 前端展示:Vue.js + ECharts(可視化日誌趨勢)

3. 核心實現步驟

(附代碼片段與關鍵配置)

3.1 Elasticsearch索引設計
json// 日誌索引模板(按時間分片)
PUT /log-template
{
  "index_patterns": ["logs-*"],
  "settings": {
    "number_of_shards": 3
  },
  "mappings": {
    "properties": {
      "timestamp": { "type": "date" },
      "level": { "type": "keyword" },
      "message": { "type": "text", "analyzer": "ik_max_word" }
    }
  }
}
// 日誌索引模板(按時間分片)
PUT /log-template
{
  "index_patterns": ["logs-*"],
  "settings": {
    "number_of_shards": 3
  },
  "mappings": {
    "properties": {
      "timestamp": { "type": "date" },
      "level": { "type": "keyword" },
      "message": { "type": "text", "analyzer": "ik_max_word" }
    }
  }
}
3.2 WebSocket服務端實現
javascript// Node.js WebSocket服務器
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => {
  console.log('New client connected');
  // 訂閲Elasticsearch日誌變更(通過_changes API或輪詢模擬)
  setInterval(() => {
    // 模擬獲取新日誌(實際可通過ES的search_after或滾動查詢)
    const newLogs = fetchNewLogsFromES(); 
    ws.send(JSON.stringify(newLogs));
  }, 1000);
});
// Node.js WebSocket服務器
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => {
  console.log('New client connected');
  // 訂閲Elasticsearch日誌變更(通過_changes API或輪詢模擬)
  setInterval(() => {
    // 模擬獲取新日誌(實際可通過ES的search_after或滾動查詢)
    const newLogs = fetchNewLogsFromES(); 
    ws.send(JSON.stringify(newLogs));
  }, 1000);
});
3.3 前端實時渲染優化
javascript// Vue.js WebSocket客户端
const socket = new WebSocket('ws://localhost:8080');
socket.onmessage = (event) => {
  const logs = JSON.parse(event.data);
  // 使用虛擬滾動列表優化性能(避免DOM爆炸)
  this.logs.unshift(...logs); // 追加到列表頂部
  if (this.logs.length > 1000) this.logs.pop(); // 限制數量
};
// Vue.js WebSocket客户端
const socket = new WebSocket('ws://localhost:8080');
socket.onmessage = (event) => {
  const logs = JSON.parse(event.data);
  // 使用虛擬滾動列表優化性能(避免DOM爆炸)
  this.logs.unshift(...logs); // 追加到列表頂部
  if (this.logs.length > 1000) this.logs.pop(); // 限制數量
};

4. 性能優化與坑點

  • ES查詢優化:使用search_after替代from/size,避免深度分頁性能問題。
  • WebSocket斷連重試:前端實現指數退避重連機制。
  • 數據壓縮:對大文本日誌啓用Gzip壓縮,減少帶寬佔用。

5. 最終效果

  • 實時性:日誌從產生到展示延遲 < 1秒
  • 吞吐量:單WebSocket連接支持500條/秒日誌推送
  • 成本:3節點ES集羣(4C16G)可支撐日誌量100GB/天

6. 擴展思考

  • 如何結合Prometheus實現告警閾值動態配置
  • 針對海量日誌場景,是否需要引入Kafka作為消息隊列