使用 Typescript 开发小程序前端错误及性能监控 SDK

2021-06-08 10:12:22 +08:00
 alex1504

前言

上篇文章分析了web 端错误及性能监控 SDK的实现方式,本篇则聚焦于小程序。尽管微信小程序本身就自带了监控能力,但如果我们需要更完善的数据,比如在出错时展示更多的信息如函数调用栈、行为轨迹、缓慢请求等,则需要自己去监控收集。

原来在开发这套前端监控 SDK 时,我将 web 和小程序的监控糅合在了一起,但后来发现平台差异使得同一个模块产生很多异化的逻辑,甚至在初始化的时候也要增加环境的判断,这种异化的处理降低了后续的可维护性,因此最终还是将 SDK 拆分成两套。

在开发小程序监控 SDK 时,首先明确的是,SDK 需要提供什么样的能力,或者说帮助我们获取什么数据。由于要获取的数据上篇已提及,这里看看我们设计的小程序端的 SDK 需要提供什么能力。

SDK 提供的能力

基础监控

附加能力

SDK 在最终使用上依然采用基于事件订阅的方式,下面分析下这些能力在小程序端如何实现

错误监控

使用小程序生命周期提供了 onError,重写 Page 即可

App = function (appOptions) {
  appHooks.forEach((methodName) => {
    const originMethod = appOptions[methodName];

    (appOptions as any)[methodName] = function (param: any) {
      const error = param as Error;

      if (methodName === "onError") {
        monitor.handleErrorEvent(TrackerEvents.jsError, error);
      }
      
      return originMethod && originMethod.call(this, param);
    };
  });

  return originApp(appOptions);
};

对于 promise 错误,小程序提供onUnhandledRejection,但官方文档指出此事件当前在安卓平台并不会生效,因此需要做一下 hack 处理,通过 console 的劫持进行判断

export function rewriteConsole(monitor: Monitor) {
  for (const key of Object.keys(console)) {
    if (key in console) {
      const methodName = key as KeyofConsole;

      if (typeof console[methodName] !== "function") {
        continue;
      }

      if (!hackConsoleFn.includes(methodName)) {
        continue;
      }

      const originMethod = console[methodName];
      console[methodName] = function (...args: any[]) {
        /**
         * 若是安卓手机则在此捕获 unhandled promise rejection 错误
         */
        if (args[0] === "Unhandled promise rejection") {
          const error = args[1] as Error;

          monitor.getSystemInfo().then((res) => {
            const isNeedEmit = hackUnhandledRejectionPlatform.includes(
              res.platform
            );
            if (isNeedEmit) {
              monitor.handleErrorEvent(TrackerEvents.unHandleRejection, error);
            }
          });
        }

        originMethod.call(this, ...args);
      };
    }
  }
}

性能监控

使用小程序提供的 performance api

export function observePagePerformance(monitor: Monitor): void {
  const canIUse = wx.canIUse("Performance");

  if (monitor.performanceData.length) return;
  if (!canIUse) {
    return;
  }

  const performance = wx.getPerformance();

  const observer = performance.createObserver(
    (entryList: WechatMiniprogram.EntryList) => {
      const performanceData: PerformanceData = entryList.getEntries();
      // ,,,
    }
  );
  observer.observe({ entryTypes: ["render", "script"] });
}

HTTP 监控

拦截 wx.request 取值,并且对 options.success 及 options.fail 进行重写

export function interceptRequest(monitor: Monitor) {
  const originRequest = wx.request;
  Object.defineProperty(wx, "request", {
    configurable: false,
    enumerable: false,
    writable: false,
    value: function (options: WechatMiniprogram.RequestOption) {
      const originSuccess = options.success;
      const originFail = options.fail;

      options.success = function (...args) {
      };

      options.fail = function (...args) {
      };

      return originRequest.call(this, options);
    }
  });
}

用户行为轨迹

在小程序中,用户行为轨迹我定义为以下类型,并用队列保存:

export enum IBehaviorItemType {
  fn = "function",
  console = "console",
  http = "http",
  tap = "tap",
  custom = "custom"
}

我们通过重写 API 就能获取以上的信息

值得注意的是,SDK 要监控页面元素点击仍需要我们做些手动工作。

由于小程序不存在 dom,并不具备类似 web 提供window.addEventListener的能力,通过重写 Page 只是为了给 PageOptions 注入一个事件处理方法onElementTrack,因此在页面根节点需要对元素做事件绑定才能触达 SDK 收集。

// index.wxml
<view class="container" catchtap="onElementTrack"></view>

自定义行为

某些情况下,你需要收集特定地方的埋点行为,只需要在埋点处调用 pushBehavior 方法即可。

public pushBehaviorItem(item: IBehaviorItem): IBehaviorItem {
    if (!item.type) {
      item.type = IBehaviorItemType.custom;
    }

    if (!item.time) {
      item.time = Date.now();
    }

    const { queueLimit } = this.$options.behavior;

    if (this.behavior.length >= queueLimit) {
      this.behavior.shift();
    }

    this.behavior.push(item);

    return item;
}

小程序 SDK 完整代码实现:欢迎 star 、fork 、issue 。

https://github.com/alex1504/femonitor-wx

1747 次点击
所在节点    分享创造
1 条回复
component
2021-06-08 14:11:24 +08:00
star 支持一下

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/782071

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX