欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Qiankun Sentry 監(jiān)控異常上報無法自動區(qū)分項目解決

 更新時間:2022年11月17日 09:25:39   作者:0o華仔o0  
這篇文章主要為大家介紹了Qiankun Sentry 監(jiān)控異常上報無法自動區(qū)分項目解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

最近項目組決定將前端異常監(jiān)控由 Fundebug 切換為 Sentry。整個切換過程可以說非常簡單,部署一個后臺服務(wù),然后將 Sentry SDK 集成到前端應(yīng)用中就完事兒了。在之后的使用過程中,小編遇到了一個問題。由于我們的項目采用的是基于 qiankun 的微前端架構(gòu),在應(yīng)用使用過程中,常常會出現(xiàn)發(fā)生異常應(yīng)用和上報應(yīng)用不匹配的情況。

為了解決這個問題,小編先去 qiankunissue 下翻了翻,看有沒有好的解決方案。雖然也有不少人遇到了同樣的問題 - 求教一下 主子應(yīng)用的sentry應(yīng)該如何實踐 #1088,但是社區(qū)里并沒有一個好的解決方案。于是乎小編決定自己去閱讀 Sentry 源碼和官方文檔,期望能找到一種合理并通用的解決方案。

經(jīng)過一番梳理,小編如愿找到了解決方案,并且效果還不錯。接下來小編就帶著大家一起了解一下整個解決方案的具體情況。

使用 Sentry 上報異常

在正式介紹解決方案之前,小編先帶大家簡單回顧一下一個前端應(yīng)用是如何接入 Sentry 的。

第一步,在 Sentry 監(jiān)控平臺構(gòu)建一個項目

項目創(chuàng)建好以后,會自動生成一個 dsn,這個 dsn 會在前端項目接入 Sentry 時作為必填項傳入。

第二步,前端應(yīng)用接入 Sentry

前端應(yīng)用接入 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"));

經(jīng)過這兩步,前端應(yīng)用的異常監(jiān)控接入就完成了。當(dāng)應(yīng)用在使用時,如果發(fā)生異常,Sentry 會自動捕獲異常,然后上報到監(jiān)控平臺。上報完成以后,我們就可以在項目的 issues 中查看異常并著手修復(fù)。

單個的 Spa 應(yīng)用接入 Sentry 時按照上面的步驟無腦操作就可以了,但如果應(yīng)用是基于 qiankun 的微前端架構(gòu),那就需要解決異常上報不匹配的問題了。

小編手上的項目就是采用了基于 qiankun 的微前端架構(gòu),一個頁面會至少同時存在兩個應(yīng)用,有時甚至?xí)?3 到 4 個應(yīng)用。在應(yīng)用使用過程中,常常會出現(xiàn)異常上報不匹配的問題。

如上圖所示,主應(yīng)用、cc sdk 應(yīng)用中的異常都會上報到 aicc 項目中,這給異常處理帶來很大的困擾。

出現(xiàn)這個問題的原因也非常好理解。

Sentry 在執(zhí)行 init 方法時會通過覆寫 window.onerrorwindow.unhandledrejection 的方式初始化異常捕獲邏輯。之后不管是哪個應(yīng)用發(fā)生異常,都最終會觸發(fā) onerror、unhandledrejectioncallback 而被 Sentry 感知,然后上報到 dsn 指定的項目中。而且 Sentryinit 代碼不管是放在主應(yīng)用中,還是放在子應(yīng)用里面,都沒有質(zhì)的改變,所有被捕獲的異常還是會一股腦的上報到某個項目中,無法自動區(qū)分。

了解了異常上報無法自動區(qū)分的問題,接下來小編就給大家講一下自己是如何解決這個問題的。

解決方案

想要解決這個問題,我們必須要先找到問題的切入點,而異常上報時的接口調(diào)用就是這個切入點。

當(dāng) Sentry 捕獲到應(yīng)用產(chǎn)生的異常時,會調(diào)用一個接口來上報異常,如下:

對比這個接口的 url 和上報應(yīng)用的 dsn,我們可以發(fā)現(xiàn)異常上報接口的 url 其實是由上報應(yīng)用的 dsn 轉(zhuǎn)化來的,轉(zhuǎn)化過程如下:

// https://62187b367e474822bb9cb733c8a89814@sentry.byai.com/56
dsn - https://{param1}@{param2}/{param3}
                |
                |
                v
url - https://{param2}/api/{param3}/store/?sentry_key={param1}&sentry_version=7

我們再來看看這個上報接口攜帶的參數(shù):

在接口參數(shù)中,exceptions.values[0].stacktrace.frames 是異常的追蹤棧信息。通過棧信息中的 filename 字段,我們可以知道發(fā)生異常的 js 文件的 url。通常情況下,微前端中各個子應(yīng)用的 jsurl 前綴是不相同的(各個子應(yīng)用靜態(tài)文件的位置是分離的),那么根據(jù)發(fā)生異常的 jsurl 就可以判斷該異常屬于哪個應(yīng)用。

有了上面兩個信息,異常上報自動區(qū)分的解決方案就清晰明了了:

  • 第一步攔截異常上報接口,拿到異常詳情,根據(jù)追蹤棧中的 filename 判斷異常屬于哪個應(yīng)用;
  • 第二步,根據(jù)匹配應(yīng)用的 dsn 重新構(gòu)建 url;
  • 第三步,使用新的 url 上報異常;

在這個方案中,最關(guān)鍵的是攔截異常上報接口。為了能實現(xiàn)這一步,小編進行了各種嘗試。

失敗的方案一

由于 Sentry 異常上報是通過 window.fetch(url, options) 來實現(xiàn)的,所以我們可以通過覆寫 window.fetch 的方式去攔截異常上報。

代碼實現(xiàn)如下:

const originFetch = window.fetch;
window.fetch = (url, options) => {
    // 根據(jù) options 中的異常信息,返回新的 url 和 options
    const [newUrl, newOptions] = sentryFilter(url, options);
    // 使用原生的 fetch
    return originFetch(newUrl, newOptions);
}

該方案看起來很靠譜,然而在實際使用的時候并未發(fā)揮作用,原因是 Sentry 內(nèi)部只會使用原生的 fetch。如果發(fā)現(xiàn) fetch 方法被覆寫,那么 Sentry 會通過自己的方式重新去獲取原生的 fetch

小編截取了 Sentry 的部分源碼給大家看一下:

// FetchTransport 是一個構(gòu)造函數(shù)
// Sentry 在執(zhí)行 init 方法時會構(gòu)建一個 FetchTransport 實例,然后通過這個 FetchTransport 實例調(diào)用 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 實現(xiàn) FetchTransport
function getNativeFetchImplementation() {
    if (cachedFetchImpl) {
        return cachedFetchImpl;
    }
    // 根據(jù) 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 是否已經(jīng)被覆寫
function isNativeFetch(func) {
    return func && /^function fetch\(\)\s+\{\s+\[native code\]\s+\}$/.test(func.toString());
}

由于 Sentry 內(nèi)部有一套邏輯來保證 fetch 必須為原生方法,所以覆寫 window.fetch 的方案失敗, pass

不通用的方案二

既然覆寫 window.fetch 的方案行不通,那我們就重新想辦法。

觀察上面的 FetchTransport 的入?yún)?。如果沒有指定 fetchImpl,Sentry 會通過 getNativeFetchImplementation 來實現(xiàn)一個 fetchImpl。那我們主動給 FetchTransport 傳遞覆寫以后的 fetch 方法,不就可以做到攔截 fetch 調(diào)用了嗎?

這個方案看起來也很靠譜,趕緊試一下,??。

FetchTransport 追本溯源,小編找到了 FetchTransport 方法調(diào)用的位置:

BrowserBackend.prototype._setupTransport = function () {
    if (!this._options.dsn) {
        return _super.prototype._setupTransport.call(this);
    }
    var transportOptions = __assign(__assign({}, this._options.transportOptions), { dsn: this._options.dsn, tunnel: this._options.tunnel, sendClientReports: this._options.sendClientReports, _metadata: this._options._metadata });
    var api = initAPIDetails(transportOptions.dsn, transportOptions._metadata, transportOptions.tunnel);
    var url = getEnvelopeEndpointWithUrlEncodedAuth(api.dsn, api.tunnel);
    if (this._options.transport) {
        return new this._options.transport(transportOptions);
    }
    if (supportsFetch()) {
        var requestOptions = __assign({}, transportOptions.fetchParameters);
        this._newTransport = makeNewFetchTransport({ requestOptions: requestOptions, url: url });
        // 使用 FetchTransport 的位置
        return new FetchTransport(transportOptions);
    }
    this._newTransport = makeNewXHRTransport({
        url: url,
        headers: transportOptions.headers,
    });
    return new XHRTransport(transportOptions);
};

在上面的這段代碼中, this._options 就是我們執(zhí)行 Sentry.init 時的入?yún)?。觀看 FetchTransport 調(diào)用的地方,由于沒有 fetchImpl 的入?yún)?,所?Sentry 會通過 getNativeFetchImplementation 來實現(xiàn) fetchImpl

既然這樣,那我們可以在 Sentry.init 方法執(zhí)行的時候添加一個 fetchImpl 入?yún)ⅲ缓笤谡{(diào)用 FetchTransport 方法時傳入。

改造后的代碼如下:

// 改動 Sentry 源碼
BrowserBackend.prototype._setupTransport = function () {
    ...
    if (supportsFetch()) {
        var requestOptions = __assign({}, transportOptions.fetchParameters);
        this._newTransport = makeNewFetchTransport({ requestOptions: requestOptions, url: url });
        return new FetchTransport(transportOptions, this._options.fetchImpl);
    }
    ...
}
// 業(yè)務(wù)代碼
const originFetch = window.fetch;
// Sentry.init 執(zhí)行
Sentry.init({
    dsn: 'xxx',
    ...
    fetchImpl: (url, options) => {
        // 根據(jù) options 中的異常信息,返回新的 url 和 options
        const [newUrl, newOptions] = sentryFilter(url, options);
        // 使用原生的 fetch
        return originFetch(newUrl, newOptions);
    }
    ...
});

經(jīng)驗證,該方案可正常工作,捕獲的異常都可自動上報到對應(yīng)的應(yīng)用中,問題解決,happy ??。

不過興奮過后,再回過頭來看看這個方案,發(fā)現(xiàn)其實槽點還是蠻多的:

  • 要修改 Sentry 源碼,重新生成一個內(nèi)部 npm 包;
  • 如果 Sentry 版本升級,必須再次修改源碼, 很不方便;

總體來說,這個方案雖然能解決問題,但是不夠通用,不夠優(yōu)雅。作為一名有追求的 ????‍??,小編當(dāng)然不能僅僅止步于實現(xiàn)功能,還得想辦法實現(xiàn)的更好,于是就有了接下來的方案三。

合理、優(yōu)雅的方案三

還是看上面 BrowserBackend.prototype._setupTransport 源碼,有這樣一段邏輯:

...
if (this._options.transport) {
    return new this._options.transport(transportOptions);
}
...

如果在 Sentry.init 執(zhí)行時,配置了 transport,那么就會使用該 transport 方法來初始化上報異常需要的 transport 實例。

既然這樣,那我們自己定義一個 CustomeTransport 構(gòu)造函數(shù)不就可以了么。另外,小編在 Sentry 暴露給外面的 exports 中發(fā)現(xiàn)了 FetchTransport,那么 CustomeTransport 就可以通過繼承 FetchTransport 來實現(xiàn)。

具體方案如下:

import { Transports, init } from '@sentry/browser';
const fetchImpl = (url, options) => {
    // 根據(jù) options 中的異常信息,返回新的 url 和 options
    const [newUrl, newOptions] = sentryFilter(url, options);
    // 使用原生的 fetch
    return originFetch(newUrl, newOptions);
}
class CustomerTransport extends Transports.FetchTransport {
    constructor(options) {
        super(options, fetchImpl)
    }
}
init({
    dsn: 'xxxx',
    ...
    transport: CustomerTransport
});

經(jīng)驗證,該方案可正常工作,捕獲的異常都可自動上報到對應(yīng)的應(yīng)用中,而且不用像方案二那樣修改 Sentry 源碼,優(yōu)雅、通用,問題真正解決,perfect ??。

7.x 版本解決方案

上面的方案三只針對 6.x 版本。如果大家使用的 Sentry 是最新的 7.x 版本,小編也設(shè)計了相應(yīng)的解決方案。

import { init, makeFetchTransport } from '@sentry/browser';
const CustomeTransport = (options) => {
    const fetchImpl = (url, options) => {
        const [newUrl, newOptions] = sentryFilter(url, options);
        return window.fetch(newUrl, newOptions);
    };
    return makeFetchTransport(options, fetchImpl);
};
init({
    dsn: 'https://525053cc037e42bcb981670e97a0a821@sentry.byai.com/52',
    ...
    transport: CustomeTransport,
});

親測可用哦!

結(jié)束語

也許有小伙伴會問,如果瀏覽器不支持 fetch 的話,那上面說的方案不就沒有用了嗎?

其實大可不必?fù)?dān)心這一點。

首先,當(dāng)前主流瀏覽器,除了比較老的版本,已經(jīng)實現(xiàn)了對 fetch 的支持。如果有些小伙伴的瀏覽器實在不支持 fetch,那也沒有關(guān)系。由于 Sentry 內(nèi)部沒有要求 xhr 必須使用原生的 send 方法,所以我們可以通過覆寫 XMLHttpRequest 原型鏈上的 send 方法來實現(xiàn)對異常上報的攔截,具體的實現(xiàn)過程就由小伙伴們自行研究了

以上就是Qiankun Sentry 監(jiān)控異常上報無法自動區(qū)分項目解決的詳細內(nèi)容,更多關(guān)于Qiankun Sentry 監(jiān)控異常區(qū)分的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JavaScript數(shù)組詳細歸納

    JavaScript數(shù)組詳細歸納

    JavaScript 數(shù)組用于在單一變量中存儲多個值,數(shù)組是一種特殊的變量,它能夠一次存放一個以上的值。下面文章小編就來詳細歸納一下JavaScript數(shù)組,需要的朋友可以參考一下
    2021-09-09
  • Web?Components實現(xiàn)類Element?UI中的Card卡片

    Web?Components實現(xiàn)類Element?UI中的Card卡片

    這篇文章主要為大家介紹了Web?Components實現(xiàn)類Element?UI中的Card卡片實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • JSON?Schema概念及使用場景

    JSON?Schema概念及使用場景

    這篇文章主要為大家介紹了JSON?Schema概念及使用場景示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • 微信小程序 獲取相冊照片實例詳解

    微信小程序 獲取相冊照片實例詳解

    這篇文章主要介紹了微信小程序 獲取相冊照片實例詳解的相關(guān)資料,需要的朋友可以參考下
    2016-11-11
  • 微信小程序 window_x64環(huán)境搭建

    微信小程序 window_x64環(huán)境搭建

    這篇文章主要介紹了微信小程序 window_x64環(huán)境搭建的相關(guān)資料,需要的朋友可以參考下
    2016-09-09
  • codemirror6實現(xiàn)在線代碼編輯器使用詳解

    codemirror6實現(xiàn)在線代碼編輯器使用詳解

    這篇文章主要為大家介紹了codemirror6實現(xiàn)在線代碼編輯器使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • 微信小程序 教程之?dāng)?shù)據(jù)綁定

    微信小程序 教程之?dāng)?shù)據(jù)綁定

    這篇文章主要介紹了微信小程序 數(shù)據(jù)綁定的相關(guān)資料,并附實例代碼,需要的朋友可以參考下
    2016-10-10
  • JS實現(xiàn)密碼箱小程序幫你記住各種密碼

    JS實現(xiàn)密碼箱小程序幫你記住各種密碼

    這篇文章主要介紹了JS實現(xiàn)密碼箱小程序再也不用記各種密碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • JavaScript的單線程和異步詳細

    JavaScript的單線程和異步詳細

    這篇文章要給大家分享的是JavaScript的單線程和異步,其實單線程和異步確實不能同時成為一個語言的特性,js選擇了成為單線程的語言,所以它本身不可能是異步的,但js宿主環(huán)境是多線程,宿主環(huán)境通過某種方式使js具備了異步屬性,下面就來具體介紹,需要的朋友可以參考一下
    2021-10-10
  • 微信小程序 同步請求授權(quán)的詳解

    微信小程序 同步請求授權(quán)的詳解

    這篇文章主要介紹了微信小程序 同步請求授權(quán)的詳解的相關(guān)資料,在小程序首次打開的時候,我需要同時請求獲取多個權(quán)限,由用戶逐一授權(quán),這樣的需求實現(xiàn),需要的朋友可以參考下
    2017-08-08

最新評論