背景
当前项目所用的是代码埋点,由于历史原因这种侵入式埋点竟还同时存在两套(其实算三套)分别是apm和通过公司接口曝光exposure
上报统计(历史原因还有trackOsrRecord
同样是接口上报统计)。代码埋点虽然能精细化的去上报数据,但随着侵入式埋点的增加,存在了一下问题
- 业务需求频繁迭代,埋点代码误删,导致线上统计失精;
- 重复埋点,不同产品来一波埋点需求,搞着搞着存在很多重复埋点;
- 项目中的埋点字段意义不清晰,不知道打的点干嘛用的;
- 耦合业务,雪上加霜的是项目存在几种埋点方法。
- 多项目使用,每个项目引入写得不一样,不仅失去复用性,还增加代码阅读难度。
如:
// 埋点之一:apm
this.$apm.addCountReocord('record', 'action', {
name: 'complete_task_from_card'
})
// 埋点之二:exposure接口上报
this.eventReport({
action: 'join_position',
channel: null,
data: null,
key: 'event_iphone',
module: '',
path: '',
trigger: 'click'
})
// 埋点之二的同胞兄弟,浏览记录addRecord上报
function trackRecord(params) {
//...其他params过滤操作,end
await addRecord(params) // addRecord是一个接口上报
},
采取措施
鉴于埋点是不可缺少的一块监控内容,为了更好的规范管理埋点使用,需要对项目中的代码埋点做了以下优化:
- 统一埋点方法:抽离埋点方法,npm包引入使用,可复用多项目;
- 埋点语义化:埋点方法调用强制注明埋点的作用(或详细信息);
- 生成路由与埋点映射:通过
webpack plugin
生成页面路由与埋点的关系,可直观看到整个项目埋点分布(还可具体到代码哪行哪列)。
以上仍然采用的是命令式的埋点,有兴趣的伙伴还可以在项目中声明式的埋点使得埋点更加整洁
// 预期调用
this.$trackReport({
key: 'view_iphone_detail',
msg: 'apm_iphone页_点击查看detail'
})
统一埋点方法调用
这里没有那么多弯弯绕绕,就是把多种埋点方法根据类型type
做一个策略,通过参数type
以及params参数
调用不同的埋点方法。
例如,通过npm包track
暴露trackReport
是策略方法,接收params打点参数,config埋点需要的配置如token uid
getDefaultTrackConfi
其实就是获取默认的config,使用中可以根据不同的想去做覆盖,如覆盖exposure接口上报的接口url。为全局方便调用最后挂载到Vue prototype原型链中。
// 初始化埋点方法,并挂载到原型链中
import Vue from 'vue'
import { trackReport, getDefaultTrackConfig } from 'track'
/**
* 描述: 埋点事件对应的传参,这里的对象的key就是调用时的key做匹配。
* this.$trackReport({
* key: 'view_iphone_detail',
* msg: 'apm_iphone页_点击查看detail'
* })
*/
const eventMapParams = {
view_iphone: () => ({
type: 'apm',
params: {
name: 'iphone',
type: 'view',
spans: { name: 'view_iphone_list' }
}
}),
view_iphone_detail: (that) => ({
// that为当前调用组件的上下文this,以获取动态的数据
type: 'apm',
params: {
name: 'iphone_detail',
type: 'view_detail',
spans: { money: that.money }
}
})
}
export const initTrack = ({ token, userInfo }) => {
const config = getDefaultTrackConfig({ token, userInfo })
Vue.prototype.$trackReport = function(info) {
// 根据传入的信息info匹配params
const { key } = info || {}
const params = eventMapParams[key](this)
trackReport(params, config)
}
}
这里特别说明的是eventMapParams
获取当前埋点事件的params(暂时没想到其他办法去管理params......),毕竟埋点上报的参数各异,统一放在这里还是比较直观好管理的,总比散落在业务组件里好吧~~
npm 包开发,发布
安装TypeScript
强烈建议用TypeScript做强类型检查以及非常友好的提示。
比如,通过 /** */ 形式的注释可以给 TS 类型做标记提示,编辑器会有更好的提示tsconfig.json
文件中指定了用来编译这个项目的根文件和编译选项,更多搓ts官方文档
// yarn安装
yarn add tslib typescript -D
// 生成ts配置文件,tsconfig.json可用来配置编译成的js版本、tslint检查等
npx tsc --init
Rollup打包配置
众所周知,rollup有着更好的tree-shaking
对应开发类库属实好用的,当然使用webpack也可以。具体看这篇好文
- rollup 是在编译打包过程中分析程序流,得益于于 ES6 静态模块(exports 和 imports 不能在运行时修改),我们在打包时就可以确定哪些代码时我们需要的。
- webpack 本身在打包时只能标记未使用的代码而不移除,而识别代码未使用标记并完成 tree-shaking 的 其实是 UglifyJS、babili、terser 这类压缩代码的工具。简单来说,就是压缩工具读取 webpack 打包结果,在压缩之前移除 bundle 中未使用的代码
安装:
yarn add rollup -D
新建rollup.config.js
文件,配置如下:
// 识别并转译ts
import typescript from '@rollup/plugin-typescript'
// 解析引入npm包,以允许我们加载第三方模块
import resolve from 'rollup-plugin-node-resolve'
// ES6 +的JavaScript解析器,mangler和压缩器工具包
import { terser } from 'rollup-plugin-terser'
import pkg from './package.json'
// 末尾可加当前的版本记录
const footer = `
if(typeof window !== 'undefined') {
window._TrackVersion = '${pkg.version}'
}`
export default {
input: './src/index.ts',
output: [
{
file: pkg.main,
format: 'umd',
footer,
name: 'Track',
sourcemap: true,
plugins: [terser()]
}
],
context: 'window',
plugins: [typescript(), resolve()]
}
调试以及发布
npm link
参阅 npm config。
在包根目录内执行npm link / yarn link, 此时会在全局文件夹{prefix}/lib/node_modules/中 创建一个符号链接,该链接链接到 npm link 执行命令的包。
npm link packageName
查看项目的node_modules
就可以看到link进来的包了。
packageName 是取自包的 package.json 中 name 字段 执行 npm link packageName 命令,将会创建一个从全局安装的 packageName 到当前文件内的 node_modules 下的符号链接
npm unlink 解除link
项目中执行 npm unlink --no-save package && npm install 包中执行 npm unlink
npm login && npm publish 发布
npm login 登录npm的账号和密码
npm publish 发布到npm
发布的话可以使用Github Actions自动化去发布,减少手动操作。可参考:NPM_TOKEN
是在github里面配置的,具体戳(感兴趣的小伙伴可以看我之前写的Github Actions文章)
name: dry
on:
pull_request:
branches:
- main # 监听main分支,有pr进来触发workFlow,push的话就分支有更新触发
jobs:
build:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2 # checkout代码
- uses: actions/setup-node@v1 # 集成node
- uses: actions/cache@v2 # cache
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: install and test
run: |
npm install
npm run test
- run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
- name: publish
if: ${{contains(github.ref, 'refs/tags/')}} # 检查git tag
run: |
npm run build
npm publish --access public
webpack plugin生成路由-埋点映射
通过 webpack plugin
生成页面路由与埋点的关系,可直观看到整个项目埋点分布(还可具体到代码哪行哪列),最终效果如下:
这里就不展开了,附上生图:
plugin跑在子进程中,生成的json可用于渲染的界面上,这样就可直观看到整个项目埋点分布啦,以后可以快乐地跟产品🤺击剑了。
总结
以上就是在整理公司埋点时的一些记录,能力有限,如果大伙有更好的实现办法去整理这种代码埋点的话,欢迎👏🏻留言。