比较全的前端监控体系搭建篇(长文预警)

2023-05-27 0 783

后端开发网志领27本图书

须知,自助式秒进后端群

源代码门牌号:https://github.com/miracle90/monitor

1. 为何要做后端监视 后端开发网志

更慢的发现问题和解决问题做产品的理论依据为销售业务扩充提供更多了更多几率提高后端技师的控制技术广度和广度,打造出个人简历看点

2. 后端监视最终目标

2.1 灵活性 stability

js严重错误:js继续执行严重错误、promise极度天然资源严重错误:js、css天然资源读取极度USB严重错误:ajax、fetch允诺USB极度CM3:网页盲点

2.2 使用者新体验 experience

2.3 销售业务 business

pv:网页下载量和点击量uv:出访某一公交站点的不同ip的数目使用者在每两个网页的等待时间

还能看一看:小厂的后端CM3监视软件系统

3. 后端监视业务流程

后端埋点统计数据呈报研磨汇整建模展现监视报案比较全的前端监控体系搭建篇(长文预警)image.png

3.1 常用的埋点计划

3.1.1 标记符埋点内嵌标记符的方式优点:准确(任一关键时刻,信息量全面性)缺点:标记符工作效率点3.1.2 建模埋点透过建模可视化的方式,替代标记符埋大列佩季哈区销售业务标记符和埋点标记符分立,提供更多两个建模可视化的网页,输入为销售业务标记符,透过这个控制系统,能在销售业务代码中自订的增加埋点该事件之类,最终输入的标记符谐振了销售业务标记符和埋点标记符用控制系统来替代纯手工填入埋点标记符3.1.3 科婷埋点后端的任一两个该事件被存取两个标记,大部份的该事件都被纪录下来透过定期上传记录文件,配合文件解析,解析出来我们想要的统计数据,并生成建模报告供专业人员分析科婷埋点的缺点是采集全量统计数据,不会出现漏埋和误埋等现象缺点是给统计数据传输和服务器增加压力,也无法灵活定制统计数据结构

4. 编写采集脚本

4.1 接入日志控制系统

各公司一般都有自己的日志控制系统,接收统计数据呈报,例如:阿里云

4.2 监视严重错误

4.2.1 严重错误分类js严重错误(js继续执行严重错误,promise极度)天然资源读取极度:监听error4.2.2 统计数据结构分析1. jsError

{

    “title”“后端监视控制系统”

, // 网页标题

    “url”“http://localhost:8080/”

, // 网页URL

    “timestamp”“1590815288710”

, // 出访时间戳

    “userAgent”“Chrome”

, // 使用者浏览器类型

    “kind”“stability”

, // 大类

    “type”“error”

, // 小类

    “errorType”“jsError”

, // 严重错误类型

    “message”“Uncaught TypeError: Cannot set property error of undefined”

, // 类型详情

    “filename”“http://localhost:8080/”

, // 出访的文件名

    “position”“0:0”

, // 行列信息

    “stack”“btnClick (http://localhost:8080/:20:39)^HTMLInputElement.onclick (http://localhost:8080/:14:72)”

, // 堆栈信息

    “selector”“HTML BODY #container .content INPUT”

 // 选择器

}

2. promiseError

{

    …

    “errorType”“promiseError”

,//严重错误类型 

    “message”“someVar is not defined”

,//类型详情

    “stack”“http://localhost:8080/:24:29^new Promise (<anonymous>)^btnPromiseClick (http://localhost:8080/:23:13)^HTMLInputElement.onclick (http://localhost:8080/:15:86)”

,//堆栈信息

    “selector”“HTML BODY #container .content INPUT”

//选择器

}

3. resourceError

    …

    “errorType”“resourceError”

,//严重错误类型

    “filename”“http://localhost:8080/error.js”

,//出访的文件名

    “tagName”“SCRIPT”

,//标签名

    “timeStamp”“76”

,//时间 

4.2.3 实现1. 天然资源读取严重错误 + js继续执行严重错误

//一般JS运行时严重错误使用window.onerror捕获处理

window.addEventListener(

  “error”

,

  function

 (event) {

    let

 lastEvent = getLastEvent();

    // 有 e.target.src(href) 的认定为天然资源读取严重错误

    if

(event.target && (event.target.src || event.target.href)) {

      tracker.send({

        //天然资源读取严重错误

        kind: “stability”

, //灵活性指标

        type“error”

, //resource

        errorType: “resourceError”

,

        filename: event.target.src || event.target.href, //读取失败的天然资源

tagName: event.target.tagName, //标签名

        timeStamp: formatTime(event.timeStamp), //时间

selector: getSelector(event.path || event.target), //选择器

      });

    } else

 {

      tracker.send({

        kind: “stability”

, //灵活性指标

        type“error”

, //error

        errorType: “jsError”

, //jsError

        message: event.message, //报错信息

        filename: event.filename, //报错链接

position: (event.lineNo || 0) +“:”

 + (event.columnNo || 0), //行列号

        stack: getLines(event.error.stack), //严重错误堆栈

selector: lastEvent

          ? getSelector(lastEvent.path || lastEvent.target)

          : “”

, //CSS选择器

      });

    }

  },

  true); // true代表在捕获阶段调用,false代表在冒泡阶段捕获,使用truefalse

都能

2. promise极度

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

window.addEventListener(

  “unhandledrejection”

,

  function

 (event) {

    let

lastEvent = getLastEvent();

    let message = “”

;

    let

 line = 0;

    let

 column = 0;

    let file = “”

;

    let stack = “”

;

    if (typeof event.reason === “string”

) {

      message = event.reason;

    } else if (typeof event.reason === “object”

) {

message = event.reason.message;

    }

    let

 reason = event.reason;

    if (typeof reason === “object”

) {

      if

 (reason.stack) {

var matchResult = reason.stack.match(/at\s+(.+):(\d+):(\d+)/);

        if

 (matchResult) {

          file = matchResult[1];

line = matchResult[2];

          column = matchResult[3];

        }

        stack = getLines(reason.stack);

      }

    }

    tracker.send({

      //未捕获的promise严重错误

      kind: “stability”

, //灵活性指标

      type“error”

, //jsError

      errorType: “promiseError”

, //unhandledrejection

message: message, //标签名

      filename: file,

      position: line + “:”

 + column, //行列

      stack,

      selector: lastEvent

? getSelector(lastEvent.path || lastEvent.target)

        : “”

,

    });

  },

  true); // true代表在捕获阶段调用,false代表在冒泡阶段捕获,使用truefalse

都能

4.3 USB极度采集脚本

4.3.1 统计数据设计

{

  “title”“后端监视控制系统”

, //标题

  “url”“http://localhost:8080/”

, //url

  “timestamp”“1590817024490”

, //timestamp

  “userAgent”“Chrome”

, //浏览器版本

  “kind”“stability”

, //大类

  “type”“xhr”

, //小类

  “eventType”“load”

, //该事件类型

  “pathname”“/success”

, //路径

  “status”“200-OK”

, //状态码

  “duration”“7”

, //持续时间

  “response”“{“id“:1}”

, //响应内容

  “params”“”

 //参数

}

{

  “title”“后端监视控制系统”

,

  “url”“http://localhost:8080/”

,

  “timestamp”“1590817025617”

,

  “userAgent”“Chrome”

,

  “kind”“stability”

,

  “type”“xhr”

,

  “eventType”“load”

,

  “pathname”“/error”

,

  “status”“500-Internal Server Error”

,

  “duration”“7”

,

  “response”“”

,

  “params”“name=zhufeng”

}

4.3.2 实现

使用webpack devServer模拟允诺

重写xhr的open、send方法监听load、error、abort该事件import tracker from “../util/tracker”

;

export function injectXHR

() {

  let

 XMLHttpRequest = window.XMLHttpRequest;

  let

oldOpen = XMLHttpRequest.prototype.open;

  XMLHttpRequest.prototype.open = function

 (

    method,

    url,

    async,

    username,

    password

  ) {

    // 呈报的USB不用处理

    if

(!url.match(/logstores/) && !url.match(/sockjs/)) {

      this.logData = {

        method,

        url,

        async,

        username,

        password,

      };

}

    return

 oldOpen.apply(this, arguments);

  };

  let

 oldSend = XMLHttpRequest.prototype.send;

  let

 start;

XMLHttpRequest.prototype.send =function

 (body) {

    if

 (this.logData) {

      start = Date.now();

      let handler = (type

) => (event) => {

        let

 duration = Date.now() – start;

        let

 status = this.status;

        let

 statusText = this.statusText;

tracker.send({

          //未捕获的promise严重错误

          kind: “stability”

, //灵活性指标

          type“xhr”

, //xhr

          eventType: type

, //load error abort

          pathname: this.logData.url, //USB的url门牌号

          status: status + “-“

 + statusText,

          duration: “”

+ duration, //接口耗时

          response: this.response ? JSON.stringify(this.response) : “”

,

          params: body || “”

,

        });

      };

this.addEventListener(“load”, handler(“load”), false

);

      this.addEventListener(“error”, handler(“error”), false

);

this.addEventListener(“abort”, handler(“abort”), false

);

    }

    oldSend.apply(this, arguments);

  };

}

4.4 CM3

CM3就是网页上什么都没有4.4.1 统计数据设计

{

  “title”“后端监视控制系统”

,

  “url”“http://localhost:8080/”

,

  “timestamp”“1590822618759”

,

  “userAgent”“chrome”

,

  “kind”“stability”

, //大类

  “type”“blank”

, //小类

  “emptyPoints”“0”

, //盲点点

  “screen”“2049×1152”

, //分辨率

  “viewPoint”“2048×994”

, //视口

  “selector”“HTML BODY #container”

 //选择器

}

4.4.2 实现elementsFromPoint[1]“../util/tracker”

;

import onload from “../util/onload”

;

function

 getSelector(element) {

  var selector;

  if

(element.id) {

    selector = `#${element.id}`;  } else if (element.className && typeof element.className === “string”

) {

selector =

      “.”

 +

      element.className

        .split(” “

)

        .filter(function

 (item) {

          return

 !!item;

        })

        .join(“.”

);

  } else

 {

selector = element.nodeName.toLowerCase();

  }

  return

 selector;

}

export function blankScreen

() {

const wrapperSelectors = [“body”“html”“#container”“.content”

];

  let

 emptyPoints = 0;

  function

 isWrapper(element) {

    let

 selector = getSelector(element);

    if

 (wrapperSelectors.indexOf(selector) >= 0) {

      emptyPoints++;

    }

  }

onload(function

 () {

    let

 xElements, yElements;

    debugger;

    for (let

 i = 1; i <= 9; i++) {

xElements = document.elementsFromPoint(

        (window.innerWidth * i) / 10,

        window.innerHeight / 2

      );

yElements = document.elementsFromPoint(

        window.innerWidth / 2,

        (window.innerHeight * i) / 10

      );

      isWrapper(xElements[0]);

isWrapper(yElements[0]);

    }

    if

 (emptyPoints >= 0) {

      let

 centerElements = document.elementsFromPoint(

        window.innerWidth / 2,

window.innerHeight / 2

      );

      tracker.send({

        kind: “stability”

,

        type“blank”

,

        emptyPoints: “”

 + emptyPoints,

screen: window.screen.width +“x”

 + window.screen.height,

        viewPoint: window.innerWidth + “x”

+ window.innerHeight,

        selector: getSelector(centerElements[0]),

      });

    }

  });

}

//screen.width  屏幕的宽度   screen.height 屏幕的高度

//window.innerWidth 去除工具条与滚动条的窗口宽度 window.innerHeight 去除工具条与滚动条的窗口高度

4.5 读取时间

PerformanceTiming[2]DOMContentLoaded[3]FMP[4]4.5.1 阶段含义比较全的前端监控体系搭建篇(长文预警)image.png字段含义navigationStart初始化网页,在同两个浏览器上下文中前两个网页unload的时间戳,如果没有前两个网页的unload,则与fetchStart值相等redirectStart第两个HTTP重定向发生的时间,有跳转且是同域的重定向,否则为0redirectEnd最终两个重定向完成时的时间,否则为0fetchStaEndDNS域名结束查询的时间connectStartTCP开始建立连接的时间,如果是持久连接,则与fetchStart值相等secureConnectionStarthttps 连接开始的时间,如果不是安全连接则为0connectEndTCP完成握手的时间,如果是持久连接则与fetchStart值相等requestStartHTTP允诺读取真实文档开始的时间,包括从本地缓存读取requestEndHTTP允诺读取真实文档结束的时间,包括从本地缓存读取responseStart返回浏览器从服务器收到(或从本地缓存读取)第两个字节时的Unix毫秒时间戳responseEnd返回浏览器从服务器收到(或从本地缓存读取,或从本地天然资源读取)最终两个字节时的Unix毫秒时间戳unloadEventStart前两个网页的unload的时间戳 如果没有则为0unloadEventEnd与unloadEventStart相对应,返回的是unload函数继续执行完成的时间戳domLoading返回当前网页DOM结构开始解析时的时间戳,此时document.readyState变成loading,并将抛出readyStateChange该事件domInteractive返回当前网页DOM结构结束解析、开始读取内嵌天然资源时时间戳,document.readyState 变成interactive,并将抛出readyStateChange该事件(注意只是DOM树解析完成,这时候并没有开始读取网页内的天然资源)domContentLoadedEventStart网页domContentLoaded该事件发生的时间domContentLoadedEventEnd网页domContentLoaded该事件脚本继续执行完毕的时间,domReady的时间domCompleteDOM树解析完成,且天然资源也准备就绪的时间,document.readyState变成complete.并将抛出readystatechange该事件loadEventStartload 该事件发送给文档,也即load回调函数开始继续执行的时间loadEventEndload回调函数继续执行完成的时间4.5.2 阶段计算字段描述计算方式意义unload前两个网页卸载耗时unloadEventEnd – unloadEventStart-redirect重定向耗时redirectEnd – redirectStart重定向的时间appCache缓存耗时domainLookupStart – fetchStart读取缓存的时间dnsDNS 解析耗时domainLookupEnd – domainLookupStart可观察域名解析服务是否正常tcpTCP 连接耗时connectEnd – connectStart建立连接的耗时sslSSL 安全连接耗时connectEnd – secureConnectionStart反映统计数据安全连接建立耗时ttfbTime to First Byte(TTFB)网络允诺耗时responseStart – requestStartTTFB是发出网页允诺到接收到应答统计数据第两个字节所花费的毫秒数response响应统计数据传输耗时responseEnd – responseStart观察网络是否正常domDOM解析耗时domInteractive – responseEnd观察DOM结构是否合理,是否有JS阻塞网页解析dclDOMContentLoaded 该事件耗时domContentLoadedEventEnd – domContentLoadedEventStart当 HTML 文档被完全读取和解析完成之后,DOMContentLoaded 该事件被触发,无需等待样式表、图像和子框架的完成读取resources天然资源读取耗时domComplete – domContentLoadedEventEnd可观察文档流是否过大domReadyDOM阶段渲染耗时domContentLoadedEventEnd – fetchStartDOM树和网页天然资源读取完成时间,会触发domContentLoaded该事件首次渲染耗时首次渲染耗时responseEnd-fetchStart读取文档到看到第一帧非空图像的时间,也叫CM3时间首次可可视化时间首次可可视化时间domInteractive-fetchStartDOM树解析完成时间,此时document.readyState为interactive首包时间耗时首包时间responseStart-domainLookupStartDNS解析到响应返回给浏览器第两个字节的时间网页完全读取时间网页完全读取时间loadEventStart – fetchStart-onLoadonLoad该事件耗时loadEventEnd – loadEventStart比较全的前端监控体系搭建篇(长文预警)image.png4.5.3 统计数据结构

{

  “title”“后端监视控制系统”

,

  “url”“http://localhost:8080/”

,

  “timestamp”“1590828364183”

,

  “userAgent”“chrome”

,

  “kind”“experience”

,

  “type”“timing”

,

  “connectTime”“0”

,

  “ttfbTime”“1”

,

  “responseTime”“1”

,

  “parseDOMTime”“80”

,

  “domContentLoadedTime”“0”

,

  “timeToInteractive”“88”

,

  “loadTime”“89”

}

4.5.4 实现import onload from “../util/onload”

;

import tracker from “../util/tracker”

;

import formatTime from “../util/formatTime”

;

import getLastEvent from “../util/getLastEvent”

;

import getSelector from“../util/getSelector”

;

export function timing

() {

  onload(function

 () {

setTimeout(() => {

      const {

        fetchStart,

        connectStart,

        connectEnd,

        requestStart,

        responseStart,

        responseEnd,

        domLoading,

domInteractive,

        domContentLoadedEventStart,

        domContentLoadedEventEnd,

        loadEventStart,

} = performance.timing;

      tracker.send({

        kind: “experience”

,

        type“timing”

,

        connectTime: connectEnd – connectStart, //TCP连接耗时

ttfbTime: responseStart – requestStart, //ttfb

        responseTime: responseEnd – responseStart, //Response响应耗时

parseDOMTime: loadEventStart – domLoading, //DOM解析渲染耗时

        domContentLoadedTime:

domContentLoadedEventEnd – domContentLoadedEventStart, //DOMContentLoaded该事件回调耗时

timeToInteractive: domInteractive – fetchStart, //首次可可视化时间

        loadTime: loadEventStart – fetchStart, //完整的读取时间

      });

    }, 3000);

  });

}

4.6 性能指标

PerformanceObserver.observe[5]方法用于观察传入的参数中指定的性能条目类型的集合。当记录两个指定类型的性能条目时,性能监测对象的回调函数将会被调用entryType[6]paint-timing[7]event-timing[8]LCP[9]FMP[10]time-to-interactive[11]字段描述备注计算方式FPFirst Paint(首次绘制)包括了任何使用者自订的背景绘制,它是首先将像素绘制到屏幕的关键时刻FCPFirst Content Paint(首次内容绘制)是浏览器将第两个 DOM 渲染到屏幕的时间,可能是文本、图像、SVG等,这其实就是CM3时间FMPFirst Meaningful Paint(首次有意义绘制)网页有意义的内容渲染的时间LCP(Largest Contentful Paint)(最大内容渲染)代表在viewport中最大的网页元素读取的时间DCL(DomContentLoaded)(DOM读取完成)当 HTML 文档被完全读取和解析完成之后, DOMContentLoaded 该事件被触发,无需等待样式表、图像和子框架的完成读取L(onLoad)当依赖的天然资源全部读取完毕之后才会触发TTI(Time to Interactive) 可可视化时间用于标记应用已进行视觉渲染并能可靠响应使用者输入的时间点FIDFirst Input Delay(首次输入延迟)使用者首次和网页可视化(单击链接,点击按钮等)到网页响应可视化的时间比较全的前端监控体系搭建篇(长文预警)image.png比较全的前端监控体系搭建篇(长文预警)image.png4.6.1 统计数据结构设计1. paint

{

  “title”“后端监视控制系统”

,

  “url”“http://localhost:8080/”

,

  “timestamp”“1590828364186”

,

  “userAgent”“chrome”

,

  “kind”“experience”

,

  “type”“paint”

,

  “firstPaint”“102”

,

  “firstContentPaint”“2130”

,

  “firstMeaningfulPaint”“2130”

,

  “largestContentfulPaint”“2130”

}

2. firstInputDelay

{

  “title”“后端监视控制系统”

,

  “url”“http://localhost:8080/”

,

  “timestamp”“1590828477284”

,

  “userAgent”“chrome”

,

  “kind”“experience”

,

  “type”“firstInputDelay”

,

  “inputDelay”“3”

,

  “duration”“8”

,

  “startTime”“4812.344999983907”

,

  “selector”“HTML BODY #container .content H1”

}

4.6.2 实现

关键时间节点透过window.

比较全的前端监控体系搭建篇(长文预警)image.pngimport tracker from “../utils/tracker”

;

import onload from “../utils/onload”

;

import getLastEvent from “../utils/getLastEvent”

;

import getSelector from “../utils/getSelector”

;

export function timing

() {

  let

 FMP, LCP;

  // 增加两个性能条目的观察者

  new PerformanceObserver((entryList, observer) => {

const perfEntries = entryList.getEntries();

    FMP = perfEntries[0];

    observer.disconnect(); // 不再观察了

}).observe({ entryTypes: [“element”

] }); // 观察网页中有意义的元素

  // 增加两个性能条目的观察者

new PerformanceObserver((entryList, observer) => {

    const perfEntries = entryList.getEntries();

const lastEntry = perfEntries[perfEntries.length – 1];

    LCP = lastEntry;

    observer.disconnect(); // 不再观察了

  }).observe({ entryTypes: [“largest-contentful-paint”

] }); // 观察网页中最大的元素

  // 增加两个性能条目的观察者

  new PerformanceObserver((entryList, observer) => {

const lastEvent = getLastEvent();

    const firstInput = entryList.getEntries()[0];

    if

 (firstInput) {

// 开始处理的时间 – 开始点击的时间,差值就是处理的延迟

      let

 inputDelay = firstInput.processingStart – firstInput.startTime;

      let

duration = firstInput.duration; // 处理的耗时

      if

 (inputDelay > 0 || duration > 0) {

        tracker.send({

          kind: “experience”

, // 使用者新体验指标

          type“firstInputDelay”

, // 首次输入延迟

          inputDelay: inputDelay ? formatTime(inputDelay) : 0, // 延迟的时间

duration: duration ? formatTime(duration) : 0,

          startTime: firstInput.startTime, // 开始处理的时间

selector: lastEvent

            ? getSelector(lastEvent.path || lastEvent.target)

            : “”

,

        });

      }

    }

    observer.disconnect(); // 不再观察了

}).observe({type“first-input”, buffered: true

 }); // 第一次可视化

  // 刚开始网页内容为空,等网页渲染完成,再去做判断

  onload(function

 () {

setTimeout(() => {

      const {

        fetchStart,

        connectStart,

        connectEnd,

        requestStart,

        responseStart,

        responseEnd,

domLoading,

        domInteractive,

        domContentLoadedEventStart,

        domContentLoadedEventEnd,

        loadEventStart,

} = window.performance.timing;

      // 发送时间指标

      tracker.send({

        kind: “experience”

, // 使用者新体验指标

        type“timing”

, // 统计每个阶段的时间

connectTime: connectEnd – connectStart, // TCP连接耗时

        ttfbTime: responseStart – requestStart, // 首字节到达时间

responseTime: responseEnd – responseStart, // response响应耗时

parseDOMTime: loadEventStart – domLoading, // DOM解析渲染的时间

        domContentLoadedTime:

domContentLoadedEventEnd – domContentLoadedEventStart, // DOMContentLoaded该事件回调耗时

        timeToInteractive: domInteractive – fetchStart, // 首次可可视化时间

loadTime: loadEventStart – fetchStart, // 完整的读取时间

      });

      // 发送性能指标

      let FP = performance.getEntriesByName(“first-paint”

)[0];

      letFCP = performance.getEntriesByName(“first-contentful-paint”

)[0];

      console.log(“FP”

, FP);

      console.log(“FCP”

, FCP);

console.log(“FMP”

, FMP);

      console.log(“LCP”

, LCP);

      tracker.send({

        kind: “experience”

,

        type“paint”

,

firstPaint: FP ? formatTime(FP.startTime) : 0,

        firstContentPaint: FCP ? formatTime(FCP.startTime) : 0,

firstMeaningfulPaint: FMP ? formatTime(FMP.startTime) : 0,

        largestContentfulPaint: LCP

? formatTime(LCP.renderTime || LCP.loadTime)

          : 0,

      });

    }, 3000);

  });

}

4.7 卡顿

响应使用者可视化的响应时间如果大于100ms,使用者就会感觉卡顿4.7.1 统计数据设计 longTask

{

  “title”“后端监视控制系统”

,

  “url”“http://localhost:8080/”

,

  “timestamp”“1590828656781”

,

  “userAgent”“chrome”

,

  “kind”“experience”

,

  “type”“longTask”

,

  “eventType”“mouseover”

,

  “startTime”“9331”

,

  “duration”“200”

,

  “selector”“HTML BODY #container .content”

}

4.7.2 实现new PerformanceObserverentry.duration > 100 判断大于100ms,即可认定为长任务使用 requestIdleCallback呈报统计数据import tracker from“../util/tracker”

;

import formatTime from “../util/formatTime”

;

import getLastEvent from “../util/getLastEvent”

;

import getSelector from “../util/getSelector”

;

export function longTask

() {

  new PerformanceObserver((list) => {

list.getEntries().forEach((entry) => {

      if

 (entry.duration > 100) {

        let

 lastEvent = getLastEvent();

        requestIdleCallback(() => {

          tracker.send({

kind:“experience”

,

            type“longTask”

,

            eventType: lastEvent.type,

startTime: formatTime(entry.startTime), // 开始时间

            duration: formatTime(entry.duration), // 持续时间

            selector: lastEvent

? getSelector(lastEvent.path || lastEvent.target)

              : “”

,

          });

        });

      }

    });

  }).observe({ entryTypes: [“longtask”

] });

}

4.8 PV、UV、使用者等待时间

4.8.1 统计数据设计 business

{

  “title”“后端监视控制系统”

,

  “url”“http://localhost:8080/”

,

  “timestamp”“1590829304423”

,

  “userAgent”“chrome”

,

  “kind”“business”

,

  “type”“pv”

,

  “effectiveType”“4g”

,

  “rtt”“50”

,

  “screen”“2049×1152”

}

4.8.2 PV、UV、使用者等待时间

PV(page view) 是网页下载量,UV(Unique visitor)使用者出访量。PV 只要出访一次网页就算一次,UV 同一天内多次出访只算一次。

对于后端来说,只要每次进入页面呈报一次 PV 就行,UV 的统计放在服务端来做,主要是分析呈报的统计数据来统计得出 UV。

import tracker from “../util/tracker”

;

export function pv

() {

  tracker.send({

    kind: “business”

,

    type“pv”

,

    startTime: performance.now(),

    pageURL: getPageURL(),

referrer: document.referrer,

    uuid: getUUID(),

  });

  let

 startTime = Date.now();

  window.addEventListener(

    “beforeunload”

,

    () => {

      let

 stayTime = Date.now() – startTime;

      tracker.send({

        kind: “business”

,

        type“stayTime”

,

stayTime,

        pageURL: getPageURL(),

        uuid: getUUID(),

      });

    },

    false

  );

}

扩充问题

性能监视指标后端怎么做性能监视线上严重错误监视怎么做导致内存泄漏的方法,怎么监视内存泄漏Node 怎么做性能监视1. 性能监视指标指标名称解释FPFirst-Paint 首次渲染表示浏览器从开始允诺网站到屏幕渲染第两个像素点的时间FCPFirst-Contentful-Paint 首次内容渲染表示浏览器渲染出第两个内容的时间,这个内容能是文本、图片或SVG元素之类,不包括iframe和白色背景的canvas元素SISpeed Index 速度指数表明了网页内容的可见填充速度LCPLargest Contentful Paint 最大内容绘制标记了渲染出最大文本或图片的时间TTITime to Interactive 可可视化时间网页从开始读取到主要子天然资源完成渲染,并能够快速、可靠的响应使用者输入所需的时间TBTTotal Blocking Time 总阻塞时间测量 FCP 与 TTI 之间的总时间,这期间,主线程被阻塞的时间过长,无法作出输入响应FIDFirst Input Delay 首次输入延迟测量读取响应度的两个以使用者为中心的重要指标CLSCumulative Layout Shift 累积布局偏移测量的是整个网页生命周期内发生的大部份意外布局偏移中最大一连串的布局偏移分数DCLDOMContentLoaded当初始的 HTML 文档被完全读取和解析完成之后,DOMContentLoaded 该事件被触发,而无需等待样式表、图像和子框架的完成读取LLoad检测两个完全读取的网页,网页的html、css、js、图片等天然资源都已经读取完之后才会触发 load 该事件2. 后端怎么做性能监视

FP、FCP、LCP、CLS、FID、FMP 可透过 PerformanceObserver 监听 longTaskconst {

    fetchStart,

    connectStart,

    connectEnd,

    requestStart,

    responseStart,

    responseEnd,

domLoading,

    domInteractive,

    domContentLoadedEventStart,

    domContentLoadedEventEnd,

    loadEventStart,

} = window.performance.timing;

const obj = {

    kind: “experience”

, // 使用者新体验指标

    type“timing”

, // 统计每个阶段的时间

dnsTime: domainLookupEnd – domainLookupStart, // DNS查询时间

    connectTime: connectEnd – connectStart, // TCP连接耗时

ttfbTime: responseStart – requestStart, // 首字节到达时间

responseTime: responseEnd – responseStart, // response响应耗时

    parseDOMTime: loadEventStart – domLoading, // DOM解析渲染的时间

    domContentLoadedTime:

domContentLoadedEventEnd – domContentLoadedEventStart, // DOMContentLoaded该事件回调耗时

timeToInteractive: domInteractive – fetchStart, // 首次可可视化时间

    loadTime: loadEventStart – fetchStart, // 完整的读取时间

}

比较全的前端监控体系搭建篇(长文预警)image.png3. 线上严重错误监视怎么做天然资源读取严重错误 window.addEventListener(error) 判断e.target.src || hrefjs运行时严重错误 window.addEventListener(error)promise极度 window.addEventListener(unhandledrejection)USB极度 重写xhr 的 open send方法,监视 load、error、abort,进行呈报4. 导致内存泄漏的方法,怎么监视内存泄漏全局变量被遗忘的定时器脱离Dom的引用闭包

监视内存泄漏

window.performance.memory

开发阶段

浏览器的 Performance移动端可使用 PerformanceDog比较全的前端监控体系搭建篇(长文预警)image.png5. Node 怎么做性能监视

日志监视能透过监视极度日志的变动,将新增的极度类型和数量反映出来 监视日志能实现pv和uv的监视,透过pv/uv的监视,能知道使用者们的使用习惯,预知出访高峰

响应时间响应时间也是两个需要监视的点。一旦控制系统的某一子控制系统出现极度或者性能瓶颈将会导致控制系统的响应时间变长。响应时间能在nginx一类的反向代理上监视,也能透过应用自己产生出访日志来监视

进程监视监视日志和响应时间都能较好地监视到控制系统的状态,但是它们的前提是控制系统是运行状态的,所以监视进程是比前两者更为紧要的任务。监视进程一般是检查操作控制系统中运行的应用进程数,比如对于采用多进程架构的web应用,就需要检查工作进程的数,如果低于低估值,就应当发出警报

磁盘监视磁盘监视主要是监视磁盘的用量。由于写日志频繁的缘故,磁盘空间渐渐被用光。一旦磁盘不够用将会引发控制系统的各种问题,给磁盘的使用量设置两个上限,一旦磁盘用量超过警戒值,服务器的管理者应该整理日志或者清理磁盘

内存监视对于node而言,一旦出现内存泄漏,不是那么容易排查的。监视服务器的内存使用情况。如果内存只升不降,那么铁定存在内存泄漏问题。符合正常的内存使用应该是有升有降,在出访量大的时候上升,在出访量回落的时候,占用量也随之回落。监视内存极度时间也是防止控制系统出现极度的好方法。如果突然出现内存极度,也能够追踪到近期的哪些标记符改动导致的问题

cpu占用监视服务器的cpu占用监视也是必不可少的项,cpu的使用分为使用者态、内核态、IOWait等。如果使用者态cpu使用率较高,说明服务器上的应用需要大量的cpu开销;如果内核态cpu使用率较高,说明服务器需要花费大量时间进行进程调度或者控制系统调用;IOWait使用率反映的是cpu等待磁盘I/O操作;cpu的使用率中,使用者态小于70%,内核态小于35%且整体小于70%,处于正常范围。监控cpu占用情况,能帮助分析应用程序在实际销售业务中的状况。合理设置监视阈值能够很好地预警系统

cpu load监视cpu load又称cpu平均负载。它用来描述操作控制系统当前的繁忙程度,又简单地理解为cpu在单位时间内正在使用和等待使用cpu的平均任务数。它有3个指标,即1分钟的平均负载、5分钟的平均负载,15分钟的平均负载。cpu load过高说明进程数量过多,这在node中可能体现在用于进程模块反复启动新的进程。监视该值能防止意外发生

I/O负载I/O负载指的主要是磁盘I/O。反应的是磁盘上的读写情况,对于node编写的应用,主要是面向网络销售业务,是不太可能出现I/O负载过高的情况,大多数的I/O压力来自于统计数据库。不管node进程是否与统计数据库或其他I/O密集的应用共同处理相同的服务器,我们都应该监视该值防止意外情况

网络监视虽然网络流量监视的优先级没有上述项目那么高,但还是需要对流量进行监视并设置流量上限值。即便应用突然受到使用者的青睐,流量暴涨的时候也能透过数值感知到网站的宣传是否有效。一旦流量超过警戒值,开发者就应当找出流量增长的原因。对于正常增长,应当评估是否该增加硬件设备来为更多使用者提供服务。网络流量监视的两个主要指标是流入流量和流出流量

应用状态监视 除了这些硬性需要检测的指标之外,应用还应该提供更多一种机制来反馈其自身的状态信息,外部监视将会持续性地调用应用地反馈USB来检查它地健康状态。dns监视 dns是网络应用的基础,在实际的对外服务产品中,多数都对域名有依赖。dns故障导致产品出现大面积影响的该事件并不少见。由于dns服务通常是稳定的,容易让人忽略,但是一旦出现故障,就可能是史无前例的故障。对于产品的灵活性,域名dns状态也需要加入监视。

关于本文

作者:火车头

https://juejin.cn/post/7078512301665419295

推荐链接

小厂的后端CM3监视解决方后端程序员个人简历模板整理和下载为何小厂后端监视都在用GIF做埋点?

 后端开发网志关键词

须知 | 进阶 | 图书 | 资料 | 面试 

个人简历 | 个人简历模板 | 思维图 | 知识点 | Vue脑图

分享好文和朋友一起看~

创作不易,加个点赞、在看支持一下哦!比较全的前端监控体系搭建篇(长文预警)

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务