index.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import { errorStore, ErrorInfo } from '/@/store/modules/error';
  2. import { useSetting } from '/@/hooks/core/useSetting';
  3. import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
  4. import { App } from 'vue';
  5. function processStackMsg(error: Error) {
  6. if (!error.stack) {
  7. return '';
  8. }
  9. let stack = error.stack
  10. .replace(/\n/gi, '') // 去掉换行,节省传输内容大小
  11. .replace(/\bat\b/gi, '@') // chrome中是at,ff中是@
  12. .split('@') // 以@分割信息
  13. .slice(0, 9) // 最大堆栈长度(Error.stackTraceLimit = 10),所以只取前10条
  14. .map((v) => v.replace(/^\s*|\s*$/g, '')) // 去除多余空格
  15. .join('~') // 手动添加分隔符,便于后期展示
  16. .replace(/\?[^:]+/gi, ''); // 去除js文件链接的多余参数(?x=1之类)
  17. const msg = error.toString();
  18. if (stack.indexOf(msg) < 0) {
  19. stack = msg + '@' + stack;
  20. }
  21. return stack;
  22. }
  23. function formatComponentName(vm: any) {
  24. if (vm.$root === vm) {
  25. return {
  26. name: 'root',
  27. path: 'root',
  28. };
  29. }
  30. const options = vm.$options as any;
  31. if (!options) {
  32. return {
  33. name: 'anonymous',
  34. path: 'anonymous',
  35. };
  36. }
  37. const name = options.name || options._componentTag;
  38. return {
  39. name: name,
  40. path: options.__file,
  41. };
  42. }
  43. function vueErrorHandler(err: Error, vm: any, info: string) {
  44. const { name, path } = formatComponentName(vm);
  45. errorStore.commitErrorInfoState({
  46. type: ErrorTypeEnum.VUE,
  47. name,
  48. file: path,
  49. message: err.message,
  50. stack: processStackMsg(err),
  51. detail: info,
  52. url: window.location.href,
  53. });
  54. }
  55. export function scriptErrorHandler(
  56. event: Event | string,
  57. source?: string,
  58. lineno?: number,
  59. colno?: number,
  60. error?: Error
  61. ) {
  62. if (event === 'Script error.' && !source) {
  63. return false;
  64. }
  65. setTimeout(function () {
  66. const errorInfo: Partial<ErrorInfo> = {};
  67. colno = colno || (window.event && (window.event as any).errorCharacter) || 0;
  68. errorInfo.message = event as string;
  69. if (error && error.stack) {
  70. errorInfo.stack = error.stack;
  71. } else {
  72. errorInfo.stack = '';
  73. }
  74. const name = source ? source.substr(source.lastIndexOf('/') + 1) : 'script';
  75. errorStore.commitErrorInfoState({
  76. type: ErrorTypeEnum.SCRIPT,
  77. name: name,
  78. file: source as string,
  79. detail: 'lineno' + lineno,
  80. url: window.location.href,
  81. ...(errorInfo as Pick<ErrorInfo, 'message' | 'stack'>),
  82. });
  83. }, 0);
  84. return true;
  85. }
  86. function registerPromiseErrorHandler() {
  87. window.addEventListener(
  88. 'unhandledrejection',
  89. function (event: any) {
  90. errorStore.commitErrorInfoState({
  91. type: ErrorTypeEnum.PROMISE,
  92. name: 'Promise Error!',
  93. file: 'none',
  94. detail: 'promise error!',
  95. url: window.location.href,
  96. stack: 'promise error!',
  97. message: event.reason,
  98. });
  99. },
  100. true
  101. );
  102. }
  103. function registerResourceErrorHandler() {
  104. // 监控资源加载错误(img,script,css,以及jsonp)
  105. window.addEventListener(
  106. 'error',
  107. function (e: Event) {
  108. const target = e.target ? e.target : (e.srcElement as any);
  109. errorStore.commitErrorInfoState({
  110. type: ErrorTypeEnum.RESOURCE,
  111. name: 'Resouce Error!',
  112. file: (e.target || ({} as any)).currentSrc,
  113. detail: JSON.stringify({
  114. tagName: target.localName,
  115. html: target.outerHTML,
  116. type: e.type,
  117. }),
  118. url: window.location.href,
  119. stack: 'resouce is not found',
  120. message: (e.target || ({} as any)).localName + ' is load error',
  121. });
  122. },
  123. true
  124. );
  125. }
  126. export function setupErrorHandle(app: App) {
  127. const { projectSetting } = useSetting();
  128. const { useErrorHandle } = projectSetting;
  129. if (!useErrorHandle) {
  130. return;
  131. }
  132. // Vue异常监控;
  133. app.config.errorHandler = vueErrorHandler;
  134. // js错误
  135. window.onerror = scriptErrorHandler;
  136. // promise 异常
  137. registerPromiseErrorHandler();
  138. // 静态资源异常
  139. registerResourceErrorHandler();
  140. }