likes
comments
collection
share

04 Umi4 插件开发 - 网友需求 - 前端数据埋点 Sentry 1/2

作者站长头像
站长
· 阅读数 31

04 Umi4 插件开发 - 网友需求 - 前端数据埋点 Sentry 1/2

现在的前端埋点方案,大同小异,好一点的平台会提供 JSSDK 供用户直接调用和接入,差一点的平台会让用户直接调用接口。 但是其实原理上都差不多。这里我们不做深入的讲解,如果有朋友感兴趣的话,可以给我留言,我会专门写个文章给大家讲解。

为了看文章的朋友都能自己动手实践一遍,我们选一个免费的数据埋点平台 Sentry 作为演示。

Sentry 是一个服务,帮助你监测和修复在实时崩溃。服务器是 Python 的,但是它包含一个完整的 API,用于在任何应用程序中从任何语言发送事件。 为了简化和聚焦这篇文章的核心,我们只关注 Sentry 提供的 React 对接方案,有其他需求的朋友,可以自己浏览 Sentry 官网。

根据官网指引,我们先在业务代码中接入 Sentry SDK。

注册账号

根据官网指引在注册页面注册一个账号。

04 Umi4 插件开发 - 网友需求 - 前端数据埋点 Sentry 1/2

下一步选择 Install Sentry

04 Umi4 插件开发 - 网友需求 - 前端数据埋点 Sentry 1/2

下一步选择创建一个 REACT 项目,点击 Create Project

04 Umi4 插件开发 - 网友需求 - 前端数据埋点 Sentry 1/2

安装依赖

官网文章如下:

# Using yarn
yarn add @sentry/react @sentry/tracing

# Using npm
npm install --save @sentry/react @sentry/tracing

为了简化前置步骤说明,我们从上一个文章的源码归档 learn-for-umi-plugin@0.0.3 继续。

因为我们的项目用的是 pnpm ,所以我们应该在项目中执行。

pnpm i @sentry/react @sentry/tracing

初始化 Sentry

安装完成之后继续跟着官网文档进行:

// 这是官方文档
import React from "react";
import ReactDOM from "react-dom";
import * as Sentry from "@sentry/react";
import { BrowserTracing } from "@sentry/tracing";
import App from "./App";

Sentry.init({
  // 此处 dsn 注意换成你自己的,直接复制页面上的代码就是正确的
  dsn: "https://96620dadccb040a3a5acc998a6078212@o1378593.ingest.sentry.io/6690607",
  integrations: [new BrowserTracing()],

  // Set tracesSampleRate to 1.0 to capture 100%
  // of transactions for performance monitoring.
  // We recommend adjusting this value in production
  tracesSampleRate: 1.0,
});

ReactDOM.render(<App />, document.getElementById("root"));

// Can also use with React Concurrent Mode
// ReactDOM.createRoot(document.getElementById('root')).render(<App />);

从文档上我们能够看出,他的期望是在 render(<App />) 之前执行 Sentry.init,如果是 create-react-app 项目的话,你可以在 src/index 之类的文件中粘贴一样的代码,但是在 umi 中,这个入口文件是一个临时文件,是当你执行 dev 或者 build 之后才会生成的,就在 src/.umi/umi.ts。这个文件也是构建时候的入口文件,你可以在这个文件中找到 renderClient 方法的调用,最终在 renderClient 中调用了 ReactDOM.createRoot。这在你需要做“入口追踪游戏”的时候,是一个需要知道的常识。

当然了这个文件是临时生成的,意味着你在这里面编写的所有的内容,都会被抛弃,因此我们无法将 Sentry.init 写在这里面。

还好 Umi 提供了一个 global 文件,它也是在较靠前的执行的,因此我们可以把 Sentry.init 写在 global 中。

新建 src/global.ts 文件

import * as Sentry from "@sentry/react";
import { BrowserTracing } from "@sentry/tracing";

Sentry.init({
  // 此处 dsn 注意换成你自己的,直接复制页面上的代码就是正确的
  dsn: "https://acb9e155e5de4ee0a418e798570feae1@o652357.ingest.sentry.io/6683799",
  integrations: [new BrowserTracing()],

  // We recommend adjusting this value in production, or using tracesSampler
  // for finer control
  tracesSampleRate: 1.0,
});

src/pages/index.tsx 中创造一个错误

Sentry 官网文档是让我们写一个 button 调用一个不存在的函数 methodDoesNotExist ,但是 umi 在编译阶段就会检测这种错误,会导致 umi 启动不起来。

04 Umi4 插件开发 - 网友需求 - 前端数据埋点 Sentry 1/2

因此我们可以像下面这么写,在点击时执行一个不存在的对象。

import React from 'react';
import styles from './index.less';

export default function Page() {
  return (
    <div>
      <h1 className={styles.title} onClick={()=>{
        // @ts-ignore
        methodDoesNotExist
      }}>Page index</h1>
    </div>
  );
}

当我们运行 npx umi dev,在页面中点击 Page index 的时候,我们就会发送一个错误,此时我们停留的 Sentry 官网会自动跳转到监控页面。

04 Umi4 插件开发 - 网友需求 - 前端数据埋点 Sentry 1/2

你可以点击进入查看任意错误的详情信息这里面包含了,用户用的系统版本号浏览器版本号还有执行了什么操作。比如:

04 Umi4 插件开发 - 网友需求 - 前端数据埋点 Sentry 1/2

从上面的信息你很容易就能看到,用户在 http://localhost:8000/ 点击了 div#root > div > div > div > h1.title___dUjAT

当然如果只是添加一个接入,那没什么必要将它写成插件,我们可以继续浏览 Sentry React 的文档,加入一些其他的我们想要的功能。

React Error Boundary

给项目加上 Error Boundary 可以在程序发生异常的时候,捕获错误。

依旧我们从官网文档中获取使用方法:

import React from "react";
import * as Sentry from "@sentry/react";

<Sentry.ErrorBoundary fallback={<p>An error has occurred</p>}>
  <Example />
</Sentry.ErrorBoundary>;

只需要用 Sentry.ErrorBoundary 包裹 RootContainer 就可以实现,当错误被捕获时间,就会渲染 fallback 提供的 UI。

巧了,刚好 umi 就有一个运行时配置 rootContainer,它提供了一个口子,让我们能够在外层包裹一个 Provider。

运行时配置 rootContainer

新建 src/app.tsx,编写如下代码:

import React from "react";
import { ErrorBoundary } from "@sentry/react";

export function rootContainer(container: JSX.Element) {
  const props = {
    fallback: () => <p>An error has occurred</p>,
  };
  return React.createElement(ErrorBoundary, props, container);
}

值得注意的是 ErrorBoundary 只捕获 React 渲染错误,不捕获语法错误。

我们可以继续在 src/pages/index.tsx 中创造一个渲染错误。

import React, { useState } from "react";
import styles from "./index.less";

export default function Page() {
  const [count, setCount] = useState(0);
  return (
    <div>
      {count}
      <h1
        className={styles.title}
        onClick={() => {
          // 这里故意设置了一个错误,会触发运行时配置
          // @ts-ignore
          setCount({ a: 123 });
        }}
      >
        Page index
      </h1>
    </div>
  );
}

关键出错代码在 {count} 因为将 count 设置成对象 { a: 123 },保存代码之后,再次点击 Page index 的时候,就可以在页面上看到 An error has occurred。 说明我们的错误已经被正确捕获了。

重置错误 resetError

当然了,这样虽然捕获了错误,但我们也看不到错误内容了,因此我们可以修改一个 fallback,它可以接收一个函数,并且会传入 error, componentStack, resetError

import { ErrorBoundary, ErrorBoundaryProps } from "@sentry/react";

  const props = {
    fallback: ({ error, componentStack, resetError }) =>
      (
        <React.Fragment>
          <div>You have encountered an error</div>
          <div>{error.toString()}</div>
          <div>{componentStack}</div>
          <button
            onClick={() => {
              resetError();
            }}
          >
            Click here to reset!
          </button>
        </React.Fragment>
      ) as React.ReactNode,
  } as ErrorBoundaryProps;

再次点击触发错误,将会看到更详细的错误信息,并且点击 Click here to reset! 按钮可以将页面重置到错误发生之前。

04 Umi4 插件开发 - 网友需求 - 前端数据埋点 Sentry 1/2

用户反馈

04 Umi4 插件开发 - 网友需求 - 前端数据埋点 Sentry 1/2

当触发错误时,会有如上所示弹窗,供用户主动反馈信息,提交成功之后,可以在后台查看用户提交数据。

04 Umi4 插件开发 - 网友需求 - 前端数据埋点 Sentry 1/2

实现也很简单,只需要将 ErrorBoundary 的 showDialog 属性设置为 true

import React from "react";
import { ErrorBoundary, ErrorBoundaryProps } from "@sentry/react";

export function rootContainer(container: JSX.Element) {
  const props = {
    showDialog: true,
    // fallback React.ReactNode,
  } as ErrorBoundaryProps;
  return React.createElement(ErrorBoundary, props, container);
}

设置用户

比如在用户登陆授权之后,全程跟踪用户发生的错误。

Sentry.setUser({ id: '123', username: 'xiaohuoni', email: '' });

设置之后用户再次触发错误,就可以从后台看到用户信息。

04 Umi4 插件开发 - 网友需求 - 前端数据埋点 Sentry 1/2

Sentry.setUser 你可以在任意时候调用,也可以在不需要的时候清除当前设置的用户:

Sentry.configureScope((scope) => scope.setUser(null));

源码归档 learn-for-umi-plugin@0.0.4

总结

以上是我个人觉得比较有用和有趣的功能,你也可以再找一些你想要的功能,根据官网的指引添加到项目中。 这篇文章的内容写的非常细,是希望在开始编写插件之前,能够更准确的理解需求。我们会在下一篇文章中,将上面的功能编写成插件。

感谢阅读,如果你对这个内容感兴趣,可以关注这个专栏:Umi 插件开发。如果你觉得这个文章对你有帮助,请点赞评论收藏支持我,并将这个文章分享给更多的朋友,文章的数据是我持续更新的动力。感谢。