一、背景:
为了使系统更加稳定,在用户使用期间,若发现异常,可及时应对,采取了“报警机制”。
通常“报警机制”分为2种,一种是后端对api监控及自定义监控,出现异常,通过钉钉或邮件的形式通知,第二种是前端对js语法,vue语法,自定义报错进行监控,以此来规范代码质量,保证系统预警
二、流程步骤
1. 收集错误(错误类型包含 vue错误 + js 错误 + Promise 错误 + 自定义错误)
2. 关联钉钉
3. 错误信息发送
三、前期准备内容
1. 钉钉软件,自定义机器人接入,文档链接
2. 签名计算(前后端联调,统一加密方式)
四、参考代码
1. 收集错误阶段(main.js)
1 import { createApp } from 'vue' 2 import { errorHandler, detectOS, digits, getBrowserInfo, format } from '@/assets/scripts/errorPlugin' // 收集错误信息 3 4 const app = createApp(App) 5 // 1. 用于组件生命周期中的错误、自定义事件处理函数内部错误、v-on DOM 监听器内部抛出的错误、处理返回 Promise 链的错误 6 app.config.errorHandler = errorHandler 7 // 2. 处理 JS 的额外错误 8 // eslint-disable-next-line max-params 9 window.onerror = function (message, source, line, column, error) { 10 // 了解文档: https://juejin.cn/post/6844904093979246606 11 if (message === 'ResizeObserver loop limit exceeded') { 12 console.warn('Ignored: ResizeObserver loop limit exceeded') 13 return false 14 } 15 if (message == 'cancel') return false 16 let errMsg = null 17 if (message == 'Script error.') { 18 // 跨域 19 errMsg = ` 20 --infoType: JS 无法访问, 请在控制台查看具体错误 21 --apName : 用户端-${process.env.NODE_ENV === 'development' ? '测试环境' : '生产环境'} 22 --url: ${window.location.href} 23 --browser:${detectOS()}-${digits()} ${getBrowserInfo()} 24 --time: ${format('yyyy-MM-dd hh:mm:ss')} 25 --userInfo: ${sessionStorage.getItem('AI_INFO')} 26 --info: 浏览器跨域请求一个脚本执行出错 27 ` 28 return false 29 } 30 // ------排除这两个文件错误信息的检查开始----- 31 let noNeedFile = ['app', 'contextMenuFilter'] 32 let noContinue = false 33 noNeedFile.map((res) => { 34 if (source.indexOf(res) != -1) noContinue = true 35 }) 36 console.log('排除文件了') 37 if (noContinue) return false 38 console.log('没排除文件') 39 // ------排除这两个文件错误信息的检查结束----- 40 errMsg = ` 41 --infoType: JS 错误 42 --apName : 用户端-${process.env.NODE_ENV === 'development' ? '测试环境' : '生产环境'} 43 --url: ${window.location.href} 44 --browser:${detectOS()}-${digits()} ${getBrowserInfo()} 45 --time: ${format('yyyy-MM-dd hh:mm:ss')} 46 --userInfo: ${sessionStorage.getItem('AI_INFO')} 47 --info: ${message}-${source}-${JSON.stringify(error)} 48 ` 49 errorHandler(errMsg, null, 'JS错误') 50 } 51 // 3. 处理 Promise 错误 52 window.addEventListener('unhandledrejection', (event) => { 53 console.log('event', event.reason) 54 // 全局存在的未处理的 Promise 异常,比如: Promise.reject() 55 // 场景: 接口异常 56 if (event.reason == 'cancel') return false 57 let errMsg = ` 58 --infoType: 捕获Promise异常 59 --apName : 用户端-${process.env.NODE_ENV === 'development' ? '测试环境' : '生产环境'} 60 --url: ${window.location.href} 61 --browser:${detectOS()}-${digits()} ${getBrowserInfo()} 62 --time: ${format('yyyy-MM-dd hh:mm:ss')} 63 --userInfo: ${sessionStorage.getItem('AI_INFO')} 64 --errorInfo: ${JSON.stringify(event.reason)} 65 ` 66 67 errorHandler(errMsg, null, 'Promise错误') 68 }) 69 app.mount('#app')View Code
2. 关联钉钉(src\assets\scripts\robot.js)
View Code3. 发送错误信息到钉钉软件(src\assets\scripts\errorPlugin.js)
import ChatBot from './robot.js' export const errorHandler = (err, vm, info) => { let token = sessionStorage.getItem('AI_TOKEN') || null if (!token) return let errInfo = null if (info !== 'JS错误' || info !== 'Promise错误') { errInfo = ` --infoType: vue异常错误 --apName : 用户端-${process.env.NODE_ENV === 'development' ? '测试环境' : '生产环境'} --url: ${window.location.href} --browser:${detectOS()}-${digits()} ${getBrowserInfo()} --time: ${format('yyyy-MM-dd hh:mm:ss')} --userInfo: ${sessionStorage.getItem('AI_INFO')} --errorInfo: ${err}-${JSON.stringify(info)} ` } else { errInfo = err } // 将捕获的错误, 通过钉钉报警 robotDD(errInfo) } const robotDD = (errMsg) => { const robot = new ChatBot({ webhook: 'https://oapi.dingtalk.com/robot/send?access_token=***', secret: '***' }) // 规定发送的消息的类型和参数 let textContent = { msgtype: 'text', text: { content: errMsg // 注意了,字符串里面的错误汉字,其实就是你在钉钉报警设置的自定义字段,两个地方需要相同,否则不会发送到群里 } } // 机器人发送消息 robot .send(textContent) .then((res) => { console.error(res) }) .catch(() => { console.log('钉钉报警错误') // ElMessage.error({ // message: `钉钉报警错误` // }) }) } export const format = (fmt) => { //author: meizz var o = { 'M+': new Date().getMonth() + 1, //月份 'd+': new Date().getDate(), //日 'h+': new Date().getHours(), //小时 'm+': new Date().getMinutes(), //分 's+': new Date().getSeconds(), //秒 'q+': Math.floor((new Date().getMonth() + 3) / 3), //季度 S: new Date().getMilliseconds() //毫秒 } if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (new Date().getFullYear() + '').substr(4 - RegExp.$1.length)) for (var k in o) if (new RegExp('(' + k + ')').test(fmt)) fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)) return fmt } /** * 初始化设备信息 */ export const initDeviceInfo = () => { let _deviceInfo = '' //设备信息 console.log(navigator, 'navigator') if (navigator == null) { _deviceInfo = 'PC' } if (navigator.userAgent != null) { var su = navigator.userAgent.toLowerCase(), mb = ['ipad', 'iphone os', 'midp', 'rv:1.2.3.4', 'ucweb', 'android', 'windows ce', 'windows mobile'] // 开始遍历提前设定好的设备关键字,如果设备信息中包含关键字,则说明是该设备 for (var i in mb) { if (su.indexOf(mb[i]) > 0) { _deviceInfo = mb[i] break } } } return _deviceInfo } /** * 获取浏览器的信息 */ export const getBrowserInfo = () => { var output = 'other' // Opera 8.0+ var isOpera = (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0 if (isOpera) { output = 'Opera' } // Firefox 1.0+ var isFirefox = typeof InstallTrigger !== 'undefined' if (isFirefox) { output = 'Firefox' } // Safari 3.0+ "[object HTMLElementConstructor]" var isSafari = /constructor/i.test(window.HTMLElement) || (function (p) { return p.toString() === '[object SafariRemoteNotification]' })(!window['safari'] || (typeof safari !== 'undefined' && safari.pushNotification)) if (isSafari) { output = 'Safari' } // Internet Explorer 6-11 var isIE = /*@cc_on!@*/ false || !!document.documentMode if (isIE) { output = 'IE' } // Edge 20+ var isEdge = !isIE && !!window.StyleMedia if (isEdge) { output = 'Edge' } // Chrome 1 - 79 var isChrome = !!window.chrome && navigator.userAgent.indexOf('Chrome') !== -1 if (isChrome) { output = 'Chrome' } // Edge (based on chromium) detection var isEdgeChromium = isChrome && navigator.userAgent.indexOf('Edg') !== -1 if (isEdgeChromium) { output = 'EdgeChromium' } return output } export const detectOS = () => { var userAgent = window.navigator.userAgent, platform = window.navigator.platform, macosPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'], windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE'], iosPlatforms = ['iPhone', 'iPad', 'iPod'], os = null if (macosPlatforms.indexOf(platform) !== -1) { os = 'Mac OS' } else if (iosPlatforms.indexOf(platform) !== -1) { os = 'iOS' } else if (windowsPlatforms.indexOf(platform) !== -1) { os = 'Windows' } else if (/Android/.test(userAgent)) { os = 'Android' } else if (!os && /Linux/.test(platform)) { os = 'Linux' } return os } export const digits = () => { var sUserAgent = navigator.userAgent var is64 = sUserAgent.indexOf('WOW64') > -1 if (is64) { return '64bit' } else { return '32bit' } }View Code
五、最终实现效果
标签:vue,return,错误,语法错误,--,window,var,navigator From: https://www.cnblogs.com/gqx-html/p/17629344.html