Sentry 接入手册

Sentry 是一个开源的异常监控工具,支持前后端多个框架。

本文主要介绍如何接入前端的 Vue 及 Uni-app 项目。

更多用户可以查阅官方文档

# 准备工作

# 创建项目

首先需要在 Sentry 后台创建一个项目,platform 一律选取“VUE”。其余参数保留默认。

sentry-choose-your-platform

# 获取项目信息

创建成功之后,需要获取项目的几项参数。
建议由项目管理员操作。

  1. 获取组织名称
    点击左上角头像,选择 Organization Settings,查看 Organization Slug sentry-get-organization-slug
  2. 获取项目名称
    通过项目列表卡片的齿轮,或者项目页面的右上角齿轮进入 Project Settings 页面,查看 Name sentry-get-project-name
  3. 获取项目 DSN(上报地址)
    同样在 Project Settings 页面,点击右侧的“Client Keys (DSN)”查看 DSN sentry-get-project-dsn
  4. 获取用户 Token(用于上传 Sourcemap) 点击左上角头像,选择 User auth tokens,点击左上角 Create New Token,勾选如下图中的权限 sentry-create-user-auth-tokens

第 4 步的 token 绑定到当前操作人员的帐号,当该帐号发生变动时(禁用或权限变动),需要重新生成 token。

# Uni-app H5 接入步骤

以下内容适用于使用 uni-app 框架的 H5 应用。

# H5 初始化配置

首先安装依赖:

npm install --save @sentry/vue @sentry/tracing --registry https://npm.devops.cndinfo.com/
复制代码

文档所使用的版本号如下:

  "dependencies": {
    "@sentry/tracing": "7.69.0",
    "@sentry/vue": "7.69.0",
  },
复制代码

main.js 中初始化:

import * as Sentry from "@sentry/vue";

Sentry.init({
  Vue,
  dsn: "https://XXXXXXXXXX@mydsn.com/4", // 上文提到的项目 DSN 配置
  integrations: [new Sentry.BrowserTracing({})], // 配置额外的 sdk 集成
  release: process.env.SENTRY_RELEASE, // 项目的版本号,方便区分报错对应的版本;手动写死或者使用本地环境变量
  environment: "development", // 环境变量,建议和项目的环境变量保持一致(例如 development、testing、uat、production)
  attachStacktrace: true, // 是否开启堆栈跟踪(默认开),开启后跟着消息一起收集
  beforeSend: (event, hint) => {
    // 发送报错前的额外操作
    return event;
  },
  tracesSampleRate: 1.0, // 随机事件发送给 sentry 的几率
});
// 上报自定义 Tag,见下文的 setTag()
// 上报用户信息,见下文的 setUser()
复制代码
  • release: 版本号的命名可以和项目 package.json 中的 version 保持一致。
    如果你的项目是将多个子应用上报到同一个 Sentry 项目,可以添加子应用前缀,便于区分。例如 subapp@0.0.1

# 验证错误是否上报成功

在 Web 类型的项目中,Sentry 会自动上报错误信息。

我们可以手动添加几行会报错的代码。

const a = {};
console.log(a.b.c);
复制代码

运行之后,可以在后台查看到报错已被采集。

sentry-issuessentry-issues

# 上传 Source Map

按上述步骤采集的报错会有个问题,无法定位到报错代码的具体位置(代码行号)。

因此必须要上传 Source Map 到 Sentry,才能将编译部署后的代码和原始代码进行映射,从而在 Sentry 中显示报错代码的具体位置。

Sentry 有两种上传方法,通过 @sentry/cli 手动上传和 @sentry/webpack-plugin 自动上传。

Web 类的项目建议使用后者。

# Source Map 上传步骤

# 自动上传

Node 版本必须 ≥14。如果 Node 仍使用较早的版本,可以采用手动上传的方式(见下文)。

首先安装依赖:

npm install --save-dev @sentry/webpack-plugin --registry https://npm.devops.cndinfo.com/
复制代码

文档所使用的版本号如下:

  "devDependencies": {
    "@sentry/webpack-plugin": "2.7.1",
  },
复制代码

vue.config.js 中添加:

const { sentryWebpackPlugin } = require("@sentry/webpack-plugin");

module.exports = {
  configureWebpack: (config) => {
    Object.assign(config, {
      plugins: [
        ...config.plugins,
        sentryWebpackPlugin({
          url: "https://sentry.devops.cndinfo.com/", // Sentry 后台地址
          release: process.env.SENTRY_RELEASE, // 和 `Sentry.init` 中的保持一致
          include: path.join(process.cwd(), "/dist"), // 需要上传到 sentry 服务器的资源目录
          ignore: ["node_modules", "vue.config.js"], // 忽略文件目录,如果在 inlcude 中已经定义了具体路径,这个参数可以不加
          authToken: process.env.SENTRY_AUTH_TOKEN, // 上文中的 User auth tokens
          org: process.env.SENTRY_ORG, // 上文中的组织名称
          project: process.env.SENTRY_PROJECT, // 上文中的项目名称
          urlPrefix: "~/static/js", // 上传资源的路径前缀,uni-app build 的路径通常是 `/static/js`, 如有变化自行更改
        }),
      ],
      // 不加这个uni-app不会生成soucemap文件
      devtool: "source-map",
    });
  },
};
复制代码

authToken, orgproject 均为获取项目信息中得到的信息。

打包后 Source Map 会自动上传至 Sentry。

# 执行打包命令(具体命令以各自项目为准)
npm run build
复制代码
# 手动上传

首先安装依赖:

npm install -D @sentry/cli --registry https://npm.devops.cndinfo.com/

# 如果下载超时,可以使用国内源
npm install -D @sentry/cli --registry https://npm.devops.cndinfo.com/ --sentrycli_cdnurl=https://cdn.npm.taobao.org/dist/sentry-cli

# 也可以直接设置成国内地址
npm config set sentrycli_cdnurl https://cdn.npm.taobao.org/dist/sentry-cli
复制代码

文档所使用的版本号如下:

  "devDependencies": {
    "@sentry/cli": "^2.20.7",
  },
复制代码

vue.config.js 中添加一行 devtool 配置,允许打包 Source Map:

module.exports = {
  configureWebpack: {
    // 不加这个uni-app不会生成soucemap文件
    devtool: "source-map"
  }
}
复制代码

在根目录添加配置文件 .sentryclirc:

[auth]
token=和自动上传相同的token

[defaults]
url=https://sentry.devops.cndinfo.com/
org=sentry
project=项目名称
复制代码

每个参数的值都和自动上传相同。

package.json 添加自定义指令:

"sentry": "sentry-cli releases new 0.0.1 && sentry-cli releases files 0.0.1 upload-sourcemaps ./dist/build/h5/static/js --url-prefix '~/static/js'",
"build": "npm run build:h5 && npm run sentry && rm -rf ./dist/build/h5/static/js/*.map",
复制代码
  • 0.0.1 是版本号,和 Sentry.init 中保持一致,每次变化需要手动修改
  • ./dist/build/h5/static/js 为打包后需要上传的地址
  • ~/static/js 为线上部署后的 js 的地址 两者根据实际情况进行调整。

uni-app H5 项目的测试环境使用的是单独的命令,因此还需要给测试环境配置单独的命令:

"sentry-test": "sentry-cli releases new 0.0.1 && sentry-cli releases files 0.0.1 upload-sourcemaps ./dist/dev/h5/static/js --url-prefix '~/static/js'",
"build-test": "npm run build:h5-test && npm run sentry-test && rm -rf ./dist/dev/h5/static/js/*.map",
复制代码

打包后 Source Map 会自动上传至 Sentry。

# 执行打包命令(具体命令以各自项目为准)
npm run build
复制代码

# 删除生产环境的 Source Map 文件

我们的目的仅仅是把 map 文件上传到 Sentry。生产环境部署本身并不需要 map 文件。

而且 map 上传到生产环境会导致包体积变大,此外 map 中包含 sentry 的 token 信息,有泄漏风险。

因此建议删除。

对于 uni-app H5 项目,可以通过调整 package.json 中的 build:h5 命令来删除。

// 在结尾增加 rm -rf ./dist/build/h5/static/js/*.map"
"build": "npm run build:h5 && rm -rf ./dist/build/h5/static/js/*.map"
复制代码

如果是手动上传的 Source Map 文件,需要改为:

// 在结尾增加 rm -rf ./dist/build/h5/static/js/*.map"
"build": "npm run build:h5 && npm run sentry && rm -rf ./dist/build/h5/static/js/*.map"
复制代码

这样在 build 时就会自动删除 dist 目录下的 *.map 文件。

# 查看 Source Map 是否上传

在 Project Settings 页面,点击左侧 Source Maps,根据 Release 版本号和上传时间判断你的 map 文件是否成功上传。

sentry-project-sourcemap

如果 map 文件已上传,可以在部署后的线上环境运行有报错的页面,此时在报错信息中可以看到报错代码的具体位置:

sentry-issues

此处需要注意,必须在“线上环境”运行有报错的页面,在本地的 npm run serve 开发环境是无用的。因为 Sentry 中的 map 文件是 build 之后生成的,和 npm run serve 无关。

如果开发人员想在本地环境验证,可以使用下面的方式来模拟线上环境:

安装 http-server 工具,这个工具可以在本地运行 http 服务。

npm install http-server -g
复制代码

进入打包后的 dist/build/h5 目录,运行命令 http-server,在 http-server 打开的页面中的运行报错的页面。

# Uni-app App 接入步骤

由于 Sentry 官方没有原生支持 uni-app 的 App 类型,因此不支持自动上报异常信息,只能通过 uni-app 的 onError 生命周期去捕获异常并且上报。
另外 uni.request 接口请求的 fail 并不会出现在 uni-apponError 生命周期,也只能手动上报异常信息。

# App 初始化配置

安装 sentry-uniapp 库:

npm install sentry-uniapp -S
复制代码

main.js 中初始化:

import * as Sentry from "sentry-uniapp";

Sentry.init({
  // 初始化代码同 H5, 但需要去掉 integrations
  dsn: "https://XXXXXXXXXX@mydsn.com/4", // 上文提到的项目 DSN 配置
  release: process.env.SENTRY_RELEASE, // 项目的版本号,方便区分报错对应的版本;手动写死或者使用本地环境变量
  environment: "development", // 环境变量,建议和项目的环境变量保持一致(例如 development、testing、uat、production)
  attachStacktrace: true, // 是否开启堆栈跟踪(默认开),开启后跟着消息一起收集
  beforeSend: (event, hint) => {
    // 发送报错前的额外操作
    return event;
  },
  tracesSampleRate: 1.0, // 随机事件发送给 sentry 的几率
});
// 上报自定义 Tag,见下文的 setTag()
// 上报用户信息,见下文的 setUser()
复制代码

初始化参数和 H5 一样,参考H5 初始化配置

App.vueonError 生命周期中上报捕获到的错误信息:

注意:要在捕获错误之前上报用户信息。

import * as Sentry from "sentry-uniapp";

export default {
  onError: function (err) {
    // #ifdef  APP-PLUS
    const userData = uni.getStorageSync("userInfo");
    if (userData) {
      Sentry.setUser({
        id: userData.usercode,
        username: userData.username,
        email: userData.email,
        ip_address: "{{auto}}",
      });
    }
    Sentry.captureException(err);
    // #endif
  },
};
复制代码

# Sentry API 介绍

此处介绍一些常用的 API ,更多 API 可自行查阅官方文档

# setTag()

给报错事件设置全局标签,方便对报错进行自定义分类。

注意:页面刷新后数据会丢失。如果想要对每个报错事件都进行设置,需要放在 main.js 中。

// 一次只能上传一个 tag,若要上传多个 tag,可以添加多行
Sentry.setTag("test", "test-name");
Sentry.setTag("test_user", "test-user");
Sentry.setTag("test_id", "test123");
复制代码

根据官方的建议,tag 的名称建议使用下划线。如果使用其他形式,需参考下面的要求。

Tag keys have a maximum length of 32 characters and can contain only letters (a-zA-Z), numbers (0-9), underscores (_), periods (.), colons (:), and dashes (-).

标签效果如下,browser, device 等均为 Sentry 自带的标签,test 则是我们自定义的标签。

sentry-tags

# setUser()

设置用户的信息,方便查询该报错是由哪个用户触发。

注意:页面刷新后数据会丢失。如果想要对每个报错事件都进行设置,需要放在 main.js 中。

可以设置的用户信息共 4 个,均非必传字段,但至少需要上传一个字段。

  • id: 用户 ID,系统内部的用户唯一标识符
  • username: 用户名称,例如用户姓名或者昵称,无需唯一
  • email: 用户邮箱
  • ip_address: 用户的 IP 地址,建议设置为 。Sentry 会自动获取 IP。如果用户未经身份验证,Sentry 将使用 IP 地址作为用户的唯一标识符
Sentry.setUser({
  id: "12345",
  username: "王明",
  email: "wangming@test.com",
  ip_address: "{{auto}}",
});
复制代码

上传其他字段是不会被 Sentry 采集的。因此有其他字段需求可以使用 setTag

# 调用时机

由于页面刷新后原先的数据会丢失,所以不能只在登录成功时调用。可以在 main.js 和登录获取用户信息两个位置都进行上传。

  1. 登录获取用户信息后调用

login.vue:

login(){
  // 登录成功并获取到用户信息后
  const userData = { id: "123", username: "test" }
  Sentry.setUser(userData);
}
复制代码

如果项目同时有使用行为分析 SDK,可以和行为分析的profileSet调用时机保持一致。

  1. main.js 中调用
// Sentry 初始化成功后调用
// 从缓存中取用户信息,有缓存才调用
const userData = uni.getStorageSync("userInfo");
if (userData) {
  Sentry.setUser({
    id: userData.usercode,
    username: userData.username,
    email: userData.email,
    ip_address: "{{auto}}",
  });
}
复制代码

# captureException(exception)

手动上报错误。传入一个 exception 对象或者类对象。

Sentry 无法自动捕获 catch 中的异常信息,因此 catch 中的异常必须要手动上传。

try {
  const a = {};
  console.log(a.b.c);
} catch (error) {
  Sentry.captureException(error);
}
复制代码

# 进阶用法

# 不同子应用使用同一个项目

如果你的项目中包含多个子应用,且所有子应用都想上报到同一个 Sentry 项目,可以使用下面的方式来区分:

通过自定义的 tag 和 release 版本号共同自定义。

main.js

Sentry.init({
  // ...
  release: process.env.SENTRY_RELEASE, // 使用带子应用名称的版本号,例如 `sub_app_name@1.0.0`
  // ...
});
// 初始化之后 通过自定义的 sub_app_name 填写对应的子应用名称
Sentry.setTag("sub_app_name", "应用名称");
复制代码

# 在统一接口请求中上报异常

// App
import * as Sentry from "sentry-uniapp";
// Web (H5)
import * as Sentry from "@sentry/vue";

uni.request({
  success:(res)=>{
    if(res.code !== 200) {
      Sentry.captureMessage("自定义上报的错误信息");
    }
  }
  fail: (err) => {
    Sentry.captureException(err);
  },
});
复制代码

# 注意事项

# Sentry 的不足点

# 无法自动捕获部分异常

Sentry 无法捕获 try/catch, ajax 请求的失败回调(例如 uni.request 中的 fail,以及 axios 中的 catch)等异常,在这些场景中必须要使用 captureException 进行上报。

try {
  const a = {};
  console.log(a.b.c);
} catch (error) {
  // 如果 catch 没有暴露错误出来 是不会被捕获的,sentry 后台不可查到
  // 需要自己手动暴露报错,或者通过 API 手动上报
  Sentry.captureException(error);
}
复制代码

# App 不支持 Source Map

Sentry 暂不支持 uni-app App 的 Source Map。因此无法定位到报错代码的具体位置。

# 常见问题

# 上传 Source Map 报错 401

检查 urltoken 是否一致,token 权限是否给够,创建 Token 时是否勾选了 project:write

# 报错 project not found

检查配置中的组织名称和项目名称是否和 Sentry 后台一致。

# Source Map 上传成功,但映射失败

后台可以看到上传的 map 信息,但无法看到报错的具体行号。

检查上传的路径是否正确。