1. 程式人生 > >運維開發實踐:基於Sentry搭建錯誤日誌監控系統

運維開發實踐:基於Sentry搭建錯誤日誌監控系統

錯誤日誌監控也可稱為業務邏輯監控, 旨在對業務系統執行過程中產生的錯誤日誌進行收集歸納和監控告警。似乎有那麼點曾相識?沒錯… 就是上一篇文章提到的“APM應用效能監控”。但它又與APM不同,APM系統主要注重應用層的行為分析,收集的更多是運營方向的資料。而sentry所做的是收集應用底層程式碼的崩潰資訊,便於碼儂們排查程式碼異常。簡單來說它就是一個面向技術碼儂的排障工具。

1. 場景描述

隨著運維自動化流程的推進, 各類運維工具和系統也像雨後春筍般湧現. 目前我們自主開發的運維繫統的數量已經接近兩位數. 這些系統部署在多臺機器上, 通常還配套一批後臺執行的指令碼. web端如果出現異常, 開發人員可以及時得到反饋進行修復. 而指令碼因為沒有互動, 可能會出現發生重大故障時才定位到問題的情況.

2. 既有方案

  1. 後端和指令碼用python內建的日誌模組記錄程式中間狀態, 同時也將兩者的輸出重定向到指定檔案, 以獲取未捕獲的異常資訊.
  2. 同臺伺服器上多個系統的日誌集中存放到同個目錄
  3. 使用rsync定時從多臺伺服器中拉取日誌檔案
  4. 對日誌檔案進行關鍵字匹配, 並將過濾結果通過郵件傳送給運維開發人員

最終整合的通知郵件如圖

運維自動化

3. 存在的問題

上面的操作部分解決了指令碼執行狀態監控盲區的問題, 但還存在如下問題

  1. 無法第一時間感知錯誤
    指令碼日誌的拉取不是實時的, web端使用者的反饋也往往存在滯後. 出現問題到解決問題的週期太長, 容易導致工作陷入被動.
  2. 錯誤資訊的獲取相對低效
    使用者反饋和郵件告警包含的錯誤資訊非常有限, 最終都不得不在大量的日誌中上下翻看關聯的資訊. 可能還需要在測試環境下給程式碼埋點多獲取一些中間變數資料, 給定位問題帶來很多麻煩.
  3. 日誌的處理方式不夠靈活
    通常來說, 除了程式執行出錯, 我們還關心其他異常情況, 比如資料汙染, 非法請求, 第三方API呼叫異常等. 如果將此類等同錯誤記錄下來, 很容易造成告警濫發, 而如果不處理此類異常, 久而久之可能導致嚴重的問題. 我們希望同樣的日誌內容可以根據場景不同靈活處理
  4. 監控覆蓋面有限
    完整的監控應該涵蓋指令碼, 後端以及前端三個部分. 特別是我們新的運維繫統實現了前後端分離之後, 很多前端的問題無法被統一記錄下來.

鑑於此, 我們瞭解了一些日誌收集和監控方案, 選擇了sentry.

4. sentry初探

4.1 概覽

sentry是一個現代化的錯誤日誌記錄和聚合平臺。支援幾乎所有主流開發語言和平臺, 並提供了現代化UI, 如圖

sentry

與ELK, splunk不同, sentry專注於應用程式產生的錯誤日誌的聚合和監控. 官方提供了多個語言的SDK.

SDK

多達30種整合方式

讓開發者第一時間獲悉錯誤資訊, 並方便的整合進自己和團隊的工作流中.

4.2 使用前後對比

為了直觀的展示sentry的強大, 這裡模擬一個常見的場景, 如有雷同, 純屬巧合.

4.2.1 場景一

接入sentry前

  • 使用者A: 釋出功能用不了
  • 開發者A: 哪個頁面? 截個圖
  • 使用者A: (發截圖)
  • 開發者A發現bug可以重現, 登入伺服器檢視錯誤日誌, 確認程式邏輯無問題, 檢視資料庫資料, 發現有髒資料. 聯絡開發者B檢查負責更新資料的python指令碼C.py.
  • 開發者B登入伺服器檢視錯誤日誌, 發現一個邏輯錯誤導致指令碼罷工, 已持續了一個小時. 影響了數千條資料

接入後

  • 開發者A,B同時收到郵件告警, 一分鐘前指令碼C.py異常退出.
  • 開發者B進入sentry後臺檢視錯誤資訊, 定位問題並將其修復, 再清理受影響的數十條資料.
  • 在此過程中沒有使用者受到影響, 無需開發者A介入

4.2.2 場景二

接入sentry前

  • 使用者: 提交按鈕點了沒反應
  • 開發者: 哪個頁面? 截個圖
  • 使用者: (發截圖)
  • 開發者: 我這邊沒這個問題, 你開啟開發者工具, 切到console面板, 截個圖我看下
  • 使用者: 怎麼弄??….省略100字
  • 開發者拿到相關資料, 確定是程式碼問題. 但是js檔案經過了壓縮, 無法定位到有問題的程式碼. 只能開啟本地開發伺服器除錯.

接入後

  • 開發者收到郵件告警, 顯示前端有錯誤日誌
  • 開發者進入sentry後臺檢視錯誤資訊, 比如使用者瀏覽器版本, 產生錯誤的頁面url, 程式碼呼叫過程和最終引發錯誤的程式碼, 確認問題所在.
  • 開發者: 兩分鐘前你提交的工單備註欄位的校驗有點問題, 你先把那一欄留空再提交, 稍後我會更新一個hotfix版本, 到時跟你說下.
  • 使用者: 好的, 剛想問.

5. sentry的配置

sentry官方提供了詳細的部署文件, 網上也可以搜尋到中文的安裝教程, 安裝過程不贅述. 想要嚐鮮的小夥伴也可以直接使用sentry官方提供的saas, 免費版支援每天5000個event. 地址是 https://sentry.io

5.1 概念

使用sentry, 需要弄清楚幾個概念:

  • event
    直譯是”事件”, 是可操作資料的基本單位. 每一次日誌輸出就產生一個event. event並不一定就是錯誤, 如果日誌記錄級別設定很低, 那麼後臺會產生很多的event, 所以正確的設定日誌級別很重要
  • issue
    直譯是”工單”或者”問題”, 是同一類event的聚合. 某一個錯誤可能因為重複執行而被記錄多出, 在sentry會自動聚合到一起, 方便處理. 通常我們操作的物件就是issue

  • DSN
    DSN即客戶端金鑰, 用來進行客戶端和伺服器的通訊. DSN是一個url, 包含一個公鑰一個私鑰, 專案標記和伺服器地址, 比如https://1703147af2094458bevb1bfadcfa1c2:[email protected]/1545. 這類DSN是私密的, 還有一類是非私密的, 在sentry後臺中顯示為DSN(public), 給前端專案使用.
  • Raven
    整個錯誤日誌監控系統包括客戶端和服務端, Sentry是服務端的名稱, 客戶端名稱是Raver, 需要兩者配合才能工作.

5.2 配置

sentry服務端的配置主要是名稱, 告警規則等, 至於被監控專案是前端還是後端區別不大.

5.2.1 建立專案

  1. 進入sentry系統後臺, 點選右上角新建專案

sentry

  1. 命名為[專案名][前|後端], 比如”藍海前端”.
  2. 在配置應用框架頁面, 點選可以檢視各個語言或框架的接入文件(可以忽略這一步)
  3. 點選左上角專案名稱, 進入專案首頁, 可以看到頁面顯示”Waiting for events…”

5.2.2 獲取和測試DSN

  1. 在”專案設定”頁, 在左側列表中點選”客戶端金鑰”, 進入頁面
  2. 拷貝DSN, 後端的是DSN, 前端是DSN(public)
  3. 以python為例, 執行pip install raven安裝客戶端後, 執行raven test DSN, 如果一切順利, 可以在sentry後臺專案首頁看到新增了一條測試訊息

DSN

5.2.3 配置警報

  1. 在”專案設定”頁面, 在左側列表中點選”警報”, 進入警報配置頁
  2. 點選規則標籤頁, 可以看到已有一個規則, 當事件首次發生時告警
  3. 根據需要修改規則

告警規則的配置相當靈活, 且可以對多個條件進行與或判斷

5.2.4 整合告警

  1. 在”專案設定”頁面, 在左側列表中點選”所有整合”
  2. 勾選需要接入的型別,比如Mail

郵件伺服器的配置請參考官方文件, 自己搭建的sentry伺服器如果發現整合型別很少, 可以安裝官方或第三方外掛進行擴充套件

在服務端配置結束後, 可以開始配置客戶端.

6. 後端的接入

因為我們的系統主要用python開發, 在此以python為例.

python接入sentry十分簡單. 官方提供了十幾種python環境(框架)下使用sentry的例子, 比如在celery中

from raven import Client from raven.contrib.celery import register_signal, register_logger_signal client = Client(DSN) # register a custom filter to filter out duplicate logs register_logger_signal(client) # The register_logger_signal function can also take an optional argument # `loglevel` which is the level used for the handler created. # Defaults to `logging.ERROR` register_logger_signal(client, loglevel=logging.INFO) # hook into the Celery error handler register_signal(client) # The register_signal function can also take an optional argument # `ignore_expected` which causes exception classes specified in Task.throws # to be ignored register_signal(client, ignore_expected=True)

個人推薦借鑑logging使用的例子, 原因是通常開發者會根據logging模組定製自己的日誌配置, 不直接使用框架內的日誌模組. 如果你在應用程式中只用了logging模組, 那麼接入sentry對已存在的程式碼來說是透明的, 無需多加修改.

用logging模組接入sentry只需兩步:

6.1 安裝客戶端

pip install raven

6.2 初始化配置 

在應用程式的入口檔案(tornado中的app.py等)中, 或者自定義的日誌模組中, 插入如下程式碼

from raven.handlers.logging import SentryHandler from raven.conf import setup_logging handler = SentryHandler(DSN) handler.setLevel(logging.ERROR) setup_logging(handler)

完成了這兩步操作之後, 就可以像之前那樣使用logging模組

import logging logger = logging.getLogger(__name__) logger.info(‘This is a test message’)

當上面的程式碼被執行時, 除了原有的打log操作之外, raven還會向sentry伺服器傳送日誌內容, 並向標準輸出新增

Sending message of length xxx to https://xxxx

如果希望向sentry傳送更多上下文資訊, 可以帶上extra引數

logger.error(‘This is a test message’, extra={‘stack’: True})

最終顯示在後臺的日誌資訊如圖

日誌

包含了日誌級別, python環境資訊, SDK資訊, 棧呼叫, 前後n次日誌輸出, 相關的其他事件等等, 如果是未捕獲的異常或帶上extra引數, 還會顯示中間變數的值, 很方便的定位到出錯的位置和資料, 無需再去程式碼埋點.

7. 前端的接入

前端的接入相對來說複雜一些

第一, 需要對sentry伺服器的域名進行解析. 內部的系統, 後端監控可以給線上機器新增hosts指定sentry伺服器的IP. 而前端, 因為錯誤日誌是從使用者瀏覽器發出的, 需要使用者能自動解析sentry伺服器的域名

第二, 如果前端專案用到了打包工具, 而通常打包工具會對程式碼進行壓縮甚至混淆, 就會出現sentry收集到的日誌無法準確定位問題程式碼的情況 所幸, sentry支援匯入sourcemap自動解析和還原始碼, 讓開發者在後臺能看到development環境一樣詳細的棧呼叫. (當然如果沒有用打包工具可以忽略這一步)

前端的接入這裡以reactjs為例

7.1 安裝依賴

npm i raven-js –save

7.2 嵌入raven

在index.js檔案(入口檔案)中,

import Raven from ‘raven-js’; # 在適當的地方加入, 儘可能讓它早執行 Raven.config(DSN(public)).install();

其他前端框架的接入請參考官方文件

https://docs.sentry.io/clients/javascript

7.3 匯入sourcemap

提前生成好sourcemap檔案, 實測source-map級別可以完美工作, cheap-source-map能定位到, 但顯示不友好. 當然, 最推薦的是cheap-module-source-map

# 安裝 npm i -g sentry-cli-binary # 登入sentry sentry-cli –url SENTRY_URL login # SENTRY_URL指自建服務或官方saas地址, 執行命令後會訪問API TOKEN建立頁面, 生成一個TOKEN, 拷貝進來, 成功後TOKEN會被保留到系統使用者某個配置目錄下, 後續的請求都會重複使用這個TOKEN # 建立一個release sentry-cli releases -o sentry -p 7d04f2c51f32 new test01 –finalize # 這裡的sentry 和7d04f2c51f32 是指 組織名稱和專案名稱, 均指*簡稱*, 與sentry頁面上預設顯示的不同, 需要到配置頁面檢視 # 上傳dist目錄下的檔案 sentry-cli releases -o sentry -p 7d04f2c51f32 files test01 upload-sourcemaps dist # 刪除舊的release下的所有檔案 sentry-cli releases -o sentry -p 7d04f2c51f32 files test01 delete –all # 當然這個命令是不想要release上的檔案的時候執行的

注意, 生成的map檔案與上傳的相對路徑需要一致. 比如, dist目錄是打包後的檔案存放目錄, map檔案為sourcemap/[file].map, 則sentry-cli上傳目錄應該是dist, 這樣map檔案才會顯示在sentry後臺的~/sourcemap/目錄下.

這樣的webpack配置

devtool = ‘source-map’; output.path: ‘dist’; output.sourceMapFilename = ‘sourcemap/[file].map’;

則對應這樣的命令

sentry-cli releases -o sentry -p 7d04f2c51f32 files test02 upload-sourcemaps dist

另外, sentry-cli提供了一個引數–url-prefix, 可以為上傳的map檔案新增字首, 預設是~/, 有興趣的同學可以試試看

再補充一點, sentry需要根據js檔案的sourceMappingURL來解析map檔案路徑, 所以sourcemap級別不能用hide-source-map或者類似的.

程式碼上傳完畢後, 在版本->工件頁面可以看到該release上的檔案, 如圖

release

最終錯誤日誌效果如圖

8. sentry管理後臺的使用

篇幅所限, sentry後臺的使用簡要講幾點

第一,自定義過濾 

sentry提供了豐富的過濾選項, 預設過濾條件是”Unresolved Issues”, 使用者也可以自己組合過濾條件, 並儲存成個人或團隊的預設選項

sentry

第二,頁面實時更新 

上圖中間的按鈕可以開啟或關閉issues頁面的實時重新整理,

第三,統計和概覽

上圖是系統管理員頁面, 可以看到系統呼叫, 等待中的任務佇列等的情況, 在個人帳號首頁也能看到專案的統計資訊.

9. 需要注意的點

  1. 用sentry做錯誤日誌監控不能取代原有的日誌儲存方案, 只是在日誌收集和監控方面做了擴充套件. 使用sentry應著重利用其實時性和快捷性, 做到快速響應. sentry會清除較舊的日誌內容, 這與ELK之類的日誌處理系統也有差別.
  2. sentry能否用得好還取決於打log的開發者的功力. 如果原始日誌記錄缺少關鍵資訊或無效資訊過多, 再強大的日誌分析系統也無能為力. 因此在引入sentry做日誌監控的同時, 也要同步加強開發團隊打log的意識, 規範日誌級別, 格式和內容

文章來自微信公眾號:運維軍團