<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
最近專案組決定將前端異常監控由 Fundebug
切換為 Sentry
。整個切換過程可以說非常簡單,部署一個後臺服務,然後將 Sentry SDK
整合到前端應用中就完事兒了。在之後的使用過程中,小編遇到了一個問題。由於我們的專案採用的是基於 qiankun
的微前端架構,在應用使用過程中,常常會出現發生異常應用和上報應用不匹配的情況。
為了解決這個問題,小編先去 qiankun
的 issue
下翻了翻,看有沒有好的解決方案。雖然也有不少人遇到了同樣的問題 - 求教一下 主子應用的sentry應該如何實踐 #1088,但是社群裡並沒有一個好的解決方案。於是乎小編決定自己去閱讀 Sentry
原始碼和官方檔案,期望能找到一種合理並通用的解決方案。
經過一番梳理,小編如願找到了解決方案,並且效果還不錯。接下來小編就帶著大家一起了解一下整個解決方案的具體情況。
在正式介紹解決方案之前,小編先帶大家簡單回顧一下一個前端應用是如何接入 Sentry
的。
第一步,在 Sentry
監控平臺構建一個專案
專案建立好以後,會自動生成一個 dsn
,這個 dsn
會在前端專案接入 Sentry 時作為必填項傳入。
第二步,前端應用接入 Sentry
前端應用接入 Sentry
也非常簡單,只要使用 Sentry
提供的 init
api,傳入必傳的 dsn
就可以了。
import React from "react"; import ReactDOM from "react-dom"; import * as Sentry from "@sentry/react"; import { Integrations } from "@sentry/tracing"; import App from "./App"; Sentry.init({ dsn: "https://90eb5fc98bf447a3bdc38713cc253933@sentry.byai.com/66", integrations: [new Integrations.BrowserTracing()], tracesSampleRate: 1.0, }); ReactDOM.render(<App />, document.getElementById("root"));
經過這兩步,前端應用的異常監控接入就完成了。當應用在使用時,如果發生異常,Sentry
會自動捕獲異常,然後上報到監控平臺。上報完成以後,我們就可以在專案的 issues
中檢視異常並著手修復。
單個的 Spa
應用接入 Sentry
時按照上面的步驟無腦操作就可以了,但如果應用是基於 qiankun
的微前端架構,那就需要解決異常上報不匹配的問題了。
小編手上的專案就是採用了基於 qiankun
的微前端架構,一個頁面會至少同時存在兩個應用,有時甚至會有 3 到 4 個應用。在應用使用過程中,常常會出現異常上報不匹配的問題。
如上圖所示,主應用、cc sdk 應用中的異常都會上報到 aicc 專案中,這給例外處理帶來很大的困擾。
出現這個問題的原因也非常好理解。
Sentry
在執行 init
方法時會通過覆寫 window.onerror
、window.unhandledrejection
的方式初始化異常捕獲邏輯。之後不管是哪個應用發生異常,都最終會觸發 onerror
、unhandledrejection
的 callback
而被 Sentry
感知,然後上報到 dsn
指定的專案中。而且 Sentry
的 init
程式碼不管是放在主應用中,還是放在子應用裡面,都沒有質的改變,所有被捕獲的異常還是會一股腦的上報到某個專案中,無法自動區分。
瞭解了異常上報無法自動區分的問題,接下來小編就給大家講一下自己是如何解決這個問題的。
想要解決這個問題,我們必須要先找到問題的切入點,而異常上報時的介面呼叫就是這個切入點。
當 Sentry
捕獲到應用產生的異常時,會呼叫一個介面來上報異常,如下:
對比這個介面的 url
和上報應用的 dsn
,我們可以發現異常上報介面的 url
其實是由上報應用的 dsn
轉化來的,轉化過程如下:
// https://62187b367e474822bb9cb733c8a89814@sentry.byai.com/56 dsn - https://{param1}@{param2}/{param3} | | v url - https://{param2}/api/{param3}/store/?sentry_key={param1}&sentry_version=7
我們再來看看這個上報介面攜帶的引數:
在介面引數中,exceptions.values[0].stacktrace.frames
是異常的追蹤棧資訊。通過棧資訊中的 filename
欄位,我們可以知道發生異常的 js
檔案的 url
。通常情況下,微前端中各個子應用的 js
的 url
字首是不相同的(各個子應用靜態檔案的位置是分離的),那麼根據發生異常的 js
的 url
就可以判斷該異常屬於哪個應用。
有了上面兩個資訊,異常上報自動區分的解決方案就清晰明瞭了:
filename
判斷異常屬於哪個應用;dsn
重新構建 url
;url
上報異常;在這個方案中,最關鍵的是攔截異常上報介面。為了能實現這一步,小編進行了各種嘗試。
由於 Sentry
異常上報是通過 window.fetch(url, options)
來實現的,所以我們可以通過覆寫 window.fetch
的方式去攔截異常上報。
程式碼實現如下:
const originFetch = window.fetch; window.fetch = (url, options) => { // 根據 options 中的異常資訊,返回新的 url 和 options const [newUrl, newOptions] = sentryFilter(url, options); // 使用原生的 fetch return originFetch(newUrl, newOptions); }
該方案看起來很靠譜,然而在實際使用的時候並未發揮作用,原因是 Sentry
內部只會使用原生的 fetch
。如果發現 fetch
方法被覆寫,那麼 Sentry
會通過自己的方式重新去獲取原生的 fetch
。
小編擷取了 Sentry 的部分原始碼給大家看一下:
// FetchTransport 是一個建構函式 // Sentry 在執行 init 方法時會構建一個 FetchTransport 範例,然後通過這個 FetchTransport 範例呼叫 window.fetch 方法去做異常上報 function FetchTransport(options, fetchImpl) { if (fetchImpl === void 0) { fetchImpl = getNativeFetchImplementation(); } var _this = _super.call(this, options) || this; _this._fetch = fetchImpl; return _this; } // 使用原生的 window.fetch 實現 FetchTransport function getNativeFetchImplementation() { if (cachedFetchImpl) { return cachedFetchImpl; } // 根據 isNativeFetch 來判斷 window.fetch 是否被覆寫 if (isNativeFetch(global$7.fetch)) { return (cachedFetchImpl = global$7.fetch.bind(global$7)); } var document = global$7.document; var fetchImpl = global$7.fetch; // 如果被覆寫,藉助 iframe 獲取原生的 window.fetch if (document && typeof document.createElement === 'function') { try { var sandbox = document.createElement('iframe'); sandbox.hidden = true; document.head.appendChild(sandbox); var contentWindow = sandbox.contentWindow; if (contentWindow && contentWindow.fetch) { fetchImpl = contentWindow.fetch; } document.head.removeChild(sandbox); } catch (e) { logger.warn('Could not create sandbox iframe for pure fetch check, bailing to window.fetch: ', e); } } return (cachedFetchImpl = fetchImpl.bind(global$7)); } // 判斷 window.fetch 是否已經被覆寫 function isNativeFetch(func) { return func && /^function fetch()s+{s+[native code]s+}$/.test(func.toString()); }
由於 Sentry
內部有一套邏輯來保證 fetch
必須為原生方法,所以覆寫 window.fetch
的方案失敗, pass
!
既然覆寫 window.fetch
的方案行不通,那我們就重新想辦法。
觀察上面的 FetchTransport
的入參。如果沒有指定 fetchImpl
,Sentry
會通過 getNativeFetchImplementation
來實現一個 fetchImpl
。那我們主動給 FetchTransport
傳遞覆寫以後的 fetch
方法,不就可以做到攔截 fetch
呼叫了嗎?
這個方案看起來也很靠譜,趕緊試一下,
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45