干货满满!如何做好前端日志和异常监控

干货满满!如何做好前端日志和异常监控在研发过程中 日志是非常重要的一环 它可以帮助我们快速定位问题 解决问题 在前端开发中 日志也是非常重要的一环 它可以帮助我们快速定位问题 解决问题 本文将介绍前端日志的规范和最佳实践 但是我们经常看到一些项目日志打得满天飞 但是到了真正定

大家好,欢迎来到IT知识分享网。

在研发过程中,日志是非常重要的一环,它可以帮助我们快速定位问题,解决问题。在前端开发中,日志也是非常重要的一环,它可以帮助我们快速定位问题,解决问题。本文将介绍前端日志的规范和最佳实践。但是我们经常看到一些项目日志打得满天飞,但是到了真正定位问题的时候,发现日志并没有什么卵用。这是因为日志打得不规范,不规范的日志是没有意义的。所以我们需要规范日志的打印,才能让日志发挥最大的作用。

那么,我们首先就要思考一下,打印什么样的日志才是有助于定位前端问题的,我想,可以从我们真是定位用户反馈问题的场景来思考。

通常,前端用户反馈的问题大概有以下几种:

  1. 页面加载慢
  2. 页面渲染错乱
  3. 页面白屏等交互异常
  4. 页面崩溃
  5. 页面卡顿

下面,我们可以基于这些场景,来思考一下,我们应该打印什么样的日志,才能帮助我们快速定位问题。

业务异常日志

页面加载慢

对于页面加载慢,这个问题,我们可以通过performance对象来获取页面加载的性能数据,然后打印出来,比如:

javascript复制代码window.addEventListener('load', function() { setTimeout(function() { var timing = window.performance.timing; var loadTime = timing.loadEventEnd - timing.navigationStart; var pageUrl = window.location.href; console.log('Page load time for ' + pageUrl + ' is ' + loadTime + ' milliseconds.'); }, 0); }); 

这样,我们就可以在页面加载完成之后,打印出页面加载时间,这样我们就可以通过日志来定位页面加载慢的问题。

通常,日志里面可以穿插一些告警,比如,我们这里可以加上一个判断,如果页面加载时间超过了3秒,我们就打印一个告警,比如:

javascript复制代码window.addEventListener('load', function() { setTimeout(function() { var timing = window.performance.timing; var loadTime = timing.loadEventEnd - timing.navigationStart; var pageUrl = window.location.href; console.log('Page load time for ' + pageUrl + ' is ' + loadTime + ' milliseconds.'); if (loadTime > 3000) { console.warn('Page load time for ' + pageUrl + ' is ' + loadTime + ' milliseconds, it is too slow.'); //todo 上报到监控系统 reportToMonitor('Page load time for ' + pageUrl + ' is ' + loadTime + ' milliseconds, it is too slow.'); } }, 0); }); 

页面渲染错乱

对于页面渲染错乱,造成这个问题的原因有很多,比如网络问题、代码问题、浏览器兼容问题等等,这个问题比较复杂,我们可以通过一些手段来定位这个问题,比如:

这个问题,我们可以通过window.onerror来做,从里面区出渲染错误的问题,比如:

javascript复制代码window.onerror = function(message, source, lineno, colno, error) { console.error('Error: ' + message + ' Script: ' + source + ' Line: ' + lineno + ' Column: ' + colno + ' StackTrace: ' + error.stack); //分析渲染错误 parseAndLogRenderError(message, source, lineno, colno, error); }; // 举例:分析渲染错误 function parseAndLogRenderError(message, source, lineno, colno, error) { if (message.indexOf('render error') > -1) { console.error('Render error: ' + message + ' Script: ' + source + ' Line: ' + lineno + ' Column: ' + colno + ' StackTrace: ' + error.stack); //todo 上报到监控系统 reportToMonitor('Render error: ' + message + ' Script: ' + source + ' Line: ' + lineno + ' Column: ' + colno + ' StackTrace: ' + error.stack); } } 

当然,window.onerror 只能捕获到一部分错误,我们还需要结合 window.addEventListener(‘error’, function(event) {}) 来捕获一些资源加载错误,比如:

javascript复制代码window.addEventListener('error', function(event) { console.error('Error: ' + event.message + ' Script: ' + event.filename + ' Line: ' + event.lineno + ' Column: ' + event.colno + ' StackTrace: ' + event error.stack); parseAndLogRenderError(event.message, event.filename, event.lineno, event.colno, event.error); }); 

页面交互异常

这里的页面交互异常,通常是指用户在页面上进行一些操作的时候,出现了一些异常,比如点击按钮无反应、输入框无法输入等等,这个问题,我们可以通过一些手段来定位。

先说一说点击按钮无反应的问题。

假如说,我们有一个这样的场景,用户点击一个按钮,理论上点击按钮会发送一个请求,成功失败可能都会有一个界面上的反馈,但是如何点击之后,界面没有任何的反馈,这个时候就,我们基本上可以判定,这种时候就是页面交互异常了。那么,我们该如何捕捉这种异常呢?

我们可以通过window.addEventListener(‘click’, function(event) {})来捕捉用户的点击事件,然后在里面做一些判断,比如:

javascript复制代码 // 设置一个标志位,当按钮被点击时,将标志位设置为 true。 var isButtonClicked = false; window.addEventListener('click', function(event) { var target = event.target; if (target.tagName === 'BUTTON') { console.log('User clicked button: ' + target.innerText); // 设置标志位 isButtonClicked = true; } }); // 创建一个 observer 实例 var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { // 如果页面有变化并且按钮被点击过,重置标志位 // 这里的mutations 可能需要根据实际情况来判断,具体要结合自己业务逻辑来判断 if(mutation.type === 'childList' || mutation.type === 'attributes') { if (isButtonClicked) { console.log('Page change detected after button click.'); isButtonClicked = false; } } }); }); // 配置观察选项: var config = { attributes: true, childList: true, characterData: true, subtree: true }; // 传入目标节点和观察选项 observer.observe(document.body, config); // 启动定时器 setInterval(function() { if (isButtonClicked) { console.log('Button click exception detected.'); // 上报到监控系统 reportToMonitor('Button click exception detected'); // 重置标志位 isButtonClicked = false; } }, 5000); // 5秒后检查标志位 

页面白屏的问题

页面白屏的问题,通常是指用户打开页面之后,页面长时间没有任何的反应,这个问题,我们可以通过一些手段来定位。

我们可以通过window.addEventListener(‘DOMContentLoaded’, function() {})来捕捉页面的加载事件,然后在里面做一些判断,比如:

javascript复制代码window.addEventListener('DOMContentLoaded', function() { setTimeout(function() { if (document.body.innerHTML === '') { console.error('Page is blank.'); // 上报到监控系统 reportToMonitor('Page is blank.'); } }, 3000); // 3秒后检查页面是否为空 }); 

或者,我们可以通过window.addEventListener(‘load’, function() {})来捕捉页面的加载事件,然后在里面做一些判断,比如:通过oberver监听页面变化,如果页面变化了,就说明页面没有白屏.

javascript复制代码window.addEventListener('load', function() { var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { console.log('Page change detected.'); // 上报到监控系统 reportToMonitor('Page change detected.'); }); }); var config = { attributes: true, childList: true, characterData: true, subtree: true }; observer.observe(document.body, config); }); 

页面卡顿

页面卡顿的问题,通常是指用户在页面上进行一些操作的时候,页面出现了卡顿的现象,我们先来分析一下,页面卡顿的原因。

页面卡顿的原因,通常有以下几种:

  1. 页面渲染性能问题
  2. 页面交互性能问题
  3. 页面资源加载性能问题
  4. 页面网络性能问题 等等,不排除可能还有其他的原因,但是这里我们只列举了一些常见的原因。
  5. 内存泄漏,这通常是元凶

那么,我们该如何捕捉页面卡顿的问题呢?

我们可以通过window.requestAnimationFrame来捕捉页面的渲染性能问题,比如:

javascript复制代码 var lastTime = 0; var vendors = ['webkit', 'moz']; for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; } if (!window.requestAnimationFrame) { window.requestAnimationFrame = function(callback, element) { var currTime = new Date().getTime(); var timeToCall = Math.max(0, 16.7 - (currTime - lastTime)); var id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall); lastTime = currTime + timeToCall; return id; }; } if (!window.cancelAnimationFrame) { window.cancelAnimationFrame = function(id) { clearTimeout(id); }; } var lastFrameTime = 0; var frameCount = 0; var frameRate = 0; var frameRateThreshold = 60; function onAnimationFrame() { var now = Date.now(); var elapsed = now - lastFrameTime; lastFrameTime = now; frameCount++; if (elapsed > 1000) { frameRate = frameCount; frameCount = 0; if (frameRate < frameRateThreshold) { console.warn('Frame rate is ' + frameRate + 'fps, it is too low.'); // 上报到监控系统 reportToMonitor('Frame rate is ' + frameRate + 'fps, it is too low.'); } } window.requestAnimationFrame(onAnimationFrame); } window.requestAnimationFrame(onAnimationFrame); 

这样,我们就可以通过window.requestAnimationFrame来捕捉页面的渲染性能问题。

对于内存泄漏,我们可以监听页面卸载事件,然后检查所有的事件处理器和定时器是否都已经被清除,比如:

javascript复制代码 // 存储所有的事件处理器和定时器 var eventHandlers = []; var timers = []; // 覆盖 addEventListener 和 removeEventListener 方法 var originalAddEventListener = EventTarget.prototype.addEventListener; EventTarget.prototype.addEventListener = function(eventName, eventHandler) { eventHandlers.push({target: this, eventName: eventName, eventHandler: eventHandler}); originalAddEventListener.call(this, eventName, eventHandler); }; var originalRemoveEventListener = EventTarget.prototype.removeEventListener; EventTarget.prototype.removeEventListener = function(eventName, eventHandler) { eventHandlers = eventHandlers.filter(function(handler) { return handler.target !== this || handler.eventName !== eventName || handler.eventHandler !== eventHandler; }); originalRemoveEventListener.call(this, eventName, eventHandler); }; // 覆盖 setTimeout 和 clearTimeout 方法 var originalSetTimeout = window.setTimeout; window.setTimeout = function(callback, delay) { var id = originalSetTimeout(callback, delay); timers.push(id); return id; }; var originalClearTimeout = window.clearTimeout; window.clearTimeout = function(id) { timers = timers.filter(function(timer) { return timer !== id; }); originalClearTimeout(id); }; // 在页面卸载时检查所有的事件处理器和定时器是否都已经被清除 window.addEventListener('unload', function() { if (eventHandlers.length > 0) { console.error('Memory leak detected: Not all event handlers were removed.'); // 上报到监控系统 reportToMonitor('Memory leak detected: Not all event handlers were removed.'); } if (timers.length > 0) { console.error('Memory leak detected: Not all timers were cleared.'); // 上报到监控系统 reportToMonitor('Memory leak detected: Not all timers were cleared.'); } }); 

用户行为日志

用户行为日志是指用户在页面上的一些操作,比如点击按钮、输入框输入等等,这些操作都是用户行为日志,这些日志是非常重要的,它可以帮助我们快速定位问题。比如,用户反馈说,我点击了一个按钮,但是没有反应,这个时候,我们就可以通过用户行为日志来定位问题。但是通常用户将的可能是问题出现的时间点,而不是问题出现的原因,所以我们需要在用户行为日志中加入一些额外的信息。用户经历过哪些路由页面,做过哪些交互,各交互步骤的数据是否正常,这些都是我们需要记录的。

对于页面的路由,我们可以通过window.addEventListener(‘hashchange’, function() {})来捕捉页面的路由变化,然后在里面打印一些日志,比如:

javascript复制代码window.addEventListener('hashchange', function() { console.log('User navigated to ' + window.location.hash); }); 

对于用户的点击事件,我们可以通过window.addEventListener(‘click’, function(event) {})来捕捉用户的点击事件,然后在里面打印一些日志,比如:

javascript复制代码window.addEventListener('click', function(event) { var target = event.target; if (target.tagName === 'BUTTON') { console.log('User clicked button: ' + target.innerText); } }); 

对于用户的输入事件,我们可以通过window.addEventListener(‘input’, function(event) {})来捕捉用户的输入事件,然后在里面打印一些日志,比如:

javascript复制代码window.addEventListener('input', function(event) { var target = event.target; if (target.tagName === 'INPUT') { console.log('User input: ' + target.value); } }); 

对于一些用户的交互事件,我们可以通过window.addEventListener(‘customEvent’, function(event) {})来捕捉用户的交互事件,然后在里面打印一些日志,比如:

javascript复制代码// 触发自定义事件 window.dispatchEvent(new CustomEvent('customEvent', {detail: 'User interaction detected.'})); // 监听自定义事件 window.addEventListener('customEvent', function(event) { console.log('User custom event: ' + event.detail); }); // 触发自定义事件,我们可以做一个工具函数,比如:这阿姨给你方便在任何地方触发自定义事件 function triggerCustomEvent(eventName, detail) { window.dispatchEvent(new CustomEvent(eventName, {detail: detail})); } 

一些情况下,前端页面的交互是通过后端返回的数据来触发的,这个时候,数据的正确性也是非常重要的,我们可以通过window.addEventListener(‘ajaxSuccess’, function(event) {})来捕捉用户的交互事件,然后在里面打印一些日志,比如:

javascript复制代码// 添加一个响应拦截器 axios.interceptors.response.use(function (response) { // 请求成功 // 请求成功 if (response.data.code !== 0) { console.error('Request success, but response data is not as expected.'); triggerCustomEvent('request abnormal', 'Request success, but response data is not as expected.'); } triggerCustomEvent('request Success', response.data); return response; }, function (error) { // 请求失败 console.error(error); triggerCustomEvent('request Error', 'Ajax request error.'); return Promise.reject(error); }); 

日志规范

我们还差一些什么,我们怎么知道打一些日志,但是怎么评估这些日志是否非常容易帮助我们定位问题呢?换句话说,当用户反馈问题的时候,我们怎么知道我们的日志是否能帮助我们快速定位问题呢?

一个场景:用户A,通过反馈说,你们页面加载好慢啊。然后就没有其他信息了。这个时候,我们需要去从日志中发现一些问题。

我们会问用户一个问题,比如用户的uid,然后我们就可以通过uid去查找用户的日志,然后我们就可以通过用户的日志来定位问题。

那么,我们就需要在日志中加入一些用户的信息,比如用户的uid,用户的设备信息,用户的网络信息等等,这样我们就可以通过用户的日志来定位问题。因此,我们需要在日志中加入一些用户的信息,比如:

javascript复制代码 // 获取用户的uid var uid = getUid(); // 获取用户的设备信息 var deviceInfo = getDeviceInfo(); // 获取用户的网络信息 var networkInfo = getNetworkInfo(); // 打印环境信息 console.log('User: ' + uid + ' Device: ' + deviceInfo + ' Network: ' + networkInfo + ' Page load time for ' + pageUrl + ' is ' + loadTime + ' milliseconds.'); 

日志上报

有了收集的日志之后,我们上报到日志系统,那么这个日志系统应该是怎么样的呢?现在,我们可以使用 mermaid 来绘制一下整个日志系统的架构图:

上报存储分析告警前端日志日志系统日志存储日志分析日志告警

这块后端可能有一些开源的日志系统,比如 ELK、Logstash、Kibana、Prometheus、Grafana 等等,这些都是比较常见的日志系统,我们可以根据自己的需求来选择。

比如,我们可以上报到ELK,然后通过Kibana来分析日志,通过Prometheus、Grafana来做一些监控。这些的搭建和使用,这里就不展开了。这里最终是需要提供前端日志的上报接口,然后后端来接收这些日志。

然后前端的日志上报,我们可以通过一些手段来做,比如:

javascript复制代码function reportToMonitor(log) { // 上报到监控系统 var img = new Image(); img.src = 'http://monitor.com/report?log=' + log; } 

这里为什么要用img标签来上报日志呢?因为img标签是不会阻塞页面的,而且可以跨域,这样我们就可以通过img标签来上报日志。

另外,我们还可以通过navigator.sendBeacon来上报日志,这个方法是异步的,不会阻塞页面,比如:

javascript复制代码function reportToMonitor(log) { // 上报到监控系统 navigator.sendBeacon('http://monitor.com/report', log); } 

然后,为了保证日志的可靠性,我们还可以通过localStorage来存储日志,然后在下一次用户访问的时候,再上报日志,比如:

javascript复制代码function reportToMonitor(log) { // 上报到监控系统 if (navigator.sendBeacon) { navigator.sendBeacon('http://monitor.com/report', log); } else { localStorage.setItem('log', log); } } 

有些人可能会说,我还需要做一些日志的压缩和加密,这个时候,我们可以通过pako来做日志的压缩,通过CryptoJS来做日志的加密,比如:

javascript复制代码function reportToMonitor(log) { // 上报到监控系统 var compressedLog = pako.deflate(log, { to: 'string' }); var encryptedLog = CryptoJS.AES.encrypt(compressedLog, 'secret key').toString(); var img = new Image(); img.src = 'http://monitor.com/report?log=' + encryptedLog; } 

有人可能会讲,频繁的发送请求,可能会导致一些性能问题,能不能积攒一批日志,然后再发送呢?这个时候,我们可以通过setTimeout来做,比如:

javascript复制代码var logs = []; // 存储日志 function reportToMonitor(log) { // 存储日志 logs.push(log); // 延迟发送日志 setTimeout(function() { var img = new Image(); img.src = 'http://monitor.com/report?log=' + logs.join(','); logs = []; }, 5000); // 5秒后发送日志 } 

结构化日志

我们有了上述日志之后就,针对一个具体用户进行搜索,看到可能是这样的一些个日志:

  • User: Device: iPhone 6s Network: 4G enter page /home 2024-02-12 12:00:00
  • User: Device: iPhone 6s Network: 4G Page load time for /home is 3000 milliseconds 2024-02-12 12:00:03
  • User: Device: iPhone 6s Network: 4G User clicked button: submit 2024-02-12 12:00:05
  • User: Device: iPhone 6s Network: 4G User input[id=userName] : zhangsan 2024-02-12 12:00:06
  • User: Device: iPhone 6s Network: 4G User input[id=password] : 2024-02-12 12:00:07
  • User: Device: iPhone 6s Network: 4G User clicked button: login 2024-02-12 12:00:08
  • User: Device: iPhone 6s Network: 4G Request success, url: /login. 2024-02-12 12:00:12
  • User: Device: iPhone 6s Network: 4G User navigated to /dashboard 2024-02-12 12:00:13
  • User: Device: iPhone 6s Network: 4G Page load time for /dashboard is 3000 milliseconds 2024-02-12 12:00:16
  • User: Device: iPhone 6s Network: 4G Request Failed, url: /dashboard. {errMessage: ‘Internal Server Error’} 2024-02-12 12:00:20

这样,我们就可以通过用户的日志来定位问题。

日志图形化

比如,我们可以将用户的日志,使用 mermaid 来画一下这个流程图:

3000ms3000msenter page /homeUser clicked button: submitUser input username: zhangsanUser input password: User clicked button: loginRequest success, url: /loginUser navigated to /dashboardRequest Failed, url: /dashboard

篇幅有限,顺着思路,本来想在写一些监控相关的内容,但是感觉篇幅有点长了,就到这里吧。后续在继续写一些监控相关的

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/123721.html

(0)
上一篇 2024-11-21 15:33
下一篇 2024-11-21 15:45

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信