记录–百分百空手接大锅

  • 记录–百分百空手接大锅已关闭评论
  • 131 次浏览
  • A+
所属分类:Web前端
摘要

愉快的双休周末刚过完,早上来忽然被运营通知线上业务挂了,用户无法下单。卧槽,赶紧进入debug模式,一查原来是服务端返回的数据有问题,赶紧问了服务端,大佬回复说是业务部门配置套餐错误。好在主责不在我们,不过赶紧写了复盘文档,主动找自己的责任,扛起这口大锅,都怪我们前端,没有做好前端监控,导致线上问题持续两天才发现。原本以为运营会把推辞一下说不,锅是她们的,可惜人家不太懂人情世故,这锅就扣在了技术部头上。虽然但是,我还是静下心来把前端异常监控搞了出来,下次一定不要主动接锅,希望看到本文的朋友们也不要随便心软接锅^_^


这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

记录--百分百空手接大锅

背景

愉快的双休周末刚过完,早上来忽然被运营通知线上业务挂了,用户无法下单。卧槽,赶紧进入debug模式,一查原来是服务端返回的数据有问题,赶紧问了服务端,大佬回复说是业务部门配置套餐错误。好在主责不在我们,不过赶紧写了复盘文档,主动找自己的责任,扛起这口大锅,都怪我们前端,没有做好前端监控,导致线上问题持续两天才发现。原本以为运营会把推辞一下说不,锅是她们的,可惜人家不太懂人情世故,这锅就扣在了技术部头上。虽然但是,我还是静下心来把前端异常监控搞了出来,下次一定不要主动接锅,希望看到本文的朋友们也不要随便心软接锅^_^

监控

因为之前基于sentry做了埋点处理,基础已经打好,支持全自动埋点、手动埋点和数据上报。相关的原理可以参考之前的一篇文章如何从0-1构建数据平台(2)- 前端埋点。本次监控的数据上报也基于sentry.js。那么如何设计整个流程呢。具体步骤如下:

  1. 监控数据分类

  2. 监控数据定义

  3. 监控数据收集

  4. 监控数据上报

  5. 监控数据输出

  6. 监控数据预警

数据分类

我们主要是前端的数据错误,一般的异常大类分为逻辑异常和代码异常。基于我们的项目,由于涉及营收,我们就将逻辑错误专注于支付异常,其他的代码导致的错误分为一大类。然后再将两大异常进行细分,如下:

  1. 支付异常

    1.1 支付成功

    1.2 支付失败

  2. 代码异常

    2.1 bindexception

 2.1.1  js_error   2.1.2  img_error   2.1.3  audio_error   2.1.4  script_error   2.1.5 video_error

  1. unhandleRejection

    3.1 promise_unhandledrejection_error

    3.2 ajax_error

  2. vueException

  3. peformanceInfo

数据定义

基于sentry的上报数据,一般都包括事件与属性。在此我们定义支付异常事件为“page_h5_pay_monitor”,定义代码异常事件为“page_monitor”。然后支付异常的属性大概为:

    pay_time,      pay_orderid,      pay_result,      pay_amount,      pay_type,      pay_use_coupon,      pay_use_coupon_id,      pay_use_coupon_name,      pay_use_discount_amount,      pay_fail_reason,      pay_platment

代码异常不同的错误类型可能属性会有所区别:

    // js_error      monitor_type,      monitor_message,      monitor_lineno,      monitor_colno,      monitor_error,      monitor_stack,      monitor_url      // src_error      monitor_type,      monitor_target_src,      monitor_url      // promise_error      monitor_type,      monitor_message,      monitor_stack,      monitor_url      // ajax_error      monitor_type,      monitor_ajax_method,      monitor_ajax_data,      monitor_ajax_params,      monitor_ajax_url,      monitor_ajax_headers,      monitor_url,      monitor_message,      monitor_ajax_code      // vue_error      monitor_type,      monitor_message,      monitor_stack,      monitor_hook,      monitor_url      // peformanceInfo 为数据添加 loading_time 属性,该属性通过entryTypes获取      try {          const observer = new PerformanceObserver((list) => {              for (const entry of list.getEntries()) {                  if (entry.entryType === 'paint') {                      sa.store.set('loading_time', entry.startTime)                  }             }          })          observer.observe({ entryTypes: ['paint'] })      } catch (err) {          console.log(err)      }

数据收集

数据收集通过事件绑定进行收集,具体绑定如下:

import {      BindErrorReporter,      VueErrorReporter,      UnhandledRejectionReporter  } from './report'  const Vue = require('vue')   // binderror绑定  const MonitorBinderror = () => {      window.addEventListener(      'error',      function(error) {          BindErrorReporter(error)      },true )  }  // unhandleRejection绑定 这里由于使用了axios,因此ajax_error也属于promise_error  const MonitorUnhandledRejection = () => {      window.addEventListener('unhandledrejection', function(error) {          if (error && error.reason) {              const { message, code, stack, isAxios, config } = error.reason              if (isAxios && config) {                  // console.log(config)                  const { data, params, headers, url, method } = config                  UnhandledRejectionReporter({                  isAjax: true,                  data: JSON.stringify(data),                  params: JSON.stringify(params),                  headers: JSON.stringify(headers),                  url,                  method,                  message: message || error.message,                  code                  })              } else {                  UnhandledRejectionReporter({                  isAjax: false,                  message,                  stack                  })              }          }      })  }  // vueException绑定  const MonitorVueError = () => {      Vue.config.errorHandler = function(error, vm, info) {          const { message, stack } = error          VueErrorReporter({              message,              stack,              vuehook: info          })      }  }  // 输出绑定方法  export const MonitorException = () => {      try {          MonitorBinderror()          MonitorUnhandledRejection()          MonitorVueError()      } catch (error) {          console.log('monitor exception init error', error)      }  }

数据上报

数据上报都是基于sentry进行上报,具体如下:

/*  * 异常监控库 基于sentry jssdk  * 监控类别:  * 1、window onerror 监控未定义属性使用 js资源加载失败问题  * 2、window addListener error 监控未定义属性使用 图片资源加载失败问题  * 3、unhandledrejection 监听promise对象未catch的错误  * 4、vue.errorHandler 监听vue脚本错误  * 5、自定义错误 包括接口错误 或其他diy错误  * 上报事件: page_monitor  */  // 错误类别常量  const ERROR_TYPE = {      JS_ERROR: 'js_error',      IMG_ERROR: 'img_error',      AUDIO_ERROR: 'audio_error',      SCRIPT_ERROR: 'script_error',      VIDEO_ERROR: 'video_error',      VUE_ERROR: 'vue_error',      PROMISE_ERROR: 'promise_unhandledrejection_error',      AJAX_ERROR: 'ajax_error'  }  const MONITOR_NAME = 'page_monitor'  const PAY_MONITOR_NAME = 'page_h5_pay_monitor'  const MEMBER_PAY_MONITOR_NAME = 'page_member_pay_monitor'  export const BindErrorReporter = function(error) {      if (error) {          if (error.error) {              const { colno, lineno } = error              const { message, stack } = error.error              // 过滤              // 客户端会有调用calljs的场景 可能有一些未知的calljs              if (message && message.toLowerCase().indexOf('calljs') !== -1) {                  return              }              sa.track(MONITOR_NAME, {              //属性              })          } else if (error.target) {              const type = error.target.nodeName.toLowerCase()              const monitorType = type + '_error'              const src = error.target.src              sa.track(MONITOR_NAME, {              //属性              })          }      }  }  export const UnhandledRejectionReporter = function({      isAjax = false,      method,      data,      params,      url,      headers,      message,      stack,      code  }) {          if (!isAjax) {              // 过滤一些特殊的场景              // 1、自动播放触发问题              if (message && message.toLowerCase().indexOf('user gesture') !== -1) {                  return              }              sa.track(MONITOR_NAME, {                  //属性              })          } else {              sa.track(MONITOR_NAME, {                  //属性              })          }      }      export const VueErrorReporter = function({ message, stack, vuehook }) {      sa.track(MONITOR_NAME, {          //属性      })  }  export const H5PayErrorReport = ({      isSuccess = true,      amount = 0,      type = -1,      couponId = -1,      couponName = '',      discountAmount = 0,      reason = '',      orderid = 0,  }) => {      // 事件名:page_member_pay_monitor      sa.track(PAY_MONITOR_NAME, {          //属性      })  }

以上,通过sentry的sa.track进行上报,具体不作展开

输出与预警

数据被上报到大数据平台,被存储到hdfs中,然后我们直接做定时任务读取hdfs进行一定的过滤通过钉钉webhook输出到钉钉群,另外如果有需要做数据备份可以通过hdfs到数据仓库再到kylin进行存储。

总结

数据监控对于大的,特别是涉及营收的平台是必要的,我们在设计项目的时候一定要考虑到,最好能说服服务端,让他们服务端也提供相应的代码监控。ngnix层或者云端最好也来一层。严重的异常可以直接给你打电话,目前云平台都有相应支持。这样有异常及时发现,锅嘛,接到手里就可以精准扔出去了。

本文转载于:

https://juejin.cn/post/7244363578429030459

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 记录--百分百空手接大锅