搭建前端异常监控系统

标签: 前端 异常 监控 | 发表时间:2021-05-28 01:50 | 作者:爱秀的演员
出处:https://juejin.cn/tag/%E6%9E%B6%E6%9E%84

搭建前端异常监控系统

涉及技能

  • 收集前端错误(原生、React、Vue)
  • 编写错误上报逻辑
  • 利用Egg.js编写一个错误日志采集服务
  • 编写webpack插件自动上传sourcemap
  • 利用sourcemap还原压缩代码源码位置
  • 利用Jest进行单元测试

工作流程

  1. 收集错误
  2. 上报错误
  3. 代码上线打包将sourcemap文件上传至错误监控服务器
  4. 发生错误时监控服务器接收错误并记录到日志中
  5. 根据sourcemap和错误日志内容进行错误分析

异常收集

首先先看看如何捕获异常。

JS异常

js异常的特点是,出现不会导致JS引擎崩溃 最多只会终止当前执行的任务。比如一个页面有两个按钮,如果点击按钮发生异常页面,这个时候页面不会崩溃,只是这个按钮的功能失效,其他按钮还会有效。

   setTimeout(() => {
  console.log('1->begin')
  error
  console.log('1->end')
})
setTimeout(() => {
  console.log('2->begin')
  console.log('2->end')
})
复制代码

上面的例子我们用setTimeout分别启动了两个任务,虽然第一个任务执行了一个错误的方法。程序执行停止了。但是另外一个任务并没有收到影响。

其实如果你不打开控制台都看不到发生了错误。好像是错误是在静默中发生的。

下面我们来看看这样的错误该如何收集。

try-catch

我们首先想到的使用try-catch来收集。

   setTimeout(() => {
  try {
    console.log('1->begin')
    error
    console.log('1->end')
  } catch (e) {
    console.log('catch',e)
  }
})
复制代码

如果在函数中错误没有被捕获,错误会上抛。

   function fun1() {
  console.log('1->begin')
  error
  console.log('1->end')
}
setTimeout(() => {
  try {
    fun1()
  } catch (e) {
    console.log('catch',e)
  }
})
复制代码

控制台中打印出的分别是错误信息和错误堆栈。

读到这里大家可能会想那就在最底层做一个错误try-catch不就好了吗。但是理想很丰满,现实很骨感。我们看看下一个例子。

   function fun1() {
  console.log('1->begin')
  error
  console.log('1->end')
}

try {
  setTimeout(() => {
    fun1()

  })
} catch (e) {
  console.log('catch', e)
}
复制代码

大家注意运行结果,异常并没有被捕获。

这是因为JS的try-catch功能非常有限一遇到异步就不好用了。那总不能为了收集错误给所有的异步都加一个try-catch吧,太坑爹了。

window.onerror

window.onerror 最大的好处就是可以同步任务还是异步任务都可捕获。

   function fun1() {
  console.log('1->begin')
  error
  console.log('1->end')
}
window.onerror = (...args) => {
  console.log('onerror:',args)
}

setTimeout(() => {
  fun1()
})
复制代码
  • onerror返回值

    onerror还有一个问题大家要注意 如果返回返回true 就不会被上抛了。不然控制台中还会看到错误日志。

监听error事件

window.addEventListener('error',() => {})

其实onerror固然好但是还是有一类异常无法捕获。这就是网络异常的错误。比如下面的例子。

   <img src="./xxxxx.png">
复制代码

试想一下我们如果页面上要显示的图片突然不显示了,而我们浑然不知那就是麻烦了。

addEventListener就是

   window.addEventListener('error', args => {
    console.log(
      'error event:', args
    );
    return true;
  }, 
  true // 利用捕获方式
);
复制代码

Promise异常捕获

Promise的出现主要是为了让我们解决回调地域问题。基本是我们程序开发的标配了。虽然我们提倡使用es7 async/await语法来写,但是不排除很多祖传代码还是存在Promise写法。

   new Promise((resolve, reject) => {
  abcxxx()
});
复制代码

这种情况无论是onerror还是监听错误事件都是无法捕获的

   new Promise((resolve, reject) => {
  error()
})
// 增加异常捕获
  .catch((err) => {
  console.log('promise catch:',err)
});
复制代码

除非每个Promise都添加一个catch方法。但是显然是不能这样做。

   window.addEventListener("unhandledrejection", e => {
  console.log('unhandledrejection',e)
});
复制代码

我们可以考虑将unhandledrejection事件捕获错误抛出交由错误事件统一处理就可以了

   window.addEventListener("unhandledrejection", e => {
  throw e.reason
});
复制代码

async/await异常捕获

   const asyncFunc = () => new Promise(resolve => {
  error
})
setTimeout(async() => {
  try {
    await asyncFun()
  } catch (e) {
    console.log('catch:',e)
  }
})
复制代码

实际上async/await语法本质还是Promise语法。区别就是async方法可以被上层的try/catch捕获。

如果不去捕获的话就会和Promise一样,需要用unhandledrejection事件捕获。这样的话我们只需要在全局增加unhandlerejection就好了。

小结

异常类型 同步方法 异步方法 资源加载 Promise async/await
try/catch ✔️ ✔️
onerror ✔️ ✔️
error事件监听 ✔️ ✔️ ✔️
unhandledrejection事件监听 ✔️ ✔️

实际上我们可以将unhandledrejection事件抛出的异常再次抛出就可以统一通过error事件进行处理了。

最终用代码表示如下:

   window.addEventListener("unhandledrejection", e => {
  throw e.reason
});
window.addEventListener('error', args => {
  console.log(
    'error event:', args
  );
  return true;
}, true);
复制代码

Webpack工程化

现在是前端工程化的时代,工程化导出的代码一般都是被压缩混淆后的。

比如:

   setTimeout(() => {
    xxx(1223)
}, 1000)
复制代码

出错的代码指向被压缩后的JS文件。

如果想将错误和原有的代码关联起来就需要sourcemap文件的帮忙了。

sourceMap是什么

简单说, sourceMap就是一个文件,里面储存着位置信息。

仔细点说,这个文件里保存的,是转换后代码的位置,和对应的转换前的位置。

那么如何利用sourceMap对还原异常代码发生的位置这个问题我们到异常分析这个章节再讲。

Vue

创建工程

利用vue-cli工具直接创建一个项目。

   # 安装vue-cli
npm install -g @vue/cli

# 创建一个项目
vue create vue-sample

cd vue-sample
npm i
// 启动应用
npm run serve

复制代码

为了测试的需要我们暂时关闭eslint 这里面还是建议大家全程打开eslint

在vue.config.js进行配置

   module.exports = {   
  // 关闭eslint规则
  devServer: {
    overlay: {
      warnings: true,
      errors: true
    }
  },
  lintOnSave:false
}
复制代码

我们故意在src/components/HelloWorld.vue

   <script>
export default {
  name: "HelloWorld",
  props: {
    msg: String
  },
  mounted() {
    // 制造一个错误
    abc()
  }
};
script>
​```html

然后在src/main.js中添加错误事件监听

​```js
window.addEventListener('error', args => {
  console.log('error', error)
})
复制代码

这个时候 错误会在控制台中被打印出来,但是错误事件并没有监听到。

handleError

为了对Vue发生的异常进行统一的上报,需要利用vue提供的handleError句柄。一旦Vue发生异常都会调用这个方法。

我们在src/main.js

   Vue.config.errorHandler = function (err, vm, info) {
  console.log('errorHandle:', err)
}
复制代码

React

   npx create-react-app react-sample
cd react-sample
yarn start
复制代码

我们利用useEffect hooks 制造一个错误

   import React ,{useEffect} from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  useEffect(() => {
    // 发生异常
    error()
  });

  return (
    <div className="App">
      // ...略...
    div>
  );
}

export default App;
复制代码

并且在src/index.js中增加错误事件监听逻辑

   window.addEventListener('error', args => {
    console.log('error', error)
})
复制代码

但是从运行结果看虽然输出了错误日志但是还是服务捕获。

ErrorBoundary标签

错误边界仅可以捕获其子组件的错误。错误边界无法捕获其自身的错误。如果一个错误边界无法渲染错误信息,则错误会向上冒泡至最接近的错误边界。这也类似于 JavaScript 中 catch {} 的工作机制。

创建ErrorBoundary组件

   import React from 'react'; 
export default class ErrorBoundary extends React.Component {
    constructor(props) {
      super(props);
    }
  
    componentDidCatch(error, info) {
      // 发生异常时打印错误
      console.log('componentDidCatch',error)
    }
  
    render() {
      return this.props.children;
    }
  }
复制代码

在src/index.js中包裹App标签

   import ErrorBoundary from './ErrorBoundary'

ReactDOM.render(
    <ErrorBoundary>
        <App />
    ErrorBoundary>
    , document.getElementById('root'));
复制代码

上一篇我们主要谈到的JS错误如何收集。这篇我们说说异常如何上报和分析。

异常上报

选择通讯方式

动态创建img标签

其实上报就是要将捕获的异常信息发送到后端。最常用的方式首推动态创建标签方式。因为这种方式无需加载任何通讯库,而且页面是无需刷新的。基本上目前包括百度统计 Google统计都是基于这个原理做的埋点。

   new Image().src = 'http://localhost:7001/monitor/error'+ '?info=xxxxxx'
复制代码

通过动态创建一个img,浏览器就会向服务器发送get请求。可以把你需要上报的错误数据放在querystring字符串中,利用这种方式就可以将错误上报到服务器了。

Ajax上报

实际上我们也可以用ajax的方式上报错误,这和我们在业务程序中并没有什么区别。在这里就不赘述。

上报哪些数据

我们先看一下error事件参数:

属性名称 含义 类型
message 错误信息 string
filename 异常的资源url string
lineno 异常行号 int
colno 异常列号 int
error 错误对象 object
error.message 错误信息 string
error.stack 错误信息 string

其中核心的应该是错误栈,其实我们定位错误最主要的就是错误栈。

错误堆栈中包含了绝大多数调试有关的信息。其中包括了异常位置(行号,列号),异常信息

上报数据序列化

由于通讯的时候只能以字符串方式传输,我们需要将对象进行序列化处理。

大概分成以下三步:

  • 将异常数据从属性中解构出来存入一个JSON对象

  • 将JSON对象转换为字符串

  • 将字符串转换为Base64

当然在后端也要做对应的反向操作 这个我们后面再说。

   window.addEventListener('error', args => {
  console.log(
    'error event:', args
  );
  uploadError(args)
  return true;
}, true);
function uploadError({
    lineno,
    colno,
    error: {
      stack
    },
    timeStamp,
    message,
    filename
  }) {
    // 过滤
    const info = {
      lineno,
      colno,
      stack,
      timeStamp,
      message,
      filename
    }
    // const str = new Buffer(JSON.stringify(info)).toString("base64");
  const str = window.btoa(JSON.stringify(info))
    const host = 'http://localhost:7001/monitor/error'
    new Image().src = `${host}?info=${str}`
}
复制代码

异常收集

异常上报的数据一定是要有一个后端服务接收才可以。

我们就以比较流行的开源框架eggjs为例来演示

搭建eggjs工程

   # 全局安装egg-cli
npm i egg-init -g 
# 创建后端项目
egg-init backend --type=simple
cd backend
npm i
# 启动项目
npm run dev
复制代码

编写error上传接口

首先在app/router.js添加一个新的路由

   module.exports = app => {
  const { router, controller } = app;
  router.get('/', controller.home.index);
  // 创建一个新的路由
  router.get('/monitor/error', controller.monitor.index);
};
复制代码

创建一个新的controller (app/controller/monitor)

   'use strict';

const Controller = require('egg').Controller;
const { getOriginSource } = require('../utils/sourcemap')
const fs = require('fs')
const path = require('path')

class MonitorController extends Controller {
  async index() {
    const { ctx } = this;
    const { info } = ctx.query
    const json = JSON.parse(Buffer.from(info, 'base64').toString('utf-8'))
    console.log('fronterror:', json)
    ctx.body = '';
  }
}

module.exports = MonitorController;

复制代码

记入日志文件

下一步就是讲错误记入日志。实现的方法可以自己用fs写,也可以借助log4js这样成熟的日志库。

当然在eggjs中是支持我们定制日志那么我么你就用这个功能定制一个前端错误日志好了。

在/config/config.default.js中增加一个定制日志配置

   // 定义前端错误日志
config.customLogger = {
  frontendLogger : {
    file: path.join(appInfo.root, 'logs/frontend.log')
  }
}
复制代码

在/app/controller/monitor.js中添加日志记录

   async index() {
    const { ctx } = this;
    const { info } = ctx.query
    const json = JSON.parse(Buffer.from(info, 'base64').toString('utf-8'))
    console.log('fronterror:', json)
    // 记入错误日志
    this.ctx.getLogger('frontendLogger').error(json)
    ctx.body = '';
  }
复制代码

异常分析

谈到异常分析最重要的工作其实是将webpack混淆压缩的代码还原。

Webpack插件实现SourceMap上传

在webpack的打包时会产生sourcemap文件,这个文件需要上传到异常监控服务器。这个功能我们试用webpack插件完成。

创建webpack插件

/source-map/plugin

   const fs = require('fs')
var http = require('http');

class UploadSourceMapWebpackPlugin {
  constructor(options) {
    this.options = options
  }

  apply(compiler) {
    // 打包结束后执行
    compiler.hooks.done.tap("upload-sourcemap-plugin", status => {
      console.log('webpack runing')
    });
  }
}

module.exports = UploadSourceMapWebpackPlugin;
复制代码

加载webpack插件

webpack.config.js

   // 自动上传Map
UploadSourceMapWebpackPlugin = require('./plugin/uploadSourceMapWebPackPlugin')

plugins: [
    // 添加自动上传插件
    new UploadSourceMapWebpackPlugin({
      uploadUrl:'http://localhost:7001/monitor/sourcemap',
      apiKey: 'xxx'
    })
  ],

复制代码

添加读取sourcemap读取逻辑

在apply函数中增加读取sourcemap文件的逻辑

/plugin/uploadSourceMapWebPlugin.js

   const glob = require('glob')
const path = require('path')
apply(compiler) {
  console.log('UploadSourceMapWebPackPlugin apply')
  // 定义在打包后执行
  compiler.hooks.done.tap('upload-sourecemap-plugin', async status => {
    // 读取sourcemap文件
    const list = glob.sync(path.join(status.compilation.outputOptions.path, `./**/*.{js.map,}`))
    for (let filename of list) {
      await this.upload(this.options.uploadUrl, filename)
    }
  })
}
复制代码

实现http上传功能

   upload(url, file) {
  return new Promise(resolve => {
    console.log('uploadMap:', file)
    const req = http.request(
      `${url}?name=${path.basename(file)}`,
      {
        method: 'POST',
        headers: {
          'Content-Type': 'application/octet-stream',
          Connection: "keep-alive",
          "Transfer-Encoding": "chunked"
        }
      }
    )
    fs.createReadStream(file)
      .on("data", chunk => {
      req.write(chunk);
    })
      .on("end", () => {
      req.end();
      resolve()
    });
  })
}
复制代码

服务器端添加上传接口

/backend/app/router.js

   module.exports = app => {
  const { router, controller } = app;
  router.get('/', controller.home.index);
  router.get('/monitor/error', controller.monitor.index);
  // 添加上传路由
 router.post('/monitor/sourcemap',controller.monitor.upload)
};
    
复制代码

添加sourcemap上传接口

/backend/app/controller/monitor.js

   async upload() {
    const { ctx } = this
    const stream = ctx.req
    const filename = ctx.query.name
    const dir = path.join(this.config.baseDir, 'uploads')
    // 判断upload目录是否存在
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir)
    }

    const target = path.join(dir, filename)
    const writeStream = fs.createWriteStream(target)
    stream.pipe(writeStream)
}
复制代码

执行webpack打包时调用插件sourcemap被上传至服务器。

解析ErrorStack

考虑到这个功能需要较多逻辑,我们准备把他开发成一个独立的函数并且用Jest来做单元测试

先看一下我们的需求

输入 stack(错误栈) ReferenceError: xxx is not defined\n' + ' at http://localhost:7001/public/bundle.e7877aa7bc4f04f5c33b.js:1:1392'
SourceMap
输出 源码错误栈 { source: 'webpack:///src/index.js', line: 24, column: 4, name: 'xxx' }

搭建Jest框架

首先创建一个/utils/stackparser.js文件

   module.exports = class StackPaser {
    constructor(sourceMapDir) {
        this.consumers = {}
        this.sourceMapDir = sourceMapDir
    }
}

复制代码

在同级目录下创建测试文件stackparser.spec.js

以上需求我们用Jest表示就是

   const StackParser = require('../stackparser')
const { resolve } = require('path')
const error = {
    stack: 'ReferenceError: xxx is not defined\n' +
        '    at http://localhost:7001/public/bundle.e7877aa7bc4f04f5c33b.js:1:1392',
    message: 'Uncaught ReferenceError: xxx is not defined',
    filename: 'http://localhost:7001/public/bundle.e7877aa7bc4f04f5c33b.js'
}

it('stackparser on-the-fly', async () => {

    const stackParser = new StackParser(__dirname)

    // 断言 
    expect(originStack[0]).toMatchObject(
        {
            source: 'webpack:///src/index.js',
            line: 24,
            column: 4,
            name: 'xxx'
        }
    )
})

复制代码

下面我们运行Jest

   npx jest stackparser --watch
复制代码

显示运行失败,原因很简单因为我们还没有实现对吧。下面我们就实现一下这个方法。

反序列Error对象

首先创建一个新的Error对象 将错误栈设置到Error中,然后利用error-stack-parser这个npm库来转化为stackFrame

   const ErrorStackParser = require('error-stack-parser')
/**
 * 错误堆栈反序列化
 * @param {*} stack 错误堆栈
 */
parseStackTrack(stack, message) {
  const error = new Error(message)
  error.stack = stack
  const stackFrame = ErrorStackParser.parse(error)
  return stackFrame
}
复制代码

解析ErrorStack

下一步我们将错误栈中的代码位置转换为源码位置

   const { SourceMapConsumer } = require("source-map");
async getOriginalErrorStack(stackFrame) {
        const origin = []
        for (let v of stackFrame) {
            origin.push(await this.getOriginPosition(v))
        }

        // 销毁所有consumers
        Object.keys(this.consumers).forEach(key => {
            console.log('key:',key)
            this.consumers[key].destroy()
        })
        return origin
    }

    async getOriginPosition(stackFrame) {
        let { columnNumber, lineNumber, fileName } = stackFrame
        fileName = path.basename(fileName)
        console.log('filebasename',fileName)
        // 判断是否存在
        let consumer = this.consumers[fileName]

        if (consumer === undefined) {
            // 读取sourcemap
            const sourceMapPath = path.resolve(this.sourceMapDir, fileName + '.map')
            // 判断目录是否存在
            if(!fs.existsSync(sourceMapPath)){
                return stackFrame
            }
            const content = fs.readFileSync(sourceMapPath, 'utf8')
            consumer = await new SourceMapConsumer(content, null);
            this.consumers[fileName] = consumer
        }
        const parseData = consumer.originalPositionFor({ line:lineNumber, column:columnNumber })
        return parseData
    }
复制代码

我们用Jest测试一下

   it('stackparser on-the-fly', async () => {

    const stackParser = new StackParser(__dirname)
    console.log('Stack:',error.stack)
    const stackFrame = stackParser.parseStackTrack(error.stack, error.message)
    stackFrame.map(v => {
        console.log('stackFrame', v)
    })
    const originStack = await stackParser.getOriginalErrorStack(stackFrame)

    // 断言 
    expect(originStack[0]).toMatchObject(
        {
            source: 'webpack:///src/index.js',
            line: 24,
            column: 4,
            name: 'xxx'
        }
    )
})
复制代码

将源码位置记入日志

   async index() {
    console.log
    const { ctx } = this;
    const { info } = ctx.query
    const json = JSON.parse(Buffer.from(info, 'base64').toString('utf-8'))
    console.log('fronterror:', json)
    
    // 转换为源码位置
    const stackParser = new StackParser(path.join(this.config.baseDir, 'uploads'))
    const stackFrame = stackParser.parseStackTrack(json.stack, json.message)
    const originStack = await stackParser.getOriginalErrorStack(stackFrame)
    this.ctx.getLogger('frontendLogger').error(json,originStack)
    
    ctx.body = '';
  }
复制代码

开源框架

Fundebug

Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有阳光保险、荔枝FM、掌门1对1、核桃编程、微脉等众多品牌企业。欢迎免费试用!

Sentry

Sentry 是一个开源的实时错误追踪系统,可以帮助开发者实时监控并修复异常问题。它主要专注于持续集成、提高效率并且提升用户体验。Sentry 分为服务端和客户端 SDK,前者可以直接使用它家提供的在线服务,也可以本地自行搭建;后者提供了对多种主流语言和框架的支持,包括 React、Angular、Node、Django、RoR、PHP、Laravel、Android、.NET、JAVA 等。同时它可提供了和其他流行服务集成的方案,例如 GitHub、GitLab、bitbuck、heroku、slack、Trello 等。目前公司的项目也都在逐步应用上 Sentry 进行错误日志管理。

总结

截止到目前为止,我们把前端异常监控的基本功能算是形成了一个MVP(最小化可行产品)。后面需要升级的还有很多,对错误日志的分析和可视化方面可以使用ELK。发布和部署可以采用Docker。对eggjs的上传和上报最好要增加权限控制功能。

参考代码位置: github.com/su37josephx…

相关 [前端 异常 监控] 推荐:

搭建前端异常监控系统

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

前端异常监控解决方案研究

- - 腾讯CDC
前端监控包括行为监控、 异常监控、性能监控等,本文主要讨论异常监控. 对于前端而言,和后端处于同一个监控系统中,前端有自己的监控方案,后端也有自己等监控方案,但两者并不分离,因为一个用户在操作应用过程中如果出现异常,有可能是前端引起,也有可能是后端引起,需要有一个机制,将前后端串联起来,使监控本身统一于监控系统.

再谈服务异常监控(9.3)

- - 人月神话的BLOG
实际上对于服务运行期间的异常监控,我们在前面已经定制开发了类似业务系统异常查询,业务异常监控告警,服务心跳监控等诸多的功能,今天还是要考虑下如何将这些监控功能进一步做到自动化异常监控推送. 初步的思路就是对于业务系统异常监控,系统异常监控,心跳监控等当前拆分的各个功能,设定进一步的异常监控规则,将每天需要关注的异常情况统一输出到一张异常监控报表中.

Spring Boot + WebSocket 实时监控异常

- - 掘金 后端
原文:cnblogs.com/jae-tech/p/15409340.html. 此异常非彼异常,标题所说的异常是业务上的异常. 最近做了一个需求,消防的设备巡检,如果巡检发现异常,通过手机端提交,后台的实时监控页面实时获取到该设备的信息及位置,然后安排员工去处理. 因为需要服务端主动向客户端发送消息,所以很容易的就想到了用WebSocket来实现这一功能.

前端性能监控系统ShowSlow

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

前端监控系统设计

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

Spark Streaming + Elasticsearch构建App异常监控平台

- - 美团点评技术团队
本文已发表在《程序员》杂志2016年10月期. 如果在使用App时遇到闪退,你可能会选择卸载App、到应用商店怒斥开发者等方式来表达不满. 但开发者也同样感到头疼,因为崩溃可能意味着用户流失、营收下滑. 为了降低崩溃率,进而提升App质量,App开发团队需要实时地监控App异常. 一旦发现严重问题,及时进行热修复,从而把损失降到最低.

监控平台前端SDK开发实践

- - 美团点评技术团队
监控是提高故障处理能力和保障服务质量必需的一环,它需要负责的内容包括:及时上报错误、收集有效信息、提供故障排查依据. 及时上报错误:发生线上问题后,经由运营或者产品反馈到开发人员,其中流转过程可能是几分钟甚至几十分钟,这段时间可能直接导致公司的经济损失. 如果有一个监控系统,在线上出现问题时,监控系统能够第一时间报警,并且通知到开发人员,那开发人员就可以第一时间修复上线,使公司损失最小化.

前端监控实践——FMP的智能获取算法

- - SegmentFault 最新的文章
今天来给大家介绍下前端监控中一个特定指标的获取算法,有人会问,为啥就单单讲一个指标. 这是因为,目前大部分的指标,比如白屏时间,dom加载时间等等,都能通过现代浏览器提供的各种api去进行较为精确的获取,而今天讲的这个指标,以往获取他的方式只能是通过逻辑埋点去获取它的值,因此在做一些前端监控时,需要根据业务需要去改变页面对这个值的埋点方式,会比较繁琐,恰巧最近刚刚好在做一些前端监控相关的项目,遇到这个问题时就在想,能不能通过一种无须埋点的方式,将这个值给获取到.