前端监控系统设计

标签: 前端 监控 系统 | 发表时间:2021-12-28 10:27 | 作者:spider集控团队
出处:https://juejin.cn/frontend

前言: 创建一个可随意插拔的插件式前端监控系统

一、数据采集

1.异常数据

1.1 静态资源异常

使用window.addEventListener('error',cb)

由于这个方法会捕获到很多error,所以我们要从中筛选出静态资源文件加载错误情况,这里只监控了js、css、img

  // 捕获静态资源加载失败错误 js css img
window.addEventListener('error', e => {
    const target = e.targetl
    if (!target) return
    const typeName = e.target.localName;
    let sourceUrl = "";
    if (typeName === "link") {
        sourceUrl = e.target.href;
    } else if (typeName === "script" || typeName === "img") {
        sourceUrl = e.target.src;
    }

    if (sourceUrl) {
        lazyReportCache({
            url: sourceUrl,
            type: 'error',
            subType: 'resource',
            startTime: e.timeStamp,
            html: target.outerHTML,
            resourceType: target.tagName,
            paths: e.path.map(item => item.tagName).filter(Boolean),
            pageURL: getPageURL(),
        })
    }
}, true)

1.2 js错误

通过 window.onerror 获取错误发生时的行、列号,以及错误堆栈

生产环境需要上传打包后生成的map文件,利用source-map 对压缩后的代码文件和行列号得出未压缩前的报错行列数和源码文件

  // parseErrorMsg.js
const fs = require('fs');
const path = require('path');
const sourceMap = require('source-map');

export default async function parseErrorMsg(error) {
  const mapObj = JSON.parse(getMapFileContent(error.url))
  const consumer = await new sourceMap.SourceMapConsumer(mapObj)
  // 将 webpack://source-map-demo/./src/index.js 文件中的 ./ 去掉
  const sources = mapObj.sources.map(item => format(item))
  // 根据压缩后的报错信息得出未压缩前的报错行列数和源码文件
  const originalInfo = consumer.originalPositionFor({ line: error.line, column: error.column })
  // sourcesContent 中包含了各个文件的未压缩前的源码,根据文件名找出对应的源码
  const originalFileContent = mapObj.sourcesContent[sources.indexOf(originalInfo.source)]
  return {
    file: originalInfo.source,
    content: originalFileContent,
    line: originalInfo.line,
    column: originalInfo.column,
    msg: error.msg,
    error: error.error
  }
}

function format(item) {
  return item.replace(/(\.\/)*/g, '')
}

function getMapFileContent(url) {
  return fs.readFileSync(path.resolve(__dirname, `./dist/${url.split('/').pop()}.map`), 'utf-8')
}

1.3 自定义异常

通过console.error打印出来的,我们将其认为是自定义错误

使用 window.console.error 上报自定义异常信息

1.4 接口异常

  1. 当状态码异常时,上报异常
  2. 重写 onloadend 方法,当其 response 对象中 code 值不为 '000000' 时上报异常
  3. 重写 onerror 方法,当网络中断时无法触发 onload(end) 事件,会触发 onerror, 此时上报异常

1.5 监听未处理的promise错误

当Promise 被reject 且没有reject 处理器的时候,就会触发 unhandledrejection 事件

使用 window.addEventListener('unhandledrejection',cb)

2.性能数据

2.1 FP/FCP/LCP/CLS

chrome 开发团队提出了一系列用于检测网页性能的指标:

  • FP(first-paint),从页面加载开始到第一个像素绘制到屏幕上的时间
  • FCP(first-contentful-paint),从页面加载开始到页面内容的任何部分在屏幕上完成渲染的时间
  • LCP(largest-contentful-paint),从页面加载开始到最大文本块或图像元素在屏幕上完成渲染的时间
  • CLS(layout-shift),从页面加载开始和其 生命周期状态变为隐藏期间发生的所有意外布局偏移的累积分数


其中,前三个性能指标都可以直接通过  PerformanceObserver (PerformanceObserver 是一个性能监测对象,用于监测性能度量事件 )来获取。而CLS 则需要通过一些计算。

在了解一下计算方式之前,我们先了解一下会话窗口的概念:一个或多个布局偏移间,它们之间有少于1秒的时间间隔,并且第一个和最后一个布局偏移时间间隔上限为5秒,超过5秒的布局偏移将被划分到新的会话窗口。

Chrome 速度指标团队在完成 大规模分析后,将 所有会话窗口中的偏移累加最大值用来反映页面布局最差的情况(即CLS)。

如下图:会话窗口2只有一个微小的布局偏移,则会话窗口2会被忽略,CLS只计算会话窗口1中布局偏移的总和。

拉低平均值的小布局偏移示例

2.2 DOMContentLoaded事件 和  onload 事件

  • DOMContentLoaded: HTML文档被加载和解析完成。在文档中没有脚本的情况下,浏览器解析完文档便能触发DOMContentLoaded;当文档中有脚本时,脚本会阻塞文档的解析,而脚本需要等位于脚本前面的css加载完才能执行。但是在任何情况下,DOMContentLoaded 都不需要等图片等其他资源的解析。
  • onload: 需要等页面中图片、视频、音频等其他所有资源都加载后才会触发。

为什么我们在开发时强调把css放在头部,js放在尾部?

image.png

首先文件放置顺序决定下载的优先级,而浏览器为了避免样式变化导致页面重排or重绘,会阻塞内容的呈现,等所有css加载并解析完成后才一次性呈现页面内容,在此期间就会出现“白屏”。

而现代浏览器为了优化用户体验,无需等到所有HTML文档都解析完成才开始构建布局渲染树,也就是说浏览器能够渲染不完整的DOM tree和cssom,尽快减少白屏时间。

假设我们把js放在头部,js会阻塞解析dom,导致FP(First Paint)延后,所以我们将js放在尾部,以减少FP的时间,但不会减少 DOMContentLoaded 被触发的时间。

2.3 资源加载耗时及是否命中缓存情况

通过  PerformanceObserver 收集,当浏览器不支持 PerformanceObserver,还可以通过 performance.getEntriesByType(entryType) 来进行降级处理,其中:

  • Navigation Timing 收集了HTML文档的性能指标
  • Resource Timing 收集了文档依赖的资源的性能指标,如:css,js,图片等等

这里不统计以下资源类型:

  • beacon: 用于上报数据,不统计
  • xmlhttprequest:单独统计

我们能够获取到资源对象的如下信息:

image.png

使用performance.now()精确计算程序执行时间:

  • performance.now()  与 Date.now()  不同的是,返回了以微秒(百万分之一秒)为单位的时间,更加精准。并且与 Date.now()  会受系统程序执行阻塞的影响不同,performance.now()  的时间是以恒定速率递增的,不受系统时间的影响(系统时间可被人为或软件调整)。
  • Date.now()  输出的是 UNIX 时间,即距离 1970 的时间,而 performance.now()  输出的是相对于 performance.timing.navigationStart(页面初始化) 的时间。
  • 使用 Date.now()  的差值并非绝对精确,因为计算时间时受系统限制(可能阻塞)。但使用 performance.now()  的差值,并不影响我们计算程序执行的精确时间。

判断该资源是否命中缓存:
在这些资源对象中有一个 transferSize 字段,它表示获取资源的大小,包括响应头字段和响应数据的大小。如果这个值为 0,说明是从缓存中直接读取的(强制缓存)。如果这个值不为 0,但是 encodedBodySize 字段为 0,说明它走的是协商缓存(encodedBodySize 表示请求响应数据 body 的大小)。不符合以上条件的,说明未命中缓存。

2.4 接口请求耗时以及接口调用成败情况

对XMLHttpRequest 原型链上的send 以及open方法进行改写

  import { originalOpen, originalSend, originalProto } from '../utils/xhr'
import { lazyReportCache } from '../utils/report'

function overwriteOpenAndSend() {
    originalProto.open = function newOpen(...args) {
        this.url = args[1]
        this.method = args[0]
        originalOpen.apply(this, args)
    }

    originalProto.send = function newSend(...args) {
        this.startTime = Date.now()

        const onLoadend = () => {
            this.endTime = Date.now()
            this.duration = this.endTime - this.startTime

            const { duration, startTime, endTime, url, method } = this
            const { readyState, status, statusText, response, responseUrl, responseText } = this
            console.log(this)
            const reportData = {
                status,
                duration,
                startTime,
                endTime,
                url,
                method: (method || 'GET').toUpperCase(),
                success: status >= 200 && status < 300,
                subType: 'xhr',
                type: 'performance',
            }

            lazyReportCache(reportData)

            this.removeEventListener('loadend', onLoadend, true)
        }

        this.addEventListener('loadend', onLoadend, true)
        originalSend.apply(this, args)
    }
}

export default function xhr() {
    overwriteOpenAndSend()
}

二、数据上报

1. 上报方法

采用 sendBeacon 和 XMLHttpRequest 相结合的方式

为什么要使用sendBeacon?

统计和诊断代码通常要在 unload 或者 beforeunload (en-US) 事件处理器中发起一个同步 XMLHttpRequest 来发送数据。同步的 XMLHttpRequest 迫使用户代理延迟卸载文档,并使得下一个导航出现的更晚。下一个页面对于这种较差的载入表现无能为力。
navigator.sendBeacon()  方法可用于通过HTTP将少量数据异步传输到Web服务器,同时不会延迟页面的卸载或影响下一导航的载入性能。这就解决了提交分析数据时的所有的问题:数据可靠,传输异步并且不会影响下一页面的加载。

2. 上报时机

  1. 先缓存上报数据,缓存到一定数量后,利用 requestIdleCallback/setTimeout 延时上报。
  2. 在即将离开当前页面(刷新或关闭)时上报 (onBeforeUnload )/ 在页面不可见时上报(onVisibilitychange,判断document.visibilityState/ document.hidden 状态)

相关 [前端 监控 系统] 推荐:

前端性能监控系统ShowSlow

- - CSDN博客Web前端推荐文章
作者:zhanhailiang 日期:2014-11-14. ShowSlow是开源的前端性能监控系统,提供了以下功能:. 前端性能指标数据收集功能:ShowSlow原生提供了数据收集工具. DOM Monster!,但也支持通过YSlow,PageSpeed等第三方工具将性能数据上报给服务端完成收集(其服务器端提供了针对多达8种不同工具上报的数据收集器dommonster,dynatrace,events,har,metric,pagespeed,webpagetest,yslow);.

搭建前端异常监控系统

- - 掘金 架构
收集前端错误(原生、React、Vue). 利用Egg.js编写一个错误日志采集服务. 编写webpack插件自动上传sourcemap. 利用sourcemap还原压缩代码源码位置. 代码上线打包将sourcemap文件上传至错误监控服务器. 发生错误时监控服务器接收错误并记录到日志中. 根据sourcemap和错误日志内容进行错误分析.

前端监控系统设计

- - 掘金 前端
前言: 创建一个可随意插拔的插件式前端监控系统. 使用window.addEventListener('error',cb). 由于这个方法会捕获到很多error,所以我们要从中筛选出静态资源文件加载错误情况,这里只监控了js、css、img. // 捕获静态资源加载失败错误 js css img window.addEventListener('error', e => {.

Linux系统监控

- - CSDN博客系统运维推荐文章
查看所有的进程和端口使用情况:. 查看nginx并发(连接数)进程数:. 查看当网络连接状态中,已建立连接的数量:. 查看系统tcp连接中各个状态的连接数. 输出每个ip的连接数,以及总的各个状态的连接数. df -hl 查看磁盘使用情况 . df -hl 查看磁盘剩余空间. df -h 查看每个根路径的分区大小.

开源监控系统 Shinken

- Le - 开源中国社区最新软件
Shinken是一款类似于Nagios的开源监控工具,由scratch重新设计和重写. 它的主要目的是迎合当前系统监控的需要同时具备Nagios一样的能力.

常用的linux系统监控命令

- Mountain - agapple
记录一下自己常用的linux系统命令,方便以后查阅,发觉记忆越来越不行了. 找到最耗CPU的java线程. 命令:ps -mp pid -o THREAD,tid,time   或者  ps -Lfp pid. 这个命令的作用,主要是可以获取到对应一个进程下的线程的一些信息. 比如你想分析一下一个java进程的一些运行瓶颈点,可以通过该命令找到所有当前Thread的占用CPU的时间,也就是这里的最后一列.

ZoneMinder 1.25 发布,视频监控系统

- 迎客松 - LinuxEden开源社区-Linux伊甸园
ZoneMinder的作者是因為家中的車庫被竊,因而產生設計監控保全的念頭. ZoneMinder使用了Linux Server,PHP,MySQL加上幾支攝影機,就可以使用web介面監控重要場所. 當異常事件發生時,你就可以收到e-mail或簡訊通知. 阅读全文 | 邮件推荐 | 评论回复.

搭建完美的监控系统

- - DCCMX
对于任何一个互联网公司来说,监控系统都是不可或缺的. 监控系统的完善程度,直接影响到系统的稳定程度,性能等各个方面. 关于监控系统的搭建,网上已经有了很多方案,在规划公司的监控体系的时候,我也参考了很多解决方案. 比较常见的有:nagios,cacti,icinga,zabbix,ganglia,还有收费的监控服务有如newrelic等.

Linux系统和性能监控

- - zzm
CPU性能表现如何一般从三个方面来衡量:运行队列、利用率和上下文切换. 正如前文所提及的,性能表现的好坏和基线数据(或预期)是密不可分的. 对大部分系统而言,一些基本的性能预期如下:. 运行队列——每个处理器运行队列中不应该超过1-3个线程. 例如,一个双核的系统中,运行队列长度不应该超过6. (译注:即一个系统的load average值不应该大于核数的4倍.

Nagios 监控系统架设全攻略

- - IBM developerWorks 中国 : 文档库
Nagios 全名为(Nagios Ain’t Goona Insist on Saintood),最初项目名字是 NetSaint. 它是一款免费的开源 IT 基础设施监控系统,其功能强大,灵活性强,能有效监控 Windows 、Linux、VMware 和 Unix 主机状态,交换机、路由器等网络设置等.