小程序埋点与性能监控体系:从 wx.getPerformance 到线上告警

小程序埋点与性能监控体系:从 wx.getPerformance 到线上告警

admin
2026-06-19 / 0 评论 / 131 阅读

小程序埋点与性能监控体系:从 wx.getPerformance 到线上告警

"线上有点卡"——这种反馈没法排查:哪个页面、哪个环节、什么机型、什么网络?没有监控体系的小程序,性能问题全靠猜。这篇搭一套完整的可观测方案:性能指标采集、业务埋点规范、异常捕获、上报通道和告警闭环。

一、监控什么:指标体系

1.1 性能指标(技术侧)

指标采集方式关注点
启动耗时wx.getPerformance下载/注入/首屏渲染分段
路由切换耗时手动打点onShow 到可交互
setData 耗时wx.getPerformance + 手动单次通信量和频率
请求耗时拦截器打点P95 接口慢查询
白屏率/错误率异常捕获稳定性红线

1.2 业务指标(产品侧)

漏斗模型:曝光 → 点击 → 提交 → 支付成功。技术指标告诉你"哪里慢",业务指标告诉你"哪里漏"。

二、性能指标采集

2.1 wx.getPerformance

// app.js onLaunch 里启动观察者
const perf = wx.getPerformance()
const observer = perf.createObserver((entryList) => {
  entryList.getEntries().forEach(entry => {
    switch (entry.name) {
      case 'appLaunch':       // 启动总耗时
        report('perf_launch', {
          duration: entry.duration,
          path: entry.path,
          scene: entry.scene   // 进入场景值,很关键
        })
        break
      case 'firstRender':     // 首次渲染
        report('perf_first_render', { duration: entry.duration })
        break
      case 'evaluateScript':  // 逻辑层注入
        report('perf_evaluate', { duration: entry.duration })
        break
    }
  })
})
observer.observe({ entryTypes: ['navigation', 'render', 'script'] })

启动耗时的正确拆解appLaunch = 代码包下载 + 代码注入 + 首次渲染 + 首屏接口。分别打点才能定位瓶颈:下载慢是包体积问题,注入慢是主包代码量问题,渲染慢是首屏 setData 数据量问题,接口慢是后端问题——处方不同,诊断必须分开

2.2 页面级打点

// utils/perf-track.js
function trackPageShow(page) {
  page._showAt = Date.now()
}
function trackPageReady(page) {
  const cost = Date.now() - page._showAt
  report(`perf_page_${page.route}`, { cost })
}

// 劫持 Page 构造器统一埋点(一次接入全局生效)
const originPage = Page
Page = function (config) {
  const { onShow, onReady } = config
  config.onShow = function () {
    trackPageShow(this)
    onShow && onShow.call(this)
  }
  config.onReady = function () {
    trackPageReady(this)
    onReady && onReady.call(this)
  }
  originPage(config)
}

2.3 setData 监控

高频 setData 是性能杀手,全局埋点监控:

// 劫持 setData,记录每次调用数据量
function wrapSetData(page) {
  const origin = page.setData.bind(page)
  page.setData = function (data, callback) {
    const size = JSON.stringify(data).length
    if (size > 100 * 1024) {  // 单次超 100KB 告警
      report('perf_setdata_oversize', {
        page: page.route, size,
        keys: Object.keys(data).slice(0, 10)
      })
    }
    const start = Date.now()
    origin(data, () => {
      callback && callback()
      report('perf_setdata', { page: page.route, size, cost: Date.now() - start })
    })
  }
}

三、业务埋点规范

3.1 事件模型

统一 schema,别每个页面自由发挥:

// 埋点 = 谁 + 在哪 + 做了什么 + 结果如何
track('order_submit', {
  page: 'pages/order/confirm',     // 页面路径
  biz_params: {                    // 业务参数
    goodsCount: 3,
    amount: 99.0,
    couponUsed: true
  },
  result: 'success',               // success / fail
  failReason: ''                   // 失败原因
})

3.2 曝光埋点的坑

列表项曝光不能用 onShow(整页级别太粗),用 IntersectionObserver:

// 组件 attach 时注册观察器,进入视口才算曝光
Component({
  lifetimes: {
    attached() {
      this._observer = this.createIntersectionObserver()
      this._observer.relativeToViewport({ bottom: 0 }).observe('.card', (res) => {
        if (res.intersectionRatio > 0.5 && !this._exposed) {
          this._exposed = true          // 只曝光一次
          track('goods_expose', { id: this.data.item.id })
          this._observer.disconnect()   // 观察完就断开,省性能
        }
      })
    },
    detached() { this._observer && this._observer.disconnect() }
  }
})

3.3 漏斗分析

把关键路径事件串起来,服务端按 session_id 聚合:

session: abc123
  10:00:01  home_banner_expose
  10:00:05  home_banner_click
  10:00:12  goods_detail_view
  10:00:40  cart_add
  10:01:15  order_submit (success)
  10:01:30  pay_success

转化率 = 后一环节人数 / 前一环节人数。掉率异常的环节,就是产品和技术共同盯的靶子。

四、异常捕获

4.1 JS 错误

// app.js
App({
  onError(err) {
    report('error_js', {
      message: err.message,
      stack: err.stack.slice(0, 2000),   // 截断,别把上报接口自己打爆
      route: getCurrentPageRoute(),
      version: APP_VERSION                // 版本号必须带,定位灰度问题
    })
  }
})

4.2 Promise 拒绝

// onError 捕获不到未处理的 Promise rejection
App({
  onUnhandledRejection(res) {
    report('error_promise', {
      reason: String(res.reason).slice(0, 1000)
    })
  }
})

4.3 请求失败

请求封装层统一捕获(业务错误码 + 网络错误分类上报):

function request(options) {
  return new Promise((resolve, reject) => {
    wx.request({
      ...options,
      success: (res) => {
        if (res.statusCode >= 500) {
          report('error_http_5xx', { url: options.url, code: res.statusCode })
        }
        resolve(res)
      },
      fail: (err) => {
        report('error_network', { url: options.url, msg: err.errMsg })
        reject(err)
      }
    })
  })
}

JSError 聚合:上报时对 stack 做 fingerprint(取函数名+文件名哈希),服务端按 fingerprint 聚合数量,否则同一个错误刷出几万条没法看。

五、上报通道设计

5.1 批量 + 采样

// utils/report.js
const queue = []
let timer = null

function report(event, data) {
  // 采样:非错误类事件 30% 采样率足够
  if (!event.startsWith('error') && Math.random() > 0.3) return

  queue.push({
    event, data,
    ts: Date.now(),
    sessionId: getApp().globalData.sessionId,
    openid: getApp().globalData.openid || '',
    systemInfo: getApp().globalData.systemInfo,  // 机型/系统/基础库,启动时采集一次缓存
    version: APP_VERSION
  })

  // 攒 10 条或 5 秒批量发
  if (queue.length >= 10 || timer) return
  timer = setTimeout(flush, 5000)
}

function flush() {
  timer = null
  if (!queue.length) return
  const batch = queue.splice(0, 20)
  wx.request({
    url: 'https://track.example.com/batch',
    method: 'POST',
    data: { events: batch },
    fail: () => {
      // 失败回队,最多重试一次,别无限堆积
      if (queue.length < 200) queue.unshift(...batch)
    }
  })
}

设计要点

  1. 批量上报:逐条上报浪费请求数,也容易被限流
  2. 错误类全量、行为类采样:监控成本和有效性平衡
  3. 体积上限:队列封顶,极端情况下丢弃最旧数据,防止内存堆积
  4. onHide 时强制 flush:切后台立刻发送,别等定时器

5.2 上报域名要求

域名必须在微信后台配置 request 合法域名,且用独立子域名(track.xxx.com)——与业务接口隔离,统计流量波动不影响业务,业务接口故障也不影响监控数据回收。

六、告警闭环

采集只是开始,告警才产生价值:

规则阈值动作
JSError 突增5 分钟内同 fingerprint > 100立即电话/短信
接口 5xx 率> 1% 持续 5 分钟群告警
启动 P90> 5s 持续 30 分钟群告警
支付成功率环比跌 10%立即电话

告警分级是关键:全是紧急告警等于没有告警。资损类电话叫醒,体验类群消息,趋势类日报周报。

七、避坑清单

现象解法
埋点代码入侵业务改个埋点改十处劫持 Page/请求层统一埋点
上报打爆自己错误风暴连环上报fingerprint 聚合 + 队列上限
没带版本号无法定位是哪个版本的问题全事件带 APP_VERSION
曝光重复统计滚动来回算多次_exposed 标记一次性
白名单域名忘了配上报静默失败上报 fail 打本地日志
只看均值长尾用户被平均看分位数 P90/P95

写在最后

监控体系的建设顺序建议:先异常(保命)→ 再性能(体检)→ 最后业务(增长)。异常监控上线第一天就能发现存量 bug;性能基线建立后,每次发版有数据回归;业务漏斗则是产品迭代的弹药库。三步都不复杂,难的是坚持让"数据说话"成为团队习惯。

0

评论 (0)

取消
0:00