目录

  • 前言
  • 需求背景
    • 解决什么问题
    • 行业通用方案
    • 定制化
  • 系统架构与融合
    • 基本构成
    • 系统关联融合
    • 高效运维
    • 小结
  • 数据收集与分析
    • 数据收集
    • 数据录入
    • 数据分析
  • 问题发现与解决
    • 自动化集成测试
    • 数据聚合
    • 数据库
  • 结语

前言

在上一篇文章《前端监控SDK开发分享》中,对客户端SDK的实现做了分享。这篇文章将会分成四节(需求背景系统架构与融合数据收集与分析问题发现与解决)分享介绍我们是如何打造前端监控系统的。

一、需求背景

1.1 解决什么问题

客户端常常会遇到如下一些问题:

  • 白屏
  • 无响应
  • 卡顿
  • 服务异常
  • bug无法复现
  • 等等

面对这些运行在用户端的问题,前端常常表示很无奈,解决这些问题之前,我们需要先知道客户端发生了什么,于是我们可以想到:

  • 收集错误,解决报错、兼容性等问题
  • 收集性能,解决慢查询、慢加载等问题
  • 收集接口,发现接口错误、打通服务端监控
  • 收集多方面辅助信息,综合多方面分析

为了实现收集功能,我们需要提供一个前端监控平台,它能够收集数据、处理数据、存储数据、查询数据。其实就有很多现成的平台或者开源项目我们可以直接使用。

1.2 行业通用方案

前端技术发展至今,相信大家已经对前端监控的这件事情非常熟悉,或多或少都会在我们的项目中用上它。比如搭建使用开源项目sentry、付费平台阿里的ARMS、甚至小程序配套的前端监控服务。

(1). sentry
sentry主要提供的功能是收集错误。支持大多流行语言的客户端和服务端,不支持小程序,但是目前有大公司根据sentry的上报数据结构,自己实现了小程序SDK并开源,目前关注度和流行度都偏低。除开错误,它的其它类型的前端监控能力相对来说很弱。

(2). 阿里ARMS
ARMS提供的功能与支持的客户端比较齐全,小程序也支持。只是需要付费。总体来说提供的功能还是比较全面、符合国内的环境。

(3). 小程序自带监控
微信小程序不断的在完善内部的监控,各方面的功能也慢慢丰富了起来,但是只能支持小程序本身。

在使用这些开源或者平台前端监控服务的时候,始终有一些不足。比如:

  • 系统分散
  • 很难满足增加一些自定义数据和查询需求
  • 特性一直不更新、BUG解决周期长
  • 二次开发难度大

1.3 定制化

早期我们使用sentry,随着公司多方面的发展,已经发现到sentry不能满足好几个方面的需求了。

如果完全从0到1来打造一套前端监控系统,成本也是很高的。甚至在早期,都可能没人愿意用,系统是否能立项或者持续发展下去也是一个问题。于是从一些开源项目中去寻找,去找一个方便改造也有一定功能模块的项目。在2019年底,我们找到了zanePerfor,因为它功能比较丰富,使用node+mongo符合我们前端的技术栈,也配套支持了微信小程序。

之后,我们开始基于它的代码,进行长期的改造和迭代。慢慢的改造成为一个更适合公司内部环境的一个前端监控系统。下一节,聊聊目前的系统基本构成、企业内部系统之间的关联融合、以及运维服务如何保驾护航。

二、系统架构与融合

2.1 基本构成

  • 客户端SDK
    • web
    • 小程序
    • ios
    • andriod
  • 服务端 node+EggJs
  • 数据库 Redis Mongo+mongooseJs(orm)
  • 管理台 Vue+ElementUI

为了实现前端监控,第一要素是要收集客户端数据,为了方便客户端集成监控系统、我们需要首先开发封装一个统一的SDK、去帮助我们收集数据。在早期,客户端方面我们优先支持的是web和微信小程序,随着系统的迭代现在也支持了native

SDK收集了数据,我们还需要通过服务端接口来接收,在服务端,使用node+EggJsnode适合i/o密集型场景,符合前端技术栈。eggjs简单易用、文档友好,大部分使用node的前端程序员都应该能很快上手。

服务端收集到数据并进行一些处理之后,我们需要存储到我们的数据库之中。在数据库方面,使用mongo做持久化存储,mongo 文档模型数据库,数据扩展方便,类json结构方便和node配合使用,天生适合日志系统。使用redis做数据缓存,redis简单易用的高性能key-value数据库,市场上占据主流,被大部分人都熟知。

最后,还需要一个管理台,做数据查询与管理。管理台使用Vue+ElementUI,简单快速。

下图是目前系统技术关系图:

打造前端监控系统-LMLPHP

客户端SDK收集数据上报,node服务端获取到数据后,先存在redis中,node服务会根据消费能力去拉取redis数据处理分析后存储到mongo之中,最后我们通过管理后台展示处理好的应用数据。

当初步的实现了我们的前端监控以后,我们还接入了公司内部现有的优质系统,丰富功能、提高了易用性、减少了工作量。

2.2 系统融合与关联

  • SSO系统
  • 加入内部导航黄页
  • 本地日志系统 finder elasticsearch
  • APM系统 skywalking
  • 告警平台
  • 操作日志平台
  • SPA平台

(1) 接入内部SSO系统

企业内部我们有一套SSO单点登录系统,系统会为每个员工注册一个账户,可以通过账户密码、企业微信扫码或者微信扫码等方式登录。统一登录,不仅仅是解决了登录不同系统之间的账户密码和登录方式问题,还更方便系统之间的相互跳转和接口请求。

(2) 加入内部导航黄页

在内部的导航页加入我们的项目,大家使用方便进入。

(3) 本地日志系统

前端监控系统的node后端服务,也会产生服务的本地日志。通过存储日志到约定目录,运维服务将会帮我们收集,并且提供了两个查询系统:finderelasticsearchfinder按照时间和文件夹结构划分,它的查看视角就像直接通过服务器看本地的日志一样,大部分时候我们更习惯用它。 elasticsearch主要适合搜索的方式查询。

(4) APM系统 skywalking

前端监控系统的node后端服务,除了系统本地产生的运行日志,它同样应该被后端的监控系统所监控。APM应用性能管理,目的是通过各种探针采集数据,收集关键指标,同时搭配数据呈现以实现对应用程序性能管理和故障管理的系统化解决方案。我们企业内部统一使用skywalking,我们的大部分服务主要是javaskywalking提供了node探针,使我们的项目也能够接入。接入之后,我们便可以通过skywalking管理台查询到后端node服务的性能以及调用情况了。

traceIdAPM工具会在服务端生成traceId,它标示一次调用的上下文id,通过此id可以查询所做事情的足迹链。后端服务可以通过前端http请求把这次调用的traceId通过响应头返回给客户端。前端监控的前端SDK探针便可以收集traceId,通过收集到它,前端监控就可以打通后端监控。在前端监控管理后台,我们不仅可以看到前端的监控网络日志,还可以通过traceId查询到后端链路信息。

(5) 告警平台

前端监控系统需要实时或定时的推送一些告警邮件等,内部告警平台提供了告警策略配置、并会拉取前端监控数据。接入告警平台可以减轻前端监控系统本身的刚需工作量。

(6) 操作日志平台

在网关层,操作日志平台可以拦截我们的管理台操作请求,以此来记录用户的系统操作。帮助我们做敏感操作追溯以及报警等。

(7) SPA平台

SPA平台是公司内部自研的静态资源发布平台,可以通过它半自动化管理业务项目,配置注入、静态资源管理等。前端监控在收集到压缩代码的报错时,需要通过sourceMap文件解析转换为源代码。大部分前端监控方案,需要手动上传sourceMap文件到监控系统,使用SPA平台后,资源被统一管理,我们可以通过内部配置直接把sourceMap文件存放在约定的位置,免去业务方手动上传,提高了易用性。

当我们的系统的功能都已经实现,关联系统也成功融合以后,程序要稳定友好的运行在线上,还需要我们的运维服务来保驾护航。

2.3 高效运维

  • 日志抓取
  • 自动化构建
  • 容器化
  • 负载均衡
  • 健康检测、安全关闭
  • 等等

健康检测、安全关闭

前端监控随时都会收到业务方上报的数据,这个系统在重启的时候,必须保障服务不间断,也要保障在关闭的时候,是安全的不会丢失数据。当我们更新了代码,重新构建的时候,首先会预先启动新的容器,当新的容器启动完成后,逐步关闭替换旧的服务,旧服务也会在关闭前收到通知,停止接收新处理任务,当所有正在执行的任务处理完后再被关闭。

一个完整的线上系统,离不开运维服务,它做的很多事情、甚至是我们平时不知道的,我们开发人员应当关注并了解它。

2.4 小结

内部的独立系统,各自负责某块任务,系统之间互相关联,运维服务保驾护航,构建了企业内部的生态环境,减少了很多重复的工作,也同样可以让某一个领域的系统做的更加深入和完善。

目前我们已经介绍了前端监控系统的需求背景和系统的构成情况,下一节我们将会稍微详细的说明一下我们最核心的数据收集与分析。

三、数据收集与分析

3.1 数据收集

打造前端监控系统-LMLPHP

(1)性能
收集Native热冷启动、Web页面加载、静态资源、ajax接口等性能信息,指标有加载时间、http协议版本、响应体大小等,这是为业务整体质量提升提供数据支撑,解决慢查询问题等。

(2)错误
收集Nativejs报错、静态资源加载错误、ajax接口加载错误,这些常规错误收集都很好理解。下面主要说明一下"业务接口错误(bussiness)":

客户端发送ajax请求后端业务接口,接口都会返回json数据结构,而其中一般都会有errorcodemessage两个字段,errorcode为业务接口内部定义的状态码。正常的业务响应内部都会约定比如errorcode==0等,那如果不为0可能是一些异常问题或者可预见的异常问题,这种错误数据就是需要收集的。

由于不同团队或者接口可能约定都不一样,所以我们只会提供一个预设方法,预设方法会在ajax请求响应后调用,业务方自己根据约定和响应的json数据,在预设的方法中编写判断逻辑控制是否上报。像是下面这样:

errcodeReport(res) {
  if (Object.prototype.toString.call(res) === '[object Object]' && res.hasOwnProperty('errcode') && res.errcode !== 0) {
    return { isReport: true, errMsg: res.errmsg,code: res.errcode };
  }
  return { isReport: false };
}

(3)辅助信息
除了上面两类硬指标数据,我们还需要很多其它的信息,比如:用户的访问轨迹、用户点击行为、用户ID、设备版本、设备型号、UV/UA标识、traceId等等。很多时候我们要解决的问题并不是那么简单直接就能排查出来,甚至我们需要前端监控和其它系统在某些情况下能够关联上,所以这些软指标信息同样很重要。

数据收集之后,并不能直接录入到数据库之中,而是要经过一定的处理。下面聊聊数据录入的两个过程。

3.2 数据录入

(1)SDK过滤
一些三方的业务接口和资源等,有一定请求量,甚至它们自己也会会常常报错。或者总有一些我们内部的接口我们并不想要做收集,这些数据收集起来会污染我们的数据视角,影响我们查看管理台数据。客户端SDK提供过滤配置,业务方可以根据具体业务过滤一些不需要收集的接口等。或者内置一些企业约定过滤的接口路径。

(2)服务端处理
SDK上报的数据,服务端也是需要经过二次处理再保存,例如为了查询方便对原始数据做拆分等。

3.3 数据分析

当我们的数据已经录入到数据库以后,我们就可以对我们的数据应该分析和查询了。除了管理台基础的数据查询功能,我们也会提供图表和日报,来帮助我们满足不同的场景。

3.3.1 日报

日报可以从已有的数据中,筛选出重点信息和分析后的内容,虽然平台就在那里,但是可能大家有时候真的不怎么关注。日报的汇报目标对象目前主要是开发和测试人员。

3.3.2 图表

图表只是一种数据的展示形式,比如页面性能加载实时图表,实时错误图表。在大部分情况下,可能我们都不会一直关注着,但是比如上线新版本的时候,我们可以一直打开关注它的变化。图表是数据视角的一种补充。

经过前面3节,对前端监控系统的功能都大致介绍完了。第四节,我们主要分享一下在打造前端监控系统的过程中,我们遇到过哪些问题,我们是如何解决的。

四、问题发现与解决

4.1 自动化集成测试

JS-SDK属于一个需要长期维护和更新的独立库,它被使用在很多业务项目中。随着代码量和特性的增加、人工测试成本变得越来越高,开发过程伴随强烈的不安全感,测试覆盖率低的可怕。于是从无到有,我们开始完善自动化集成测试。

我们的JS-SDK主要是加入探针监听业务项目的运行状态而收集信息,集成测试是我们关注的重点。我们的Web SDK运行环境依赖Web浏览器环境,而不能单纯的在node之中运行。目前我们有两种测试方式并存。

通过终端测试,可以帮助我们支持持续集成环境(代码提交仓库以后托管平台提供的环境中进行托管测试)。
通过浏览器测试,可以让代码运行在最真实的环境中,也可以做浏览器的兼容测试。

4.2 数据聚合

在管理台视角,我们是需要将数据进行聚合归类的,这是为了让我们更清晰的查看我们的监控数据。但是一些原因会对数据造成聚合影响。

(1) 动态路由

有部分接口使用动态路由设置参数,如xxx.com/api/getuid/15501/detail。此类接口会导致根据url无法聚合同一个接口,因此SDK默认会把此类接口的动态参数部分替换为*。替换后上面的链接变成xxx.com/api/getuid/*/detail

打造前端监控系统-LMLPHP

这样我们就能在服务端把他们归类为一个接口。目前是正则匹配的全数字,刚好内部有此类接口的服务参数部分都是纯数字。如果动态参数部分会存在非纯数字的参数就会无法判别。只有SDK提供配置列表,业务方去一一配置相关链接,让SDK自动替换。但是这样对业务方使用不友好,也不好维护。所以一旦有此类情况发生,最好的建议就是用?或者消息体的方式带参数吧!

(2) 接口错误信息不固定

前端监控收集到ajax业务错误响应信息以后,会按照接口路径+错误信息的方式聚合同一个错误。有发现过一个接口响应的错误信息带了随机值,随机值可能是当前请求的配置的唯一id。大致如下:

{
    errMsg: '发生了异常(d1nbj1az5)',//括号内的为随机值
    errcode: 1
}

解决方法就是,和服务端约定好,如果需要返回额外设置一个字段,而不是放在错误信息中。

打造前端监控系统-LMLPHP

4.3 数据库

4.3.1 表设计

在迭代native版本时,对于表结构设计有多种方案的考虑。对于日志系统,最终决定还是使用反规范化设计,通过牺牲空间来提高吞吐。

4.3.2 查询优化

(1)索引

当数据量变得越来越大,查询越来越慢时。我们对索引做了调整,以时间字段建立索引,同时客户端查询条件默认定义合理的时间范围,以此来优化查询速度。并适当的使用复合索引。

(2)慢查询

在早期,一些慢查询、比如分钟级定时任务使用的mapReduce语句,拉低了数据库性能,通过一定的优化、低效语句替换,我们解决了这些慢查询。线上优化前数据库24小时平均cpu使用率78%,优化后为27%。下图便是测试环境优化后的效果截图,峰值发生了明显下降。

打造前端监控系统-LMLPHP

4.3.3 独享数据库

最初前端监控系统和其他业务系统共享使用云数据库。由于前端监控在某段时期存在数据库慢查询的问题,而这个慢查询刚好也是分钟级别的定时任务。导致共享数据库有很多慢查询记录,CPU消耗也一直维持在较高位置。前端监控本身也会保持一定的监控数据并发量在做存储。前端监控拖累了整个数据库,并且由于慢查询日志很多,导致其它业务在排查数据库慢日志的时候,很难找到他们自己的日志。后来,运维同事就给单独配置了一个独享数据库给前端监控。

迁移前配置12核32g三节点(一主两从),迁移后为2核4g三节点。虽然配置变小了,但是前端监控独享数据库后平均响应速度反而变快了。前端监控的数据是属于日志数据,量更大,导致数据库性能下降,影响到业务数据服务。日志系统的数据库和业务分离也是正确合理的。

当然系统的迭代过程中还有很多问题和细节,这里就不一一列举了。

结语

目前前端监控系统已经运行1年多,服务公司几十个应用,它仍然有很多不足,它也一直保持规划和迭代。记录于此,继续努力。

End

03-08 10:37