解决方案:让前端监控数据采集更高效

优采云 发布时间: 2022-10-17 19:13

  解决方案:让前端监控数据采集更高效

  随着业务的快速发展,我们越来越重视对生产环境问题的感知能力。作为离用户最近的一层,前端性能是否可靠、稳定、好用在很大程度上决定了用户对整个产品的体验和感受。因此,前端的监控也不容忽视。

  搭建前端监控平台需要考虑很多方面,比如数据采集、埋藏模式、数据处理分析、告警、监控平台在具体业务中的应用等等。在所有这些环节中、准确、完整、全面的数据采集是一切的前提,也为用户后续的精细化运营提供了依据。

  前端技术的飞速发展给数据带来了变化和挑战采集,传统的人工管理模式已经不能满足需求。如何让前端数据采集在新的技术背景下工作更加完整高效是本文的重点。

  前端监控数据采集

  在采集数据之前,考虑一下采集什么样的数据。我们关注两类数据,一类与用户体验相关,如首屏时间、文件加载时间、页面性能等;另一个是帮助我们及时感知产品上线后是否出现异常,比如资源错误、API响应时间等。具体来说,我们的前端数据采集主要分为:

  路由交换机

  Vue、React、Angular等前端技术的快速发展,使得单页应用流行起来。我们都知道,传统的页面应用使用一些超链接来实现页面切换和跳转,而单页面应用使用自己的路由系统来管理各个前端页面切换,比如vue-router、react-router等,只有跳转时刷新本地资源,js、css等公共资源只需加载一次,使得传统的网页进出方式只有在第一次打开时才会被记录。在单页应用中切换所有后续路由有两种方式,一种是Hash,另一种是HTML5推出的History API。

  1.href

  href是页面初始化的第一个入口,这里只需要简单的报告“进入页面”事件。

  2.hashchange

  哈希路由的一个明显标志是带有“#”。Hash的优点是兼容性更好,但问题是URL中的“#”不美观。我们主要通过*敏*感*词* URL 中的 hashchange 来捕获具体的 hash 值进行检测。

  window.addEventListener('hashchange', function() {

    // 上报【进入页面】事件

}, true)

  需要注意的是,在新版本的vue-router中,如果浏览器支持history,即使模式选择hash,也会先选择history模式。虽然表达式暂时还是#,但实际上是模拟出来的,所以不要以为你在如果模式选择hash,那肯定是hash。

  3.历史API

  History利用HTML5 History接口中新增的pushState()和replaceState()方法进行路由切换,是目前主流的非刷新切换路由方式。与 hashchange 只能更改 # 后面的代码片段相比,History API(pushState、replaceState)给了前端完全的自由。

  PopState 是浏览器返回事件的回调,但是更新路由的 pushState 和 replaceState 没有回调事件。因此,URL 更改需要分别在 history.pushState() 和 history.replaceState() 方法中处理。在这里,我们使用类似Java的AOP编程思想来改造pushState和replaceState。

  AOP(Aspect-oriented programming)是面向方面的编程,它提倡对同一类型的问题进行统一处理。AOP的核心思想是让某个模块能够被复用。它采用横向抽取机制,将功能代码与业务逻辑代码分离,在不修改源代码的情况下扩展功能。与封装相比,隔离更彻底。

  下面介绍我们具体的改造方法:

  // 第一阶段:我们对原生方法进行包装,调用前执行 dispatchEvent 了一个同样的事件

function aop (type) {

    var source = window.history[type];

    return function () {

        var event = new Event(type);

        event.arguments = arguments;

        window.dispatchEvent(event);

        var rewrite = source.apply(this, arguments);

        return rewrite;

    };

}

// 第二阶段:将 pushState 和 replaceState 进行基于 AOP 思想的代码注入

window.history.pushState = aop('pushState');

window.history.replaceState = aop('replaceState'); // 更改路由,不会留下历史记录

// 第三阶段:捕获pushState 和 replaceState

<p>

window.addEventListener('pushState', function() {

    // 上报【进入页面】事件

}, true)

window.addEventListener('replaceState', function() {

    // 上报【进入页面】事件

}, true)

</p>

  window.history.pushState的实际调用关系如图:

  至此,我们完成了pushState和replaceState的转换,实现了路由切换的有效抓包。可以看出,我们在不侵入业务代码的情况下扩展了window.history.pushState,调用时会主动dispatchEvent一个pushState。

  但是这里我们也可以看到一个缺点,就是如果AOP代理函数发生JS错误,会阻塞后续调用关系,从而无法调用实际的window.history.pushState。因此,在使用该方法时,需要对AOP代理功能的内容进行一次完整的try catch,以防止出现业务异常。

  *__提示:如果要自动捕获页面停留时间,只需在触发下一页进入事件时,将上一页的时间与当前时间做差值即可。这时候可以上报【离开页面】事件。

  错误

  在前端项目中,由于 JavaScript 本身是一种弱类型语言,再加上浏览器环境的复杂性、网络问题等,很容易出现错误。因此,做好网页错误监控,不断优化代码,提高代码的健壮性是非常重要的。

  JsError的捕获可以帮助我们分析和监控在线问题,和我们在Chrome浏览器的调试工具Console中看到的一致。

  1.window.onerror

  我们通常使用 window.onerror 来捕获 JS 错误的异常信息。有两种方法可以捕获 JS 错误,window.onerror 和 window.addEventListener('error')。一般不建议使用 addEventListener('error') 来捕获 JS 异常,主要是它没有堆栈信息,而且还需要区分捕获的信息,因为它会捕获所有的异常信息,包括资源加载错误. 等待。

  window.onerror = function (msg, url, lineno, colno, stack) {

    // 上报 【js错误】事件

}

  2. Uncaught (in promise)

  当 Promise 发生 JS 错误或者拒绝信息没有被业务处理时,会抛出 unhandledrejection,并且这个错误不会被 window.onerror 和 window.addEventListener('error') 捕获,还有一个特殊的 window . addEventListener('unhandledrejection') 用于捕获处理:

  window.addEventListener('unhandledrejection', function (e) {

    var reg_url = /\(([^)]*)\)/;

    var fileMsg = e.reason.stack.split('\n')[1].match(reg_url)[1];

    var fileArr = fileMsg.split(':');

    var lineno = fileArr[fileArr.length - 2];

    var colno = fileArr[fileArr.length - 1];

    var url = fileMsg.slice(0, -lno.length - cno.length - 2);}, true);

    var msg = e.reason.message;

    // 上报 【js错误】事件

}

  我们注意到unhandledrejection继承自PromiseRejectionEvent,而PromiseRejectionEvent继承自Event,所以msg、url、lineno、colno、stack以字符串的形式放在e.reason.stack中,我们需要将上面的参数解析出来与onerror参数对齐,为后续监测平台各项指标的统一奠定了基础。

  3. 常见问题

  如果抓到的msg都是“Script error.”,问题是你的JS地址和当前网页不在同一个域下。因为我们经常需要对网络版的静态资源进行CDN,会导致经常访问的页面和脚本文件来自不同的域名。这时候如果不进行额外配置,浏览器很容易出现“脚本错误”。出于安全考虑。我们可以利用当前流行的 Webpack bundler 来处理此类问题。

  // webpack config 配置

<p>

// 处理 html 注入 js 添加跨域标识

plugins: [

    new HtmlWebpackPlugin({

      filename: 'html/index.html',

      template: HTML_PATH,

      attributes: {

        crossorigin: 'anonymous'

      }

    }),

    new HtmlWebpackPluginCrossorigin({

      inject: true

    })

]

// 处理按需加载的 js 添加跨域标识

output: {

    crossOriginLoading: true

}

</p>

  大多数场景下,生产环境中的代码都是压缩合并的,这使得我们抓到的错误很难映射到具体的源码中,给我们解决问题带来了很大的麻烦。这里有两个解决方案。想法。

  在生产环境中,我们需要添加sourceMap的配置,这样会带来安全隐患,因为这样外网可以通过sourceMap进行source map的映射。为了降低风险,我们可以做到以下几点:

  设置sourceMap生成的.map文件访问公司内网,降低源代码安全风险。将代码发布到CDN时,将.map文件存放在公司内网下

  此时,我们已经有了 .map 文件。接下来我们需要做的就是通过捕获的lineno、colno、url调用mozilla/source-map库进行源码映射,然后就可以得到真正的源码错误信息了。

  表现

  性能指标的获取比较简单,在onload之后读取window.performance就可以了,里面收录了性能、内存等信息。这部分内容在很多现有的文章中都有介绍。由于篇幅所限,本文不再过多展开。稍后,我们将在相关主题文章中进行相关讨论。感兴趣的朋友可以添加“马蜂窝技术”公众号继续关注。

  资源错误

  首先,我们需要明确资源错误捕获的使用场景,更多的是感知DNS劫持和CDN节点异常等,具体方法如下:

  window.addEventListener('error', function (e) {

    var target = e.target || e.srcElement;

    if (target instanceof HTMLScriptElement) {

        // 上报 【资源错误】事件

    }

}, true)

  这只是一个基本的演示。在实际环境中,我们会关心更多的Element错误,比如css、img、woff等,大家可以根据不同的场景添加。

  *资源错误的使用场景更多地依赖于其他维度,如:__region、operator等。我们将在后面的页面中详细解释。

  API

  在市面上的主流框架(如axios、jQuery.ajax等)中,基本上所有的API请求都是基于xmlHttpRequest或者fetch,所以捕获全局接口错误的方式就是封装xmlHttpRequest或者fetch。在这里,我们的SDK还是使用了上面提到的AOP思想来拦截API。

  整套解决方案:SaaS系统框架搭建详解

  SaaS系统可以为行业中的一种或多种常见场景提供功能支持。只要有网络,就可以“随处可用,即用,无需下载”,所以也是现在的流行趋势。本文介绍了SaaS系统的框架,一起来学习。

  根据百度百科的解释:“SaaS是Software-as-a-Service的缩写,意思是软件即服务。SaaS平台提供商将应用软件部署在自己的服务器上,制造商订购所需的应用软件服务,向制造商付款根据订购服务的数量和时长,通过互联网获取SaaS平台提供商提供的服务。”

  SaaS系统可以为行业中的一种或多种常见场景提供功能支持,只要有网络,就具有“随处可用、即用、无需下载”的特点。

  对于SaaS服务商来说,边际成本随着客户的增加而大大降低;对客户而言,可以在业务发展初期尝试小成本,降低软件整体成本,可以更专注于业务本身的发展;也就是说,开箱即用,SaaS系统的常规设计符合相应领域用户的心智模式,使用起来非常方便。

  所以现在SaaS系统的普及已经是一种趋势。接下来详细介绍一下SaaS系统的框架,也就是SaaS与其他常规B端平台不同的地方——权限的配置和数据的隔离更加复杂。

  1.菜单管理

  菜单管理主要是管理后台系统菜单的显示、排序、跳转等。开发者每次做一个新功能,都可以直接从这里配置到后台,不用往数据库里插入数据,也不需要经过开发、发布、上线的过程。

  参考原型如下:

  2. 现场管理

  网站管理主要针对不同机构的品牌推广,专门为机构配置专属域名&amp;名称&amp;标识。多个组织也可以使用相同的域名。无论是否使用不同的域名,不同组织的用户数据都会被隔离。

  大致涉及的领域如下:

  不同的组织需要做很多个性化的配置维度和配置中涉及的参数。比如上面提到的“支付相关配置”中,不同租户的收款商户肯定是不同的,所以需要配置微信开放平台、微信公众平台、微信商户号、支付宝商户号等。不同配置维度的具体配置,后面我们会写文章详细解释。

  3.组织管理

  SaaS系统通过组织实现多租户管理,为租户配置管理员和系统功能权限等。此外,还可以根据实际需要设置租户可以管理的其他组织以及组织下的内容。SaaS 服务提供商需要相应地设计跨组织共享内容的功能。我接下来要和大家分享的 SaaS 框架支持跨组织管理数据和跨组织共享内容。

  参考原型如下:

  1.组织与管理员的关系

  ①管理员默认拥有组织最高职能权限;

  ②管理员默认拥有管理组织的所有数据权限;

  ③SaaS服务商(原型中的机构A)默认拥有一个普通管理员账号,拥有全系统最高的数据和功能权限;

  ④操作者可以对其管理的其他组织进行所有信息更改,但本组织只能进行【重置密码】操作;

  ⑤ 组织中的管理员账号只出现在组织模块中,不会出现在账号管理模块中;

  2.系统有效期

  ①制度到期后,如机构不续约,一般资料保留1至3年;

  ②前端用户一般过期后无法登录;

  ③过期后后台用户设置为只能查看部分数据,无法操作。如果清除数据后无法登录;

  3.前端模块权限

  ①门户网站的功能模块配置;

  ②未配置的模块在前端看不到或未经许可点击提示;

  

  ③选项为运营商组织有权使用的前端模块;

  4.后台功能权限

  ① 配置组织拥有的后台功能权限;

  ② 默认授权组织管理员的功能权限;

  ③选项是操作者拥有的功能权限,操作者可以根据需要进行选择;

  5. 组织权限

  ① 分配组织可以管理的组织和每个组织对应的模块内容(课程包&amp;信息&amp;角色&amp;账号等);

  ②默认分配给管理员;

  ③可查看的选项:运营商有权访问的组织和组织下的所有内容模块;

  ④ 可操作选项:运营商有权访问的组织和组织下有权的内容模块;

  原型如下:

  6. **内容权限配置(课程包&amp;资料等)

  ①分享运营机构的具体内容,与管理员同步分享;

  ②不能与所属组织共享,可通过账号与组织共享,稍后在账号管理中讨论;

  ③ 跨组织共享是一种复制共享。同一个ID的内容可以多次分享,每次分享都会产生一个新的内容(生成一个新的ID);

  ④选项是运营商有权限的内容。如果运营商的内容来源之一是所运营的组织,那么内容仍然可以共享,因为内容和原创内容已经是两个产品。如果实际业务场景需要做限制也可以;

  原型如下:

  ⑤ 可以查看的内容是【运营组织的所有共享内容】和【运营商有权查看的内容】(来源ID)的交集。无论同一内容的细节是否发生变化,重复分享都会产生一个新的ID,对应一条新的分享记录。

  原型如下:

  4. 角色管理

  角色是权限的集合,充当向后台帐户授予权限的桥梁。操作员可以看到的角色分为两种:一种是管理机构下的操作员拥有的角色,启用了角色模块权限,另一种是操作员组织下的角色,权限小于等于运营商的权限。. 操作员可以通过【组织下拉列表】查看不同的组织角色。

  涉及的具体领域如下:

  5.后台账号管理

  根据实际场景需要,为后台账号配置数据和功能权限。运营商可以看到的账号分为两种:

  一是开通账户模块权限的管理机构(不包括自己的机构)下运营商拥有的后台账户;

  另一个是运营商在自己的组织下自己的层级结构下拥有的后端账号(同一级别的看不到,比如A部门的经理看不到自己和B部门的经理的账号)。

  参考原型如下:

  1.功能权限

  

  ①如果运营商和运营商在不同的组织,选项都是运营商所属组织下的所有角色;

  ②如果操作者和操作者在同一个组织中,选项是权限小于或等于操作者的角色;

  ③ 支持多选;

  2. 组织权限

  ①显示的选项是运营商所在组织有权访问的组织和每个组织有权访问的模块内容(课程包&amp;信息&amp;账号&amp;角色等);

  ② 可操作的选项是运营商有权组织的组织和【运营商组织有权的组织】的交集,模块内容相同。

  3. **内容权利(课程包和信息等)

  ① 一种是跨组织后台账号的内容分享:可以查看的内容是【运营商所属组织共享的所有内容】与【运营商有权限的内容】的交集,运营商有权(分享ID)的内容不能被分享。

  原型如下:

  注意:如果分享的内容之前已经分享给运营商和组织的其他账号aa,运营商获取的内容应该与aa账号下的内容一致。

  因此,更规范的操作流程是:在跨组织共享时,将内容同步分享给运营组织的管理员后,稍后再使用管理员账号或其他账号在组织内共享。

  ②另一种是与组织后台账号的内容分享。可以查看的内容是运营商有权限的内容,运营商已经有权限的内容是不能分享的。

  原型如下:

  注意:跨组织共享后一个产品,相当于复制了另一个具有相同内容的产品,后续的任何更改都不会同步。与组织分享后,还是一样的内容,后续的任何改动都会同步。

  6.前端账户管理

  前端用户可以在门户网站上看到自己组织的授权前端模块。如果有场景需求,可以细化同一组织下不同前端用户的设置。前端数据隔离有两种类型:

  ①不同组织发布的内容,只有该组织的前端用户才能看到。

  ② 对于SaaS服务商为多个租户提供内容服务的业务,可以对其发布的内容进行特殊处理,使其发布的内容可以被所有组织的前端用户看到,但不同组织产生的用户内容只能是组织的用户看到。

  前台用户涉及的字段如下:

  概括

  传统 SaaS 系统设计中使用的概念或思想大概类似,但是否需要跨组织管理,跨组织管理需要细化到什么程度。

  不同组织的用户数据,同一组织的用户数据如何隔离,处理方式是否相同等都是根据实际业务场景设计的。

  没有完全标准和通用的SaaS系统。我们需要设计的不是一个完美的SaaS,而是一个最大程度满足业务需求的SaaS系统,在通用的同时还可以兼顾后续长期规划,从而降低成本,提高尽可能提高效率,提升用户体验。系统。

  这部分分享到此结束,希望这篇文章可以帮助到需要的人~

  本文由@Grace 原创 发布,人人都是产品经理,未经允许禁止转载。

  标题图片来自 Unsplash,基于 CC0 协议。

  本文观点仅代表作者本人,大家都是产品经理。平台仅提供信息存储空间服务。

  奖励作者,鼓励TA抓紧创作!

  欣赏

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线