iT邦幫忙

2024 iThome 鐵人賽

DAY 26
2

https://ithelp.ithome.com.tw/upload/images/20241010/20152073gQZtTr9Hzp.png

前言

在一開始介紹 Grafana Faro 的架構時,我們了解過 Grafana 本身經由 Angular 轉換成 React 的版本,之後許多前端的介面或是應用程式也都以 React 框架進行開發,包括先前介紹的 Grafana Scenes 以及這個階段我們所介紹的 Faro。因此本章節將介紹如何將 Faro 與 React 框架結合使用,且 Faro 是如何封裝為 React 的套件。

Faro React 套件

我們先前文章中介紹的 Faro Web SDK 主要是基於 React 開發的套件,可以適用於前端所有使用 JavaScript 的網頁應用程式。也是因為以 React 為基礎,以及 React Router 的一些限制,所以 Grafana 另外將 React 的套件進行了封裝,在其中提供了一些額外的功能。而 Faro React 套件所提供的功能除了 Faro Web SDK 中的所有功能,還增加了 React 特有的功能,包括 Router 的 Intergration 以及自定義的 Error Boundary 等。但要特別注意的是,在初始化時調用 TraceInstrumentation 時,還是需要從 web-tracing 中引入。

這邊可以複習一下 Faro 在 React 版本套件中所提供開箱即用的功能,例如有 FaroErrorBoundaryFaroRouteswithFaroProfilersetReactRouterV6SSRDependencies 等功能。而在 Faro 中要初始化 React 的依賴儀器化工具,除了引用從 faro-react 中的初始化 api 之外,以及 web-tracing 中引入 TraceInstrumentation,還有最重要的 ReactIntegration 元件來進行可觀測性。

安裝

首先,使用以下指令安裝 @grafana/faro-react 套件:

npm install @grafana/faro-react

安裝完成後,你可以在 React 專案中引入 faro-react 中的所有 API,這些 API 與 web-sdk 中相同。設置也很簡單,只需在應用程式的根目錄進行 Faro 的初始化。以下範例展示了如何在專案中設置 Faro:

import React, { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { initializeFaro } from "@grafana/faro-react";
import App from "./App";

const faro = initializeFaro({
  url: "https://<your-endpoint>/collect/<your-api-key>",
  app: {
    name: "my-app",
  },
});

const root = createRoot(document.getElementById("root"));
root.render(
  <StrictMode>
    <App />
  </StrictMode>
);

ReactIntegration

ReactIntegration 是 faro-react 套件中最重要的元件,一個專門為 React 設計的儀器化元件,尤其是與 React Router 的整合。元件本身也是繼承 BaseInstrumentation 的類別,因此可以與 web-sdk 中的所有 API 一起使用。而這項設置最主要是為了解決 SPA 的問題,在 React 中,當使用者進行頁面導航時,並不會重新整理頁面,而是會利用 React Router 的機制來進行頁面的更新,而這時候就會需要 ReactIntegration 來進行頁面導航的追蹤,最後會以 EVENT_ROUTE_CHANGE 的事件傳送至後端。

而 ReactIntegration 的初始化方法會進行兩個步驟,一個是以 setDependencies 設定依賴,一個是初始化 React Router 的儀器化工具:

  • setDependencies: 設定依賴的函數,會傳入 internalLogger 和 API 兩個參數,確保 React 可以利用其功能。
  • initializeReactRouterInstrumentation:會傳入在 ReactIntegration 中定義的 config 設定,主要會依據使用者所使用的 React Router 版本,來進行不同的設定。

Config 設定

在 config 設定中只有一個 router 的設定,會依據使用者所使用的 React Router 版本,來進行不同的 initialize 設定。而根據應用中使用的 React Router 版本,可以選擇以下三種設置:

  • ReactRouterVersion.V4 及 ReactRouterVersion.V5
  • ReactRouterVersion.V6
  • ReactRouterVersion.V6_data_router

另外也提供一個 utils 函數 withFaroRouterInstrumentation,將 Data Router 傳遞給 Faro-React,讓其可以自動追蹤數據加載的過程。

依賴設定

  1. ReactRouterVersion.V4 及 ReactRouterVersion.V5 這兩個版本會依據元件的動態路由設置並使用 Route 和 Switch 處理路由,而路由的變化會依賴於 history API,所以 Config 需要設置的依賴包括 Route 和 history。
import { Route, history } from "react-router";

const faro = initializeFaro({
  // 其他設定
  instrumentation: [
    new ReactIntegration({
      router: {
        version: ReactRouterVersion.V4, // 或 ReactRouterVersion.V5
        dependencies: {
          Route,
          history,
        },
      },
    }),
  ],
});
  1. ReactRouterVersion.V6:React Router v6 使用 Hooks 驅動的 API,並使用 Routes 處理多個路徑匹配的問題,因此 Config 需要設置 createRoutesFromChildren、matchRoutes、Routes、useLocation 和 useNavigationType 等依賴。
import { createRoutesFromChildren, matchRoutes, Routes, useLocation, useNavigationType } from "react-router";

const faro = initializeFaro({
  // 其他設定
  instrumentation: [
    new ReactIntegration({
      router: {
        version: ReactRouterVersion.V6,
        dependencies: {
          createRoutesFromChildren,
          matchRoutes,
          Routes,
          useLocation,
          useNavigationType,
        },
      },
    }),
  ],
});
  1. ReactRouterVersion.V6_data_router:基於 React Router v6 並解決了數據加載的問題,可以在路由定義時設定 loader 和 action 作為數據加載和表單提交的處理,而 Config 中只需要設置 matchRoutes 依賴。
import { matchRoutes } from "react-router-dom";

const faro = initializeFaro({
  // 其他設定
  instrumentation: [
    new ReactIntegration({
      router: {
        version: ReactRouterVersion.V6_data_router,
        dependencies: {
          matchRoutes,
        },
      },
    }),
  ],
});
  1. withFaroRouterInstrumentation: 先以 createBrowserRouter 建立一個 Router,並以 withFaroRouterInstrumentation 進行包裝,最後以 RouterProvider 進行渲染。
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { withFaroRouterInstrumentation } from "@grafana/faro-react";

const router = createBrowserRouter([
  // 路由設定
]);
const faroRouter = withFaroRouterInstrumentation(router);

function App() {
  return <RouterProvider router={router} />;
}

export default App;

FaroErrorBoundary

FaroErrorBoundary 是一個基於 React 的 Error Boundary 元件,專門用來捕獲 React 應用程式中的異常並進行處理,這個元件同時整合了 Grafana Faro 的錯誤監控機制,讓開發者能夠自動記錄和回報應用中發生的錯誤。當發生錯誤時,它會發送一個 Faro 錯誤事件(pushError),包含錯誤訊息、發生錯誤的 React 元件 stack,並且還支援錯誤發生後提供替代的 UI。需要在 ReactIntegration 設定後才可以使用此元件。

import { FaroErrorBoundary } from "@grafana/faro-react";

function App() {
  return (
    <FaroErrorBoundary>
      <App />
    </FaroErrorBoundary>
  );
}

export default App;

FaroRoutes 或 FaroRoute

FaroRoutes 或 FaroRoute 是兩個專門用來追蹤 React Router 的元件,兩者是繼承 React Router 的 <Routes><Route> 元件,並在元件的 useEffect 中進行路由的追蹤,才能在路由變化時立即取得當下的路由資訊,包括使用 matchRoutes 取得當下的 pathname 以及完整的 url,最後再以 EVENT_ROUTE_CHANGE 的事件傳送至後端。

  • FaroRoutes:適用於 React Router v6 版本,主要取代 <Routes> 元件,並在子元件中使用正常的 <Route> 元件。

    import { FaroRoutes } from "@grafana/faro-react";
    
    function App() {
      return (
        <FaroRoutes>
          <Route path="/" element={<Home />} />
        </FaroRoutes>
      );
    }
    
    export default App;
    
  • FaroRoute:適用於 React Router v4 和 v5 版本,主要取代 <Route> 元件,需要在 <Switch> 元件中使用。

    import { FaroRoute } from "@grafana/faro-react";
    
    function App() {
      return (
        <Switch>
          <FaroRoute path="/" component={Home} />
        </Switch>
      );
    }
    
    export default App;
    

withFaroProfiler

withFaroProfiler 是一個專門用來追蹤 React 應用程式效能的功能,在 React DevTools 中會以 Profiler 的形式顯示出來,可以查看在渲染過程中各個元件的渲染時間、更新次數和重渲染的次數,並以樹狀圖的形式顯示出來,而在 Faro 中會以 FaroProfiler 的元件包覆傳入的元件,最後再使用 faro 的 getOTEL API 在 Span 紀錄元件的生命週期,包含紀錄元件的名稱及 componentMount、componentUpdate、componentRender 的時間。

💡NOTICE:withFaroProfiler 需要在 tracing instrumentation 有設置的情況下才有作用,並且因為效能的影響,只適用於開發環境,在生產環境中不建議使用。

import { withFaroProfiler } from "@grafana/faro-react";

function DemoComponent() {
  return <div>DemoComponent</div>;
}

export default withFaroProfiler(DemoComponent);

https://ithelp.ithome.com.tw/upload/images/20241010/20152073xSmEF61CbY.png

筆者語錄
Faro React 套件為我們帶來了前端效能監控的新視野,無論是針對 React Router 的整合,還是利用 Profiler 精準追蹤效能,皆展現出 Faro 的靈活與深度。相信對於 React 開發者而言,這個工具讓效能監控和可觀測性的設置變得更加容易和高效,也解決了 SPA 的 Router 切換問題,希望可以透過這篇文章讓廣大的 React 開發者能夠更了解 Faro React 套件的優勢。

參考資料

https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/faro-react/

https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/quickstart/react/


上一篇
靠 Grafana 吃飯的第二十五天 - Grafana Faro 前端可觀測性的進階技巧
下一篇
靠 Grafana 吃飯的第二十七天 - 解讀前端可觀測性平台的視覺化資訊
系列文
論前端工程師如何靠 Grafana 吃飯:從 Grafana App 到前端可觀測性30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言