Kevin Cui's Blog

JavaScript集成Sentry

Sentry-JavaScript

Sentry是一套用于捕获产品错误的开源项目,其下支持很多语言、框架。

这里就只阐述在前端JavaScript方向的处理操作

在我们公司之前的应用场景里,很多项目都是使用kibana来做信息统计。但是我们无法清楚的知道应用的运行状态是怎么样的。当某个客户在使用我们开发产品时,如果报错、崩溃。用户只能向客服寻求帮助,再交接给我们的开发人员进行复现、修复。其中因为不清楚具体的数据,开发人员是在复现时会非常的耗时。

Sentry的用途就是解决这一痛点问题,让开发人员快速准确的定位到问题的根源所在,以达到快速修复,让开发人员更注重于开发新的功能上面。减少时间资源上的浪费。

JavaScript

接入

因为Sentry使用的是一种Hook错误函数的技术,来达到捕获错误的目的,所以我们基本可以无损耗的接入到现有的项目中去。

下面是ReactSentry进行结合的一些基本步骤。

React:

#SentryBoundary.js
import { Component } from "react";
import Raven from "raven-js";

export default class SentryBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { error: null };
  }

  componentDidCatch(error, errorInfo) {
    this.setState({ error });
    // 发送错误信息
    Raven.captureException(error, { extra: errorInfo });
  }

  render() {
    if (this.state.error) {
      // 此处可以写成组件,当组件崩溃后,可以替换崩溃的组件
      console.log("React Error");
    }
    return this.props.children;
  }
}
#index.js
Raven.config("DSN", {
  release: release,
}).install();

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

上传source-map

如果上面的代码已经配置好后,那么现在的应用是可以捕获到错误的,但是存在了一个问题,我们目前的项目大多都使用webpack进行打包,而打包后的代码是混淆加密的代码,无法让我们准确的知道抛出错误的位置在哪里。所以我们需要上传source-map和混淆后的文件一起上传到Sentry服务器上。方便我们快速查找到问题所在的位置。

这个上传的配置及命令是比较繁琐的。也是项目结合Sentry的一个难点。

上传source-map目前有两种方式:

其配置较为繁琐,这里就不在阐述。具体的React与Sentry结合的例子。可见我在github上的项目: react-sentry-demo。对每个配置都有详细的说明。其中的上传source-map,我使用的是第二种方法,并写了一个脚本,实现了: 打包、环境检测、认证检测、上传source-map、删除本地source-map的操作,完成自动化,可以把脚本直接迁移到现有的项目中去,改动也不会太大。

其核心上传命令如下:

sentry-cli releases files v1.8 upload-sourcemaps {js文件和js.map所在目录。如果没有找到,sentry会遍历其子目录} --url-prefix '~/{过滤规则}'`;

浅入原理

在JavaScript中是有window.onerror这个方法的,而Sentry在前端的核心捕获原理,就是通过重写此方法,来对所有的错误进行捕获。其实现的代码大致如下:

let _winError = window.onerror;
window.onerror = function (message, url, lineNo, colNo, errorObj) {
    console.log(`
	错误信息: ${message}
	错误文件地址: ${url}
	错误行号: ${lineNo}
	错误列号: ${colNo}
	错误的详细信息 ${errorObj}`);
}

然后Sentry的工作就是获取非错误的数据,如: user-agent浏览器信息系统信息自定义信息等信息,然后交给Sentry的生命周期函数,最后在把数据发送到Sentry服务端,进行错误信息展示。

兼容性

这里所说的兼容性,其实也就是window.onerror的兼容性

运行环境兼容性

环境messageurllineNocolNoerrorObj
Firefox
Chrom
Edge
IE 11
IE 10
IE 9
IE 8
Safari 10 and up
Safari 9
Opera 15+
Android Browser 4.4
Android Browser 4 - 4.3
微信webview(安卓)
微信webview(IOS)
WKWebview
UIWebview

标签兼容性

标签window.onerror是否能捕获
img可以
script需要再script标签添加 crossorigin属性,并在服务端允许跨域。如果不使用这个属性,错误信息只会显示Script error.
css不能
iframe不能

可以发现其浏览器都支持此方法。只是有些运行环境不支持colNoerrorObj,但是这块,Sentry已经帮你处理好了,所以不用担心。只是会在展示错误的时候,信息不太完整而已。

所能捕获的信息

错误信息

从上面的浅入原理可以看到,其核心捕获是window.onerror。那么只要它可以捕获到的错误,都会发送到Sentry上。

window.onerror能捕获到的错误,除了Promise,基本上能在控制台出现的错误,都会捕获到。也就是运行时的错误,包括语法错误。

关于捕获Promise错误的方案,可以使用:

window.addEventListener('unhandledrejection', event => {})

来进行捕获,但是此事件的兼容性不太好,目前只有webkit内核支持这个事件。

如下代码,是此方法所能捕获到的:

const p = new Promise((reslove, reject) => reject('Error'))
p.then(data => {
  console.log(data)
})
// Promise触发了reject回调函数,但是却没有相应到catch来应对。从而导致报错。

面包屑信息

展示信息

Electron集成

这里的集成,也不是说捕获Electron应用的错误,而是崩溃。因为Electron只是一个容器,里面的内容还是JavaScript应用。

接入

刚刚也说到这里的集成也只是去捕获Electron崩溃的信息。而当Electron崩溃时,会触发Electron的函数:crashReporter.start,那么我们在这个函数里去配置一下自己的sentry信息:

import { crashReporter } from 'electron'

crashReporter.start({
  productName: 'aoc-desktop',
  companyName: 'alo7',
  submitURL:
    'https://sentry.com/api/15376/minidump/?sentry_key=3e05fa101f035008e953ff56909b8eb',	// sentry提供的minidump接口
  extra: {
    // 额外信息
  }
})

配置好后,可以使用process.crash()来模拟崩溃,以便查看Sentry是否能收到崩溃信息。

上传Symbol(符号表)

在上面的应用说的是上传source-map,但是这里上传的是Symbol。可以把Symbol理解为另一种source-map

Symbol的格式(后缀)有很多,Mac下是dSYM,windows是pdb。而在Sentry里,暂时是不支持上传pdb的。需要使用dump_syms.exe来把pdb格式转化成sym格式。再上传到Sentry里。这样就可以在Sentry崩溃的时候,看到起崩溃的上下文了。如下图:

这样就可以准确的定位到是哪里出现了问题。

浅入上传检索的原理

当Sentry服务端收到source-map时,是通过你上传时的url-prefix信息,与source-map文件以及运行时的js文件,产生对应。流程图如下:

#javascript #react #electron

Reply to this post by email ↪