這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 背景 愉快的雙休周末剛過完,早上來忽然被運營通知線上業務掛了,用戶無法下單。卧槽,趕緊進入debug模式,一查原來是服務端返回的數據有問題,趕緊問了服務端,大佬回覆說是業務部門配置套餐錯誤。好在主責不在我們,不過趕緊寫了復盤文檔,主動找自 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
背景
愉快的雙休周末剛過完,早上來忽然被運營通知線上業務掛了,用戶無法下單。卧槽,趕緊進入debug模式,一查原來是服務端返回的數據有問題,趕緊問了服務端,大佬回覆說是業務部門配置套餐錯誤。好在主責不在我們,不過趕緊寫了復盤文檔,主動找自己的責任,扛起這口大鍋,都怪我們前端,沒有做好前端監控,導致線上問題持續兩天才發現。原本以為運營會把推辭一下說不,鍋是她們的,可惜人家不太懂人情世故,這鍋就扣在了技術部頭上。雖然但是,我還是靜下心來把前端異常監控搞了出來,下次一定不要主動接鍋,希望看到本文的朋友們也不要隨便心軟接鍋^_^
監控
因為之前基於sentry做了埋點處理,基礎已經打好,支持全自動埋點、手動埋點和數據上報。相關的原理可以參考之前的一篇文章如何從0-1構建數據平臺(2)- 前端埋點。本次監控的數據上報也基於sentry.js。那麼如何設計整個流程呢。具體步驟如下:
-
監控數據分類
-
監控數據定義
-
監控數據收集
-
監控數據上報
-
監控數據輸出
-
監控數據預警
數據分類
我們主要是前端的數據錯誤,一般的異常大類分為邏輯異常和代碼異常。基於我們的項目,由於涉及營收,我們就將邏輯錯誤專註於支付異常,其他的代碼導致的錯誤分為一大類。然後再將兩大異常進行細分,如下:
-
支付異常
1.1 支付成功
1.2 支付失敗
-
代碼異常
2.1 bindexception
2.1.1 js_error 2.1.2 img_error 2.1.3 audio_error 2.1.4 script_error 2.1.5 video_error
-
unhandleRejection
3.1 promise_unhandledrejection_error
3.2 ajax_error
-
vueException
-
peformanceInfo
數據定義
基於sentry的上報數據,一般都包括事件與屬性。在此我們定義支付異常事件為“page_h5_pay_monitor”,定義代碼異常事件為“page_monitor”。然後支付異常的屬性大概為:
pay_time, pay_orderid, pay_result, pay_amount, pay_type, pay_use_coupon, pay_use_coupon_id, pay_use_coupon_name, pay_use_discount_amount, pay_fail_reason, pay_platment代碼異常不同的錯誤類型可能屬性會有所區別:
// js_error monitor_type, monitor_message, monitor_lineno, monitor_colno, monitor_error, monitor_stack, monitor_url // src_error monitor_type, monitor_target_src, monitor_url // promise_error monitor_type, monitor_message, monitor_stack, monitor_url // ajax_error monitor_type, monitor_ajax_method, monitor_ajax_data, monitor_ajax_params, monitor_ajax_url, monitor_ajax_headers, monitor_url, monitor_message, monitor_ajax_code // vue_error monitor_type, monitor_message, monitor_stack, monitor_hook, monitor_url // peformanceInfo 為數據添加 loading_time 屬性,該屬性通過entryTypes獲取 try { const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.entryType === 'paint') { sa.store.set('loading_time', entry.startTime) } } }) observer.observe({ entryTypes: ['paint'] }) } catch (err) { console.log(err) }
數據收集
數據收集通過事件綁定進行收集,具體綁定如下:
import { BindErrorReporter, VueErrorReporter, UnhandledRejectionReporter } from './report' const Vue = require('vue') // binderror綁定 const MonitorBinderror = () => { window.addEventListener( 'error', function(error) { BindErrorReporter(error) },true ) } // unhandleRejection綁定 這裡由於使用了axios,因此ajax_error也屬於promise_error const MonitorUnhandledRejection = () => { window.addEventListener('unhandledrejection', function(error) { if (error && error.reason) { const { message, code, stack, isAxios, config } = error.reason if (isAxios && config) { // console.log(config) const { data, params, headers, url, method } = config UnhandledRejectionReporter({ isAjax: true, data: JSON.stringify(data), params: JSON.stringify(params), headers: JSON.stringify(headers), url, method, message: message || error.message, code }) } else { UnhandledRejectionReporter({ isAjax: false, message, stack }) } } }) } // vueException綁定 const MonitorVueError = () => { Vue.config.errorHandler = function(error, vm, info) { const { message, stack } = error VueErrorReporter({ message, stack, vuehook: info }) } } // 輸出綁定方法 export const MonitorException = () => { try { MonitorBinderror() MonitorUnhandledRejection() MonitorVueError() } catch (error) { console.log('monitor exception init error', error) } }
數據上報
數據上報都是基於sentry進行上報,具體如下:
/* * 異常監控庫 基於sentry jssdk * 監控類別: * 1、window onerror 監控未定義屬性使用 js資源載入失敗問題 * 2、window addListener error 監控未定義屬性使用 圖片資源載入失敗問題 * 3、unhandledrejection 監聽promise對象未catch的錯誤 * 4、vue.errorHandler 監聽vue腳本錯誤 * 5、自定義錯誤 包括介面錯誤 或其他diy錯誤 * 上報事件: page_monitor */ // 錯誤類別常量 const ERROR_TYPE = { JS_ERROR: 'js_error', IMG_ERROR: 'img_error', AUDIO_ERROR: 'audio_error', SCRIPT_ERROR: 'script_error', VIDEO_ERROR: 'video_error', VUE_ERROR: 'vue_error', PROMISE_ERROR: 'promise_unhandledrejection_error', AJAX_ERROR: 'ajax_error' } const MONITOR_NAME = 'page_monitor' const PAY_MONITOR_NAME = 'page_h5_pay_monitor' const MEMBER_PAY_MONITOR_NAME = 'page_member_pay_monitor' export const BindErrorReporter = function(error) { if (error) { if (error.error) { const { colno, lineno } = error const { message, stack } = error.error // 過濾 // 客戶端會有調用calljs的場景 可能有一些未知的calljs if (message && message.toLowerCase().indexOf('calljs') !== -1) { return } sa.track(MONITOR_NAME, { //屬性 }) } else if (error.target) { const type = error.target.nodeName.toLowerCase() const monitorType = type + '_error' const src = error.target.src sa.track(MONITOR_NAME, { //屬性 }) } } } export const UnhandledRejectionReporter = function({ isAjax = false, method, data, params, url, headers, message, stack, code }) { if (!isAjax) { // 過濾一些特殊的場景 // 1、自動播放觸發問題 if (message && message.toLowerCase().indexOf('user gesture') !== -1) { return } sa.track(MONITOR_NAME, { //屬性 }) } else { sa.track(MONITOR_NAME, { //屬性 }) } } export const VueErrorReporter = function({ message, stack, vuehook }) { sa.track(MONITOR_NAME, { //屬性 }) } export const H5PayErrorReport = ({ isSuccess = true, amount = 0, type = -1, couponId = -1, couponName = '', discountAmount = 0, reason = '', orderid = 0, }) => { // 事件名:page_member_pay_monitor sa.track(PAY_MONITOR_NAME, { //屬性 }) }
以上,通過sentry的sa.track進行上報,具體不作展開
輸出與預警
數據被上報到大數據平臺,被存儲到hdfs中,然後我們直接做定時任務讀取hdfs進行一定的過濾通過釘釘webhook輸出到釘釘群,另外如果有需要做數據備份可以通過hdfs到數據倉庫再到kylin進行存儲。
總結
數據監控對於大的,特別是涉及營收的平臺是必要的,我們在設計項目的時候一定要考慮到,最好能說服服務端,讓他們服務端也提供相應的代碼監控。ngnix層或者雲端最好也來一層。嚴重的異常可以直接給你打電話,目前雲平臺都有相應支持。這樣有異常及時發現,鍋嘛,接到手裡就可以精準扔出去了。