这是在公司分享的文本整理,删除敏感数据后的 PPT

前端的痛点

我们的应用部署在客户端,当出现问题,不像服务端,问题发生在我们自己的机器上,前端没有用户日志,用户操作对我们来说是一个黑盒,只能通过提前做一些埋点或者用户反馈去推测问题发生并验证。 前端环境过于复杂,不同型号的手机,不同的系统版本,不同的浏览器内核,不同的网络环境,如果不幸遇到偶发性问题, 复现就变的难上加难,更不用说 debug。

所以我们需要一些可供回溯用户行为的日志去分析

轨迹回放是什么

业界比较成熟的系统有 sentry, logRocket, rrweb

sentry

sentry 是非常流行的错误监控系统,可用于 c++/Go/python/js 等各种语言,js 接入时,sentry 会在特定时机(用户手动上报/前端发生错误时)上报当前环境的上下文信息,包括一段用户的行为日志

通过完善配置或者集成插件等,可以实现 sourcemap 分析,数据可视化分析等。使用 sentry 能在某种意义上解决问题,在 sentry 上观测到问题后,通过代码分析发现问题出现的原因。前端探索无止尽,sentry 还是有一些不甚让人满意的地方

logRocket & rrweb

logRocket & rrweb 提供另外一种可能,通过预埋 sdk,通过完善记录用户行为的操作日志,包括手势行为,交互行为等引起的各种 dom 变动,网络变动等,在特定时机上报到 server 端,通过拉取 server 端相关日志重建用户侧的行为,通过类似视频的形式回放。

这个是 logRocket 的截图,logRocket 的回放平台包括了用户的整个行为回放,用户机器的 console 输出,网络请求等,甚至可以审查 dom 元素。

做用户行为轨迹回放的意义

我们做一套东西,非常关注的是投入产出比,做这个东西到底有没有必要,也就是说做回放的意义是什么

For Developers

For PM

产品经理最关心的是什么,用户!用户行为回放给 PM 提供了最真实最珍贵的第一手资料。

For QA

类似系统的实现(rrweb 为例)

这个录屏系统上传是一系列的用户行为日志,是一段带时间戳的事件文本,而不是真实的视频文件/canavs 图等。以 rrweb 为例,rrweb 主要分为三个组成部分,分别是

rrweb 的核心设计有

序列化

真实 DOM 是由浏览器实现的一套标准,它不可被传输,其一个 dom 节点涵盖过多的我们并不关系的数据,所以为了对真实 DOM 打快照并进行传输,rrweb 做了一套自己的序列化。(可以简单理解为真实 DOM 转为虚拟 DOM 再转为一套文本)

<html>
  <body>
    <header></header>
  </body>
</html>

会被解析为

{
  "type": "Document",
  "childNodes": [
    {
      "type": "Element",
      "tagName": "html",
      "attributes": {},
      "childNodes": [
        {
          "type": "Element",
          "tagName": "head",
          "attributes": {},
          "childNodes": [],
          "id": 3
        },
        {
          "type": "Element",
          "tagName": "body",
          "attributes": {},
          "childNodes": [
            {
              "type": "Text",
              "textContent": "\n    ",
              "id": 5
            },
            {
              "type": "Element",
              "tagName": "header",
              "attributes": {},
              "childNodes": [
                {
                  "type": "Text",
                  "textContent": "\n    ",
                  "id": 7
                }
              ],
              "id": 6
            }
          ],
          "id": 4
        }
      ],
      "id": 2
    }
  ],
  "id": 1
}

内容来自 rrweb 序列化

增量快照

rrweb 并不是对每帧或者每几帧生成一次快照,这里有点 git 的设计,在特定时机生成一次全量快照,在之后的操作过程中,用户的每一次操作生成一条日志,有点类似用户初始代码仓库时 git init 了一下,之后每次提交都commit

增量快照记录的事件包括

每次变化都会生成一条数据,数据格式为

{
  type: EventType;
  timeStamp: number;
  data: {
  }
}

详细可以参考 增量快照

沙盒回放

因为现在是需要根据记录的日志去回播生成一个操作序列,代码执行发生在我们自己的容器内,所以我们需要做一些事情比如

rrweb 针对性的做了以下处理

可行性分析

经过一些测试,发现用户行为回放的主要瓶颈是日志大小的问题,在复杂 DOM 环境下,简单操作半分钟会生成 1-20M 的数据,对用户流量,服务器的 IO 以及存储是个考验。

针对日志大小的问题,可以考虑以下优化

参考