[转] vue前端异常监控sentry实践
<h2>1. 监控原理</h2><h3>1.1 onerror</h3>
<p>传统的前端监控原理分为异常捕获和异常上报。一般使用<code>onerror</code>捕获前端错误:</p>
<pre class="prism-token tokenlanguage-js">window<span class="token punctuation">.<span class="token function-variable function">onerror <span class="token operator">= <span class="token punctuation">(msg<span class="token punctuation">, url<span class="token punctuation">, line<span class="token punctuation">, col<span class="token punctuation">, error<span class="token punctuation">) <span class="token operator">=> <span class="token punctuation">{
console<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(<span class="token string">'onerror'<span class="token punctuation">)
<span class="token comment">// TODO
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<h3>1.2 promise</h3>
<p>但是<code>onerror</code>事件无法捕获到网络异常的错误(资源加载失败、图片显示异常等),例如<code>img</code>标签下图片url 404 网络请求异常的时候,<code>onerror</code>无法捕获到异常,此时需要监听<code>unhandledrejection</code>。</p>
<pre class="prism-token tokenlanguage-js">window<span class="token punctuation">.<span class="token function">addEventListener<span class="token punctuation">(<span class="token string">'unhandledrejection'<span class="token punctuation">, <span class="token keyword">function<span class="token punctuation">(err<span class="token punctuation">) <span class="token punctuation">{
console<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(err<span class="token punctuation">)
<span class="token punctuation">}<span class="token punctuation">)</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<h3>1.3 上报</h3>
<p>捕获的异常如何上报?常用的发送形式主要有两种:</p>
<ol class="ol-level-0">
<li>通过 ajax 发送数据(xhr、jquery...)</li>
<li>动态创建 img 标签的形式</li>
</ol>
<pre class="prism-token tokenlanguage-js"><span class="token keyword">function <span class="token function">report<span class="token punctuation">(error<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">var reportUrl <span class="token operator">= <span class="token string">'http://xxxx/report'
<span class="token keyword">new <span class="token class-name">Image<span class="token punctuation">(<span class="token punctuation">)<span class="token punctuation">.src <span class="token operator">= reportUrl <span class="token operator">+ <span class="token string">'?error=' <span class="token operator">+ error
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<h3>1.4 使用sentry</h3>
<p>sentry是一套开源的强大的前端异常监控上报工具,官网地址:https://sentry.io,官方提供了如何搭建sentry服务,此处略过安装流程,直接使用已有的服务。</p>
<h3>1.5 与vue结合</h3>
<p>针对vue,sentry官方推荐使用raven配合sentry进行异常捕获和上报。而在vue中,vue提供了错误捕获方法<code>vue error handler</code>,官方也推荐使用错误追踪服务 sentry 并通过<code>vue error handler</code>选项提供了官方支持。</p>
<h2>2. 安装raven</h2>
<p>raven是sentry官方针对vue推荐的插件,yarn安装raven-js即可。</p>
<pre class="prism-token tokenlanguage-js">$ yarn add raven<span class="token operator">-js</span></pre>
<h2>3. 初始化sentry</h2>
<p>初始化引入Vue、Raven、RavenVue即可,sentry能主动监听上报错误。</p>
<pre class="prism-token tokenlanguage-js"><span class="token keyword">import Raven <span class="token keyword">from <span class="token string">'raven-js'
<span class="token keyword">import RavenVue <span class="token keyword">from <span class="token string">'raven-js/plugins/vue'
<span class="token keyword">const dsn <span class="token operator">= <span class="token string">'https://<key1>@sentry.io/<key2>'
Raven<span class="token punctuation">.<span class="token function">config<span class="token punctuation">(dsn<span class="token punctuation">)<span class="token punctuation">.<span class="token function">addPlugin<span class="token punctuation">(RavenVue<span class="token punctuation">, Vue<span class="token punctuation">)<span class="token punctuation">.<span class="token function">install<span class="token punctuation">(<span class="token punctuation">)</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<h2>4. 手动上报</h2>
<p>对于一些其他信息,如提示日志等,无法自动捕获,需要手动进行上报。</p>
<pre class="prism-token tokenlanguage-js"><span class="token function">log <span class="token punctuation">(data <span class="token operator">= <span class="token keyword">null<span class="token punctuation">, type <span class="token operator">= <span class="token string">'error'<span class="token punctuation">, options <span class="token operator">= <span class="token punctuation">{<span class="token punctuation">}<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment">// 添加面包屑
Raven<span class="token punctuation">.<span class="token function">captureBreadcrumb<span class="token punctuation">(<span class="token punctuation">{
message<span class="token punctuation">: data<span class="token punctuation">,
category<span class="token punctuation">: <span class="token string">'manual message'
<span class="token punctuation">}<span class="token punctuation">)
<span class="token comment">// 异常上报
<span class="token keyword">if <span class="token punctuation">(data <span class="token keyword">instanceof <span class="token class-name">Error<span class="token punctuation">) <span class="token punctuation">{
Raven<span class="token punctuation">.<span class="token function">captureException<span class="token punctuation">(data<span class="token punctuation">, <span class="token punctuation">{
level<span class="token punctuation">: type<span class="token punctuation">,
logger<span class="token punctuation">: <span class="token string">'manual exception'<span class="token punctuation">,
tags<span class="token punctuation">: <span class="token punctuation">{ options<span class="token punctuation">: options <span class="token punctuation">}
<span class="token punctuation">}<span class="token punctuation">)
<span class="token punctuation">} <span class="token keyword">else <span class="token punctuation">{
Raven<span class="token punctuation">.<span class="token function">captureException<span class="token punctuation">(<span class="token string">'error'<span class="token punctuation">, <span class="token punctuation">{
level<span class="token punctuation">: type<span class="token punctuation">,
logger<span class="token punctuation">: <span class="token string">'manual data'<span class="token punctuation">,
extra<span class="token punctuation">: <span class="token punctuation">{
data<span class="token punctuation">: data<span class="token punctuation">,
options<span class="token punctuation">: <span class="token keyword">this<span class="token punctuation">.options<span class="token punctuation">,
date<span class="token punctuation">: <span class="token keyword">new <span class="token class-name">Date<span class="token punctuation">(<span class="token punctuation">)
<span class="token punctuation">}
<span class="token punctuation">}<span class="token punctuation">)
<span class="token punctuation">}
<span class="token punctuation">}</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<h2>5. 封装异常上报类 Report.js</h2>
<p>针对上述内容,封装异常上报类Report,使用单例模式,避免监控类重复实例化。</p>
<pre class="prism-token tokenlanguage-js"><span class="token comment">/**
* by csxiaoyao
* 2019.03.17
* sunjianfeng@csxiaoyao.com
*/
<span class="token keyword">import Raven <span class="token keyword">from <span class="token string">'raven-js'
<span class="token keyword">import RavenVue <span class="token keyword">from <span class="token string">'raven-js/plugins/vue'
<span class="token keyword">class <span class="token class-name">Report <span class="token punctuation">{
<span class="token keyword">static dsn <span class="token operator">= <span class="token string">'https://<key1>@sentry.io/<key2>'
<span class="token function">constructor <span class="token punctuation">(Vue<span class="token punctuation">, options <span class="token operator">= <span class="token punctuation">{<span class="token punctuation">}<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">if <span class="token punctuation">(process<span class="token punctuation">.env<span class="token punctuation">.NODE_ENV <span class="token operator">=== <span class="token string">'production'<span class="token punctuation">) <span class="token punctuation">{ <span class="token comment">// TODO }
<span class="token keyword">this<span class="token punctuation">.Vue <span class="token operator">= Vue
<span class="token keyword">this<span class="token punctuation">.options <span class="token operator">= options
<span class="token punctuation">}
<span class="token comment">/**
* 单例模式
*/
<span class="token keyword">static <span class="token function">getInstance <span class="token punctuation">(Vue<span class="token punctuation">, options<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">if <span class="token punctuation">(<span class="token operator">!<span class="token punctuation">(<span class="token keyword">this<span class="token punctuation">.instance <span class="token keyword">instanceof <span class="token class-name">this<span class="token punctuation">)<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">this<span class="token punctuation">.instance <span class="token operator">= <span class="token keyword">new <span class="token class-name">this<span class="token punctuation">(Vue<span class="token punctuation">, options<span class="token punctuation">)
<span class="token keyword">this<span class="token punctuation">.instance<span class="token punctuation">.<span class="token function">install<span class="token punctuation">(<span class="token punctuation">)
<span class="token keyword">this<span class="token punctuation">.instance<span class="token punctuation">.<span class="token function">registerError<span class="token punctuation">(<span class="token punctuation">)
<span class="token punctuation">}
<span class="token keyword">return <span class="token keyword">this<span class="token punctuation">.instance
<span class="token punctuation">}
<span class="token comment">/**
* init
*/
<span class="token function">install <span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
Raven<span class="token punctuation">.<span class="token function">config<span class="token punctuation">(Report<span class="token punctuation">.dsn<span class="token punctuation">, <span class="token punctuation">{
release<span class="token punctuation">: <span class="token string">'1.0.0'<span class="token punctuation">,
environment<span class="token punctuation">: <span class="token string">'production'
<span class="token comment">// whitelistUrls:
<span class="token punctuation">}<span class="token punctuation">)<span class="token punctuation">.<span class="token function">addPlugin<span class="token punctuation">(RavenVue<span class="token punctuation">, <span class="token keyword">this<span class="token punctuation">.Vue<span class="token punctuation">)<span class="token punctuation">.<span class="token function">install<span class="token punctuation">(<span class="token punctuation">)
<span class="token comment">// 记录用户信息
Raven<span class="token punctuation">.<span class="token function">setUserContext<span class="token punctuation">(<span class="token punctuation">{
user<span class="token punctuation">: <span class="token keyword">this<span class="token punctuation">.options<span class="token punctuation">.user <span class="token operator">|| <span class="token string">''
<span class="token punctuation">}<span class="token punctuation">)
<span class="token comment">// 设置全局tag标签
Raven<span class="token punctuation">.<span class="token function">setTagsContext<span class="token punctuation">(<span class="token punctuation">{ environment<span class="token punctuation">: <span class="token keyword">this<span class="token punctuation">.options<span class="token punctuation">.env <span class="token operator">|| <span class="token string">'' <span class="token punctuation">}<span class="token punctuation">)
<span class="token punctuation">}
<span class="token comment">/**
* 注册全局错误处理
*/
<span class="token function">registerError <span class="token punctuation">(<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment">// 监听error
window<span class="token punctuation">.<span class="token function-variable function">onerror <span class="token operator">= <span class="token punctuation">(msg<span class="token punctuation">, url<span class="token punctuation">, line<span class="token punctuation">, col<span class="token punctuation">, error<span class="token punctuation">) <span class="token operator">=> <span class="token punctuation">{
console<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(<span class="token string">'onerror'<span class="token punctuation">)
<span class="token keyword">if <span class="token punctuation">(msg <span class="token operator">!== <span class="token string">'Script error.' <span class="token operator">&& <span class="token operator">!url<span class="token punctuation">) <span class="token punctuation">{
<span class="token keyword">return <span class="token boolean">true
<span class="token punctuation">}
<span class="token function">setTimeout<span class="token punctuation">(<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=> <span class="token punctuation">{
<span class="token keyword">let data <span class="token operator">= <span class="token punctuation">{<span class="token punctuation">}
col <span class="token operator">= col <span class="token operator">|| <span class="token punctuation">(window<span class="token punctuation">.event <span class="token operator">&& window<span class="token punctuation">.event<span class="token punctuation">.errorCharacter<span class="token punctuation">) <span class="token operator">|| <span class="token number">0
data<span class="token punctuation">.url <span class="token operator">= url
data<span class="token punctuation">.line <span class="token operator">= line
data<span class="token punctuation">.col <span class="token operator">= col
data<span class="token punctuation">.error <span class="token operator">= error
<span class="token keyword">if <span class="token punctuation">(<span class="token operator">&& error<span class="token punctuation">.stack<span class="token punctuation">) <span class="token punctuation">{
data<span class="token punctuation">.msg <span class="token operator">= error<span class="token punctuation">.stack<span class="token punctuation">.<span class="token function">toString<span class="token punctuation">(<span class="token punctuation">)
<span class="token punctuation">}
<span class="token keyword">this<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(data<span class="token punctuation">)
<span class="token punctuation">}<span class="token punctuation">, <span class="token number">0<span class="token punctuation">)
<span class="token keyword">return <span class="token boolean">true
<span class="token punctuation">}
<span class="token comment">// 监听promise
window<span class="token punctuation">.<span class="token function">addEventListener<span class="token punctuation">(<span class="token string">'unhandledrejection'<span class="token punctuation">, err <span class="token operator">=> <span class="token punctuation">{
console<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(<span class="token string">'unhandledrejection'<span class="token punctuation">)
<span class="token function">setTimeout<span class="token punctuation">(<span class="token punctuation">(<span class="token punctuation">) <span class="token operator">=> <span class="token punctuation">{
<span class="token keyword">this<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(JSON<span class="token punctuation">.<span class="token function">stringify<span class="token punctuation">(err<span class="token punctuation">)<span class="token punctuation">)
<span class="token punctuation">}<span class="token punctuation">, <span class="token number">0<span class="token punctuation">)
<span class="token punctuation">}<span class="token punctuation">)
<span class="token punctuation">}
<span class="token comment">/**
* 主动上报
* type: 'info','warning','error'
*/
<span class="token function">log <span class="token punctuation">(data <span class="token operator">= <span class="token keyword">null<span class="token punctuation">, type <span class="token operator">= <span class="token string">'error'<span class="token punctuation">, options <span class="token operator">= <span class="token punctuation">{<span class="token punctuation">}<span class="token punctuation">) <span class="token punctuation">{
<span class="token comment">// 添加面包屑
Raven<span class="token punctuation">.<span class="token function">captureBreadcrumb<span class="token punctuation">(<span class="token punctuation">{
message<span class="token punctuation">: data<span class="token punctuation">,
category<span class="token punctuation">: <span class="token string">'manual message'
<span class="token punctuation">}<span class="token punctuation">)
<span class="token comment">// 异常上报
<span class="token keyword">if <span class="token punctuation">(data <span class="token keyword">instanceof <span class="token class-name">Error<span class="token punctuation">) <span class="token punctuation">{
Raven<span class="token punctuation">.<span class="token function">captureException<span class="token punctuation">(data<span class="token punctuation">, <span class="token punctuation">{
level<span class="token punctuation">: type<span class="token punctuation">,
logger<span class="token punctuation">: <span class="token string">'manual exception'<span class="token punctuation">,
tags<span class="token punctuation">: <span class="token punctuation">{ options<span class="token punctuation">: options <span class="token punctuation">}
<span class="token punctuation">}<span class="token punctuation">)
<span class="token punctuation">} <span class="token keyword">else <span class="token punctuation">{
Raven<span class="token punctuation">.<span class="token function">captureException<span class="token punctuation">(<span class="token string">'error'<span class="token punctuation">, <span class="token punctuation">{
level<span class="token punctuation">: type<span class="token punctuation">,
logger<span class="token punctuation">: <span class="token string">'manual data'<span class="token punctuation">,
extra<span class="token punctuation">: <span class="token punctuation">{
data<span class="token punctuation">: data<span class="token punctuation">,
options<span class="token punctuation">: <span class="token keyword">this<span class="token punctuation">.options<span class="token punctuation">,
date<span class="token punctuation">: <span class="token keyword">new <span class="token class-name">Date<span class="token punctuation">(<span class="token punctuation">)
<span class="token punctuation">}
<span class="token punctuation">}<span class="token punctuation">)
<span class="token punctuation">}
<span class="token punctuation">}
<span class="token punctuation">}
<span class="token keyword">export <span class="token keyword">default Report</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<h2>6. 调用 Report.js 类</h2>
<p><code>main.js</code>中引入Report类,并绑定实例化后的sentry实例到Vue上以便全局调用。</p>
<pre class="prism-token tokenlanguage-js"><span class="token keyword">import Report <span class="token keyword">from <span class="token string">'@/assets/Report'
<span class="token keyword">let sentry <span class="token operator">= Report<span class="token punctuation">.<span class="token function">getInstance<span class="token punctuation">(Vue<span class="token punctuation">, <span class="token punctuation">{<span class="token punctuation">}<span class="token punctuation">)
Vue<span class="token punctuation">.prototype<span class="token punctuation">.$sentry <span class="token operator">= sentry <span class="token comment">// 设置全局变量</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
<p>在其他的vue组件中手动上报日志。</p>
<pre class="prism-token tokenlanguage-js"><span class="token keyword">this<span class="token punctuation">.$sentry<span class="token punctuation">.<span class="token function">log<span class="token punctuation">(<span class="token string">'test'<span class="token punctuation">)</span></span></span></span></span></span></span></pre>
<h2>7. sourceMap</h2>
<p>sentry针对压缩过的js文件提供了sourceMap分析,只需要上传版本对应的sourceMap,就可以在错误日志中查看对应的源码信息。详细方法见官方文档:https://docs.sentry.io/clients/javascript/sourcemaps/(https://docs.sentry.io/clients/javascript/sourcemaps/)</p>
<p> </p>
<p>转自:https://cloud.tencent.com/developer/article/1404307</p>
<p> </p>
<p></audio></p><br><br>
来源:https://www.cnblogs.com/chris-oil/p/11512923.html
頁:
[1]