[关闭]
@hucheng91 2020-04-26T00:21:42.000000Z 字数 3708 阅读 1168

SourceMap还原之Vue Demo 实现

未分类


上篇写完后,有人留言说不知道在 Vue 中怎么实现这个功能,所以我写了这篇,会讲2种方式来实现

首先是在 build 后把 sourcemap文件上传到 cdn 或者静态资源服务器上去
我这里写个简单的demo,在工业环境,应该是直接集成到持续集成脚本里去比较好

因为 Vue 全局捕获错误 Vue.config.errorHandler = function(error, vm, info){},只要在这个方法处理就好了,问题出在这里面的 error 和 window.onerror 吐出来的格式是不一样的,直接通过 source-map 包处理是搞不成的,映射关系映射不上去,一堆问题,需要用 TraceKit这个包来转换下就可以用了

大家知道 sentry是一个开源的 错误收集系统,其中有个功能就是 source-map 映射,他们有个开源的 npm 包 raven-js,是收集浏览器错误的, 已经集成了 TraceKit,raven-js 在下篇收集用户行为,辅助判断错误,还会出现,我这里就直接用这个包,在实际生产中没有特别需求,或者时间要求比较紧,可以直接使用这个包来上报错误

  1. import Raven from "raven-js";
  2. import axios from 'axios'
  3. const _ravenConfig = {
  4. release: "staging@2.0.0"
  5. }
  6. const raven = Raven.config("http://localhost:3000/xxx", _ravenConfig)
  7. raven.install();
  8. raven.setTransport(function(option){
  9. let data = {
  10. stack:option.data,
  11. msg:(()=>{
  12. let res = []
  13. let data = option.data
  14. data.exception && data.exception.values && data.exception.values.length && res.push(`${data.exception.values[0].type}:${data.exception.values[0].value}`)
  15. return res.join(';')
  16. })(),
  17. }
  18. axios.post('http://localhost:3000/sourcemap/reportErrorMessage',data);
  19. option.onSuccess();
  20. });
  21. function formatComponentName(vm) {
  22. if (vm.$root === vm) {
  23. return 'root instance';
  24. }
  25. var name = vm._isVue ? vm.$options.name || vm.$options._componentTag : vm.name;
  26. return (
  27. (name ? 'component <' + name + '>' : 'anonymous component') +
  28. (vm._isVue && vm.$options.__file ? ' at ' + vm.$options.__file : '')
  29. );
  30. }
  31. Vue.config.productionTip = false;
  32. const _oldOnError = Vue.config.errorHandler;
  33. Vue.config.errorHandler = function(error, vm, info) {
  34. const metaData = {};
  35. if (Object.prototype.toString.call(vm) === '[object Object]') {
  36. metaData.componentName = formatComponentName(vm);
  37. metaData.propsData = vm.$options.propsData;
  38. }
  39. if (typeof info !== 'undefined') {
  40. metaData.lifecycleHook = info;
  41. }
  42. Raven.captureException(error, {
  43. extra: metaData
  44. });
  45. if (typeof _oldOnError === 'function') {
  46. _oldOnError.call(this, error, vm, info);
  47. }
  48. }

在 setTransport 方法里上报处理好的的异常,异常信息如下

  1. {
  2. "stack": {
  3. "project": "xxx",
  4. "logger": "javascript",
  5. "platform": "javascript",
  6. "request": {
  7. "headers": {
  8. "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.122 Safari/537.36"
  9. },
  10. "url": "http://localhost:8080/"
  11. },
  12. "exception": {
  13. "values": [
  14. {
  15. "type": "Error",
  16. "value": "手动触发异常",
  17. "stacktrace": {
  18. "frames": [
  19. {
  20. "filename": "http://localhost:8080/js/chunk-vendors.f3f154f7.js",
  21. "lineno": 7,
  22. "colno": 85724,
  23. "function": "HTMLButtonElement.o",
  24. "in_app": true
  25. },
  26. {
  27. "filename": "http://localhost:8080/js/chunk-vendors.f3f154f7.js",
  28. "lineno": 7,
  29. "colno": 51758,
  30. "function": "HTMLButtonElement.Qo.i._wrapper",
  31. "in_app": true
  32. },
  33. {
  34. "filename": "http://localhost:8080/js/chunk-vendors.f3f154f7.js",
  35. "lineno": 7,
  36. "colno": 13484,
  37. "function": "HTMLButtonElement.n",
  38. "in_app": true
  39. },
  40. {
  41. "filename": "http://localhost:8080/js/chunk-vendors.f3f154f7.js",
  42. "lineno": 7,
  43. "colno": 11664,
  44. "function": "ne",
  45. "in_app": true
  46. },
  47. {
  48. "filename": "http://localhost:8080/js/main.d41a5444.js",
  49. "lineno": 1,
  50. "colno": 2356,
  51. "function": "a.makeError",
  52. "in_app": true
  53. }
  54. ]
  55. }
  56. }
  57. ],
  58. "mechanism": {
  59. "type": "generic",
  60. "handled": true
  61. }
  62. },
  63. "transaction": "http://localhost:8080/js/main.d41a5444.js",
  64. "trimHeadFrames": 0,
  65. "extra": {
  66. "componentName": "component <app>",
  67. "lifecycleHook": "v-on handler",
  68. "session:duration": 99540
  69. },
  70. "breadcrumbs": {
  71. "values": [
  72. {
  73. "timestamp": 1587858857.627,
  74. "category": "ui.click",
  75. "message": "body > div#app > button"
  76. },
  77. {
  78. "timestamp": 1587858857.627,
  79. "message": "before make error",
  80. "level": "log",
  81. "category": "console"
  82. }
  83. ]
  84. },
  85. "release": "staging@2.0.0",
  86. "event_id": "6df36620747042ccb71161de4c82e207"
  87. },
  88. "msg": "Error:手动触发异常"
  89. }

上面这个上报数据里面 exception.values 就包含了我们需要的信息,其他字段在下篇讲解

上报完成后,接下来就是服务端处理了 map文件,返回 map 对应的信息,服务端就主要用到 source-map 包,具体怎么处理也不是难事,对着 api 来就好,就不详细写,放到 demo 里,demo 在文末,最后效果如下,实现了一个最小闭环

image.png-14

通过浏览器映射

大家知道 我们构建好的js文件后面会跟一个 sourcemap的路径

image.png-25kB

通过 webpack 插件 SourceMapDevToolPlugin 把这个map 替换成内网才能访问的域名就好了,上报还是按照上面那样上报,处理映射就交给浏览器处理,唯一不足的是 看具体错误的打开 浏览器 控制台,不是太直观

demo 地址

https://github.com/hucheng91/fe-error-sourcemap

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注