├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── babel.cjs.json
├── babel.esm.json
├── demo
├── app.js
├── app.json
├── app.wxss
├── miniprogram_npm
│ └── @cloudcare
│ │ └── rum-miniapp
│ │ ├── boot
│ │ ├── buildEnv.js
│ │ ├── rum.entry.js
│ │ └── rum.js
│ │ ├── core
│ │ ├── baseInfo.js
│ │ ├── configuration.js
│ │ ├── dataMap.js
│ │ ├── downloadProxy.js
│ │ ├── errorCollection.js
│ │ ├── errorTools.js
│ │ ├── lifeCycle.js
│ │ ├── observable.js
│ │ ├── sdk.js
│ │ ├── transport.js
│ │ └── xhrProxy.js
│ │ ├── helper
│ │ ├── enums.js
│ │ ├── tracekit.js
│ │ └── utils.js
│ │ ├── index.js
│ │ └── rumEventsCollection
│ │ ├── app
│ │ ├── appCollection.js
│ │ └── index.js
│ │ ├── assembly.js
│ │ ├── error
│ │ └── errorCollection.js
│ │ ├── page
│ │ ├── index.js
│ │ └── viewCollection.js
│ │ ├── parentContexts.js
│ │ ├── performanceCollection.js
│ │ ├── requestCollection.js
│ │ ├── resource
│ │ ├── resourceCollection.js
│ │ └── resourceUtils.js
│ │ ├── trackEventCounts.js
│ │ └── transport
│ │ └── batch.js
├── package.json
├── pages
│ ├── index
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ ├── logs
│ │ ├── logs.js
│ │ ├── logs.json
│ │ ├── logs.wxml
│ │ └── logs.wxss
│ ├── rum.js
│ ├── rum.json
│ ├── rum.wxml
│ └── rum.wxss
├── project.config.json
├── sitemap.json
└── utils
│ └── util.js
├── demo2
├── .hbuilderx
│ └── launch.json
├── App.vue
├── demo2
│ ├── App.vue
│ ├── main.js
│ ├── manifest.json
│ ├── pages.json
│ ├── pages
│ │ └── index
│ │ │ └── index.vue
│ ├── static
│ │ └── logo.png
│ └── uni.scss
├── main.js
├── manifest.json
├── miniprogram
│ ├── dataflux-rum-miniapp.js
│ └── dataflux-rum-miniapp.js.map
├── pages.json
├── pages
│ ├── home
│ │ └── home.vue
│ └── index
│ │ └── index.vue
├── static
│ └── logo.png
├── test-sdk
│ ├── App.vue
│ ├── main.js
│ ├── manifest.json
│ ├── pages.json
│ ├── pages
│ │ └── index
│ │ │ └── index.vue
│ ├── static
│ │ └── logo.png
│ └── uni.scss
├── uni.scss
└── unpackage
│ └── dist
│ └── dev
│ ├── .automator
│ ├── mp-alipay
│ │ └── .automator.json
│ └── mp-weixin
│ │ └── .automator.json
│ ├── .sourcemap
│ └── mp-weixin
│ │ ├── common
│ │ ├── main.js.map
│ │ ├── runtime.js.map
│ │ └── vendor.js.map
│ │ └── pages
│ │ ├── home
│ │ └── home.js.map
│ │ └── index
│ │ └── index.js.map
│ └── mp-weixin
│ ├── app.js
│ ├── app.json
│ ├── app.wxss
│ ├── common
│ ├── main.js
│ ├── main.wxss
│ ├── runtime.js
│ └── vendor.js
│ ├── pages
│ ├── home
│ │ ├── home.js
│ │ ├── home.json
│ │ └── home.wxml
│ └── index
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ ├── project.config.json
│ ├── sitemap.json
│ └── static
│ └── logo.png
├── package.json
├── scripts
├── build-env.js
└── replace-build-env.js
├── src
├── boot
│ ├── buildEnv.js
│ ├── rum.entry.js
│ └── rum.js
├── core
│ ├── baseInfo.js
│ ├── configuration.js
│ ├── dataMap.js
│ ├── downloadProxy.js
│ ├── errorCollection.js
│ ├── errorTools.js
│ ├── lifeCycle.js
│ ├── miniaTouch.js
│ ├── observable.js
│ ├── sdk.js
│ ├── transport.js
│ └── xhrProxy.js
├── helper
│ ├── enums.js
│ ├── tracekit.js
│ └── utils.js
├── index.js
└── rumEventsCollection
│ ├── action
│ ├── actionCollection.js
│ └── trackActions.js
│ ├── app
│ ├── appCollection.js
│ └── index.js
│ ├── assembly.js
│ ├── error
│ └── errorCollection.js
│ ├── page
│ ├── index.js
│ └── viewCollection.js
│ ├── parentContexts.js
│ ├── performanceCollection.js
│ ├── requestCollection.js
│ ├── resource
│ ├── resourceCollection.js
│ └── resourceUtils.js
│ ├── setDataCollection.js
│ ├── tracing
│ ├── ddtraceTracer.js
│ ├── jaegerTracer.js
│ ├── skywalkingTracer.js
│ ├── tracer.js
│ ├── w3cTraceParentTracer.js
│ ├── zipkinMultiTracer.js
│ └── zipkinSingleTracer.js
│ ├── trackEventCounts.js
│ ├── trackPageActiveites.js
│ └── transport
│ └── batch.js
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | cjs
2 | esm
3 | bundle
4 | demo
5 | demo2
6 | node_modules
7 | .vscode
8 | *.pyc
9 | *.tsbuildinfo
10 | scripts/publish-oss.js
11 | # logs
12 | yarn-error.log
13 | npm-debug.log
14 | lerna-debug.log
15 | local.log
16 |
17 | # ide
18 |
19 | .idea
20 | *.sublime-*
21 | # misc
22 | .DS_Store
23 | ._*
24 | .Spotlight-V100
25 | .Trashes
26 |
27 | package-lock.json
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | *
2 | !/cjs/**/*
3 | !/esm/**/*
4 | *.tsbuildinfo
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 CloudCare
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 微信小程序DataFlux RUM 数据采集SDK
2 | 通过引入sdk文件,监控小程序性能指标,错误log,以及资源请求情况数据,上报到DataFlux 平台datakit
3 |
4 | ## 使用方法
5 | ### 在小程序的app.js文件以如下方式引入代码
6 | ### npm 引入(可参考微信官方[npm引入方式](https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html))
7 | ```javascript
8 | const { datafluxRum } = require('@cloudcare/rum-miniapp')
9 | // 初始化 Rum
10 | datafluxRum.init({
11 | datakitOrigin: 'https://datakit.xxx.com/',// 必填,Datakit域名地址 需要在微信小程序管理后台加上域名白名单
12 | applicationId: 'appid_xxxxxxx', // 必填,dataflux 平台生成的应用ID
13 | env: 'testing', // 选填,小程序的环境
14 | version: '1.0.0' // 选填,小程序版本
15 | })
16 | ```
17 | ### CDN 下载文件本地方式引入([下载地址](https://static.dataflux.cn/miniapp-sdk/v2/dataflux-rum-miniapp.js))
18 |
19 | ```javascript
20 | const { datafluxRum } = require('./lib/dataflux-rum-miniapp.js')
21 | // 初始化 Rum
22 | datafluxRum.init({
23 | datakitOrigin: 'https://datakit.xxx.com/',// 必填,Datakit域名地址 需要在微信小程序管理后台加上域名白名单
24 | applicationId: 'appid_xxxxxxx', // 必填,dataflux 平台生成的应用ID
25 | env: 'testing', // 选填,小程序的环境
26 | version: '1.0.0' // 选填,小程序版本
27 | })
28 | ```
29 |
30 | ## 配置
31 |
32 | ### 初始化参数
33 |
34 | | 参数 | 类型 | 是否必须 | 默认值 | 描述 |
35 | | ----------------------------------------------- | ------- | -------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
36 | | `applicationId` | String | 是 | | 从 dataflux 创建的应用 ID |
37 | | `datakitOrigin` | String | 是 | | datakit 数据上报 Origin;`注意:需要在小程序管理后台加上request白名单` |
38 | | `env` | String | 否 | | 小程序 应用当前环境, 如 prod:线上环境;gray:灰度环境;pre:预发布环境 common:日常环境;local:本地环境; |
39 | | `version` | String | 否 | | 小程序 应用的版本号 |
40 | | `sampleRate` | Number | 否 | `100` | 指标数据收集百分比: `100`表示全收集,`0`表示不收集 |
41 | | `traceType` $\color{#FF0000}{新增}$ | Enum | 否 | `ddtrace` | 与 APM 采集工具连接的请求header类型,目前兼容的类型包括:`ddtrace`、`zipkin`、`skywalking_v3`、`jaeger`、`zipkin_single_header`、`w3c_traceparent`。*注: opentelemetry 支持 `zipkin_single_header`,`w3c_traceparent`,`zipkin`三种类型* |
42 | | `traceId128Bit` $\color{#FF0000}{新增}$ | Boolean | 否 | `false` | 是否以128位的方式生成 `traceID`,与`traceType` 对应,目前支持类型 `zipkin`、`jaeger` |
43 | | `allowedTracingOrigins` $\color{#FF0000}{新增}$ | Array | 否 | `[]` | 允许注入 `trace` 采集器所需header头部的所有请求列表。可以是请求的origin,也可以是是正则,origin: `协议(包括://),域名(或IP地址)[和端口号]` 例如:`["https://api.example.com", /https:\/\/.*\.my-api-domain\.com/]` |
44 | | `trackInteractions` | Boolean | 否 | `false` | 是否开启用户行为采集 |
45 |
46 | ## 注意事项
47 |
48 | 1. `datakitOrigin` 所对应的datakit域名必须在小程序管理后台加上request白名单
49 | 2. 因为目前微信小程序请求资源API`wx.request`、`wx.downloadFile`返回数据中`profile`字段目前ios系统不支持返回,所以会导致收集的资源信息中和timing相关的数据收集不全。目前暂无解决方案,[request](https://developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html), [downloadFile](https://developers.weixin.qq.com/miniprogram/dev/api/network/download/wx.downloadFile.html) ;[API支持情况](https://developers.weixin.qq.com/community/develop/doc/000ecaa8b580c80601cac8e6f56000?highLine=%2520request%2520profile)
50 | 3. `trackInteractions` 用户行为采集开启后,因为微信小程序的限制,无法采集到控件的内容和结构数据,所以在小程序 SDK 里面我们采取的是声明式编程,通过在 wxml 文件里面设置 data-name 属性,可以给 交互元素 添加名称,方便后续统计是定位操作记录, 例如:
51 | ```js
52 |
53 | ```
54 |
55 |
--------------------------------------------------------------------------------
/babel.cjs.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/env",
5 | {
6 | "targets": {
7 | "esmodules": false
8 | },
9 | "modules": "cjs"
10 | }
11 | ]
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/babel.esm.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/env",
5 | {
6 | "targets": {
7 | "esmodules": true
8 | },
9 | "modules": false
10 | }
11 | ]
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/demo/app.js:
--------------------------------------------------------------------------------
1 | // //app.js
2 | const { datafluxRum } = require('./miniprogram/dataflux-rum-miniapp')
3 | // // 初始化 Sentry
4 | // const { datafluxRum } = require('@cloudcare/rum-miniapp')
5 |
6 | datafluxRum.init({
7 | datakitOrigin: 'http://172.16.2.201:31845',
8 | applicationId: 'appid_6cb4c98eba9143c88c83e544407b1c74',
9 | env: 'prod',
10 | version: '1.0.0',
11 | trackInteractions: true,
12 | allowedTracingOrigins: ['http://testing-ft2x-api.cloudcare.cn','http://172.16.5.9:5001'],
13 | traceType: 'skywalking_v3'
14 | })
15 | // datafluxRum.setUser({
16 | // id: '1234',
17 | // name: 'John Doe',
18 | // email: 'john@doe.com',
19 | // })
20 | // datafluxRum && datafluxRum.addRumGlobalContext('isvip', 'xxxx');
21 | // datafluxRum.addRumGlobalContext('activity', {
22 | // hasPaid: true,
23 | // amount: 23.42
24 | // });
25 | // const { datafluxRum } = require('@cloudcare/rum-miniapp')
26 | // // // 初始化 Sentry
27 | // datafluxRum.init({
28 | // datakitOrigin: 'http://10.100.64.161:9529/',// 必填,Datakit域名地址 需要在微信小程序管理后台加上域名白名单
29 | // applicationId: 'appid_14eae490469e11eba9eb920038d3be75', // 必填,dataflux 平台生成的应用ID
30 | // env: 'testing', // 选填,小程序的环境
31 | // version: '1.0.0', // 选填,小程序版本
32 | // })
33 | // wx.request({
34 | // url: 'url',
35 | // })
36 | // var UNKNOWN_FUNCTION = '?'
37 | // function computeStackTraceFromStackProp(ex) {
38 | // if (!ex.stack) {
39 | // return
40 | // }
41 |
42 | // // tslint:disable-next-line max-line-length
43 | // var chrome = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack||\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i
44 | // // tslint:disable-next-line max-line-length
45 | // var gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?|[^@]*bundle)(?::(\d+))?(?::(\d+))?\s*$/i
46 | // // tslint:disable-next-line max-line-length
47 | // var winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i
48 |
49 | // // Used to additionally parse URL/line/column from eval frames
50 | // var isEval
51 | // var geckoEval = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i
52 | // var chromeEval = /\((\S*)(?::(\d+))(?::(\d+))\)/
53 | // var lines = ex.stack.split('\n')
54 | // var stack = []
55 | // var submatch
56 | // var parts
57 | // var element
58 |
59 | // for (var i = 0, j = lines.length; i < j; i += 1) {
60 | // if (chrome.exec(lines[i])) {
61 | // parts = chrome.exec(lines[i])
62 | // var isNative = parts[2] && parts[2].indexOf('native') === 0 // start of line
63 | // isEval = parts[2] && parts[2].indexOf('eval') === 0 // start of line
64 | // submatch = chromeEval.exec(parts[2])
65 | // if (isEval && submatch) {
66 | // // throw out eval line/column and use top-most line/column number
67 | // parts[2] = submatch[1] // url
68 | // parts[3] = submatch[2] // line
69 | // parts[4] = submatch[3] // column
70 | // }
71 | // element = {
72 | // args: isNative ? [parts[2]] : [],
73 | // column: parts[4] ? +parts[4] : undefined,
74 | // func: parts[1] || UNKNOWN_FUNCTION,
75 | // line: parts[3] ? +parts[3] : undefined,
76 | // url: !isNative ? parts[2] : undefined,
77 | // }
78 | // } else if (winjs.exec(lines[i])) {
79 | // parts = winjs.exec(lines[i])
80 | // element = {
81 | // args: [],
82 | // column: parts[4] ? +parts[4] : undefined,
83 | // func: parts[1] || UNKNOWN_FUNCTION,
84 | // line: +parts[3],
85 | // url: parts[2],
86 | // }
87 | // } else if (gecko.exec(lines[i])) {
88 | // parts = gecko.exec(lines[i])
89 | // isEval = parts[3] && parts[3].indexOf(' > eval') > -1
90 | // submatch = geckoEval.exec(parts[3])
91 | // if (isEval && submatch) {
92 | // // throw out eval line/column and use top-most line number
93 | // parts[3] = submatch[1]
94 | // parts[4] = submatch[2]
95 | // parts[5] = undefined // no column when eval
96 | // } else if (i === 0 && !parts[5] && !ex.columnNumber) {
97 | // // FireFox uses this awesome columnNumber property for its top frame
98 | // // Also note, Firefox's column number is 0-based and everything else expects 1-based,
99 | // // so adding 1
100 | // // NOTE: this hack doesn't work if top-most frame is eval
101 | // stack[0].column = ex.columnNumber + 1
102 | // }
103 | // element = {
104 | // args: parts[2] ? parts[2].split(',') : [],
105 | // column: parts[5] ? +parts[5] : undefined,
106 | // func: parts[1] || UNKNOWN_FUNCTION,
107 | // line: parts[4] ? +parts[4] : undefined,
108 | // url: parts[3],
109 | // }
110 | // } else {
111 | // continue
112 | // }
113 |
114 | // if (!element.func && element.line) {
115 | // element.func = UNKNOWN_FUNCTION
116 | // }
117 | // stack.push(element)
118 | // }
119 |
120 | // if (!stack.length) {
121 | // return
122 | // }
123 |
124 | // return {
125 | // stack,
126 | // message: ex.message,
127 | // name: ex.name,
128 | // }
129 | // }
130 | // wx.onError((err) => {
131 | // var error = new Error(err)
132 | // var ERROR_TYPES_RE = /^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$/
133 | // console.log(error.message,'message')
134 | // console.log(error.message.split('\n')[2].match(ERROR_TYPES_RE))
135 | // })
136 | App({
137 | // onError: function(t) {
138 | // var error = new Error(t)
139 | // var ERROR_TYPES_RE = /^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$/
140 | // console.log(Object.getOwnPropertyDescriptors(error),'error')
141 | // var groups = error.message.match(ERROR_TYPES_RE)
142 | // console.log(groups,error.message,'arguments')
143 | // },
144 | onLaunch: function () {
145 | // 展示本地存储能力
146 | var logs = wx.getStorageSync('logs') || []
147 | logs.unshift(Date.now())
148 | wx.setStorageSync('logs', logs)
149 |
150 | // 登录
151 | wx.login({
152 | success: (res) => {
153 | // 发送 res.code 到后台换取 openId, sessionKey, unionId
154 | },
155 | })
156 | // 获取用户信息
157 | wx.getSetting({
158 | success: (res) => {
159 | if (res.authSetting['scope.userInfo']) {
160 | // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
161 | wx.getUserInfo({
162 | success: (res) => {
163 | // 可以将 res 发送给后台解码出 unionId
164 | this.globalData.userInfo = res.userInfo
165 |
166 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
167 | // 所以此处加入 callback 以防止这种情况
168 | if (this.userInfoReadyCallback) {
169 | this.userInfoReadyCallback(res)
170 | }
171 | },
172 | })
173 | }
174 | },
175 | })
176 | },
177 | globalData: {
178 | userInfo: null,
179 | },
180 | })
181 |
--------------------------------------------------------------------------------
/demo/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [
3 | "pages/index/index",
4 | "pages/logs/logs",
5 | "pages/rum"
6 | ],
7 | "window": {
8 | "backgroundTextStyle": "light",
9 | "navigationBarBackgroundColor": "#fff",
10 | "navigationBarTitleText": "Weixin",
11 | "navigationBarTextStyle": "black"
12 | },
13 | "style": "v2",
14 | "sitemapLocation": "sitemap.json"
15 | }
--------------------------------------------------------------------------------
/demo/app.wxss:
--------------------------------------------------------------------------------
1 | /**app.wxss**/
2 | .container {
3 | height: 100%;
4 | display: flex;
5 | flex-direction: column;
6 | align-items: center;
7 | justify-content: space-between;
8 | padding: 200rpx 0;
9 | box-sizing: border-box;
10 | }
11 |
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/boot/buildEnv.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.buildEnv = void 0;
7 | var buildEnv = {
8 | sdkVersion: '2.0.3',
9 | sdkName: 'df_miniapp_rum_sdk'
10 | };
11 | exports.buildEnv = buildEnv;
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/boot/rum.entry.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.datafluxRum = exports.makeRum = void 0;
7 |
8 | var _utils = require("../helper/utils");
9 |
10 | var _rum = require("./rum");
11 |
12 | var makeRum = function makeRum(startRumImpl) {
13 | var isAlreadyInitialized = false;
14 | var rumGlobal = {
15 | init: function init(userConfiguration) {
16 | if (typeof userConfiguration === 'undefined') {
17 | userConfiguration = {};
18 | }
19 |
20 | if (!canInitRum(userConfiguration)) {
21 | return;
22 | }
23 |
24 | startRumImpl(userConfiguration);
25 | isAlreadyInitialized = true;
26 | }
27 | };
28 | return rumGlobal;
29 |
30 | function canInitRum(userConfiguration) {
31 | if (isAlreadyInitialized) {
32 | console.error('DATAFLUX_RUM is already initialized.');
33 | return false;
34 | }
35 |
36 | if (!userConfiguration.applicationId) {
37 | console.error('Application ID is not configured, no RUM data will be collected.');
38 | return false;
39 | }
40 |
41 | if (!userConfiguration.datakitOrigin) {
42 | console.error('datakitOrigin is not configured, no RUM data will be collected.');
43 | return false;
44 | }
45 |
46 | if (userConfiguration.sampleRate !== undefined && !(0, _utils.isPercentage)(userConfiguration.sampleRate)) {
47 | console.error('Sample Rate should be a number between 0 and 100');
48 | return false;
49 | }
50 |
51 | return true;
52 | }
53 | };
54 |
55 | exports.makeRum = makeRum;
56 | var datafluxRum = makeRum(_rum.startRum);
57 | exports.datafluxRum = datafluxRum;
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/boot/rum.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.startRum = void 0;
7 |
8 | var _buildEnv = require("./buildEnv");
9 |
10 | var _lifeCycle = require("../core/lifeCycle");
11 |
12 | var _configuration = require("../core/configuration");
13 |
14 | var _errorCollection = require("../rumEventsCollection/error/errorCollection");
15 |
16 | var _assembly = require("../rumEventsCollection/assembly");
17 |
18 | var _parentContexts = require("../rumEventsCollection/parentContexts");
19 |
20 | var _batch = require("../rumEventsCollection/transport/batch");
21 |
22 | var _viewCollection = require("../rumEventsCollection/page/viewCollection");
23 |
24 | var _requestCollection = require("../rumEventsCollection/requestCollection");
25 |
26 | var _resourceCollection = require("../rumEventsCollection/resource/resourceCollection");
27 |
28 | var _appCollection = require("../rumEventsCollection/app/appCollection");
29 |
30 | var _performanceCollection = require("../rumEventsCollection/performanceCollection");
31 |
32 | var _setDataCollection = require("../rumEventsCollection/setDataCollection");
33 |
34 | var _actionCollection = require("../rumEventsCollection/action/actionCollection");
35 |
36 | var _sdk = require("../core/sdk");
37 |
38 | var startRum = function startRum(userConfiguration) {
39 | var configuration = (0, _configuration.commonInit)(userConfiguration, _buildEnv.buildEnv);
40 | var lifeCycle = new _lifeCycle.LifeCycle();
41 | var parentContexts = (0, _parentContexts.startParentContexts)(lifeCycle);
42 | var batch = (0, _batch.startRumBatch)(configuration, lifeCycle);
43 | (0, _assembly.startRumAssembly)(userConfiguration.applicationId, configuration, lifeCycle, parentContexts);
44 | (0, _appCollection.startAppCollection)(lifeCycle, configuration);
45 | (0, _resourceCollection.startResourceCollection)(lifeCycle, configuration);
46 | (0, _viewCollection.startViewCollection)(lifeCycle, configuration);
47 | (0, _errorCollection.startErrorCollection)(lifeCycle, configuration);
48 | (0, _requestCollection.startRequestCollection)(lifeCycle, configuration);
49 | (0, _performanceCollection.startPagePerformanceObservable)(lifeCycle, configuration);
50 | (0, _setDataCollection.startSetDataColloction)(lifeCycle);
51 | (0, _actionCollection.startActionCollection)(lifeCycle, configuration);
52 | };
53 |
54 | exports.startRum = startRum;
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/core/baseInfo.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports["default"] = void 0;
7 |
8 | var _sdk = require("../core/sdk");
9 |
10 | var _utils = require("../helper/utils");
11 |
12 | var _enums = require("../helper/enums");
13 |
14 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
15 |
16 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
17 |
18 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
19 |
20 | var BaseInfo = /*#__PURE__*/function () {
21 | function BaseInfo() {
22 | _classCallCheck(this, BaseInfo);
23 |
24 | this.sessionId = (0, _utils.UUID)();
25 | this.getDeviceInfo();
26 | this.getNetWork();
27 | }
28 |
29 | _createClass(BaseInfo, [{
30 | key: "getDeviceInfo",
31 | value: function getDeviceInfo() {
32 | try {
33 | var deviceInfo = _sdk.sdk.getSystemInfoSync();
34 |
35 | var osInfo = deviceInfo.system.split(' ');
36 | var osVersion = osInfo.length > 1 && osInfo[1];
37 | var osVersionMajor = osVersion.split('.').length && osVersion.split('.')[0];
38 | var deviceUUid = '';
39 |
40 | if (deviceInfo.host) {
41 | deviceUUid = deviceInfo.host.appId;
42 | }
43 |
44 | this.deviceInfo = {
45 | screenSize: "".concat(deviceInfo.screenWidth, "*").concat(deviceInfo.screenHeight, " "),
46 | platform: deviceInfo.platform,
47 | platformVersion: deviceInfo.version,
48 | osVersion: osVersion,
49 | osVersionMajor: osVersionMajor,
50 | os: osInfo.length > 1 && osInfo[0],
51 | brand: deviceInfo.brand,
52 | model: deviceInfo.model,
53 | frameworkVersion: deviceInfo.SDKVersion,
54 | pixelRatio: deviceInfo.pixelRatio,
55 | deviceUuid: deviceUUid
56 | };
57 | } catch (e) {}
58 | }
59 | }, {
60 | key: "getClientID",
61 | value: function getClientID() {
62 | var clienetId = _sdk.sdk.getStorageSync(_enums.CLIENT_ID_TOKEN);
63 |
64 | if (!clienetId) {
65 | clienetId = (0, _utils.UUID)();
66 |
67 | _sdk.sdk.setStorageSync(_enums.CLIENT_ID_TOKEN, clienetId);
68 | }
69 |
70 | return clienetId;
71 | }
72 | }, {
73 | key: "getNetWork",
74 | value: function getNetWork() {
75 | var _this = this;
76 |
77 | _sdk.sdk.getNetworkType({
78 | success: function success(e) {
79 | _this.deviceInfo.network = e.networkType ? e.networkType : 'unknown';
80 | }
81 | });
82 |
83 | _sdk.sdk.onNetworkStatusChange(function (e) {
84 | _this.deviceInfo.network = e.networkType ? e.networkType : 'unknown';
85 | });
86 | }
87 | }, {
88 | key: "getSessionId",
89 | value: function getSessionId() {
90 | return this.sessionId;
91 | }
92 | }]);
93 |
94 | return BaseInfo;
95 | }();
96 |
97 | var _default = new BaseInfo();
98 |
99 | exports["default"] = _default;
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/core/configuration.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.commonInit = commonInit;
7 | exports.isIntakeRequest = isIntakeRequest;
8 | exports.DEFAULT_CONFIGURATION = void 0;
9 |
10 | var _utils = require("../helper/utils");
11 |
12 | var _enums = require("../helper/enums");
13 |
14 | var TRIM_REGIX = /^\s+|\s+$/g;
15 | var DEFAULT_CONFIGURATION = {
16 | sampleRate: 100,
17 | flushTimeout: 30 * _enums.ONE_SECOND,
18 | maxErrorsByMinute: 3000,
19 |
20 | /**
21 | * Logs intake limit
22 | */
23 | maxBatchSize: 50,
24 | maxMessageSize: 256 * _enums.ONE_KILO_BYTE,
25 |
26 | /**
27 | * beacon payload max queue size implementation is 64kb
28 | * ensure that we leave room for logs, rum and potential other users
29 | */
30 | batchBytesLimit: 16 * _enums.ONE_KILO_BYTE,
31 | datakitUrl: '',
32 |
33 | /**
34 | * arbitrary value, byte precision not needed
35 | */
36 | requestErrorResponseLengthLimit: 32 * _enums.ONE_KILO_BYTE,
37 | trackInteractions: false
38 | };
39 | exports.DEFAULT_CONFIGURATION = DEFAULT_CONFIGURATION;
40 |
41 | function trim(str) {
42 | return str.replace(TRIM_REGIX, '');
43 | }
44 |
45 | function getDatakitUrlUrl(url) {
46 | if (url && url.lastIndexOf('/') === url.length - 1) return trim(url) + 'v1/write/rum';
47 | return trim(url) + '/v1/write/rum';
48 | }
49 |
50 | function commonInit(userConfiguration, buildEnv) {
51 | var transportConfiguration = {
52 | applicationId: userConfiguration.applicationId,
53 | env: userConfiguration.env || '',
54 | version: userConfiguration.version || '',
55 | sdkVersion: buildEnv.sdkVersion,
56 | sdkName: buildEnv.sdkName,
57 | datakitUrl: getDatakitUrlUrl(userConfiguration.datakitUrl || userConfiguration.datakitOrigin),
58 | tags: userConfiguration.tags || []
59 | };
60 |
61 | if ('trackInteractions' in userConfiguration) {
62 | transportConfiguration.trackInteractions = !!userConfiguration.trackInteractions;
63 | }
64 |
65 | return (0, _utils.extend2Lev)(DEFAULT_CONFIGURATION, transportConfiguration);
66 | }
67 |
68 | var haveSameOrigin = function haveSameOrigin(url1, url2) {
69 | var parseUrl1 = (0, _utils.urlParse)(url1).getParse();
70 | var parseUrl2 = (0, _utils.urlParse)(url2).getParse();
71 | return parseUrl1.Origin === parseUrl2.Origin;
72 | };
73 |
74 | function isIntakeRequest(url, configuration) {
75 | return haveSameOrigin(url, configuration.datakitUrl);
76 | }
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/core/dataMap.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.dataMap = exports.commonTags = void 0;
7 |
8 | var _enums = require("../helper/enums");
9 |
10 | // 需要用双引号将字符串类型的field value括起来, 这里有数组标示[string, path]
11 | var commonTags = {
12 | sdk_name: '_dd.sdk_name',
13 | sdk_version: '_dd.sdk_version',
14 | app_id: 'application.id',
15 | env: '_dd.env',
16 | version: '_dd.version',
17 | userid: 'user.user_id',
18 | session_id: 'session.id',
19 | session_type: 'session.type',
20 | is_signin: 'user.is_signin',
21 | device: 'device.brand',
22 | model: 'device.model',
23 | device_uuid: 'device.device_uuid',
24 | os: 'device.os',
25 | os_version: 'device.os_version',
26 | os_version_major: 'device.os_version_major',
27 | screen_size: 'device.screen_size',
28 | network_type: 'device.network_type',
29 | platform: 'device.platform',
30 | platform_version: 'device.platform_version',
31 | app_framework_version: 'device.framework_version',
32 | view_id: 'page.id',
33 | view_name: 'page.route',
34 | view_referer: 'page.referer'
35 | };
36 | exports.commonTags = commonTags;
37 | var dataMap = {
38 | view: {
39 | type: _enums.RumEventType.VIEW,
40 | tags: {
41 | view_apdex_level: 'page.apdex_level',
42 | is_active: 'page.is_active'
43 | },
44 | fields: {
45 | page_fmp: 'page.fmp',
46 | first_paint_time: 'page.fpt',
47 | loading_time: 'page.loading_time',
48 | onload_to_onshow: 'page.onload2onshow',
49 | onshow_to_onready: 'page.onshow2onready',
50 | time_spent: 'page.time_spent',
51 | view_error_count: 'page.error.count',
52 | view_resource_count: 'page.resource.count',
53 | view_long_task_count: 'page.long_task.count',
54 | view_action_count: 'page.action.count',
55 | view_setdata_count: 'page.setdata.count'
56 | }
57 | },
58 | resource: {
59 | type: _enums.RumEventType.RESOURCE,
60 | tags: {
61 | resource_type: 'resource.type',
62 | resource_status: 'resource.status',
63 | resource_status_group: 'resource.status_group',
64 | resource_method: 'resource.method',
65 | resource_url: 'resource.url',
66 | resource_url_host: 'resource.url_host',
67 | resource_url_path: 'resource.url_path',
68 | resource_url_path_group: 'resource.url_path_group',
69 | resource_url_query: 'resource.url_query'
70 | },
71 | fields: {
72 | resource_size: 'resource.size',
73 | resource_load: 'resource.load',
74 | resource_dns: 'resource.dns',
75 | resource_tcp: 'resource.tcp',
76 | resource_ssl: 'resource.ssl',
77 | resource_ttfb: 'resource.ttfb',
78 | resource_trans: 'resource.trans',
79 | resource_first_byte: 'resource.firstbyte',
80 | duration: 'resource.duration'
81 | }
82 | },
83 | error: {
84 | type: _enums.RumEventType.ERROR,
85 | tags: {
86 | error_source: 'error.source',
87 | error_type: 'error.type',
88 | resource_url: 'error.resource.url',
89 | resource_url_host: 'error.resource.url_host',
90 | resource_url_path: 'error.resource.url_path',
91 | resource_url_path_group: 'error.resource.url_path_group',
92 | resource_status: 'error.resource.status',
93 | resource_status_group: 'error.resource.status_group',
94 | resource_method: 'error.resource.method'
95 | },
96 | fields: {
97 | error_message: ['string', 'error.message'],
98 | error_stack: ['string', 'error.stack']
99 | }
100 | },
101 | long_task: {
102 | type: _enums.RumEventType.LONG_TASK,
103 | tags: {},
104 | fields: {
105 | duration: 'long_task.duration'
106 | }
107 | },
108 | action: {
109 | type: _enums.RumEventType.ACTION,
110 | tags: {
111 | action_id: 'action.id',
112 | action_name: 'action.target.name',
113 | action_type: 'action.type'
114 | },
115 | fields: {
116 | duration: 'action.loading_time',
117 | action_error_count: 'action.error.count',
118 | action_resource_count: 'action.resource.count',
119 | action_long_task_count: 'action.long_task.count'
120 | }
121 | },
122 | app: {
123 | alias_key: 'action',
124 | // metrc 别名,
125 | type: _enums.RumEventType.APP,
126 | tags: {
127 | action_id: 'app.id',
128 | action_name: 'app.name',
129 | action_type: 'app.type'
130 | },
131 | fields: {
132 | duration: 'app.duration'
133 | }
134 | }
135 | };
136 | exports.dataMap = dataMap;
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/core/downloadProxy.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.startDownloadProxy = startDownloadProxy;
7 | exports.resetDownloadProxy = resetDownloadProxy;
8 |
9 | var _sdk = require("./sdk");
10 |
11 | var _utils = require("../helper/utils");
12 |
13 | var _enums = require("../helper/enums");
14 |
15 | var downloadProxySingleton;
16 | var beforeSendCallbacks = [];
17 | var onRequestCompleteCallbacks = [];
18 | var originalDownloadRequest;
19 |
20 | function startDownloadProxy() {
21 | if (!downloadProxySingleton) {
22 | proxyDownload();
23 | downloadProxySingleton = {
24 | beforeSend: function beforeSend(callback) {
25 | beforeSendCallbacks.push(callback);
26 | },
27 | onRequestComplete: function onRequestComplete(callback) {
28 | onRequestCompleteCallbacks.push(callback);
29 | }
30 | };
31 | }
32 |
33 | return downloadProxySingleton;
34 | }
35 |
36 | function resetDownloadProxy() {
37 | if (downloadProxySingleton) {
38 | downloadProxySingleton = undefined;
39 | beforeSendCallbacks.splice(0, beforeSendCallbacks.length);
40 | onRequestCompleteCallbacks.splice(0, onRequestCompleteCallbacks.length);
41 | _sdk.sdk.downloadFile = originalDownloadRequest;
42 | }
43 | }
44 |
45 | function proxyDownload() {
46 | originalDownloadRequest = _sdk.sdk.downloadFile;
47 |
48 | _sdk.sdk.downloadFile = function () {
49 | var _this = this;
50 |
51 | var dataflux_xhr = {
52 | method: 'GET',
53 | startTime: 0,
54 | url: arguments[0].url,
55 | type: _enums.RequestType.DOWNLOAD,
56 | responseType: 'file'
57 | };
58 | dataflux_xhr.startTime = (0, _utils.now)();
59 | var originalSuccess = arguments[0].success;
60 |
61 | arguments[0].success = function () {
62 | reportXhr(arguments[0]);
63 |
64 | if (originalSuccess) {
65 | originalSuccess.apply(_this, arguments);
66 | }
67 | };
68 |
69 | var originalFail = arguments[0].fail;
70 |
71 | arguments[0].fail = function () {
72 | reportXhr(arguments[0]);
73 |
74 | if (originalFail) {
75 | originalFail.apply(_this, arguments);
76 | }
77 | };
78 |
79 | var hasBeenReported = false;
80 |
81 | var reportXhr = function reportXhr(res) {
82 | if (hasBeenReported) {
83 | return;
84 | }
85 |
86 | hasBeenReported = true;
87 | dataflux_xhr.duration = (0, _utils.now)() - dataflux_xhr.startTime;
88 | dataflux_xhr.response = JSON.stringify({
89 | filePath: res.filePath,
90 | tempFilePath: res.tempFilePath
91 | });
92 | dataflux_xhr.header = res.header || {};
93 | dataflux_xhr.profile = res.profile;
94 | dataflux_xhr.status = res.statusCode || res.status || 0;
95 | onRequestCompleteCallbacks.forEach(function (callback) {
96 | callback(dataflux_xhr);
97 | });
98 | };
99 |
100 | beforeSendCallbacks.forEach(function (callback) {
101 | callback(dataflux_xhr);
102 | });
103 | return originalDownloadRequest.apply(this, arguments);
104 | };
105 | }
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/core/errorCollection.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.startConsoleTracking = startConsoleTracking;
7 | exports.stopConsoleTracking = stopConsoleTracking;
8 | exports.filterErrors = filterErrors;
9 | exports.startRuntimeErrorTracking = startRuntimeErrorTracking;
10 | exports.stopRuntimeErrorTracking = stopRuntimeErrorTracking;
11 | exports.startAutomaticErrorCollection = startAutomaticErrorCollection;
12 | exports.trackNetworkError = trackNetworkError;
13 |
14 | var _utils = require("../helper/utils");
15 |
16 | var _enums = require("../helper/enums");
17 |
18 | var _errorTools = require("./errorTools");
19 |
20 | var _tracekit = require("../helper/tracekit");
21 |
22 | var _observable = require("./observable");
23 |
24 | var _configuration = require("./configuration");
25 |
26 | var _xhrProxy = require("./xhrProxy");
27 |
28 | var _downloadProxy = require("./downloadProxy");
29 |
30 | var originalConsoleError;
31 |
32 | function startConsoleTracking(errorObservable) {
33 | originalConsoleError = console.error;
34 |
35 | console.error = function () {
36 | originalConsoleError.apply(console, arguments);
37 | var args = (0, _utils.toArray)(arguments);
38 | var message = [];
39 | args.concat(['console error:']).forEach(function (para) {
40 | message.push(formatConsoleParameters(para));
41 | });
42 | errorObservable.notify({
43 | message: message.join(' '),
44 | source: _errorTools.ErrorSource.CONSOLE,
45 | startTime: (0, _utils.now)()
46 | });
47 | };
48 | }
49 |
50 | function stopConsoleTracking() {
51 | console.error = originalConsoleError;
52 | }
53 |
54 | function formatConsoleParameters(param) {
55 | if (typeof param === 'string') {
56 | return param;
57 | }
58 |
59 | if (param instanceof Error) {
60 | return (0, _errorTools.toStackTraceString)((0, _tracekit.computeStackTrace)(param));
61 | }
62 |
63 | return JSON.stringify(param, undefined, 2);
64 | }
65 |
66 | function filterErrors(configuration, errorObservable) {
67 | var errorCount = 0;
68 | var filteredErrorObservable = new _observable.Observable();
69 | errorObservable.subscribe(function (error) {
70 | if (errorCount < configuration.maxErrorsByMinute) {
71 | errorCount += 1;
72 | filteredErrorObservable.notify(error);
73 | } else if (errorCount === configuration.maxErrorsByMinute) {
74 | errorCount += 1;
75 | filteredErrorObservable.notify({
76 | message: 'Reached max number of errors by minute: ' + configuration.maxErrorsByMinute,
77 | source: _errorTools.ErrorSource.AGENT,
78 | startTime: (0, _utils.now)()
79 | });
80 | }
81 | });
82 | setInterval(function () {
83 | errorCount = 0;
84 | }, _enums.ONE_MINUTE);
85 | return filteredErrorObservable;
86 | }
87 |
88 | var traceKitReportHandler;
89 |
90 | function startRuntimeErrorTracking(errorObservable) {
91 | traceKitReportHandler = function traceKitReportHandler(stackTrace, _, errorObject) {
92 | var error = (0, _errorTools.formatUnknownError)(stackTrace, errorObject, 'Uncaught');
93 | errorObservable.notify({
94 | message: error.message,
95 | stack: error.stack,
96 | type: error.type,
97 | source: _errorTools.ErrorSource.SOURCE,
98 | startTime: (0, _utils.now)()
99 | });
100 | };
101 |
102 | _tracekit.report.subscribe(traceKitReportHandler);
103 | }
104 |
105 | function stopRuntimeErrorTracking() {
106 | _tracekit.report.unsubscribe(traceKitReportHandler);
107 | }
108 |
109 | var filteredErrorsObservable;
110 |
111 | function startAutomaticErrorCollection(configuration) {
112 | if (!filteredErrorsObservable) {
113 | var errorObservable = new _observable.Observable();
114 | trackNetworkError(configuration, errorObservable);
115 | startConsoleTracking(errorObservable);
116 | startRuntimeErrorTracking(errorObservable);
117 | filteredErrorsObservable = filterErrors(configuration, errorObservable);
118 | }
119 |
120 | return filteredErrorsObservable;
121 | }
122 |
123 | function trackNetworkError(configuration, errorObservable) {
124 | (0, _xhrProxy.startXhrProxy)().onRequestComplete(function (context) {
125 | return handleCompleteRequest(context.type, context);
126 | });
127 | (0, _downloadProxy.startDownloadProxy)().onRequestComplete(function (context) {
128 | return handleCompleteRequest(context.type, context);
129 | });
130 |
131 | function handleCompleteRequest(type, request) {
132 | if (!(0, _configuration.isIntakeRequest)(request.url, configuration) && (isRejected(request) || isServerError(request))) {
133 | errorObservable.notify({
134 | message: format(type) + 'error' + request.method + ' ' + request.url,
135 | resource: {
136 | method: request.method,
137 | statusCode: request.status,
138 | url: request.url
139 | },
140 | type: _errorTools.ErrorSource.NETWORK,
141 | source: _errorTools.ErrorSource.NETWORK,
142 | stack: truncateResponse(request.response, configuration) || 'Failed to load',
143 | startTime: request.startTime
144 | });
145 | }
146 | }
147 |
148 | return {
149 | stop: function stop() {
150 | (0, _xhrProxy.resetXhrProxy)();
151 | (0, _downloadProxy.resetDownloadProxy)();
152 | }
153 | };
154 | }
155 |
156 | function isRejected(request) {
157 | return request.status === 0 && request.responseType !== 'opaque';
158 | }
159 |
160 | function isServerError(request) {
161 | return request.status >= 500;
162 | }
163 |
164 | function truncateResponse(response, configuration) {
165 | if (response && response.length > configuration.requestErrorResponseLengthLimit) {
166 | return response.substring(0, configuration.requestErrorResponseLengthLimit) + '...';
167 | }
168 |
169 | return response;
170 | }
171 |
172 | function format(type) {
173 | if (_enums.RequestType.XHR === type) {
174 | return 'XHR';
175 | }
176 |
177 | return _enums.RequestType.DOWNLOAD;
178 | }
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/core/errorTools.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.formatUnknownError = formatUnknownError;
7 | exports.toStackTraceString = toStackTraceString;
8 | exports.ErrorSource = void 0;
9 | var ErrorSource = {
10 | AGENT: 'agent',
11 | CONSOLE: 'console',
12 | NETWORK: 'network',
13 | SOURCE: 'source',
14 | LOGGER: 'logger'
15 | };
16 | exports.ErrorSource = ErrorSource;
17 |
18 | function formatUnknownError(stackTrace, errorObject, nonErrorPrefix) {
19 | if (!stackTrace || stackTrace.message === undefined && !(errorObject instanceof Error)) {
20 | return {
21 | message: nonErrorPrefix + '' + JSON.stringify(errorObject),
22 | stack: 'No stack, consider using an instance of Error',
23 | type: stackTrace && stackTrace.name
24 | };
25 | }
26 |
27 | return {
28 | message: stackTrace.message || 'Empty message',
29 | stack: toStackTraceString(stackTrace),
30 | type: stackTrace.name
31 | };
32 | }
33 |
34 | function toStackTraceString(stack) {
35 | var result = stack.name || 'Error' + ': ' + stack.message;
36 | stack.stack.forEach(function (frame) {
37 | var func = frame.func === '?' ? '' : frame.func;
38 | var args = frame.args && frame.args.length > 0 ? '(' + frame.args.join(', ') + ')' : '';
39 | var line = frame.line ? ':' + frame.line : '';
40 | var column = frame.line && frame.column ? ':' + frame.column : '';
41 | result += '\n at ' + func + args + ' @ ' + frame.url + line + column;
42 | });
43 | return result;
44 | }
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/core/lifeCycle.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.LifeCycleEventType = exports.LifeCycle = void 0;
7 |
8 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
9 |
10 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
11 |
12 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
13 |
14 | var LifeCycle = /*#__PURE__*/function () {
15 | function LifeCycle() {
16 | _classCallCheck(this, LifeCycle);
17 |
18 | this.callbacks = {};
19 | }
20 |
21 | _createClass(LifeCycle, [{
22 | key: "notify",
23 | value: function notify(eventType, data) {
24 | var eventCallbacks = this.callbacks[eventType];
25 |
26 | if (eventCallbacks) {
27 | eventCallbacks.forEach(function (callback) {
28 | return callback(data);
29 | });
30 | }
31 | }
32 | }, {
33 | key: "subscribe",
34 | value: function subscribe(eventType, callback) {
35 | var _this = this;
36 |
37 | if (!this.callbacks[eventType]) {
38 | this.callbacks[eventType] = [];
39 | }
40 |
41 | this.callbacks[eventType].push(callback);
42 | return {
43 | unsubscribe: function unsubscribe() {
44 | _this.callbacks[eventType] = _this.callbacks[eventType].filter(function (other) {
45 | return callback !== other;
46 | });
47 | }
48 | };
49 | }
50 | }]);
51 |
52 | return LifeCycle;
53 | }();
54 |
55 | exports.LifeCycle = LifeCycle;
56 | var LifeCycleEventType = {
57 | PERFORMANCE_ENTRY_COLLECTED: 'PERFORMANCE_ENTRY_COLLECTED',
58 | AUTO_ACTION_CREATED: 'AUTO_ACTION_CREATED',
59 | AUTO_ACTION_COMPLETED: 'AUTO_ACTION_COMPLETED',
60 | AUTO_ACTION_DISCARDED: 'AUTO_ACTION_DISCARDED',
61 | APP_HIDE: 'APP_HIDE',
62 | APP_UPDATE: 'APP_UPDATE',
63 | PAGE_SET_DATA_UPDATE: 'PAGE_SET_DATA_UPDATE',
64 | PAGE_ALIAS_ACTION: 'PAGE_ALIAS_ACTION',
65 | VIEW_CREATED: 'VIEW_CREATED',
66 | VIEW_UPDATED: 'VIEW_UPDATED',
67 | VIEW_ENDED: 'VIEW_ENDED',
68 | REQUEST_STARTED: 'REQUEST_STARTED',
69 | REQUEST_COMPLETED: 'REQUEST_COMPLETED',
70 | RAW_RUM_EVENT_COLLECTED: 'RAW_RUM_EVENT_COLLECTED',
71 | RUM_EVENT_COLLECTED: 'RUM_EVENT_COLLECTED'
72 | };
73 | exports.LifeCycleEventType = LifeCycleEventType;
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/core/observable.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.Observable = void 0;
7 |
8 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
9 |
10 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
11 |
12 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
13 |
14 | var Observable = /*#__PURE__*/function () {
15 | function Observable() {
16 | _classCallCheck(this, Observable);
17 |
18 | this.observers = [];
19 | }
20 |
21 | _createClass(Observable, [{
22 | key: "subscribe",
23 | value: function subscribe(f) {
24 | this.observers.push(f);
25 | }
26 | }, {
27 | key: "notify",
28 | value: function notify(data) {
29 | this.observers.forEach(function (observer) {
30 | observer(data);
31 | });
32 | }
33 | }]);
34 |
35 | return Observable;
36 | }();
37 |
38 | exports.Observable = Observable;
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/core/sdk.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.tracker = exports.sdk = void 0;
7 |
8 | var _utils = require("../helper/utils");
9 |
10 | function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
11 |
12 | function getSDK() {
13 | var sdk = null,
14 | tracker = '';
15 |
16 | try {
17 | if (wx && (typeof wx === "undefined" ? "undefined" : _typeof(wx)) === 'object' && typeof wx.request === 'function') {
18 | sdk = (0, _utils.deepMixObject)({}, wx);
19 | tracker = 'wx';
20 | wx = sdk;
21 | }
22 | } catch (err) {
23 | console.warn('unsupport platform, Fail to start');
24 | }
25 |
26 | console.log('------get SDK-------');
27 | return {
28 | sdk: sdk,
29 | tracker: tracker
30 | };
31 | }
32 |
33 | var instance = getSDK();
34 | var sdk = instance.sdk;
35 | exports.sdk = sdk;
36 | var tracker = instance.tracker;
37 | exports.tracker = tracker;
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/core/xhrProxy.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.startXhrProxy = startXhrProxy;
7 | exports.resetXhrProxy = resetXhrProxy;
8 |
9 | var _sdk = require("./sdk");
10 |
11 | var _utils = require("../helper/utils");
12 |
13 | var _enums = require("../helper/enums");
14 |
15 | var xhrProxySingleton;
16 | var beforeSendCallbacks = [];
17 | var onRequestCompleteCallbacks = [];
18 | var originalXhrRequest;
19 |
20 | function startXhrProxy() {
21 | if (!xhrProxySingleton) {
22 | proxyXhr();
23 | xhrProxySingleton = {
24 | beforeSend: function beforeSend(callback) {
25 | beforeSendCallbacks.push(callback);
26 | },
27 | onRequestComplete: function onRequestComplete(callback) {
28 | onRequestCompleteCallbacks.push(callback);
29 | }
30 | };
31 | }
32 |
33 | return xhrProxySingleton;
34 | }
35 |
36 | function resetXhrProxy() {
37 | if (xhrProxySingleton) {
38 | xhrProxySingleton = undefined;
39 | beforeSendCallbacks.splice(0, beforeSendCallbacks.length);
40 | onRequestCompleteCallbacks.splice(0, onRequestCompleteCallbacks.length);
41 | _sdk.sdk.request = originalXhrRequest;
42 | }
43 | }
44 |
45 | function proxyXhr() {
46 | originalXhrRequest = _sdk.sdk.request;
47 |
48 | _sdk.sdk.request = function () {
49 | var _this = this;
50 |
51 | var dataflux_xhr = {
52 | method: arguments[0].method || 'GET',
53 | startTime: 0,
54 | url: arguments[0].url,
55 | type: _enums.RequestType.XHR,
56 | responseType: arguments[0].responseType || 'text'
57 | };
58 | dataflux_xhr.startTime = (0, _utils.now)();
59 | var originalSuccess = arguments[0].success;
60 |
61 | arguments[0].success = function () {
62 | reportXhr(arguments[0]);
63 |
64 | if (originalSuccess) {
65 | originalSuccess.apply(_this, arguments);
66 | }
67 | };
68 |
69 | var originalFail = arguments[0].fail;
70 |
71 | arguments[0].fail = function () {
72 | reportXhr(arguments[0]);
73 |
74 | if (originalFail) {
75 | originalFail.apply(_this, arguments);
76 | }
77 | };
78 |
79 | var hasBeenReported = false;
80 |
81 | var reportXhr = function reportXhr(res) {
82 | if (hasBeenReported) {
83 | return;
84 | }
85 |
86 | hasBeenReported = true;
87 | dataflux_xhr.duration = (0, _utils.now)() - dataflux_xhr.startTime;
88 | dataflux_xhr.response = JSON.stringify(res.data);
89 | dataflux_xhr.header = res.header || {};
90 | dataflux_xhr.profile = res.profile;
91 | dataflux_xhr.status = res.statusCode || res.status || 0;
92 | onRequestCompleteCallbacks.forEach(function (callback) {
93 | callback(dataflux_xhr);
94 | });
95 | };
96 |
97 | beforeSendCallbacks.forEach(function (callback) {
98 | callback(dataflux_xhr);
99 | });
100 | return originalXhrRequest.apply(this, arguments);
101 | };
102 | }
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/helper/enums.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.MpHook = exports.ActionType = exports.RequestType = exports.RumEventType = exports.CLIENT_ID_TOKEN = exports.ONE_KILO_BYTE = exports.ONE_HOUR = exports.ONE_MINUTE = exports.ONE_SECOND = void 0;
7 |
8 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
9 |
10 | var ONE_SECOND = 1000;
11 | exports.ONE_SECOND = ONE_SECOND;
12 | var ONE_MINUTE = 60 * ONE_SECOND;
13 | exports.ONE_MINUTE = ONE_MINUTE;
14 | var ONE_HOUR = 60 * ONE_MINUTE;
15 | exports.ONE_HOUR = ONE_HOUR;
16 | var ONE_KILO_BYTE = 1024;
17 | exports.ONE_KILO_BYTE = ONE_KILO_BYTE;
18 | var CLIENT_ID_TOKEN = 'datafluxRum:client:id';
19 | exports.CLIENT_ID_TOKEN = CLIENT_ID_TOKEN;
20 |
21 | var RumEventType = _defineProperty({
22 | ACTION: 'action',
23 | ERROR: 'error',
24 | LONG_TASK: 'long_task',
25 | VIEW: 'view',
26 | RESOURCE: 'resource',
27 | APP: 'app'
28 | }, "ACTION", 'action');
29 |
30 | exports.RumEventType = RumEventType;
31 | var RequestType = {
32 | XHR: 'network',
33 | DOWNLOAD: 'resource'
34 | };
35 | exports.RequestType = RequestType;
36 | var ActionType = {
37 | tap: 'tap',
38 | longpress: 'longpress',
39 | longtap: 'longtap'
40 | };
41 | exports.ActionType = ActionType;
42 | var MpHook = {
43 | data: 1,
44 | onLoad: 1,
45 | onShow: 1,
46 | onReady: 1,
47 | onPullDownRefresh: 1,
48 | onReachBottom: 1,
49 | onShareAppMessage: 1,
50 | onPageScroll: 1,
51 | onResize: 1,
52 | onTabItemTap: 1,
53 | onHide: 1,
54 | onUnload: 1
55 | };
56 | exports.MpHook = MpHook;
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | Object.defineProperty(exports, "datafluxRum", {
7 | enumerable: true,
8 | get: function get() {
9 | return _rum.datafluxRum;
10 | }
11 | });
12 |
13 | var _rum = require("./boot/rum.entry");
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/rumEventsCollection/app/appCollection.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.startAppCollection = startAppCollection;
7 |
8 | var _index = require("./index");
9 |
10 | var _lifeCycle = require("../../core/lifeCycle");
11 |
12 | var _enums = require("../../helper/enums");
13 |
14 | var _utils = require("../../helper/utils");
15 |
16 | function startAppCollection(lifeCycle, configuration) {
17 | lifeCycle.subscribe(_lifeCycle.LifeCycleEventType.APP_UPDATE, function (appinfo) {
18 | lifeCycle.notify(_lifeCycle.LifeCycleEventType.RAW_RUM_EVENT_COLLECTED, processAppUpdate(appinfo));
19 | });
20 | return (0, _index.rewriteApp)(configuration, lifeCycle);
21 | }
22 |
23 | function processAppUpdate(appinfo) {
24 | var appEvent = {
25 | date: appinfo.startTime,
26 | type: _enums.RumEventType.APP,
27 | app: {
28 | type: appinfo.type,
29 | name: appinfo.name,
30 | id: appinfo.id,
31 | duration: (0, _utils.msToNs)(appinfo.duration)
32 | }
33 | };
34 | console.log(appEvent, 'appEvent====');
35 | return {
36 | rawRumEvent: appEvent,
37 | startTime: appinfo.startTime
38 | };
39 | }
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/rumEventsCollection/app/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.rewriteApp = rewriteApp;
7 | exports.startupTypes = exports.THROTTLE_VIEW_UPDATE_PERIOD = void 0;
8 |
9 | var _utils = require("../../helper/utils");
10 |
11 | var _lifeCycle = require("../../core/lifeCycle");
12 |
13 | // 劫持原小程序App方法
14 | var THROTTLE_VIEW_UPDATE_PERIOD = 3000;
15 | exports.THROTTLE_VIEW_UPDATE_PERIOD = THROTTLE_VIEW_UPDATE_PERIOD;
16 | var startupTypes = {
17 | COLD: 'cold',
18 | HOT: 'hot'
19 | };
20 | exports.startupTypes = startupTypes;
21 |
22 | function rewriteApp(configuration, lifeCycle) {
23 | var originApp = App;
24 | var appInfo = {
25 | isStartUp: false // 是否启动
26 |
27 | };
28 | var startTime;
29 |
30 | App = function App(app) {
31 | startTime = (0, _utils.now)() // 合并方法,插入记录脚本
32 | ;
33 | ['onLaunch', 'onShow', 'onHide'].forEach(function (methodName) {
34 | var userDefinedMethod = app[methodName]; // 暂存用户定义的方法
35 |
36 | app[methodName] = function (options) {
37 | console.log(methodName, 'methodName app');
38 |
39 | if (methodName === 'onLaunch') {
40 | appInfo.isStartUp = true;
41 | appInfo.isHide = false;
42 | appInfo.startupType = startupTypes.COLD;
43 | } else if (methodName === 'onShow') {
44 | if (appInfo.isStartUp && appInfo.isHide) {
45 | // 判断是热启动
46 | appInfo.startupType = startupTypes.HOT; // appUpdate()
47 | }
48 | } else if (methodName === 'onHide') {
49 | lifeCycle.notify(_lifeCycle.LifeCycleEventType.APP_HIDE);
50 | appInfo.isHide = true;
51 | }
52 |
53 | return userDefinedMethod && userDefinedMethod.call(this, options);
54 | };
55 | });
56 | return originApp(app);
57 | };
58 |
59 | startPerformanceObservable(lifeCycle);
60 | }
61 |
62 | function startPerformanceObservable(lifeCycle) {
63 | var subscribe = lifeCycle.subscribe(_lifeCycle.LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED, function (entitys) {
64 | // 过滤掉其他页面监听,只保留首次启动
65 | var codeDownloadDuration;
66 | var launchEntity = entitys.find(function (entity) {
67 | return entity.entryType === 'navigation' && entity.navigationType === 'appLaunch';
68 | });
69 |
70 | if (typeof launchEntity !== 'undefined') {
71 | lifeCycle.notify(_lifeCycle.LifeCycleEventType.APP_UPDATE, {
72 | startTime: launchEntity.startTime,
73 | name: '启动',
74 | type: 'launch',
75 | id: (0, _utils.UUID)(),
76 | duration: launchEntity.duration
77 | });
78 | }
79 |
80 | var scriptentity = entitys.find(function (entity) {
81 | return entity.entryType === 'script' && entity.name === 'evaluateScript';
82 | });
83 |
84 | if (typeof scriptentity !== 'undefined') {
85 | lifeCycle.notify(_lifeCycle.LifeCycleEventType.APP_UPDATE, {
86 | startTime: scriptentity.startTime,
87 | name: '脚本注入',
88 | type: 'script_insert',
89 | id: (0, _utils.UUID)(),
90 | duration: scriptentity.duration
91 | });
92 | }
93 |
94 | var firstEntity = entitys.find(function (entity) {
95 | return entity.entryType === 'render' && entity.name === 'firstRender';
96 | });
97 |
98 | if (firstEntity && scriptentity && launchEntity) {
99 | if (!(0, _utils.areInOrder)(firstEntity.duration, launchEntity.duration) || !(0, _utils.areInOrder)(scriptentity.duration, launchEntity.duration)) {
100 | return;
101 | }
102 |
103 | codeDownloadDuration = launchEntity.duration - firstEntity.duration - scriptentity.duration; // 资源下载耗时
104 |
105 | lifeCycle.notify(_lifeCycle.LifeCycleEventType.APP_UPDATE, {
106 | startTime: launchEntity.startTime,
107 | name: '小程序包下载',
108 | type: 'package_download',
109 | id: (0, _utils.UUID)(),
110 | duration: codeDownloadDuration
111 | }); // 资源下载时间暂时定为:首次启动时间-脚本加载时间-初次渲染时间
112 | }
113 | });
114 | return {
115 | stop: subscribe.unsubscribe
116 | };
117 | }
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/rumEventsCollection/assembly.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.startRumAssembly = startRumAssembly;
7 |
8 | var _utils = require("../helper/utils");
9 |
10 | var _lifeCycle = require("../core/lifeCycle");
11 |
12 | var _enums = require("../helper/enums");
13 |
14 | var _baseInfo = _interopRequireDefault(require("../core/baseInfo"));
15 |
16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
17 |
18 | function isTracked(configuration) {
19 | return (0, _utils.performDraw)(configuration.sampleRate);
20 | }
21 |
22 | var SessionType = {
23 | SYNTHETICS: 'synthetics',
24 | USER: 'user'
25 | };
26 |
27 | function startRumAssembly(applicationId, configuration, lifeCycle, parentContexts) {
28 | lifeCycle.subscribe(_lifeCycle.LifeCycleEventType.RAW_RUM_EVENT_COLLECTED, function (data) {
29 | var startTime = data.startTime;
30 | var rawRumEvent = data.rawRumEvent;
31 | var viewContext = parentContexts.findView(startTime);
32 | var deviceContext = {
33 | device: _baseInfo["default"].deviceInfo
34 | };
35 |
36 | if (isTracked(configuration) && (viewContext || rawRumEvent.type === _enums.RumEventType.APP)) {
37 | var actionContext = parentContexts.findAction(startTime);
38 | var rumContext = {
39 | _dd: {
40 | sdkName: configuration.sdkName,
41 | sdkVersion: configuration.sdkVersion,
42 | env: configuration.env,
43 | version: configuration.version
44 | },
45 | tags: configuration.tags,
46 | application: {
47 | id: applicationId
48 | },
49 | device: {},
50 | date: new Date().getTime(),
51 | session: {
52 | id: _baseInfo["default"].getSessionId(),
53 | type: SessionType.USER
54 | },
55 | user: {
56 | user_id: configuration.user_id || _baseInfo["default"].getClientID(),
57 | is_signin: configuration.user_id ? 'T' : 'F'
58 | }
59 | };
60 | var rumEvent = (0, _utils.extend2Lev)(rumContext, deviceContext, viewContext, actionContext, rawRumEvent);
61 | var serverRumEvent = (0, _utils.withSnakeCaseKeys)(rumEvent); // if (
62 | // serverRumEvent.type === 'view' ||
63 | // serverRumEvent.type === 'action'
64 | // ) {
65 | // console.log(serverRumEvent, 'serverRumEvent')
66 | // }
67 |
68 | lifeCycle.notify(_lifeCycle.LifeCycleEventType.RUM_EVENT_COLLECTED, serverRumEvent);
69 | }
70 | });
71 | }
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/rumEventsCollection/error/errorCollection.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.startErrorCollection = startErrorCollection;
7 | exports.doStartErrorCollection = doStartErrorCollection;
8 |
9 | var _errorCollection = require("../../core/errorCollection");
10 |
11 | var _enums = require("../../helper/enums");
12 |
13 | var _lifeCycle = require("../../core/lifeCycle");
14 |
15 | var _utils = require("../../helper/utils");
16 |
17 | function startErrorCollection(lifeCycle, configuration) {
18 | return doStartErrorCollection(lifeCycle, configuration, (0, _errorCollection.startAutomaticErrorCollection)(configuration));
19 | }
20 |
21 | function doStartErrorCollection(lifeCycle, configuration, observable) {
22 | observable.subscribe(function (error) {
23 | lifeCycle.notify(_lifeCycle.LifeCycleEventType.RAW_RUM_EVENT_COLLECTED, processError(error));
24 | });
25 | }
26 |
27 | function processError(error) {
28 | var resource = error.resource;
29 |
30 | if (resource) {
31 | var urlObj = (0, _utils.urlParse)(error.resource.url).getParse();
32 | resource = {
33 | method: error.resource.method,
34 | status: error.resource.statusCode,
35 | statusGroup: (0, _utils.getStatusGroup)(error.resource.statusCode),
36 | url: error.resource.url,
37 | urlHost: urlObj.Host,
38 | urlPath: urlObj.Path,
39 | urlPathGroup: (0, _utils.replaceNumberCharByPath)(urlObj.Path)
40 | };
41 | }
42 |
43 | var rawRumEvent = {
44 | date: error.startTime,
45 | error: {
46 | message: error.message,
47 | resource: resource,
48 | source: error.source,
49 | stack: error.stack,
50 | type: error.type,
51 | starttime: error.startTime
52 | },
53 | type: _enums.RumEventType.ERROR
54 | };
55 | return {
56 | rawRumEvent: rawRumEvent,
57 | startTime: error.startTime
58 | };
59 | }
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/rumEventsCollection/page/viewCollection.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.startViewCollection = startViewCollection;
7 |
8 | var _index = require("./index");
9 |
10 | var _enums = require("../../helper/enums");
11 |
12 | var _utils = require("../../helper/utils");
13 |
14 | var _lifeCycle = require("../../core/lifeCycle");
15 |
16 | function startViewCollection(lifeCycle, configuration) {
17 | lifeCycle.subscribe(_lifeCycle.LifeCycleEventType.VIEW_UPDATED, function (view) {
18 | lifeCycle.notify(_lifeCycle.LifeCycleEventType.RAW_RUM_EVENT_COLLECTED, processViewUpdate(view));
19 | });
20 | return (0, _index.rewritePage)(configuration, lifeCycle);
21 | }
22 |
23 | function processViewUpdate(view) {
24 | var apdexLevel;
25 |
26 | if (view.fmp) {
27 | apdexLevel = parseInt(Number(view.fmp) / 1000);
28 | apdexLevel = apdexLevel > 9 ? 9 : apdexLevel;
29 | }
30 |
31 | var viewEvent = {
32 | _dd: {
33 | documentVersion: view.documentVersion
34 | },
35 | date: view.startTime,
36 | type: _enums.RumEventType.VIEW,
37 | page: {
38 | action: {
39 | count: view.eventCounts.userActionCount
40 | },
41 | error: {
42 | count: view.eventCounts.errorCount
43 | },
44 | setdata: {
45 | count: view.setdataCount
46 | },
47 | setdata_duration: (0, _utils.msToNs)(view.setdataDuration),
48 | loadingTime: (0, _utils.msToNs)(view.loadingTime),
49 | stayTime: (0, _utils.msToNs)(view.stayTime),
50 | onload2onshow: (0, _utils.msToNs)(view.onload2onshowTime),
51 | onshow2onready: (0, _utils.msToNs)(view.onshow2onready),
52 | fpt: (0, _utils.msToNs)(view.fpt),
53 | fmp: (0, _utils.msToNs)(view.fmp),
54 | isActive: view.isActive,
55 | apdexLevel: apdexLevel,
56 | // longTask: {
57 | // count: view.eventCounts.longTaskCount
58 | // },
59 | resource: {
60 | count: view.eventCounts.resourceCount
61 | },
62 | timeSpent: (0, _utils.msToNs)(view.duration)
63 | }
64 | };
65 | return {
66 | rawRumEvent: viewEvent,
67 | startTime: view.startTime
68 | };
69 | }
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/rumEventsCollection/parentContexts.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.startParentContexts = startParentContexts;
7 | exports.CLEAR_OLD_CONTEXTS_INTERVAL = exports.VIEW_CONTEXT_TIME_OUT_DELAY = void 0;
8 |
9 | var _enums = require("../helper/enums");
10 |
11 | var _utils = require("../helper/utils");
12 |
13 | var _lifeCycle = require("../core/lifeCycle");
14 |
15 | var VIEW_CONTEXT_TIME_OUT_DELAY = 4 * _enums.ONE_HOUR;
16 | exports.VIEW_CONTEXT_TIME_OUT_DELAY = VIEW_CONTEXT_TIME_OUT_DELAY;
17 | var CLEAR_OLD_CONTEXTS_INTERVAL = _enums.ONE_MINUTE;
18 | exports.CLEAR_OLD_CONTEXTS_INTERVAL = CLEAR_OLD_CONTEXTS_INTERVAL;
19 |
20 | function startParentContexts(lifeCycle) {
21 | var currentView;
22 | var currentAction;
23 | var previousViews = [];
24 | var previousActions = [];
25 | lifeCycle.subscribe(_lifeCycle.LifeCycleEventType.VIEW_CREATED, function (currentContext) {
26 | currentView = currentContext;
27 | });
28 | lifeCycle.subscribe(_lifeCycle.LifeCycleEventType.VIEW_UPDATED, function (currentContext) {
29 | // A view can be updated after its end. We have to ensure that the view being updated is the
30 | // most recently created.
31 | if (currentView && currentView.id === currentContext.id) {
32 | currentView = currentContext;
33 | }
34 | });
35 | lifeCycle.subscribe(_lifeCycle.LifeCycleEventType.VIEW_ENDED, function (data) {
36 | if (currentView) {
37 | previousViews.unshift({
38 | endTime: data.endClocks,
39 | context: buildCurrentViewContext(),
40 | startTime: currentView.startTime
41 | });
42 | currentView = undefined;
43 | }
44 | });
45 | lifeCycle.subscribe(_lifeCycle.LifeCycleEventType.AUTO_ACTION_CREATED, function (currentContext) {
46 | currentAction = currentContext;
47 | });
48 | lifeCycle.subscribe(_lifeCycle.LifeCycleEventType.AUTO_ACTION_COMPLETED, function (action) {
49 | if (currentAction) {
50 | previousActions.unshift({
51 | context: buildCurrentActionContext(),
52 | endTime: currentAction.startClocks + action.duration,
53 | startTime: currentAction.startClocks
54 | });
55 | }
56 |
57 | currentAction = undefined;
58 | });
59 | lifeCycle.subscribe(_lifeCycle.LifeCycleEventType.AUTO_ACTION_DISCARDED, function () {
60 | currentAction = undefined;
61 | });
62 | lifeCycle.subscribe(_lifeCycle.LifeCycleEventType.SESSION_RENEWED, function () {
63 | previousViews = [];
64 | previousActions = [];
65 | currentView = undefined;
66 | currentAction = undefined;
67 | });
68 | var clearOldContextsInterval = setInterval(function () {
69 | clearOldContexts(previousViews, VIEW_CONTEXT_TIME_OUT_DELAY);
70 | }, CLEAR_OLD_CONTEXTS_INTERVAL);
71 |
72 | function clearOldContexts(previousContexts, timeOutDelay) {
73 | var oldTimeThreshold = (0, _utils.now)() - timeOutDelay;
74 |
75 | while (previousContexts.length > 0 && previousContexts[previousContexts.length - 1].startTime < oldTimeThreshold) {
76 | previousContexts.pop();
77 | }
78 | }
79 |
80 | function buildCurrentActionContext() {
81 | return {
82 | userAction: {
83 | id: currentAction.id
84 | }
85 | };
86 | }
87 |
88 | function buildCurrentViewContext() {
89 | return {
90 | page: {
91 | id: currentView.id,
92 | referer: previousViews.length && previousViews[previousViews.length - 1].context.page.route || undefined,
93 | route: currentView.route
94 | }
95 | };
96 | }
97 |
98 | function findContext(buildContext, previousContexts, currentContext, startTime) {
99 | if (startTime === undefined) {
100 | return currentContext ? buildContext() : undefined;
101 | }
102 |
103 | if (currentContext && startTime >= currentContext.startTime) {
104 | return buildContext();
105 | }
106 |
107 | var flag = undefined;
108 | (0, _utils.each)(previousContexts, function (previousContext) {
109 | if (startTime > previousContext.endTime) {
110 | return false;
111 | }
112 |
113 | if (startTime >= previousContext.startTime) {
114 | flag = previousContext.context;
115 | return false;
116 | }
117 | });
118 | return flag;
119 | }
120 |
121 | var parentContexts = {
122 | findView: function findView(startTime) {
123 | return findContext(buildCurrentViewContext, previousViews, currentView, startTime);
124 | },
125 | findAction: function findAction(startTime) {
126 | return findContext(buildCurrentActionContext, previousActions, currentAction, startTime);
127 | },
128 | stop: function stop() {
129 | clearInterval(clearOldContextsInterval);
130 | }
131 | };
132 | return parentContexts;
133 | }
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/rumEventsCollection/performanceCollection.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.startPagePerformanceObservable = startPagePerformanceObservable;
7 |
8 | var _lifeCycle = require("../core/lifeCycle");
9 |
10 | var _sdk = require("../core/sdk");
11 |
12 | function startPagePerformanceObservable(lifeCycle, configuration) {
13 | if (!!_sdk.sdk.getPerformance) {
14 | var performance = _sdk.sdk.getPerformance();
15 |
16 | var observer = performance.createObserver(function (entryList) {
17 | lifeCycle.notify(_lifeCycle.LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED, entryList.getEntries());
18 | });
19 | observer.observe({
20 | entryTypes: ['render', 'script', 'navigation']
21 | });
22 | }
23 | }
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/rumEventsCollection/requestCollection.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.startRequestCollection = startRequestCollection;
7 | exports.trackXhr = trackXhr;
8 | exports.trackDownload = trackDownload;
9 |
10 | var _xhrProxy = require("../core/xhrProxy");
11 |
12 | var _downloadProxy = require("../core/downloadProxy");
13 |
14 | var _lifeCycle = require("../core/lifeCycle");
15 |
16 | var _utils = require("../helper/utils");
17 |
18 | var _resourceUtils = require("../rumEventsCollection/resource/resourceUtils");
19 |
20 | var nextRequestIndex = 1;
21 |
22 | function startRequestCollection(lifeCycle, configuration) {
23 | trackXhr(lifeCycle, configuration);
24 | trackDownload(lifeCycle, configuration);
25 | }
26 |
27 | function parseHeader(header) {
28 | // 大小写兼容
29 | if (!(0, _utils.isObject)(header)) return header;
30 | var res = {};
31 | Object.keys(header).forEach(function (key) {
32 | res[key.toLowerCase()] = header[key];
33 | });
34 | return res;
35 | }
36 |
37 | function getHeaderString(header) {
38 | if (!(0, _utils.isObject)(header)) return header;
39 | var headerStr = '';
40 | Object.keys(header).forEach(function (key) {
41 | headerStr += key + ':' + header[key] + ';';
42 | });
43 | return headerStr;
44 | }
45 |
46 | function trackXhr(lifeCycle, configuration) {
47 | var xhrProxy = (0, _xhrProxy.startXhrProxy)();
48 | xhrProxy.beforeSend(function (context) {
49 | if ((0, _resourceUtils.isAllowedRequestUrl)(configuration, context.url)) {
50 | context.requestIndex = getNextRequestIndex();
51 | lifeCycle.notify(_lifeCycle.LifeCycleEventType.REQUEST_STARTED, {
52 | requestIndex: context.requestIndex
53 | });
54 | }
55 | });
56 | xhrProxy.onRequestComplete(function (context) {
57 | if ((0, _resourceUtils.isAllowedRequestUrl)(configuration, context.url)) {
58 | lifeCycle.notify(_lifeCycle.LifeCycleEventType.REQUEST_COMPLETED, {
59 | duration: context.duration,
60 | method: context.method,
61 | requestIndex: context.requestIndex,
62 | performance: context.profile,
63 | response: context.response,
64 | startTime: context.startTime,
65 | status: context.status,
66 | type: context.type,
67 | url: context.url
68 | });
69 | }
70 | });
71 | return xhrProxy;
72 | }
73 |
74 | function trackDownload(lifeCycle, configuration) {
75 | var dwonloadProxy = (0, _downloadProxy.startDownloadProxy)();
76 | dwonloadProxy.beforeSend(function (context) {
77 | if ((0, _resourceUtils.isAllowedRequestUrl)(configuration, context.url)) {
78 | context.requestIndex = getNextRequestIndex();
79 | lifeCycle.notify(_lifeCycle.LifeCycleEventType.REQUEST_STARTED, {
80 | requestIndex: context.requestIndex
81 | });
82 | }
83 | });
84 | dwonloadProxy.onRequestComplete(function (context) {
85 | if ((0, _resourceUtils.isAllowedRequestUrl)(configuration, context.url)) {
86 | lifeCycle.notify(_lifeCycle.LifeCycleEventType.REQUEST_COMPLETED, {
87 | duration: context.duration,
88 | method: context.method,
89 | requestIndex: context.requestIndex,
90 | performance: context.profile,
91 | response: context.response,
92 | startTime: context.startTime,
93 | status: context.status,
94 | type: context.type,
95 | url: context.url
96 | });
97 | }
98 | });
99 | return dwonloadProxy;
100 | }
101 |
102 | function getNextRequestIndex() {
103 | var result = nextRequestIndex;
104 | nextRequestIndex += 1;
105 | return result;
106 | }
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/rumEventsCollection/resource/resourceCollection.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.startResourceCollection = startResourceCollection;
7 |
8 | var _resourceUtils = require("./resourceUtils");
9 |
10 | var _lifeCycle = require("../../core/lifeCycle");
11 |
12 | var _utils = require("../../helper/utils");
13 |
14 | var _enums = require("../../helper/enums");
15 |
16 | function startResourceCollection(lifeCycle, configuration) {
17 | lifeCycle.subscribe(_lifeCycle.LifeCycleEventType.REQUEST_COMPLETED, function (request) {
18 | lifeCycle.notify(_lifeCycle.LifeCycleEventType.RAW_RUM_EVENT_COLLECTED, processRequest(request));
19 | });
20 | }
21 |
22 | function processRequest(request) {
23 | var type = request.type;
24 | var timing = request.performance;
25 | var correspondingTimingOverrides = timing ? computePerformanceEntryMetrics(timing) : undefined;
26 | var urlObj = (0, _utils.urlParse)(request.url).getParse();
27 | var startTime = request.startTime;
28 | var resourceEvent = (0, _utils.extend2Lev)({
29 | date: startTime,
30 | resource: {
31 | type: type,
32 | duration: (0, _utils.msToNs)(request.duration),
33 | method: request.method,
34 | status: request.status,
35 | statusGroup: (0, _utils.getStatusGroup)(request.status),
36 | url: request.url,
37 | urlHost: urlObj.Host,
38 | urlPath: urlObj.Path,
39 | urlPathGroup: (0, _utils.replaceNumberCharByPath)(urlObj.Path),
40 | urlQuery: (0, _utils.jsonStringify)((0, _utils.getQueryParamsFromUrl)(request.url))
41 | },
42 | type: _enums.RumEventType.RESOURCE
43 | }, correspondingTimingOverrides);
44 | return {
45 | startTime: startTime,
46 | rawRumEvent: resourceEvent
47 | };
48 | }
49 |
50 | function computePerformanceEntryMetrics(timing) {
51 | return {
52 | resource: (0, _utils.extend2Lev)({}, {
53 | load: (0, _resourceUtils.computePerformanceResourceDuration)(timing),
54 | size: (0, _resourceUtils.computeSize)(timing)
55 | }, (0, _resourceUtils.computePerformanceResourceDetails)(timing))
56 | };
57 | }
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/rumEventsCollection/trackEventCounts.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.trackEventCounts = trackEventCounts;
7 |
8 | var _utils = require("../helper/utils");
9 |
10 | var _enums = require("../helper/enums");
11 |
12 | var _lifeCycle = require("../core/lifeCycle");
13 |
14 | function trackEventCounts(lifeCycle, callback) {
15 | if (typeof callback === 'undefined') {
16 | callback = _utils.noop;
17 | }
18 |
19 | var eventCounts = {
20 | errorCount: 0,
21 | resourceCount: 0,
22 | longTaskCount: 0,
23 | userActionCount: 0
24 | };
25 | var subscription = lifeCycle.subscribe(_lifeCycle.LifeCycleEventType.RAW_RUM_EVENT_COLLECTED, function (data) {
26 | var rawRumEvent = data.rawRumEvent;
27 |
28 | switch (rawRumEvent.type) {
29 | case _enums.RumEventType.ERROR:
30 | eventCounts.errorCount += 1;
31 | callback(eventCounts);
32 | break;
33 |
34 | case _enums.RumEventType.RESOURCE:
35 | eventCounts.resourceCount += 1;
36 | callback(eventCounts);
37 | break;
38 |
39 | case _enums.RumEventType.ACTION:
40 | eventCounts.userActionCount += 1;
41 | callback(eventCounts);
42 | break;
43 | }
44 | });
45 | return {
46 | stop: function stop() {
47 | subscription.unsubscribe();
48 | },
49 | eventCounts: eventCounts
50 | };
51 | }
--------------------------------------------------------------------------------
/demo/miniprogram_npm/@cloudcare/rum-miniapp/rumEventsCollection/transport/batch.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.startRumBatch = startRumBatch;
7 |
8 | var _lifeCycle = require("../../core/lifeCycle");
9 |
10 | var _transport = require("../../core/transport");
11 |
12 | var _enums = require("../../helper/enums");
13 |
14 | function startRumBatch(configuration, lifeCycle) {
15 | var batch = makeRumBatch(configuration, lifeCycle);
16 | lifeCycle.subscribe(_lifeCycle.LifeCycleEventType.RUM_EVENT_COLLECTED, function (serverRumEvent) {
17 | if (serverRumEvent.type === _enums.RumEventType.VIEW) {
18 | batch.upsert(serverRumEvent, serverRumEvent.page.id);
19 | } else {
20 | batch.add(serverRumEvent);
21 | }
22 | });
23 | return {
24 | stop: function stop() {
25 | batch.stop();
26 | }
27 | };
28 | }
29 |
30 | function makeRumBatch(configuration, lifeCycle) {
31 | var primaryBatch = createRumBatch(configuration.datakitUrl, lifeCycle);
32 |
33 | function createRumBatch(endpointUrl, lifeCycle) {
34 | return new _transport.Batch(new _transport.HttpRequest(endpointUrl, configuration.batchBytesLimit), configuration.maxBatchSize, configuration.batchBytesLimit, configuration.maxMessageSize, configuration.flushTimeout, lifeCycle);
35 | }
36 |
37 | var stopped = false;
38 | return {
39 | add: function add(message) {
40 | if (stopped) {
41 | return;
42 | }
43 |
44 | primaryBatch.add(message);
45 | },
46 | stop: function stop() {
47 | stopped = true;
48 | },
49 | upsert: function upsert(message, key) {
50 | if (stopped) {
51 | return;
52 | }
53 |
54 | primaryBatch.upsert(message, key);
55 | }
56 | };
57 | }
--------------------------------------------------------------------------------
/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo",
3 | "version": "2.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "@cloudcare/rum-miniapp": "^2.0.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/demo/pages/index/index.js:
--------------------------------------------------------------------------------
1 | //index.js
2 | //获取应用实例
3 | const app = getApp()
4 | Page({
5 | data: {
6 | motto: 'Hello World',
7 | userInfo: {},
8 | hasUserInfo: false,
9 | canIUse: wx.canIUse('button.open-type.getUserInfo'),
10 | },
11 | bindSetData: function () {
12 | this.setData({motto: 'hahhah'})
13 | },
14 |
15 | onAddToFavorites(res) {
16 | // webview 页面返回 webViewUrl
17 | console.log('webViewUrl: ', res.webViewUrl)
18 | return {
19 | title: '自定义标题',
20 | imageUrl: 'http://demo.png',
21 | query: 'name=xxx&age=xxx',
22 | }
23 | },
24 | onShareAppMessage() {
25 | const promise = new Promise(resolve => {
26 | setTimeout(() => {
27 | resolve({
28 | title: '自定义转发标题'
29 | })
30 | }, 2000)
31 | })
32 | console.log('分享app')
33 | return {
34 | title: '自定义转发标题',
35 | path: '/page/user?id=123',
36 | promise
37 | }
38 | },
39 | onShareTimeline() {
40 | console.log(111111,'分享朋友圈')
41 | return {
42 | title: '自定义标题',
43 | imageUrl: 'http://demo.png',
44 | query: 'name=xxx&age=xxx',
45 | }
46 | },
47 | onTabItemTap(item) {
48 | console.log(item.index)
49 | console.log(item.pagePath)
50 | console.log(item.text)
51 | },
52 | //事件处理函数
53 | bindViewTap: function () {
54 | wx.request({
55 | url: "http://testing-ft2x-api.cloudcare.cn/api/v1/tag/list",
56 | method: "get",
57 | header: {
58 | "content-type": "application/json",
59 | "X-FT-Auth-Token":
60 | "front.eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhY250X3V1aWQiOiJhY250XzM1OTNkNmQwNzFjYzExZWI5NTZmMWVmM2RkZTUyNTlmIiwid3NfdXVpZCI6Indrc3BfMmRjNDMxZDY2OTM3MTFlYjhmZjk3YWVlZTA0YjU0YWYiLCJ0b2tlbl91dWlkIjoiMjRhZWJiNTlkZDVlNDVlNGIxNDdjYzIyZTY2MDJlYjMiLCJ0aW1lIjoxNjQ4NDM0OTk1LjE2MTg4NTUsInJhbmdzdHIiOiJQT1k0RVNGWiIsImxvZ2luX3R5cGUiOiJ3ZWIifQ.loI4aNgFnXwcBNfVcMDNJbH9jmgLew3YmDR2l_Tk_T8",
61 | },
62 | })
63 | wx.downloadFile({
64 | url: 'https://www.xxxxx.com/sdtf',
65 | })
66 | },
67 | onError: function () {
68 | console.log(arguments, 'arguments')
69 | },
70 | onLoad: function () {
71 | console.log(getCurrentPages(), 'getCurrentPages')
72 | console.error('xxxxxxx')
73 | const xxx = sdfs
74 |
75 | if (app.globalData.userInfo) {
76 | this.setData({
77 | userInfo: app.globalData.userInfo,
78 | hasUserInfo: true,
79 | })
80 | } else if (this.data.canIUse) {
81 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
82 | // 所以此处加入 callback 以防止这种情况
83 | app.userInfoReadyCallback = (res) => {
84 | this.setData({
85 | userInfo: res.userInfo,
86 | hasUserInfo: true,
87 | })
88 | }
89 | } else {
90 | // 在没有 open-type=getUserInfo 版本的兼容处理
91 | wx.getUserInfo({
92 | success: (res) => {
93 | app.globalData.userInfo = res.userInfo
94 | this.setData({
95 | userInfo: res.userInfo,
96 | hasUserInfo: true,
97 | })
98 | },
99 | })
100 | }
101 | },
102 | getUserInfo: function (e) {
103 | console.log(e)
104 | app.globalData.userInfo = e.detail.userInfo
105 | this.setData({
106 | userInfo: e.detail.userInfo,
107 | hasUserInfo: true,
108 | })
109 | },
110 | })
111 |
--------------------------------------------------------------------------------
/demo/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {
3 | "test": "/components/test/test"
4 | }
5 | }
--------------------------------------------------------------------------------
/demo/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{userInfo.nickName}}
8 |
9 |
10 |
11 |
12 |
13 | {{motto}}
14 | rum
15 | redirect rum
16 |
17 |
18 |
--------------------------------------------------------------------------------
/demo/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | /**index.wxss**/
2 | .userinfo {
3 | display: flex;
4 | flex-direction: column;
5 | align-items: center;
6 | }
7 |
8 | .userinfo-avatar {
9 | width: 128rpx;
10 | height: 128rpx;
11 | margin: 20rpx;
12 | border-radius: 50%;
13 | }
14 |
15 | .userinfo-nickname {
16 | color: #aaa;
17 | }
18 |
19 | .usermotto {
20 | margin-top: 200px;
21 | }
--------------------------------------------------------------------------------
/demo/pages/logs/logs.js:
--------------------------------------------------------------------------------
1 | //logs.js
2 | const util = require('../../utils/util.js')
3 |
4 | Page({
5 | data: {
6 | logs: []
7 | },
8 | onLoad: function () {
9 | this.setData({
10 | logs: (wx.getStorageSync('logs') || []).map(log => {
11 | return util.formatTime(new Date(log))
12 | })
13 | })
14 | }
15 | })
16 |
--------------------------------------------------------------------------------
/demo/pages/logs/logs.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarTitleText": "查看启动日志",
3 | "usingComponents": {}
4 | }
--------------------------------------------------------------------------------
/demo/pages/logs/logs.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{index + 1}}. {{log}}
5 |
6 |
7 |
--------------------------------------------------------------------------------
/demo/pages/logs/logs.wxss:
--------------------------------------------------------------------------------
1 | .log-list {
2 | display: flex;
3 | flex-direction: column;
4 | padding: 40rpx;
5 | }
6 | .log-item {
7 | margin: 10rpx;
8 | }
9 |
--------------------------------------------------------------------------------
/demo/pages/rum.js:
--------------------------------------------------------------------------------
1 | // pages/rum.js
2 | Page({
3 |
4 | /**
5 | * 页面的初始数据
6 | */
7 | data: {
8 | text: ''
9 | },
10 |
11 | /**
12 | * 生命周期函数--监听页面加载
13 | */
14 | onLoad: function (options) {
15 | var t = 'xxxx'
16 | setTimeout(() => {
17 | t = 'xxxxxss'
18 | console.log(t,'t')
19 | this.setData({
20 | text: 'xxxx'
21 | })
22 | }, 1000)
23 | },
24 |
25 | /**
26 | * 生命周期函数--监听页面初次渲染完成
27 | */
28 | onReady: function () {
29 |
30 | },
31 | bindSetData: function() {
32 | // this.setData({
33 | // text: '111111'
34 | // })
35 | },
36 | /**
37 | * 生命周期函数--监听页面显示
38 | */
39 | onShow: function () {
40 |
41 | },
42 |
43 | /**
44 | * 生命周期函数--监听页面隐藏
45 | */
46 | onHide: function () {
47 |
48 | },
49 |
50 | /**
51 | * 生命周期函数--监听页面卸载
52 | */
53 | onUnload: function () {
54 |
55 | },
56 |
57 | /**
58 | * 页面相关事件处理函数--监听用户下拉动作
59 | */
60 | onPullDownRefresh: function () {
61 |
62 | },
63 |
64 | /**
65 | * 页面上拉触底事件的处理函数
66 | */
67 | onReachBottom: function () {
68 |
69 | },
70 |
71 | /**
72 | * 用户点击右上角分享
73 | */
74 | onShareAppMessage: function () {
75 |
76 | }
77 | })
--------------------------------------------------------------------------------
/demo/pages/rum.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {}
3 | }
--------------------------------------------------------------------------------
/demo/pages/rum.wxml:
--------------------------------------------------------------------------------
1 |
2 | {{text}}
3 |
4 |
--------------------------------------------------------------------------------
/demo/pages/rum.wxss:
--------------------------------------------------------------------------------
1 | /* pages/rum.wxss */
--------------------------------------------------------------------------------
/demo/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "项目配置文件",
3 | "packOptions": {
4 | "ignore": []
5 | },
6 | "setting": {
7 | "urlCheck": false,
8 | "es6": true,
9 | "enhance": false,
10 | "postcss": true,
11 | "preloadBackgroundData": false,
12 | "minified": true,
13 | "newFeature": false,
14 | "coverView": true,
15 | "nodeModules": false,
16 | "autoAudits": false,
17 | "showShadowRootInWxmlPanel": true,
18 | "scopeDataCheck": false,
19 | "uglifyFileName": false,
20 | "checkInvalidKey": true,
21 | "checkSiteMap": true,
22 | "uploadWithSourceMap": true,
23 | "compileHotReLoad": false,
24 | "useMultiFrameRuntime": true,
25 | "useApiHook": true,
26 | "useApiHostProcess": true,
27 | "babelSetting": {
28 | "ignore": [],
29 | "disablePlugins": [],
30 | "outputPath": ""
31 | },
32 | "useIsolateContext": false,
33 | "userConfirmedBundleSwitch": false,
34 | "packNpmManually": false,
35 | "packNpmRelationList": [],
36 | "minifyWXSS": true,
37 | "disableUseStrict": false,
38 | "minifyWXML": true,
39 | "showES6CompileOption": false,
40 | "useCompilerPlugins": false,
41 | "ignoreUploadUnusedFiles": true
42 | },
43 | "compileType": "miniprogram",
44 | "libVersion": "2.17.0",
45 | "appid": "wx4bffe864f9649b91",
46 | "projectname": "demo",
47 | "debugOptions": {
48 | "hidedInDevtools": []
49 | },
50 | "scripts": {},
51 | "isGameTourist": false,
52 | "simulatorType": "wechat",
53 | "simulatorPluginLibVersion": {},
54 | "condition": {
55 | "search": {
56 | "list": []
57 | },
58 | "conversation": {
59 | "list": []
60 | },
61 | "game": {
62 | "list": []
63 | },
64 | "plugin": {
65 | "list": []
66 | },
67 | "gamePlugin": {
68 | "list": []
69 | },
70 | "miniprogram": {
71 | "list": []
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/demo/sitemap.json:
--------------------------------------------------------------------------------
1 | {
2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
3 | "rules": [{
4 | "action": "allow",
5 | "page": "*"
6 | }]
7 | }
--------------------------------------------------------------------------------
/demo/utils/util.js:
--------------------------------------------------------------------------------
1 | const formatTime = date => {
2 | const year = date.getFullYear()
3 | const month = date.getMonth() + 1
4 | const day = date.getDate()
5 | const hour = date.getHours()
6 | const minute = date.getMinutes()
7 | const second = date.getSeconds()
8 |
9 | return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
10 | }
11 |
12 | const formatNumber = n => {
13 | n = n.toString()
14 | return n[1] ? n : '0' + n
15 | }
16 |
17 | module.exports = {
18 | formatTime: formatTime
19 | }
20 |
--------------------------------------------------------------------------------
/demo2/.hbuilderx/launch.json:
--------------------------------------------------------------------------------
1 | { // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
2 | // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
3 | "version": "0.0",
4 | "configurations": [{
5 | "type": "uniCloud",
6 | "default": {
7 | "launchtype": "local"
8 | }
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/demo2/App.vue:
--------------------------------------------------------------------------------
1 |
14 |
15 |
18 |
--------------------------------------------------------------------------------
/demo2/demo2/App.vue:
--------------------------------------------------------------------------------
1 |
14 |
15 |
18 |
--------------------------------------------------------------------------------
/demo2/demo2/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App'
3 |
4 | Vue.config.productionTip = false
5 |
6 | App.mpType = 'app'
7 |
8 | const app = new Vue({
9 | ...App
10 | })
11 | app.$mount()
12 |
--------------------------------------------------------------------------------
/demo2/demo2/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name" : "demo2",
3 | "appid" : "",
4 | "description" : "",
5 | "versionName" : "1.0.0",
6 | "versionCode" : "100",
7 | "transformPx" : false,
8 | /* 5+App特有相关 */
9 | "app-plus" : {
10 | "usingComponents" : true,
11 | "nvueStyleCompiler" : "uni-app",
12 | "compilerVersion" : 3,
13 | "splashscreen" : {
14 | "alwaysShowBeforeRender" : true,
15 | "waiting" : true,
16 | "autoclose" : true,
17 | "delay" : 0
18 | },
19 | /* 模块配置 */
20 | "modules" : {},
21 | /* 应用发布信息 */
22 | "distribute" : {
23 | /* android打包配置 */
24 | "android" : {
25 | "permissions" : [
26 | "",
27 | "",
28 | "",
29 | "",
30 | "",
31 | "",
32 | "",
33 | "",
34 | "",
35 | "",
36 | "",
37 | "",
38 | "",
39 | "",
40 | ""
41 | ]
42 | },
43 | /* ios打包配置 */
44 | "ios" : {},
45 | /* SDK配置 */
46 | "sdkConfigs" : {}
47 | }
48 | },
49 | /* 快应用特有相关 */
50 | "quickapp" : {},
51 | /* 小程序特有相关 */
52 | "mp-weixin" : {
53 | "appid" : "",
54 | "setting" : {
55 | "urlCheck" : false
56 | },
57 | "usingComponents" : true
58 | },
59 | "mp-alipay" : {
60 | "usingComponents" : true
61 | },
62 | "mp-baidu" : {
63 | "usingComponents" : true
64 | },
65 | "mp-toutiao" : {
66 | "usingComponents" : true
67 | },
68 | "uniStatistics": {
69 | "enable": false
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/demo2/demo2/pages.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
3 | {
4 | "path": "pages/index/index",
5 | "style": {
6 | "navigationBarTitleText": "uni-app"
7 | }
8 | }
9 | ],
10 | "globalStyle": {
11 | "navigationBarTextStyle": "black",
12 | "navigationBarTitleText": "uni-app",
13 | "navigationBarBackgroundColor": "#F8F8F8",
14 | "backgroundColor": "#F8F8F8"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/demo2/demo2/pages/index/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{title}}
6 |
7 |
8 |
9 |
10 |
25 |
26 |
53 |
--------------------------------------------------------------------------------
/demo2/demo2/static/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GuanceCloud/datakit-miniprogram/5c24eb3f4980ff1cab5a2cb6550e2c16cbea204e/demo2/demo2/static/logo.png
--------------------------------------------------------------------------------
/demo2/demo2/uni.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * 这里是uni-app内置的常用样式变量
3 | *
4 | * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
5 | * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
6 | *
7 | */
8 |
9 | /**
10 | * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
11 | *
12 | * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
13 | */
14 |
15 | /* 颜色变量 */
16 |
17 | /* 行为相关颜色 */
18 | $uni-color-primary: #007aff;
19 | $uni-color-success: #4cd964;
20 | $uni-color-warning: #f0ad4e;
21 | $uni-color-error: #dd524d;
22 |
23 | /* 文字基本颜色 */
24 | $uni-text-color:#333;//基本色
25 | $uni-text-color-inverse:#fff;//反色
26 | $uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
27 | $uni-text-color-placeholder: #808080;
28 | $uni-text-color-disable:#c0c0c0;
29 |
30 | /* 背景颜色 */
31 | $uni-bg-color:#ffffff;
32 | $uni-bg-color-grey:#f8f8f8;
33 | $uni-bg-color-hover:#f1f1f1;//点击状态颜色
34 | $uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
35 |
36 | /* 边框颜色 */
37 | $uni-border-color:#c8c7cc;
38 |
39 | /* 尺寸变量 */
40 |
41 | /* 文字尺寸 */
42 | $uni-font-size-sm:24rpx;
43 | $uni-font-size-base:28rpx;
44 | $uni-font-size-lg:32rpx;
45 |
46 | /* 图片尺寸 */
47 | $uni-img-size-sm:40rpx;
48 | $uni-img-size-base:52rpx;
49 | $uni-img-size-lg:80rpx;
50 |
51 | /* Border Radius */
52 | $uni-border-radius-sm: 4rpx;
53 | $uni-border-radius-base: 6rpx;
54 | $uni-border-radius-lg: 12rpx;
55 | $uni-border-radius-circle: 50%;
56 |
57 | /* 水平间距 */
58 | $uni-spacing-row-sm: 10px;
59 | $uni-spacing-row-base: 20rpx;
60 | $uni-spacing-row-lg: 30rpx;
61 |
62 | /* 垂直间距 */
63 | $uni-spacing-col-sm: 8rpx;
64 | $uni-spacing-col-base: 16rpx;
65 | $uni-spacing-col-lg: 24rpx;
66 |
67 | /* 透明度 */
68 | $uni-opacity-disabled: 0.3; // 组件禁用态的透明度
69 |
70 | /* 文章场景相关 */
71 | $uni-color-title: #2C405A; // 文章标题颜色
72 | $uni-font-size-title:40rpx;
73 | $uni-color-subtitle: #555555; // 二级标题颜色
74 | $uni-font-size-subtitle:36rpx;
75 | $uni-color-paragraph: #3F536E; // 文章段落颜色
76 | $uni-font-size-paragraph:30rpx;
--------------------------------------------------------------------------------
/demo2/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App'
3 |
4 | Vue.config.productionTip = false
5 |
6 | App.mpType = 'app'
7 |
8 | const { datafluxRum } = require('./miniprogram/dataflux-rum-uniapp')
9 | // // 初始化 Sentry
10 | datafluxRum.init(Vue, {
11 | datakitOrigin: 'http://172.16.2.201:31845',
12 | applicationId: 'appid_6cb4c98eba9143c88c83e544407b1c74',
13 | env: 'prod',
14 | version: '1.0.0',
15 | trackInteractions: true,
16 | allowedTracingOrigins: ['http://testing-ft2x-api.cloudcare.cn'],
17 | traceType: 'zipkin'
18 | })
19 | datafluxRum.setUser({
20 | id: '1234',
21 | name: 'John Doe',
22 | email: 'john@doe.com',
23 | })
24 | datafluxRum && datafluxRum.addRumGlobalContext('isvip', 'xxxx');
25 | datafluxRum.addRumGlobalContext('activity', {
26 | hasPaid: true,
27 | amount: 23.42
28 | });
29 | const app = new Vue({
30 | ...App,
31 | })
32 | app.$mount()
33 |
--------------------------------------------------------------------------------
/demo2/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name" : "rrr",
3 | "appid" : "",
4 | "description" : "",
5 | "versionName" : "1.0.0",
6 | "versionCode" : "100",
7 | "transformPx" : false,
8 | /* 5+App特有相关 */
9 | "app-plus" : {
10 | "usingComponents" : true,
11 | "nvueStyleCompiler" : "uni-app",
12 | "compilerVersion" : 3,
13 | "splashscreen" : {
14 | "alwaysShowBeforeRender" : true,
15 | "waiting" : true,
16 | "autoclose" : true,
17 | "delay" : 0
18 | },
19 | /* 模块配置 */
20 | "modules" : {},
21 | /* 应用发布信息 */
22 | "distribute" : {
23 | /* android打包配置 */
24 | "android" : {
25 | "permissions" : [
26 | "",
27 | "",
28 | "",
29 | "",
30 | "",
31 | "",
32 | "",
33 | "",
34 | "",
35 | "",
36 | "",
37 | "",
38 | "",
39 | "",
40 | ""
41 | ]
42 | },
43 | /* ios打包配置 */
44 | "ios" : {},
45 | /* SDK配置 */
46 | "sdkConfigs" : {}
47 | }
48 | },
49 | /* 快应用特有相关 */
50 | "quickapp" : {},
51 | /* 小程序特有相关 */
52 | "mp-weixin" : {
53 | "appid" : "",
54 | "setting" : {
55 | "urlCheck" : false,
56 | "es6" : false
57 | },
58 | "usingComponents" : true
59 | },
60 | "mp-alipay" : {
61 | "usingComponents" : true
62 | },
63 | "mp-baidu" : {
64 | "usingComponents" : true
65 | },
66 | "mp-toutiao" : {
67 | "usingComponents" : true
68 | },
69 | "uniStatistics" : {
70 | "enable" : false
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/demo2/pages.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
3 | {
4 | "path": "pages/index/index",
5 | "style": {
6 | "navigationBarTitleText": "uni-app"
7 | }
8 | },
9 | {
10 | "path": "pages/home/home",
11 | "style": {
12 | "navigationBarTitleText": "uni-app"
13 | }
14 | }
15 | ],
16 | "globalStyle": {
17 | "navigationBarTextStyle": "black",
18 | "navigationBarTitleText": "uni-app",
19 | "navigationBarBackgroundColor": "#F8F8F8",
20 | "backgroundColor": "#F8F8F8"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/demo2/pages/home/home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
19 |
20 |
22 |
--------------------------------------------------------------------------------
/demo2/pages/index/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
117 |
118 |
145 |
--------------------------------------------------------------------------------
/demo2/static/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GuanceCloud/datakit-miniprogram/5c24eb3f4980ff1cab5a2cb6550e2c16cbea204e/demo2/static/logo.png
--------------------------------------------------------------------------------
/demo2/test-sdk/App.vue:
--------------------------------------------------------------------------------
1 |
14 |
15 |
18 |
--------------------------------------------------------------------------------
/demo2/test-sdk/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App'
3 |
4 | Vue.config.productionTip = false
5 |
6 | App.mpType = 'app'
7 |
8 | const app = new Vue({
9 | ...App
10 | })
11 | app.$mount()
12 |
--------------------------------------------------------------------------------
/demo2/test-sdk/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name" : "test-sdk",
3 | "appid" : "",
4 | "description" : "",
5 | "versionName" : "1.0.0",
6 | "versionCode" : "100",
7 | "transformPx" : false,
8 | /* 5+App特有相关 */
9 | "app-plus" : {
10 | "usingComponents" : true,
11 | "nvueStyleCompiler" : "uni-app",
12 | "compilerVersion" : 3,
13 | "splashscreen" : {
14 | "alwaysShowBeforeRender" : true,
15 | "waiting" : true,
16 | "autoclose" : true,
17 | "delay" : 0
18 | },
19 | /* 模块配置 */
20 | "modules" : {},
21 | /* 应用发布信息 */
22 | "distribute" : {
23 | /* android打包配置 */
24 | "android" : {
25 | "permissions" : [
26 | "",
27 | "",
28 | "",
29 | "",
30 | "",
31 | "",
32 | "",
33 | "",
34 | "",
35 | "",
36 | "",
37 | "",
38 | "",
39 | "",
40 | ""
41 | ]
42 | },
43 | /* ios打包配置 */
44 | "ios" : {},
45 | /* SDK配置 */
46 | "sdkConfigs" : {}
47 | }
48 | },
49 | /* 快应用特有相关 */
50 | "quickapp" : {},
51 | /* 小程序特有相关 */
52 | "mp-weixin" : {
53 | "appid" : "",
54 | "setting" : {
55 | "urlCheck" : false
56 | },
57 | "usingComponents" : true
58 | },
59 | "mp-alipay" : {
60 | "usingComponents" : true
61 | },
62 | "mp-baidu" : {
63 | "usingComponents" : true
64 | },
65 | "mp-toutiao" : {
66 | "usingComponents" : true
67 | },
68 | "uniStatistics": {
69 | "enable": false
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/demo2/test-sdk/pages.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
3 | {
4 | "path": "pages/index/index",
5 | "style": {
6 | "navigationBarTitleText": "uni-app"
7 | }
8 | }
9 | ],
10 | "globalStyle": {
11 | "navigationBarTextStyle": "black",
12 | "navigationBarTitleText": "uni-app",
13 | "navigationBarBackgroundColor": "#F8F8F8",
14 | "backgroundColor": "#F8F8F8"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/demo2/test-sdk/pages/index/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{title}}
6 |
7 |
8 |
9 |
10 |
25 |
26 |
53 |
--------------------------------------------------------------------------------
/demo2/test-sdk/static/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GuanceCloud/datakit-miniprogram/5c24eb3f4980ff1cab5a2cb6550e2c16cbea204e/demo2/test-sdk/static/logo.png
--------------------------------------------------------------------------------
/demo2/test-sdk/uni.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * 这里是uni-app内置的常用样式变量
3 | *
4 | * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
5 | * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
6 | *
7 | */
8 |
9 | /**
10 | * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
11 | *
12 | * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
13 | */
14 |
15 | /* 颜色变量 */
16 |
17 | /* 行为相关颜色 */
18 | $uni-color-primary: #007aff;
19 | $uni-color-success: #4cd964;
20 | $uni-color-warning: #f0ad4e;
21 | $uni-color-error: #dd524d;
22 |
23 | /* 文字基本颜色 */
24 | $uni-text-color:#333;//基本色
25 | $uni-text-color-inverse:#fff;//反色
26 | $uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
27 | $uni-text-color-placeholder: #808080;
28 | $uni-text-color-disable:#c0c0c0;
29 |
30 | /* 背景颜色 */
31 | $uni-bg-color:#ffffff;
32 | $uni-bg-color-grey:#f8f8f8;
33 | $uni-bg-color-hover:#f1f1f1;//点击状态颜色
34 | $uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
35 |
36 | /* 边框颜色 */
37 | $uni-border-color:#c8c7cc;
38 |
39 | /* 尺寸变量 */
40 |
41 | /* 文字尺寸 */
42 | $uni-font-size-sm:24rpx;
43 | $uni-font-size-base:28rpx;
44 | $uni-font-size-lg:32rpx;
45 |
46 | /* 图片尺寸 */
47 | $uni-img-size-sm:40rpx;
48 | $uni-img-size-base:52rpx;
49 | $uni-img-size-lg:80rpx;
50 |
51 | /* Border Radius */
52 | $uni-border-radius-sm: 4rpx;
53 | $uni-border-radius-base: 6rpx;
54 | $uni-border-radius-lg: 12rpx;
55 | $uni-border-radius-circle: 50%;
56 |
57 | /* 水平间距 */
58 | $uni-spacing-row-sm: 10px;
59 | $uni-spacing-row-base: 20rpx;
60 | $uni-spacing-row-lg: 30rpx;
61 |
62 | /* 垂直间距 */
63 | $uni-spacing-col-sm: 8rpx;
64 | $uni-spacing-col-base: 16rpx;
65 | $uni-spacing-col-lg: 24rpx;
66 |
67 | /* 透明度 */
68 | $uni-opacity-disabled: 0.3; // 组件禁用态的透明度
69 |
70 | /* 文章场景相关 */
71 | $uni-color-title: #2C405A; // 文章标题颜色
72 | $uni-font-size-title:40rpx;
73 | $uni-color-subtitle: #555555; // 二级标题颜色
74 | $uni-font-size-subtitle:36rpx;
75 | $uni-color-paragraph: #3F536E; // 文章段落颜色
76 | $uni-font-size-paragraph:30rpx;
--------------------------------------------------------------------------------
/demo2/uni.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * 这里是uni-app内置的常用样式变量
3 | *
4 | * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
5 | * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
6 | *
7 | */
8 |
9 | /**
10 | * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
11 | *
12 | * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
13 | */
14 |
15 | /* 颜色变量 */
16 |
17 | /* 行为相关颜色 */
18 | $uni-color-primary: #007aff;
19 | $uni-color-success: #4cd964;
20 | $uni-color-warning: #f0ad4e;
21 | $uni-color-error: #dd524d;
22 |
23 | /* 文字基本颜色 */
24 | $uni-text-color:#333;//基本色
25 | $uni-text-color-inverse:#fff;//反色
26 | $uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
27 | $uni-text-color-placeholder: #808080;
28 | $uni-text-color-disable:#c0c0c0;
29 |
30 | /* 背景颜色 */
31 | $uni-bg-color:#ffffff;
32 | $uni-bg-color-grey:#f8f8f8;
33 | $uni-bg-color-hover:#f1f1f1;//点击状态颜色
34 | $uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
35 |
36 | /* 边框颜色 */
37 | $uni-border-color:#c8c7cc;
38 |
39 | /* 尺寸变量 */
40 |
41 | /* 文字尺寸 */
42 | $uni-font-size-sm:24rpx;
43 | $uni-font-size-base:28rpx;
44 | $uni-font-size-lg:32rpx;
45 |
46 | /* 图片尺寸 */
47 | $uni-img-size-sm:40rpx;
48 | $uni-img-size-base:52rpx;
49 | $uni-img-size-lg:80rpx;
50 |
51 | /* Border Radius */
52 | $uni-border-radius-sm: 4rpx;
53 | $uni-border-radius-base: 6rpx;
54 | $uni-border-radius-lg: 12rpx;
55 | $uni-border-radius-circle: 50%;
56 |
57 | /* 水平间距 */
58 | $uni-spacing-row-sm: 10px;
59 | $uni-spacing-row-base: 20rpx;
60 | $uni-spacing-row-lg: 30rpx;
61 |
62 | /* 垂直间距 */
63 | $uni-spacing-col-sm: 8rpx;
64 | $uni-spacing-col-base: 16rpx;
65 | $uni-spacing-col-lg: 24rpx;
66 |
67 | /* 透明度 */
68 | $uni-opacity-disabled: 0.3; // 组件禁用态的透明度
69 |
70 | /* 文章场景相关 */
71 | $uni-color-title: #2C405A; // 文章标题颜色
72 | $uni-font-size-title:40rpx;
73 | $uni-color-subtitle: #555555; // 二级标题颜色
74 | $uni-font-size-subtitle:36rpx;
75 | $uni-color-paragraph: #3F536E; // 文章段落颜色
76 | $uni-font-size-paragraph:30rpx;
--------------------------------------------------------------------------------
/demo2/unpackage/dist/dev/.automator/mp-alipay/.automator.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GuanceCloud/datakit-miniprogram/5c24eb3f4980ff1cab5a2cb6550e2c16cbea204e/demo2/unpackage/dist/dev/.automator/mp-alipay/.automator.json
--------------------------------------------------------------------------------
/demo2/unpackage/dist/dev/.automator/mp-weixin/.automator.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GuanceCloud/datakit-miniprogram/5c24eb3f4980ff1cab5a2cb6550e2c16cbea204e/demo2/unpackage/dist/dev/.automator/mp-weixin/.automator.json
--------------------------------------------------------------------------------
/demo2/unpackage/dist/dev/.sourcemap/mp-weixin/common/runtime.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":[null],"names":[],"mappings":";QAAA;QACA;QACA;QACA;QACA;;QAEA;QACA;QACA;QACA,QAAQ,oBAAoB;QAC5B;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;;QAEA;QACA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA;QACA;QACA,iBAAiB,4BAA4B;QAC7C;QACA;QACA,kBAAkB,2BAA2B;QAC7C;QACA;QACA;QACA;QACA;QACA;QACA;QACA;;QAEA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA;QACA;QACA;;QAEA;;QAEA;QACA;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;;QAEA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;;;QAGA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA,0CAA0C,gCAAgC;QAC1E;QACA;;QAEA;QACA;QACA;QACA,wDAAwD,kBAAkB;QAC1E;QACA,iDAAiD,cAAc;QAC/D;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA,yCAAyC,iCAAiC;QAC1E,gHAAgH,mBAAmB,EAAE;QACrI;QACA;;QAEA;QACA;QACA;QACA,2BAA2B,0BAA0B,EAAE;QACvD,iCAAiC,eAAe;QAChD;QACA;QACA;;QAEA;QACA,sDAAsD,+DAA+D;;QAErH;QACA;;QAEA;QACA;QACA;QACA;QACA,gBAAgB,uBAAuB;QACvC;;;QAGA;QACA","file":"common/runtime.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tfunction webpackJsonpCallback(data) {\n \t\tvar chunkIds = data[0];\n \t\tvar moreModules = data[1];\n \t\tvar executeModules = data[2];\n\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [];\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(data);\n\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n\n \t\t// add entry modules from loaded chunk to deferred list\n \t\tdeferredModules.push.apply(deferredModules, executeModules || []);\n\n \t\t// run deferred modules when all chunks ready\n \t\treturn checkDeferredModules();\n \t};\n \tfunction checkDeferredModules() {\n \t\tvar result;\n \t\tfor(var i = 0; i < deferredModules.length; i++) {\n \t\t\tvar deferredModule = deferredModules[i];\n \t\t\tvar fulfilled = true;\n \t\t\tfor(var j = 1; j < deferredModule.length; j++) {\n \t\t\t\tvar depId = deferredModule[j];\n \t\t\t\tif(installedChunks[depId] !== 0) fulfilled = false;\n \t\t\t}\n \t\t\tif(fulfilled) {\n \t\t\t\tdeferredModules.splice(i--, 1);\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = deferredModule[0]);\n \t\t\t}\n \t\t}\n\n \t\treturn result;\n \t}\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// object to store loaded and loading chunks\n \t// undefined = chunk not loaded, null = chunk preloaded/prefetched\n \t// Promise = chunk loading, 0 = chunk loaded\n \tvar installedChunks = {\n \t\t\"common/runtime\": 0\n \t};\n\n \tvar deferredModules = [];\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n \tvar jsonpArray = global[\"webpackJsonp\"] = global[\"webpackJsonp\"] || [];\n \tvar oldJsonpFunction = jsonpArray.push.bind(jsonpArray);\n \tjsonpArray.push = webpackJsonpCallback;\n \tjsonpArray = jsonpArray.slice();\n \tfor(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);\n \tvar parentJsonpFunction = oldJsonpFunction;\n\n\n \t// run deferred modules from other chunks\n \tcheckDeferredModules();\n"],"sourceRoot":""}
--------------------------------------------------------------------------------
/demo2/unpackage/dist/dev/.sourcemap/mp-weixin/pages/home/home.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["uni-app:///main.js","webpack:////Users/arnolddeng/Desktop/www/dataflux-rum-sdk-wechat/demo2/pages/home/home.vue?4be5","webpack:////Users/arnolddeng/Desktop/www/dataflux-rum-sdk-wechat/demo2/pages/home/home.vue?4a4f","webpack:////Users/arnolddeng/Desktop/www/dataflux-rum-sdk-wechat/demo2/pages/home/home.vue?b7f0","webpack:////Users/arnolddeng/Desktop/www/dataflux-rum-sdk-wechat/demo2/pages/home/home.vue?ef02","uni-app:///pages/home/home.vue"],"names":["createPage","Page"],"mappings":";;;;;;;;;;kDAAA;AACA;AACA,yF;AACAA,UAAU,CAACC,aAAD,CAAV,C;;;;;;;;;;;;;ACHA;AAAA;AAAA;AAAA;AAAA;AAAiH;AACjH;AACwD;AACL;;;AAGnD;AACmN;AACnN,gBAAgB,iNAAU;AAC1B,EAAE,0EAAM;AACR,EAAE,+EAAM;AACR,EAAE,wFAAe;AACjB;AACA;AACA;AACA;AACA;AACA,EAAE,mFAAU;AACZ;AACA;;AAEA;AACe,gF;;;;;;;;;;;;ACtBf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;;;;;;;;;;;ACAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;ACRA;AAAA;AAAA;AAAA;AAAi1B,CAAgB,kyBAAG,EAAC,C;;;;;;;;;;;;;;;;;;;ACOr2B;AACA,QADA,oBACA;;AAEA,GAHA;AAIA;AACA;AACA;AACA,KAHA,EAJA,E","file":"pages/home/home.js","sourcesContent":["import 'uni-pages';\nimport Vue from 'vue'\nimport Page from './pages/home/home.vue'\ncreatePage(Page)","import { render, staticRenderFns, recyclableRender, components } from \"./home.vue?vue&type=template&id=92bb8f34&\"\nvar renderjs\nimport script from \"./home.vue?vue&type=script&lang=js&\"\nexport * from \"./home.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../../../../../Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/@dcloudio/vue-cli-plugin-uni/packages/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null,\n false,\n components,\n renderjs\n)\n\ncomponent.options.__file = \"pages/home/home.vue\"\nexport default component.exports","export * from \"-!../../../../../../../../Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/@dcloudio/vue-cli-plugin-uni/packages/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!../../../../../../../../Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/@dcloudio/vue-cli-plugin-uni/packages/webpack-preprocess-loader/index.js??ref--16-0!../../../../../../../../Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/@dcloudio/webpack-uni-mp-loader/lib/template.js!../../../../../../../../Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/@dcloudio/vue-cli-plugin-uni/packages/webpack-uni-app-loader/page-meta.js!../../../../../../../../Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/@dcloudio/vue-cli-plugin-uni/packages/vue-loader/lib/index.js??vue-loader-options!../../../../../../../../Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/@dcloudio/webpack-uni-mp-loader/lib/style.js!./home.vue?vue&type=template&id=92bb8f34&\"","var components\nvar render = function() {\n var _vm = this\n var _h = _vm.$createElement\n var _c = _vm._self._c || _h\n}\nvar recyclableRender = false\nvar staticRenderFns = []\nrender._withStripped = true\n\nexport { render, staticRenderFns, recyclableRender, components }","import mod from \"-!../../../../../../../../Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/babel-loader/lib/index.js!../../../../../../../../Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/@dcloudio/vue-cli-plugin-uni/packages/webpack-preprocess-loader/index.js??ref--12-1!../../../../../../../../Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/@dcloudio/webpack-uni-mp-loader/lib/script.js!../../../../../../../../Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/@dcloudio/vue-cli-plugin-uni/packages/vue-loader/lib/index.js??vue-loader-options!../../../../../../../../Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/@dcloudio/webpack-uni-mp-loader/lib/style.js!./home.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../../../../../../Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/babel-loader/lib/index.js!../../../../../../../../Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/@dcloudio/vue-cli-plugin-uni/packages/webpack-preprocess-loader/index.js??ref--12-1!../../../../../../../../Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/@dcloudio/webpack-uni-mp-loader/lib/script.js!../../../../../../../../Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/@dcloudio/vue-cli-plugin-uni/packages/vue-loader/lib/index.js??vue-loader-options!../../../../../../../../Applications/HBuilderX.app/Contents/HBuilderX/plugins/uniapp-cli/node_modules/@dcloudio/webpack-uni-mp-loader/lib/style.js!./home.vue?vue&type=script&lang=js&\"","\n\t\n\t\t\n\t\n\n\n\n\n\n"],"sourceRoot":""}
--------------------------------------------------------------------------------
/demo2/unpackage/dist/dev/mp-weixin/app.js:
--------------------------------------------------------------------------------
1 |
2 | require('./common/runtime.js')
3 | require('./common/vendor.js')
4 | require('./common/main.js')
--------------------------------------------------------------------------------
/demo2/unpackage/dist/dev/mp-weixin/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [
3 | "pages/index/index",
4 | "pages/home/home"
5 | ],
6 | "subPackages": [],
7 | "window": {
8 | "navigationBarTextStyle": "black",
9 | "navigationBarTitleText": "uni-app",
10 | "navigationBarBackgroundColor": "#F8F8F8",
11 | "backgroundColor": "#F8F8F8"
12 | },
13 | "usingComponents": {}
14 | }
--------------------------------------------------------------------------------
/demo2/unpackage/dist/dev/mp-weixin/app.wxss:
--------------------------------------------------------------------------------
1 | @import './common/main.wxss';
2 |
3 | [data-custom-hidden="true"],[bind-data-custom-hidden="true"]{display: none !important;}
--------------------------------------------------------------------------------
/demo2/unpackage/dist/dev/mp-weixin/common/main.wxss:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | /*每个页面公共css */
17 |
18 |
--------------------------------------------------------------------------------
/demo2/unpackage/dist/dev/mp-weixin/pages/home/home.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarTitleText": "uni-app",
3 | "usingComponents": {}
4 | }
--------------------------------------------------------------------------------
/demo2/unpackage/dist/dev/mp-weixin/pages/home/home.wxml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demo2/unpackage/dist/dev/mp-weixin/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarTitleText": "uni-app",
3 | "usingComponents": {}
4 | }
--------------------------------------------------------------------------------
/demo2/unpackage/dist/dev/mp-weixin/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demo2/unpackage/dist/dev/mp-weixin/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 |
2 | .content {
3 | display: -webkit-box;
4 | display: -webkit-flex;
5 | display: flex;
6 | -webkit-box-orient: vertical;
7 | -webkit-box-direction: normal;
8 | -webkit-flex-direction: column;
9 | flex-direction: column;
10 | -webkit-box-align: center;
11 | -webkit-align-items: center;
12 | align-items: center;
13 | -webkit-box-pack: center;
14 | -webkit-justify-content: center;
15 | justify-content: center;
16 | }
17 | .logo {
18 | height: 200rpx;
19 | width: 200rpx;
20 | margin-top: 200rpx;
21 | margin-left: auto;
22 | margin-right: auto;
23 | margin-bottom: 50rpx;
24 | }
25 | .text-area {
26 | display: -webkit-box;
27 | display: -webkit-flex;
28 | display: flex;
29 | -webkit-box-pack: center;
30 | -webkit-justify-content: center;
31 | justify-content: center;
32 | }
33 | .title {
34 | font-size: 36rpx;
35 | color: #8f8f94;
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/demo2/unpackage/dist/dev/mp-weixin/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "项目配置文件。",
3 | "packOptions": {
4 | "ignore": []
5 | },
6 | "setting": {
7 | "urlCheck": false,
8 | "es6": false
9 | },
10 | "compileType": "miniprogram",
11 | "libVersion": "",
12 | "appid": "touristappid",
13 | "projectname": "rrr",
14 | "condition": {
15 | "search": {
16 | "current": -1,
17 | "list": []
18 | },
19 | "conversation": {
20 | "current": -1,
21 | "list": []
22 | },
23 | "game": {
24 | "current": -1,
25 | "list": []
26 | },
27 | "miniprogram": {
28 | "current": -1,
29 | "list": []
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/demo2/unpackage/dist/dev/mp-weixin/sitemap.json:
--------------------------------------------------------------------------------
1 | {
2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
3 | "rules": [{
4 | "action": "allow",
5 | "page": "*"
6 | }]
7 | }
--------------------------------------------------------------------------------
/demo2/unpackage/dist/dev/mp-weixin/static/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GuanceCloud/datakit-miniprogram/5c24eb3f4980ff1cab5a2cb6550e2c16cbea204e/demo2/unpackage/dist/dev/mp-weixin/static/logo.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@cloudcare/rum-miniapp",
3 | "version": "2.1.0",
4 | "main": "cjs/index.js",
5 | "module": "esm/index.js",
6 | "miniprogram": "cjs",
7 | "types": "cjs/index.d.ts",
8 | "devDependencies": {
9 | "@babel/core": "^7.12.10",
10 | "@babel/preset-env": "^7.12.10",
11 | "ali-oss": "^6.12.0",
12 | "npm-run-all": "^4.1.5",
13 | "replace-in-file": "^6.1.0",
14 | "webpack": "^5.37.0",
15 | "webpack-cli": "^4.7.0",
16 | "webpack-dev-server": "^3.11.2"
17 | },
18 | "scripts": {
19 | "test": "echo \"Error: no test specified\" && exit 1",
20 | "build": "run-p build:cjs build:esm build:wx",
21 | "build:watch": "webpack --watch --config ./webpack.config.js --mode=development",
22 | "build:wx": "rm -rf demo/miniprogram && webpack --config webpack.config.js --mode=production && npm run replace-build-env demo/miniprogram",
23 | "build:cjs": "rm -rf cjs && babel --config-file ./babel.cjs.json --out-dir cjs ./src && npm run replace-build-env cjs",
24 | "build:esm": "rm -rf esm && babel --config-file ./babel.esm.json --out-dir esm ./src && npm run replace-build-env esm",
25 | "publish:npm": "npm run build && node ./scripts/publish-oss.js && npm publish --access=public",
26 | "publish:oss:test": "npm run build && node ./scripts/publish-oss.js test",
27 | "replace-build-env": "node scripts/replace-build-env.js"
28 | },
29 | "keywords": [
30 | "dataflux",
31 | "rum",
32 | "sdk",
33 | "小程序",
34 | "miniapp"
35 | ],
36 | "repository": {
37 | "url": "https://github.com/DataFlux-cn/datakit-miniprogram",
38 | "type": "git"
39 | },
40 | "author": "dataflux",
41 | "license": "MIT",
42 | "description": "DataFlux RUM 小程序 端数据指标监控",
43 | "dependencies": {}
44 | }
45 |
--------------------------------------------------------------------------------
/scripts/build-env.js:
--------------------------------------------------------------------------------
1 | const execSync = require('child_process').execSync
2 | const packageJSON = require('../package.json')
3 |
4 | let sdkVersion = packageJSON.version
5 |
6 | module.exports = {
7 | SDK_VERSION: sdkVersion,
8 | }
9 |
--------------------------------------------------------------------------------
/scripts/replace-build-env.js:
--------------------------------------------------------------------------------
1 | const replace = require('replace-in-file')
2 | const buildEnv = require('./build-env')
3 |
4 | /**
5 | * Replace BuildEnv in build files
6 | * Usage:
7 | * TARGET_DATACENTER=xxx BUILD_MODE=zzz node replace-build-env.js /path/to/build/directory
8 | */
9 |
10 | const buildDirectory = process.argv[2]
11 | console.log(process.argv, '=====')
12 | console.log(`Replace BuildEnv in '${buildDirectory}' with:`)
13 | console.log(JSON.stringify(buildEnv, null, 2))
14 |
15 | try {
16 | const results = replace.sync({
17 | files: `${buildDirectory}/**/*.js`,
18 | from: Object.keys(buildEnv).map((entry) => `<<< ${entry} >>>`),
19 | to: Object.values(buildEnv),
20 | })
21 |
22 | console.log(
23 | 'Changed files:',
24 | results.filter((entry) => entry.hasChanged).map((entry) => entry.file),
25 | )
26 | process.exit(0)
27 | } catch (error) {
28 | console.error('Error occurred:', error)
29 | process.exit(1)
30 | }
31 |
--------------------------------------------------------------------------------
/src/boot/buildEnv.js:
--------------------------------------------------------------------------------
1 | export const buildEnv = {
2 | sdkVersion: '<<< SDK_VERSION >>>',
3 | sdkName: 'df_miniapp_rum_sdk',
4 | }
5 |
--------------------------------------------------------------------------------
/src/boot/rum.entry.js:
--------------------------------------------------------------------------------
1 | import { isPercentage, extend2Lev, createContextManager } from '../helper/utils'
2 | import { startRum } from './rum'
3 |
4 | export const makeRum = function (startRumImpl) {
5 | var isAlreadyInitialized = false
6 | var globalContextManager = createContextManager()
7 | var user = {}
8 | function clonedCommonContext() {
9 | return extend2Lev(
10 | {},
11 | {
12 | context: globalContextManager.get(),
13 | user: user
14 | }
15 | )
16 | }
17 | var rumGlobal = {
18 | init: function (userConfiguration) {
19 | if (typeof userConfiguration === 'undefined') {
20 | userConfiguration = {}
21 | }
22 | if (!canInitRum(userConfiguration)) {
23 | return
24 | }
25 | startRumImpl(userConfiguration, function () {
26 | return {
27 | user: user,
28 | context: globalContextManager.get()
29 | }
30 | })
31 | isAlreadyInitialized = true
32 | },
33 | addRumGlobalContext: globalContextManager.add,
34 | removeRumGlobalContext: globalContextManager.remove,
35 | getRumGlobalContext: globalContextManager.get,
36 | setRumGlobalContext: globalContextManager.set,
37 | setUser: function (newUser) {
38 | var sanitizedUser = sanitizeUser(newUser)
39 | if (sanitizedUser) {
40 | user = sanitizedUser
41 | } else {
42 | console.error('Unsupported user:', newUser)
43 | }
44 | },
45 | removeUser: function () {
46 | user = {}
47 | }
48 | }
49 | return rumGlobal
50 |
51 | function canInitRum(userConfiguration) {
52 | if (isAlreadyInitialized) {
53 | console.error('DATAFLUX_RUM is already initialized.')
54 | return false
55 | }
56 |
57 | if (!userConfiguration.applicationId) {
58 | console.error(
59 | 'Application ID is not configured, no RUM data will be collected.',
60 | )
61 | return false
62 | }
63 | if (!userConfiguration.datakitOrigin) {
64 | console.error(
65 | 'datakitOrigin is not configured, no RUM data will be collected.',
66 | )
67 | return false
68 | }
69 | if (
70 | userConfiguration.sampleRate !== undefined &&
71 | !isPercentage(userConfiguration.sampleRate)
72 | ) {
73 | console.error('Sample Rate should be a number between 0 and 100')
74 | return false
75 | }
76 | return true
77 | }
78 | function sanitizeUser(newUser) {
79 | if (typeof newUser !== 'object' || !newUser) {
80 | return
81 | }
82 | var result = extend2Lev({}, newUser)
83 | if ('id' in result) {
84 | result.id = String(result.id)
85 | }
86 | if ('name' in result) {
87 | result.name = String(result.name)
88 | }
89 | if ('email' in result) {
90 | result.email = String(result.email)
91 | }
92 | return result
93 | }
94 | }
95 | export const datafluxRum = makeRum(startRum)
96 |
--------------------------------------------------------------------------------
/src/boot/rum.js:
--------------------------------------------------------------------------------
1 | import { buildEnv } from './buildEnv'
2 | import { LifeCycle } from '../core/lifeCycle'
3 | import { commonInit } from '../core/configuration'
4 | import { startErrorCollection } from '../rumEventsCollection/error/errorCollection'
5 | import { startRumAssembly } from '../rumEventsCollection/assembly'
6 | import { startParentContexts } from '../rumEventsCollection/parentContexts'
7 | import { startRumBatch } from '../rumEventsCollection/transport/batch'
8 | import { startViewCollection } from '../rumEventsCollection/page/viewCollection'
9 | import { startRequestCollection } from '../rumEventsCollection/requestCollection'
10 | import { startResourceCollection } from '../rumEventsCollection/resource/resourceCollection'
11 | import { startAppCollection } from '../rumEventsCollection/app/appCollection'
12 | import { startPagePerformanceObservable } from '../rumEventsCollection/performanceCollection'
13 | import { startSetDataColloction } from '../rumEventsCollection/setDataCollection'
14 | import { startActionCollection } from '../rumEventsCollection/action/actionCollection'
15 |
16 | import { sdk } from '../core/sdk'
17 | export const startRum = function (userConfiguration, getCommonContext) {
18 | const configuration = commonInit(userConfiguration, buildEnv)
19 | const lifeCycle = new LifeCycle()
20 | var parentContexts = startParentContexts(lifeCycle)
21 | var batch = startRumBatch(configuration, lifeCycle)
22 | startRumAssembly(
23 | userConfiguration.applicationId,
24 | configuration,
25 | lifeCycle,
26 | parentContexts,
27 | getCommonContext
28 | )
29 | startAppCollection(lifeCycle, configuration)
30 | startResourceCollection(lifeCycle, configuration)
31 | startViewCollection(lifeCycle, configuration)
32 | startErrorCollection(lifeCycle, configuration)
33 | startRequestCollection(lifeCycle, configuration)
34 | startPagePerformanceObservable(lifeCycle, configuration)
35 | startSetDataColloction(lifeCycle)
36 | startActionCollection(lifeCycle, configuration)
37 | }
38 |
--------------------------------------------------------------------------------
/src/core/baseInfo.js:
--------------------------------------------------------------------------------
1 | import { sdk } from '../core/sdk'
2 | import { UUID } from '../helper/utils'
3 | import { CLIENT_ID_TOKEN } from '../helper/enums'
4 | class BaseInfo {
5 | constructor() {
6 | this.sessionId = UUID()
7 | this.getDeviceInfo()
8 | this.getNetWork()
9 | }
10 | getDeviceInfo() {
11 | try {
12 | const deviceInfo = sdk.getSystemInfoSync()
13 | var osInfo = deviceInfo.system.split(' ')
14 | var osVersion = osInfo.length > 1 && osInfo[1]
15 | var osVersionMajor =
16 | osVersion.split('.').length && osVersion.split('.')[0]
17 | var deviceUUid = ''
18 | if (deviceInfo.host) {
19 | deviceUUid = deviceInfo.host.appId
20 | }
21 | this.deviceInfo = {
22 | screenSize: `${deviceInfo.screenWidth}*${deviceInfo.screenHeight} `,
23 | platform: deviceInfo.platform,
24 | platformVersion: deviceInfo.version,
25 | osVersion: osVersion,
26 | osVersionMajor: osVersionMajor,
27 | os: osInfo.length > 1 && osInfo[0],
28 | brand: deviceInfo.brand,
29 | model: deviceInfo.model,
30 | frameworkVersion: deviceInfo.SDKVersion,
31 | pixelRatio: deviceInfo.pixelRatio,
32 | deviceUuid: deviceUUid,
33 | }
34 | } catch (e) {}
35 | }
36 | getClientID() {
37 | var clienetId = sdk.getStorageSync(CLIENT_ID_TOKEN)
38 | if (!clienetId) {
39 | clienetId = UUID()
40 | sdk.setStorageSync(CLIENT_ID_TOKEN, clienetId)
41 | }
42 | return clienetId
43 | }
44 | getNetWork() {
45 | sdk.getNetworkType({
46 | success: (e) => {
47 | this.deviceInfo.network = e.networkType ? e.networkType : 'unknown'
48 | },
49 | })
50 | sdk.onNetworkStatusChange((e) => {
51 | this.deviceInfo.network = e.networkType ? e.networkType : 'unknown'
52 | })
53 | }
54 | getSessionId() {
55 | return this.sessionId
56 | }
57 | }
58 |
59 | export default new BaseInfo()
60 |
--------------------------------------------------------------------------------
/src/core/configuration.js:
--------------------------------------------------------------------------------
1 | import { extend2Lev, urlParse, values } from '../helper/utils'
2 | import { ONE_KILO_BYTE, ONE_SECOND, TraceType } from '../helper/enums'
3 | var TRIM_REGIX = /^\s+|\s+$/g
4 | export var DEFAULT_CONFIGURATION = {
5 | sampleRate: 100,
6 | flushTimeout: 30 * ONE_SECOND,
7 | maxErrorsByMinute: 3000,
8 | /**
9 | * Logs intake limit
10 | */
11 | maxBatchSize: 50,
12 | maxMessageSize: 256 * ONE_KILO_BYTE,
13 |
14 | /**
15 | * beacon payload max queue size implementation is 64kb
16 | * ensure that we leave room for logs, rum and potential other users
17 | */
18 | batchBytesLimit: 16 * ONE_KILO_BYTE,
19 | datakitUrl: '',
20 | /**
21 | * arbitrary value, byte precision not needed
22 | */
23 | requestErrorResponseLengthLimit: 32 * ONE_KILO_BYTE,
24 | trackInteractions: false,
25 | traceType: TraceType.DDTRACE,
26 | traceId128Bit: false,
27 | allowedTracingOrigins:[], // 新增
28 | }
29 | function trim(str) {
30 | return str.replace(TRIM_REGIX, '')
31 | }
32 | function getDatakitUrlUrl(url) {
33 | if (url && url.lastIndexOf('/') === url.length - 1)
34 | return trim(url) + 'v1/write/rum'
35 | return trim(url) + '/v1/write/rum'
36 | }
37 | export function commonInit(userConfiguration, buildEnv) {
38 | var transportConfiguration = {
39 | applicationId: userConfiguration.applicationId,
40 | env: userConfiguration.env || '',
41 | version: userConfiguration.version || '',
42 | sdkVersion: buildEnv.sdkVersion,
43 | sdkName: buildEnv.sdkName,
44 | datakitUrl: getDatakitUrlUrl(
45 | userConfiguration.datakitUrl || userConfiguration.datakitOrigin,
46 | ),
47 | tags: userConfiguration.tags || [],
48 | }
49 | if ('trackInteractions' in userConfiguration) {
50 | transportConfiguration.trackInteractions = !!userConfiguration.trackInteractions
51 | }
52 | if ('allowedTracingOrigins' in userConfiguration) {
53 | transportConfiguration.allowedTracingOrigins = userConfiguration.allowedTracingOrigins
54 | }
55 | if ('traceId128Bit' in userConfiguration) {
56 | transportConfiguration.traceId128Bit = !!userConfiguration.traceId128Bit
57 | }
58 | if ('traceType' in userConfiguration && hasTraceType(userConfiguration.traceType)) {
59 | transportConfiguration.traceType = userConfiguration.traceType
60 | }
61 | return extend2Lev(DEFAULT_CONFIGURATION, transportConfiguration)
62 | }
63 | function hasTraceType(traceType) {
64 | if (traceType && values(TraceType).indexOf(traceType) > -1) return true
65 | return false
66 | }
67 | const haveSameOrigin = function (url1, url2) {
68 | const parseUrl1 = urlParse(url1).getParse()
69 | const parseUrl2 = urlParse(url2).getParse()
70 | return parseUrl1.Origin === parseUrl2.Origin
71 | }
72 | export function isIntakeRequest(url, configuration) {
73 | return haveSameOrigin(url, configuration.datakitUrl)
74 | }
75 |
--------------------------------------------------------------------------------
/src/core/dataMap.js:
--------------------------------------------------------------------------------
1 | import { RumEventType } from '../helper/enums'
2 | // 需要用双引号将字符串类型的field value括起来, 这里有数组标示[string, path]
3 | export var commonTags = {
4 | sdk_name: '_dd.sdk_name',
5 | sdk_version: '_dd.sdk_version',
6 | app_id: 'application.id',
7 | env: '_dd.env',
8 | version: '_dd.version',
9 | userid: 'user.id',
10 | user_email: 'user.email',
11 | user_name: 'user.name',
12 | session_id: 'session.id',
13 | session_type: 'session.type',
14 | is_signin: 'user.is_signin',
15 | device: 'device.brand',
16 | model: 'device.model',
17 | device_uuid: 'device.device_uuid',
18 | os: 'device.os',
19 | app: 'device.app',
20 | os_version: 'device.os_version',
21 | os_version_major: 'device.os_version_major',
22 | screen_size: 'device.screen_size',
23 | network_type: 'device.network_type',
24 | platform: 'device.platform',
25 | platform_version: 'device.platform_version',
26 | app_framework_version: 'device.framework_version',
27 | view_id: 'page.id',
28 | view_name: 'page.route',
29 | view_referer: 'page.referer',
30 | }
31 | export var dataMap = {
32 | view: {
33 | type: RumEventType.VIEW,
34 | tags: {
35 | view_apdex_level: 'page.apdex_level',
36 | is_active: 'page.is_active',
37 | },
38 | fields: {
39 | page_fmp: 'page.fmp',
40 | first_paint_time: 'page.fpt',
41 | loading_time: 'page.loading_time',
42 | onload_to_onshow: 'page.onload2onshow',
43 | onshow_to_onready: 'page.onshow2onready',
44 | time_spent: 'page.time_spent',
45 | view_error_count: 'page.error.count',
46 | view_resource_count: 'page.resource.count',
47 | view_long_task_count: 'page.long_task.count',
48 | view_action_count: 'page.action.count',
49 | view_setdata_count: 'page.setdata.count',
50 | },
51 | },
52 | resource: {
53 | type: RumEventType.RESOURCE,
54 | tags: {
55 | trace_id: '_dd.trace_id',
56 | span_id: '_dd.span_id',
57 | resource_type: 'resource.type',
58 | resource_status: 'resource.status',
59 | resource_status_group: 'resource.status_group',
60 | resource_method: 'resource.method',
61 | resource_url: 'resource.url',
62 | resource_url_host: 'resource.url_host',
63 | resource_url_path: 'resource.url_path',
64 | resource_url_path_group: 'resource.url_path_group',
65 | resource_url_query: 'resource.url_query',
66 | },
67 | fields: {
68 | resource_size: 'resource.size',
69 | resource_load: 'resource.load',
70 | resource_dns: 'resource.dns',
71 | resource_tcp: 'resource.tcp',
72 | resource_ssl: 'resource.ssl',
73 | resource_ttfb: 'resource.ttfb',
74 | resource_trans: 'resource.trans',
75 | resource_first_byte: 'resource.firstbyte',
76 | duration: 'resource.duration',
77 | },
78 | },
79 | error: {
80 | type: RumEventType.ERROR,
81 | tags: {
82 | error_source: 'error.source',
83 | error_type: 'error.type',
84 | resource_url: 'error.resource.url',
85 | resource_url_host: 'error.resource.url_host',
86 | resource_url_path: 'error.resource.url_path',
87 | resource_url_path_group: 'error.resource.url_path_group',
88 | resource_status: 'error.resource.status',
89 | resource_status_group: 'error.resource.status_group',
90 | resource_method: 'error.resource.method',
91 | },
92 | fields: {
93 | error_message: ['string', 'error.message'],
94 | error_stack: ['string', 'error.stack'],
95 | },
96 | },
97 | long_task: {
98 | type: RumEventType.LONG_TASK,
99 | tags: {},
100 | fields: {
101 | duration: 'long_task.duration',
102 | },
103 | },
104 | action: {
105 | type: RumEventType.ACTION,
106 | tags: {
107 | action_id: 'action.id',
108 | action_name: 'action.target.name',
109 | action_type: 'action.type',
110 | },
111 | fields: {
112 | duration: 'action.loading_time',
113 | action_error_count: 'action.error.count',
114 | action_resource_count: 'action.resource.count',
115 | action_long_task_count: 'action.long_task.count',
116 | },
117 | },
118 | app: {
119 | alias_key: 'action', // metrc 别名,
120 | type: RumEventType.APP,
121 | tags: {
122 | action_id: 'app.id',
123 | action_name: 'app.name',
124 | action_type: 'app.type',
125 | },
126 | fields: {
127 | duration: 'app.duration',
128 | },
129 | },
130 | }
131 |
--------------------------------------------------------------------------------
/src/core/downloadProxy.js:
--------------------------------------------------------------------------------
1 | import { sdk } from './sdk'
2 | import { now } from '../helper/utils'
3 | import { RequestType } from '../helper/enums'
4 | var downloadProxySingleton
5 | var beforeSendCallbacks = []
6 | var onRequestCompleteCallbacks = []
7 | var originalDownloadRequest
8 | export function startDownloadProxy() {
9 | if (!downloadProxySingleton) {
10 | proxyDownload()
11 | downloadProxySingleton = {
12 | beforeSend: function (callback) {
13 | beforeSendCallbacks.push(callback)
14 | },
15 | onRequestComplete: function (callback) {
16 | onRequestCompleteCallbacks.push(callback)
17 | },
18 | }
19 | }
20 | return downloadProxySingleton
21 | }
22 |
23 | export function resetDownloadProxy() {
24 | if (downloadProxySingleton) {
25 | downloadProxySingleton = undefined
26 | beforeSendCallbacks.splice(0, beforeSendCallbacks.length)
27 | onRequestCompleteCallbacks.splice(0, onRequestCompleteCallbacks.length)
28 | sdk.downloadFile = originalDownloadRequest
29 | }
30 | }
31 |
32 | function proxyDownload() {
33 | originalDownloadRequest = sdk.downloadFile
34 | sdk.downloadFile = function () {
35 | var _this = this
36 | var dataflux_xhr = {
37 | method: 'GET',
38 | startTime: 0,
39 | url: arguments[0].url,
40 | type: RequestType.DOWNLOAD,
41 | responseType: 'file',
42 | }
43 | dataflux_xhr.startTime = now()
44 |
45 | var originalSuccess = arguments[0].success
46 |
47 | arguments[0].success = function () {
48 | reportXhr(arguments[0])
49 |
50 | if (originalSuccess) {
51 | originalSuccess.apply(_this, arguments)
52 | }
53 | }
54 | var originalFail = arguments[0].fail
55 | arguments[0].fail = function () {
56 | reportXhr(arguments[0])
57 | if (originalFail) {
58 | originalFail.apply(_this, arguments)
59 | }
60 | }
61 | var hasBeenReported = false
62 | var reportXhr = function (res) {
63 | if (hasBeenReported) {
64 | return
65 | }
66 | hasBeenReported = true
67 | dataflux_xhr.duration = now() - dataflux_xhr.startTime
68 | dataflux_xhr.response = JSON.stringify({
69 | filePath: res.filePath,
70 | tempFilePath: res.tempFilePath,
71 | })
72 | dataflux_xhr.header = res.header || {}
73 | dataflux_xhr.profile = res.profile
74 | dataflux_xhr.status = res.statusCode || res.status || 0
75 | onRequestCompleteCallbacks.forEach(function (callback) {
76 | callback(dataflux_xhr)
77 | })
78 | }
79 | beforeSendCallbacks.forEach(function (callback) {
80 | callback(dataflux_xhr)
81 | })
82 | return originalDownloadRequest.apply(this, arguments)
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/core/errorCollection.js:
--------------------------------------------------------------------------------
1 | import { toArray, now } from '../helper/utils'
2 | import { ONE_MINUTE, RequestType } from '../helper/enums'
3 | import {
4 | ErrorSource,
5 | formatUnknownError,
6 | toStackTraceString,
7 | } from './errorTools'
8 | import { computeStackTrace, report } from '../helper/tracekit'
9 | import { Observable } from './observable'
10 | import { isIntakeRequest } from './configuration'
11 | import { resetXhrProxy, startXhrProxy } from './xhrProxy'
12 | import { resetDownloadProxy, startDownloadProxy } from './downloadProxy'
13 | var originalConsoleError
14 |
15 | export function startConsoleTracking(errorObservable) {
16 | originalConsoleError = console.error
17 | console.error = function () {
18 | originalConsoleError.apply(console, arguments)
19 | var args = toArray(arguments)
20 | var message = []
21 | args.concat(['console error:']).forEach(function (para) {
22 | message.push(formatConsoleParameters(para))
23 | })
24 |
25 | errorObservable.notify({
26 | message: message.join(' '),
27 | source: ErrorSource.CONSOLE,
28 | startTime: now(),
29 | })
30 | }
31 | }
32 |
33 | export function stopConsoleTracking() {
34 | console.error = originalConsoleError
35 | }
36 |
37 | function formatConsoleParameters(param) {
38 | if (typeof param === 'string') {
39 | return param
40 | }
41 | if (param instanceof Error) {
42 | return toStackTraceString(computeStackTrace(param))
43 | }
44 | return JSON.stringify(param, undefined, 2)
45 | }
46 | export function filterErrors(configuration, errorObservable) {
47 | var errorCount = 0
48 | var filteredErrorObservable = new Observable()
49 | errorObservable.subscribe(function (error) {
50 | if (errorCount < configuration.maxErrorsByMinute) {
51 | errorCount += 1
52 | filteredErrorObservable.notify(error)
53 | } else if (errorCount === configuration.maxErrorsByMinute) {
54 | errorCount += 1
55 | filteredErrorObservable.notify({
56 | message:
57 | 'Reached max number of errors by minute: ' +
58 | configuration.maxErrorsByMinute,
59 | source: ErrorSource.AGENT,
60 | startTime: now(),
61 | })
62 | }
63 | })
64 | setInterval(function () {
65 | errorCount = 0
66 | }, ONE_MINUTE)
67 | return filteredErrorObservable
68 | }
69 | var traceKitReportHandler
70 |
71 | export function startRuntimeErrorTracking(errorObservable) {
72 | traceKitReportHandler = function (stackTrace, _, errorObject) {
73 | var error = formatUnknownError(stackTrace, errorObject, 'Uncaught')
74 | errorObservable.notify({
75 | message: error.message,
76 | stack: error.stack,
77 | type: error.type,
78 | source: ErrorSource.SOURCE,
79 | startTime: now(),
80 | })
81 | }
82 | report.subscribe(traceKitReportHandler)
83 | }
84 |
85 | export function stopRuntimeErrorTracking() {
86 | report.unsubscribe(traceKitReportHandler)
87 | }
88 | var filteredErrorsObservable
89 |
90 | export function startAutomaticErrorCollection(configuration) {
91 | if (!filteredErrorsObservable) {
92 | var errorObservable = new Observable()
93 | trackNetworkError(configuration, errorObservable)
94 | startConsoleTracking(errorObservable)
95 | startRuntimeErrorTracking(errorObservable)
96 | filteredErrorsObservable = filterErrors(configuration, errorObservable)
97 | }
98 | return filteredErrorsObservable
99 | }
100 |
101 | export function trackNetworkError(configuration, errorObservable) {
102 | startXhrProxy().onRequestComplete(function (context) {
103 | return handleCompleteRequest(context.type, context)
104 | })
105 | startDownloadProxy().onRequestComplete(function (context) {
106 | return handleCompleteRequest(context.type, context)
107 | })
108 |
109 | function handleCompleteRequest(type, request) {
110 | if (
111 | !isIntakeRequest(request.url, configuration) &&
112 | (isRejected(request) || isServerError(request))
113 | ) {
114 | errorObservable.notify({
115 | message: format(type) + 'error' + request.method + ' ' + request.url,
116 | resource: {
117 | method: request.method,
118 | statusCode: request.status,
119 | url: request.url,
120 | },
121 | type: ErrorSource.NETWORK,
122 | source: ErrorSource.NETWORK,
123 | stack:
124 | truncateResponse(request.response, configuration) || 'Failed to load',
125 | startTime: request.startTime,
126 | })
127 | }
128 | }
129 |
130 | return {
131 | stop: function () {
132 | resetXhrProxy()
133 | resetDownloadProxy()
134 | },
135 | }
136 | }
137 | function isRejected(request) {
138 | return request.status === 0 && request.responseType !== 'opaque'
139 | }
140 |
141 | function isServerError(request) {
142 | return request.status >= 500
143 | }
144 |
145 | function truncateResponse(response, configuration) {
146 | if (
147 | response &&
148 | response.length > configuration.requestErrorResponseLengthLimit
149 | ) {
150 | return (
151 | response.substring(0, configuration.requestErrorResponseLengthLimit) +
152 | '...'
153 | )
154 | }
155 | return response
156 | }
157 |
158 | function format(type) {
159 | if (RequestType.XHR === type) {
160 | return 'XHR'
161 | }
162 | return RequestType.DOWNLOAD
163 | }
164 |
--------------------------------------------------------------------------------
/src/core/errorTools.js:
--------------------------------------------------------------------------------
1 | export var ErrorSource = {
2 | AGENT: 'agent',
3 | CONSOLE: 'console',
4 | NETWORK: 'network',
5 | SOURCE: 'source',
6 | LOGGER: 'logger',
7 | }
8 | export function formatUnknownError(stackTrace, errorObject, nonErrorPrefix) {
9 | if (
10 | !stackTrace ||
11 | (stackTrace.message === undefined && !(errorObject instanceof Error))
12 | ) {
13 | return {
14 | message: nonErrorPrefix + '' + JSON.stringify(errorObject),
15 | stack: 'No stack, consider using an instance of Error',
16 | type: stackTrace && stackTrace.name,
17 | }
18 | }
19 | return {
20 | message: stackTrace.message || 'Empty message',
21 | stack: toStackTraceString(stackTrace),
22 | type: stackTrace.name,
23 | }
24 | }
25 |
26 | export function toStackTraceString(stack) {
27 | var result = stack.name || 'Error' + ': ' + stack.message
28 | stack.stack.forEach(function (frame) {
29 | var func = frame.func === '?' ? '' : frame.func
30 | var args =
31 | frame.args && frame.args.length > 0
32 | ? '(' + frame.args.join(', ') + ')'
33 | : ''
34 | var line = frame.line ? ':' + frame.line : ''
35 | var column = frame.line && frame.column ? ':' + frame.column : ''
36 | result += '\n at ' + func + args + ' @ ' + frame.url + line + column
37 | })
38 | return result
39 | }
40 |
--------------------------------------------------------------------------------
/src/core/lifeCycle.js:
--------------------------------------------------------------------------------
1 | export class LifeCycle {
2 | constructor() {
3 | this.callbacks = {}
4 | }
5 | notify(eventType, data) {
6 | const eventCallbacks = this.callbacks[eventType]
7 | if (eventCallbacks) {
8 | eventCallbacks.forEach((callback) => callback(data))
9 | }
10 | }
11 | subscribe(eventType, callback) {
12 | if (!this.callbacks[eventType]) {
13 | this.callbacks[eventType] = []
14 | }
15 | this.callbacks[eventType].push(callback)
16 | return {
17 | unsubscribe: () => {
18 | this.callbacks[eventType] = this.callbacks[eventType].filter(
19 | (other) => callback !== other,
20 | )
21 | },
22 | }
23 | }
24 | }
25 |
26 | export var LifeCycleEventType = {
27 | PERFORMANCE_ENTRY_COLLECTED: 'PERFORMANCE_ENTRY_COLLECTED',
28 | AUTO_ACTION_CREATED: 'AUTO_ACTION_CREATED',
29 | AUTO_ACTION_COMPLETED: 'AUTO_ACTION_COMPLETED',
30 | AUTO_ACTION_DISCARDED: 'AUTO_ACTION_DISCARDED',
31 | APP_HIDE: 'APP_HIDE',
32 | APP_UPDATE: 'APP_UPDATE',
33 | PAGE_SET_DATA_UPDATE: 'PAGE_SET_DATA_UPDATE',
34 | PAGE_ALIAS_ACTION: 'PAGE_ALIAS_ACTION',
35 | VIEW_CREATED: 'VIEW_CREATED',
36 | VIEW_UPDATED: 'VIEW_UPDATED',
37 | VIEW_ENDED: 'VIEW_ENDED',
38 | REQUEST_STARTED: 'REQUEST_STARTED',
39 | REQUEST_COMPLETED: 'REQUEST_COMPLETED',
40 | RAW_RUM_EVENT_COLLECTED: 'RAW_RUM_EVENT_COLLECTED',
41 | RUM_EVENT_COLLECTED: 'RUM_EVENT_COLLECTED',
42 | }
43 |
--------------------------------------------------------------------------------
/src/core/observable.js:
--------------------------------------------------------------------------------
1 | export class Observable {
2 | constructor() {
3 | this.observers = []
4 | }
5 | subscribe(f) {
6 | this.observers.push(f)
7 | }
8 | notify(data) {
9 | this.observers.forEach(function (observer) {
10 | observer(data)
11 | })
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/core/sdk.js:
--------------------------------------------------------------------------------
1 | import { deepMixObject } from '../helper/utils'
2 |
3 | function getSDK() {
4 | var sdk = null,
5 | tracker = ''
6 | try {
7 | if (wx && typeof wx === 'object' && typeof wx.request === 'function') {
8 | sdk = deepMixObject({}, wx)
9 | tracker = 'wx'
10 | wx = sdk
11 | }
12 | } catch (err) {
13 | console.warn('unsupport platform, Fail to start')
14 | }
15 | console.log('------get SDK-------')
16 | return { sdk, tracker }
17 | }
18 | const instance = getSDK()
19 |
20 | export const sdk = instance.sdk
21 | export const tracker = instance.tracker
22 |
--------------------------------------------------------------------------------
/src/core/xhrProxy.js:
--------------------------------------------------------------------------------
1 | import { sdk } from './sdk'
2 | import { now } from '../helper/utils'
3 | import { RequestType } from '../helper/enums'
4 | var xhrProxySingleton
5 | var beforeSendCallbacks = []
6 | var onRequestCompleteCallbacks = []
7 | var originalXhrRequest
8 | export function startXhrProxy() {
9 | if (!xhrProxySingleton) {
10 | proxyXhr()
11 | xhrProxySingleton = {
12 | beforeSend: function (callback) {
13 | beforeSendCallbacks.push(callback)
14 | },
15 | onRequestComplete: function (callback) {
16 | onRequestCompleteCallbacks.push(callback)
17 | },
18 | }
19 | }
20 | return xhrProxySingleton
21 | }
22 |
23 | export function resetXhrProxy() {
24 | if (xhrProxySingleton) {
25 | xhrProxySingleton = undefined
26 | beforeSendCallbacks.splice(0, beforeSendCallbacks.length)
27 | onRequestCompleteCallbacks.splice(0, onRequestCompleteCallbacks.length)
28 | sdk.request = originalXhrRequest
29 | }
30 | }
31 |
32 | function proxyXhr() {
33 | originalXhrRequest = sdk.request
34 | sdk.request = function () {
35 | var _this = this
36 | var dataflux_xhr = {
37 | method: arguments[0].method || 'GET',
38 | startTime: 0,
39 | url: arguments[0].url,
40 | type: RequestType.XHR,
41 | responseType: arguments[0].responseType || 'text',
42 | option: arguments[0]
43 | }
44 | dataflux_xhr.startTime = now()
45 |
46 | var originalSuccess = arguments[0].success
47 |
48 | arguments[0].success = function () {
49 | reportXhr(arguments[0])
50 |
51 | if (originalSuccess) {
52 | originalSuccess.apply(_this, arguments)
53 | }
54 | }
55 | var originalFail = arguments[0].fail
56 | arguments[0].fail = function () {
57 | reportXhr(arguments[0])
58 | if (originalFail) {
59 | originalFail.apply(_this, arguments)
60 | }
61 | }
62 | var hasBeenReported = false
63 | var reportXhr = function (res) {
64 | if (hasBeenReported) {
65 | return
66 | }
67 | hasBeenReported = true
68 | dataflux_xhr.duration = now() - dataflux_xhr.startTime
69 | dataflux_xhr.response = JSON.stringify(res.data)
70 | dataflux_xhr.header = res.header || {}
71 | dataflux_xhr.profile = res.profile
72 | dataflux_xhr.status = res.statusCode || res.status || 0
73 | onRequestCompleteCallbacks.forEach(function (callback) {
74 | callback(dataflux_xhr)
75 | })
76 | }
77 | beforeSendCallbacks.forEach(function (callback) {
78 | callback(dataflux_xhr)
79 | })
80 | return originalXhrRequest.call(this, dataflux_xhr.option)
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/helper/enums.js:
--------------------------------------------------------------------------------
1 | export const ONE_SECOND = 1000
2 | export const ONE_MINUTE = 60 * ONE_SECOND
3 | export const ONE_HOUR = 60 * ONE_MINUTE
4 | export const ONE_KILO_BYTE = 1024
5 | export const CLIENT_ID_TOKEN = 'datafluxRum:client:id'
6 | export const RumEventType = {
7 | ACTION: 'action',
8 | ERROR: 'error',
9 | LONG_TASK: 'long_task',
10 | VIEW: 'view',
11 | RESOURCE: 'resource',
12 | APP: 'app',
13 | ACTION: 'action',
14 | }
15 |
16 | export var RequestType = {
17 | XHR: 'network',
18 | DOWNLOAD: 'resource',
19 | }
20 |
21 | export var ActionType = {
22 | tap: 'tap',
23 | longpress: 'longpress',
24 | longtap: 'longtap',
25 | }
26 | export var MpHook = {
27 | data: 1,
28 | onLoad: 1,
29 | onShow: 1,
30 | onReady: 1,
31 | onPullDownRefresh: 1,
32 | onReachBottom: 1,
33 | onShareAppMessage: 1,
34 | onPageScroll: 1,
35 | onResize: 1,
36 | onTabItemTap: 1,
37 | onHide: 1,
38 | onUnload: 1,
39 | }
40 | export var TraceType = {
41 | DDTRACE: 'ddtrace',
42 | ZIPKIN_MULTI_HEADER: 'zipkin',
43 | ZIPKIN_SINGLE_HEADER: 'zipkin_single_header',
44 | W3C_TRACEPARENT: 'w3c_traceparent',
45 | SKYWALKING_V3: 'skywalking_v3',
46 | JAEGER: 'jaeger',
47 | }
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export { datafluxRum } from './boot/rum.entry'
2 |
--------------------------------------------------------------------------------
/src/rumEventsCollection/action/actionCollection.js:
--------------------------------------------------------------------------------
1 | import { msToNs, extend2Lev } from '../../helper/utils'
2 | import { LifeCycleEventType } from '../../core/lifeCycle'
3 | import { RumEventType } from '../../helper/enums'
4 | import { trackActions } from './trackActions'
5 |
6 | export function startActionCollection(lifeCycle, configuration) {
7 | lifeCycle.subscribe(
8 | LifeCycleEventType.AUTO_ACTION_COMPLETED,
9 | function (action) {
10 | lifeCycle.notify(
11 | LifeCycleEventType.RAW_RUM_EVENT_COLLECTED,
12 | processAction(action),
13 | )
14 | },
15 | )
16 | if (configuration.trackInteractions) {
17 | trackActions(lifeCycle)
18 | }
19 | }
20 |
21 | function processAction(action) {
22 | var autoActionProperties = {
23 | action: {
24 | error: {
25 | count: action.counts.errorCount,
26 | },
27 | id: action.id,
28 | loadingTime: msToNs(action.duration),
29 | long_task: {
30 | count: action.counts.longTaskCount,
31 | },
32 | resource: {
33 | count: action.counts.resourceCount,
34 | },
35 | },
36 | }
37 | var actionEvent = extend2Lev(
38 | {
39 | action: {
40 | target: {
41 | name: action.name,
42 | },
43 | type: action.type,
44 | },
45 | date: action.startClocks,
46 | type: RumEventType.ACTION,
47 | },
48 | autoActionProperties,
49 | )
50 | return {
51 | rawRumEvent: actionEvent,
52 | startTime: action.startClocks,
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/rumEventsCollection/action/trackActions.js:
--------------------------------------------------------------------------------
1 | import { elapsed, now, UUID, getMethods, isObject } from '../../helper/utils'
2 | import { LifeCycleEventType } from '../../core/lifeCycle'
3 | import { MinaTouch } from '../../core/miniaTouch'
4 | import { trackEventCounts } from '../trackEventCounts'
5 | import { waitIdlePageActivity } from '../trackPageActiveites'
6 | import { ActionType } from '../../helper/enums'
7 | export function trackActions(lifeCycle) {
8 | var action = startActionManagement(lifeCycle)
9 |
10 | // New views trigger the discard of the current pending Action
11 | lifeCycle.subscribe(LifeCycleEventType.VIEW_CREATED, function () {
12 | action.discardCurrent()
13 | })
14 | var originPage = Page
15 | Page = function (page) {
16 | const methods = getMethods(page)
17 | methods.forEach((methodName) => {
18 | clickProxy(
19 | page,
20 | methodName,
21 | function (_action) {
22 | action.create(_action.type, _action.name)
23 | },
24 | lifeCycle,
25 | )
26 | })
27 | return originPage(page)
28 | }
29 | var originComponent = Component
30 | Component = function (component) {
31 | const methods = getMethods(component)
32 | methods.forEach((methodName) => {
33 | clickProxy(component, methodName, function (_action) {
34 | action.create(_action.type, _action.name)
35 | })
36 | })
37 | return originComponent(component)
38 | }
39 | return {
40 | stop: function () {
41 | action.discardCurrent()
42 | // stopListener()
43 | },
44 | }
45 | }
46 | function clickProxy(page, methodName, callback, lifeCycle) {
47 | var oirginMethod = page[methodName]
48 |
49 | page[methodName] = function () {
50 | const result = oirginMethod.apply(this, arguments)
51 | var action = {}
52 | if (isObject(arguments[0])) {
53 | var currentTarget = arguments[0].currentTarget || {}
54 | var dataset = currentTarget.dataset || {}
55 | var actionType = arguments[0].type
56 | if (actionType && ActionType[actionType]) {
57 | action.type = actionType
58 | action.name = dataset.name || dataset.content || dataset.type
59 | callback(action)
60 | lifeCycle.notify(LifeCycleEventType.PAGE_ALIAS_ACTION, true)
61 | } else if (methodName === 'onAddToFavorites') {
62 | action.type = 'click'
63 | action.name =
64 | '收藏 ' +
65 | '标题: ' +
66 | result.title +
67 | (result.query ? ' query: ' + result.query : '')
68 | callback(action)
69 | lifeCycle.notify(LifeCycleEventType.PAGE_ALIAS_ACTION, true)
70 | } else if (methodName === 'onShareAppMessage') {
71 | action.type = 'click'
72 | action.name =
73 | '转发 ' +
74 | '标题: ' +
75 | result.title +
76 | (result.path ? ' path: ' + result.path : '')
77 | callback(action)
78 | lifeCycle.notify(LifeCycleEventType.PAGE_ALIAS_ACTION, true)
79 | } else if (methodName === 'onShareTimeline') {
80 | action.type = 'click'
81 | action.name =
82 | '分享到朋友圈 ' +
83 | '标题: ' +
84 | result.title +
85 | (result.query ? ' query: ' + result.query : '')
86 | callback(action)
87 | lifeCycle.notify(LifeCycleEventType.PAGE_ALIAS_ACTION, true)
88 | } else if (methodName === 'onTabItemTap') {
89 | var item = arguments.length && arguments[0]
90 | action.type = 'click'
91 | action.name =
92 | 'tab ' +
93 | '名称: ' +
94 | item.text +
95 | (item.pagePath ? ' 跳转到: ' + item.pagePath : '')
96 | callback(action)
97 | lifeCycle.notify(LifeCycleEventType.PAGE_ALIAS_ACTION, true)
98 | }
99 | }
100 | return result
101 | }
102 | }
103 | function startActionManagement(lifeCycle) {
104 | var currentAction
105 | var currentIdlePageActivitySubscription
106 |
107 | return {
108 | create: function (type, name) {
109 | if (currentAction) {
110 | // Ignore any new action if another one is already occurring.
111 | return
112 | }
113 | var pendingAutoAction = new PendingAutoAction(lifeCycle, type, name)
114 |
115 | currentAction = pendingAutoAction
116 | currentIdlePageActivitySubscription = waitIdlePageActivity(
117 | lifeCycle,
118 | function (params) {
119 | if (params.hadActivity) {
120 | pendingAutoAction.complete(params.endTime)
121 | } else {
122 | pendingAutoAction.discard()
123 | }
124 | currentAction = undefined
125 | },
126 | )
127 | },
128 | discardCurrent: function () {
129 | if (currentAction) {
130 | currentIdlePageActivitySubscription.stop()
131 | currentAction.discard()
132 | currentAction = undefined
133 | }
134 | },
135 | }
136 | }
137 | var PendingAutoAction = function (lifeCycle, type, name) {
138 | this.id = UUID()
139 | this.startClocks = now()
140 | this.name = name
141 | this.type = type
142 | this.lifeCycle = lifeCycle
143 | this.eventCountsSubscription = trackEventCounts(lifeCycle)
144 | this.lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_CREATED, {
145 | id: this.id,
146 | startClocks: this.startClocks,
147 | })
148 | }
149 | PendingAutoAction.prototype = {
150 | complete: function (endTime) {
151 | var eventCounts = this.eventCountsSubscription.eventCounts
152 | this.lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_COMPLETED, {
153 | counts: {
154 | errorCount: eventCounts.errorCount,
155 | longTaskCount: eventCounts.longTaskCount,
156 | resourceCount: eventCounts.resourceCount,
157 | },
158 | duration: elapsed(this.startClocks, endTime),
159 | id: this.id,
160 | name: this.name,
161 | startClocks: this.startClocks,
162 | type: this.type,
163 | })
164 | this.eventCountsSubscription.stop()
165 | },
166 | discard: function () {
167 | this.lifeCycle.notify(LifeCycleEventType.AUTO_ACTION_DISCARDED)
168 | this.eventCountsSubscription.stop()
169 | },
170 | }
171 |
--------------------------------------------------------------------------------
/src/rumEventsCollection/app/appCollection.js:
--------------------------------------------------------------------------------
1 | import { rewriteApp } from './index'
2 | import { LifeCycleEventType } from '../../core/lifeCycle'
3 | import { RumEventType } from '../../helper/enums'
4 | import { msToNs } from '../../helper/utils'
5 | export function startAppCollection(lifeCycle, configuration) {
6 | lifeCycle.subscribe(LifeCycleEventType.APP_UPDATE, function (appinfo) {
7 | lifeCycle.notify(
8 | LifeCycleEventType.RAW_RUM_EVENT_COLLECTED,
9 | processAppUpdate(appinfo),
10 | )
11 | })
12 |
13 | return rewriteApp(configuration, lifeCycle)
14 | }
15 |
16 | function processAppUpdate(appinfo) {
17 | var appEvent = {
18 | date: appinfo.startTime,
19 | type: RumEventType.APP,
20 | app: {
21 | type: appinfo.type,
22 | name: appinfo.name,
23 | id: appinfo.id,
24 | duration: msToNs(appinfo.duration),
25 | },
26 | }
27 | console.log(appEvent, 'appEvent====')
28 | return {
29 | rawRumEvent: appEvent,
30 | startTime: appinfo.startTime,
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/rumEventsCollection/app/index.js:
--------------------------------------------------------------------------------
1 | import { now, areInOrder, UUID } from '../../helper/utils'
2 | import { LifeCycleEventType } from '../../core/lifeCycle'
3 |
4 | // 劫持原小程序App方法
5 | export var THROTTLE_VIEW_UPDATE_PERIOD = 3000
6 | export const startupTypes = {
7 | COLD: 'cold',
8 | HOT: 'hot',
9 | }
10 | export function rewriteApp(configuration, lifeCycle) {
11 | const originApp = App
12 | var appInfo = {
13 | isStartUp: false, // 是否启动
14 | }
15 | var startTime
16 | App = function (app) {
17 | startTime = now()
18 | // 合并方法,插入记录脚本
19 | ;['onLaunch', 'onShow', 'onHide'].forEach((methodName) => {
20 | const userDefinedMethod = app[methodName] // 暂存用户定义的方法
21 | app[methodName] = function (options) {
22 | console.log(methodName, 'methodName app')
23 | if (methodName === 'onLaunch') {
24 | appInfo.isStartUp = true
25 | appInfo.isHide = false
26 | appInfo.startupType = startupTypes.COLD
27 | } else if (methodName === 'onShow') {
28 | if (appInfo.isStartUp && appInfo.isHide) {
29 | // 判断是热启动
30 | appInfo.startupType = startupTypes.HOT
31 | // appUpdate()
32 | }
33 | } else if (methodName === 'onHide') {
34 | lifeCycle.notify(LifeCycleEventType.APP_HIDE)
35 | appInfo.isHide = true
36 | }
37 | return userDefinedMethod && userDefinedMethod.call(this, options)
38 | }
39 | })
40 | return originApp(app)
41 | }
42 |
43 | startPerformanceObservable(lifeCycle)
44 | }
45 |
46 | function startPerformanceObservable(lifeCycle) {
47 | var subscribe = lifeCycle.subscribe(
48 | LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED,
49 | function (entitys) {
50 | // 过滤掉其他页面监听,只保留首次启动
51 | var codeDownloadDuration
52 | const launchEntity = entitys.find(
53 | (entity) =>
54 | entity.entryType === 'navigation' &&
55 | entity.navigationType === 'appLaunch',
56 | )
57 | if (typeof launchEntity !== 'undefined') {
58 | lifeCycle.notify(LifeCycleEventType.APP_UPDATE, {
59 | startTime: launchEntity.startTime,
60 | name: '启动',
61 | type: 'launch',
62 | id: UUID(),
63 | duration: launchEntity.duration,
64 | })
65 | }
66 | const scriptentity = entitys.find(
67 | (entity) =>
68 | entity.entryType === 'script' && entity.name === 'evaluateScript',
69 | )
70 | if (typeof scriptentity !== 'undefined') {
71 | lifeCycle.notify(LifeCycleEventType.APP_UPDATE, {
72 | startTime: scriptentity.startTime,
73 | name: '脚本注入',
74 | type: 'script_insert',
75 | id: UUID(),
76 | duration: scriptentity.duration,
77 | })
78 | }
79 | const firstEntity = entitys.find(
80 | (entity) =>
81 | entity.entryType === 'render' && entity.name === 'firstRender',
82 | )
83 | if (firstEntity && scriptentity && launchEntity) {
84 | if (
85 | !areInOrder(firstEntity.duration, launchEntity.duration) ||
86 | !areInOrder(scriptentity.duration, launchEntity.duration)
87 | ) {
88 | return
89 | }
90 | codeDownloadDuration =
91 | launchEntity.duration - firstEntity.duration - scriptentity.duration
92 | // 资源下载耗时
93 | lifeCycle.notify(LifeCycleEventType.APP_UPDATE, {
94 | startTime: launchEntity.startTime,
95 | name: '小程序包下载',
96 | type: 'package_download',
97 | id: UUID(),
98 | duration: codeDownloadDuration,
99 | })
100 | // 资源下载时间暂时定为:首次启动时间-脚本加载时间-初次渲染时间
101 | }
102 | },
103 | )
104 | return {
105 | stop: subscribe.unsubscribe,
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/rumEventsCollection/assembly.js:
--------------------------------------------------------------------------------
1 | import { extend2Lev, withSnakeCaseKeys, performDraw, isEmptyObject } from '../helper/utils'
2 | import { LifeCycleEventType } from '../core/lifeCycle'
3 | import { RumEventType } from '../helper/enums'
4 | import baseInfo from '../core/baseInfo'
5 | function isTracked(configuration) {
6 | return performDraw(configuration.sampleRate)
7 | }
8 | var SessionType = {
9 | SYNTHETICS: 'synthetics',
10 | USER: 'user',
11 | }
12 | export function startRumAssembly(
13 | applicationId,
14 | configuration,
15 | lifeCycle,
16 | parentContexts,
17 | getCommonContext
18 | ) {
19 | lifeCycle.subscribe(
20 | LifeCycleEventType.RAW_RUM_EVENT_COLLECTED,
21 | function (data) {
22 | var startTime = data.startTime
23 | var rawRumEvent = data.rawRumEvent
24 | var viewContext = parentContexts.findView(startTime)
25 | var savedCommonContext = data.savedGlobalContext
26 | var customerContext = data.customerContext
27 | var deviceContext = {
28 | device: baseInfo.deviceInfo,
29 | }
30 | if (
31 | isTracked(configuration) &&
32 | (viewContext || rawRumEvent.type === RumEventType.APP)
33 | ) {
34 | var actionContext = parentContexts.findAction(startTime)
35 | var commonContext = savedCommonContext || getCommonContext()
36 | var rumContext = {
37 | _dd: {
38 | sdkName: configuration.sdkName,
39 | sdkVersion: configuration.sdkVersion,
40 | env: configuration.env,
41 | version: configuration.version,
42 | },
43 | tags: configuration.tags,
44 | application: {
45 | id: applicationId,
46 | },
47 | device: {},
48 | date: new Date().getTime(),
49 | session: {
50 | id: baseInfo.getSessionId(),
51 | type: SessionType.USER,
52 | },
53 | user: {
54 | id: configuration.user_id || baseInfo.getClientID(),
55 | is_signin: configuration.user_id ? 'T' : 'F',
56 | },
57 | }
58 |
59 | var rumEvent = extend2Lev(
60 | rumContext,
61 | deviceContext,
62 | viewContext,
63 | actionContext,
64 | rawRumEvent,
65 | )
66 |
67 | var serverRumEvent = withSnakeCaseKeys(rumEvent)
68 | var context = extend2Lev(commonContext.context, customerContext)
69 | if (!isEmptyObject(context)) {
70 | serverRumEvent.tags = context
71 | }
72 | if (!isEmptyObject(commonContext.user)) {
73 | // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
74 | serverRumEvent.user = extend2Lev(
75 | {
76 | id: baseInfo.getClientID(),
77 | is_signin: 'T'
78 | },
79 | commonContext.user
80 | )
81 | }
82 | lifeCycle.notify(LifeCycleEventType.RUM_EVENT_COLLECTED, serverRumEvent)
83 | }
84 | },
85 | )
86 | }
87 |
--------------------------------------------------------------------------------
/src/rumEventsCollection/error/errorCollection.js:
--------------------------------------------------------------------------------
1 | import { startAutomaticErrorCollection } from '../../core/errorCollection'
2 | import { RumEventType } from '../../helper/enums'
3 | import { LifeCycleEventType } from '../../core/lifeCycle'
4 | import {
5 | urlParse,
6 | replaceNumberCharByPath,
7 | getStatusGroup,
8 | } from '../../helper/utils'
9 | export function startErrorCollection(lifeCycle, configuration) {
10 | return doStartErrorCollection(
11 | lifeCycle,
12 | configuration,
13 | startAutomaticErrorCollection(configuration),
14 | )
15 | }
16 |
17 | export function doStartErrorCollection(lifeCycle, configuration, observable) {
18 | observable.subscribe(function (error) {
19 | lifeCycle.notify(
20 | LifeCycleEventType.RAW_RUM_EVENT_COLLECTED,
21 | processError(error),
22 | )
23 | })
24 | }
25 |
26 | function processError(error) {
27 | var resource = error.resource
28 | if (resource) {
29 | var urlObj = urlParse(error.resource.url).getParse()
30 | resource = {
31 | method: error.resource.method,
32 | status: error.resource.statusCode,
33 | statusGroup: getStatusGroup(error.resource.statusCode),
34 | url: error.resource.url,
35 | urlHost: urlObj.Host,
36 | urlPath: urlObj.Path,
37 | urlPathGroup: replaceNumberCharByPath(urlObj.Path),
38 | }
39 | }
40 | var rawRumEvent = {
41 | date: error.startTime,
42 | error: {
43 | message: error.message,
44 | resource: resource,
45 | source: error.source,
46 | stack: error.stack,
47 | type: error.type,
48 | starttime: error.startTime,
49 | },
50 | type: RumEventType.ERROR,
51 | }
52 | return {
53 | rawRumEvent: rawRumEvent,
54 | startTime: error.startTime,
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/rumEventsCollection/page/index.js:
--------------------------------------------------------------------------------
1 | import { extend, now, throttle, UUID, isNumber, getActivePage } from '../../helper/utils'
2 | import { trackEventCounts } from '../trackEventCounts'
3 | import { LifeCycleEventType } from '../../core/lifeCycle'
4 | // 劫持原小程序App方法
5 | export var THROTTLE_VIEW_UPDATE_PERIOD = 3000
6 |
7 | export function rewritePage(configuration, lifeCycle) {
8 | const originPage = Page
9 | console.log(originPage, 'originPage=====')
10 | Page = function (page) {
11 | // 合并方法,插入记录脚本
12 | var currentView,
13 | startTime = now()
14 | ;['onReady', 'onShow', 'onLoad', 'onUnload', 'onHide'].forEach(
15 | (methodName) => {
16 | const userDefinedMethod = page[methodName]
17 | page[methodName] = function () {
18 | console.log(methodName, 'methodName page')
19 | if (methodName === 'onShow' || methodName === 'onLoad') {
20 | if (typeof currentView === 'undefined') {
21 | const activePage = getActivePage()
22 | currentView = newView(
23 | lifeCycle,
24 | activePage && activePage.route,
25 | startTime,
26 | )
27 | }
28 | }
29 |
30 | currentView && currentView.setLoadEventEnd(methodName)
31 |
32 | if (
33 | (methodName === 'onUnload' ||
34 | methodName === 'onHide' ||
35 | methodName === 'onShow') &&
36 | currentView
37 | ) {
38 | currentView.triggerUpdate()
39 | if (methodName === 'onUnload' || methodName === 'onHide') {
40 | currentView.end()
41 | }
42 | }
43 | return userDefinedMethod && userDefinedMethod.apply(this, arguments)
44 | }
45 | },
46 | )
47 | return originPage(page)
48 | }
49 | }
50 | function newView(lifeCycle, route, startTime) {
51 | if (typeof startTime === 'undefined') {
52 | startTime = now()
53 | }
54 | var id = UUID()
55 | var isActive = true
56 | var eventCounts = {
57 | errorCount: 0,
58 | resourceCount: 0,
59 | userActionCount: 0,
60 | }
61 | var setdataCount = 0
62 |
63 | var documentVersion = 0
64 | var setdataDuration = 0
65 | var loadingDuration = 0
66 | var loadingTime
67 | var showTime
68 | var onload2onshowTime
69 | var onshow2onready
70 | var stayTime
71 | var fpt, fmp
72 | lifeCycle.notify(LifeCycleEventType.VIEW_CREATED, {
73 | id,
74 | startTime,
75 | route,
76 | })
77 | var scheduleViewUpdate = throttle(
78 | triggerViewUpdate,
79 | THROTTLE_VIEW_UPDATE_PERIOD,
80 | {
81 | leading: false,
82 | },
83 | )
84 | var cancelScheduleViewUpdate = scheduleViewUpdate.cancel
85 | var _trackEventCounts = trackEventCounts(
86 | lifeCycle,
87 | function (newEventCounts) {
88 | eventCounts = newEventCounts
89 | scheduleViewUpdate()
90 | },
91 | )
92 | var stopEventCountsTracking = _trackEventCounts.stop
93 | var _trackFptTime = trackFptTime(lifeCycle, function (duration) {
94 | fpt = duration
95 | scheduleViewUpdate()
96 | })
97 | var stopFptTracking = _trackFptTime.stop
98 | var _trackSetDataTime = trackSetDataTime(lifeCycle, function (duration) {
99 | if (isNumber(duration)) {
100 | setdataDuration += duration
101 | setdataCount++
102 | scheduleViewUpdate()
103 | }
104 | })
105 | var stopSetDataTracking = _trackSetDataTime.stop
106 | var _trackLoadingTime = trackLoadingTime(lifeCycle, function (duration) {
107 | if (isNumber(duration)) {
108 | loadingDuration = duration
109 | scheduleViewUpdate()
110 | }
111 | })
112 | var stopLoadingTimeTracking = _trackLoadingTime.stop
113 |
114 | var setLoadEventEnd = function (type) {
115 | if (type === 'onLoad') {
116 | loadingTime = now()
117 | } else if (type === 'onShow') {
118 | showTime = now()
119 | if (
120 | typeof onload2onshowTime === 'undefined' &&
121 | typeof loadingTime !== 'undefined'
122 | ) {
123 | onload2onshowTime = showTime - loadingTime
124 | }
125 | } else if (type === 'onReady') {
126 | if (
127 | typeof onshow2onready === 'undefined' &&
128 | typeof showTime !== 'undefined'
129 | ) {
130 | onshow2onready = now() - showTime
131 | }
132 | if (typeof fmp === 'undefined') {
133 | fmp = now() - startTime // 从开发者角度看,小程序首屏渲染完成的标志是首页 Page.onReady 事件触发。
134 | }
135 | } else if (type === 'onHide' || type === 'onUnload') {
136 | if (typeof showTime !== 'undefined') {
137 | stayTime = now() - showTime
138 | }
139 | isActive = false
140 | }
141 | triggerViewUpdate()
142 | }
143 | function triggerViewUpdate() {
144 | documentVersion += 1
145 | lifeCycle.notify(LifeCycleEventType.VIEW_UPDATED, {
146 | documentVersion: documentVersion,
147 | eventCounts: eventCounts,
148 | id: id,
149 | loadingTime: loadingDuration,
150 | stayTime,
151 | onload2onshowTime,
152 | onshow2onready,
153 | setdataDuration,
154 | setdataCount,
155 | fmp,
156 | fpt,
157 | startTime: startTime,
158 | route: route,
159 | duration: now() - startTime,
160 | isActive: isActive,
161 | })
162 | }
163 | return {
164 | scheduleUpdate: scheduleViewUpdate,
165 | setLoadEventEnd,
166 | triggerUpdate: function () {
167 | cancelScheduleViewUpdate()
168 | triggerViewUpdate()
169 | },
170 | end: function () {
171 | stopEventCountsTracking()
172 | stopFptTracking()
173 | cancelScheduleViewUpdate()
174 | stopSetDataTracking()
175 | stopLoadingTimeTracking()
176 | lifeCycle.notify(LifeCycleEventType.VIEW_ENDED, { endClocks: now() })
177 | },
178 | }
179 | }
180 | function trackFptTime(lifeCycle, callback) {
181 | var subscribe = lifeCycle.subscribe(
182 | LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED,
183 | function (entitys) {
184 | const firstRenderEntity = entitys.find(
185 | (entity) =>
186 | entity.entryType === 'render' && entity.name === 'firstRender',
187 | )
188 |
189 | if (typeof firstRenderEntity !== 'undefined') {
190 | callback(firstRenderEntity.duration)
191 | }
192 | },
193 | )
194 | return {
195 | stop: subscribe.unsubscribe,
196 | }
197 | }
198 | function trackLoadingTime(lifeCycle, callback) {
199 | var subscribe = lifeCycle.subscribe(
200 | LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED,
201 | function (entitys) {
202 | const navigationEnity = entitys.find(
203 | (entity) => entity.entryType === 'navigation',
204 | )
205 | if (typeof navigationEnity !== 'undefined') {
206 | callback(navigationEnity.duration)
207 | }
208 | },
209 | )
210 | return {
211 | stop: subscribe.unsubscribe,
212 | }
213 | }
214 | function trackSetDataTime(lifeCycle, callback) {
215 | var subscribe = lifeCycle.subscribe(
216 | LifeCycleEventType.PAGE_SET_DATA_UPDATE,
217 | function (data) {
218 | if (!data) return
219 | callback(data.updateEndTimestamp - data.pendingStartTimestamp)
220 | },
221 | )
222 | return {
223 | stop: subscribe.unsubscribe,
224 | }
225 | }
226 | // function getActivePage() {
227 | // const curPages = getCurrentPages()
228 | // if (curPages.length) {
229 | // return curPages[curPages.length - 1]
230 | // }
231 | // return {}
232 | // }
233 |
--------------------------------------------------------------------------------
/src/rumEventsCollection/page/viewCollection.js:
--------------------------------------------------------------------------------
1 | import { rewritePage } from './index'
2 | import { RumEventType } from '../../helper/enums'
3 | import { msToNs } from '../../helper/utils'
4 | import { LifeCycleEventType } from '../../core/lifeCycle'
5 | export function startViewCollection(lifeCycle, configuration) {
6 | lifeCycle.subscribe(LifeCycleEventType.VIEW_UPDATED, function (view) {
7 | lifeCycle.notify(
8 | LifeCycleEventType.RAW_RUM_EVENT_COLLECTED,
9 | processViewUpdate(view),
10 | )
11 | })
12 |
13 | return rewritePage(configuration, lifeCycle)
14 | }
15 | function processViewUpdate(view) {
16 | var apdexLevel
17 | if (view.fmp) {
18 | apdexLevel = parseInt(Number(view.fmp) / 1000)
19 | apdexLevel = apdexLevel > 9 ? 9 : apdexLevel
20 | }
21 | var viewEvent = {
22 | _dd: {
23 | documentVersion: view.documentVersion,
24 | },
25 | date: view.startTime,
26 | type: RumEventType.VIEW,
27 | page: {
28 | action: {
29 | count: view.eventCounts.userActionCount,
30 | },
31 | error: {
32 | count: view.eventCounts.errorCount,
33 | },
34 | setdata: {
35 | count: view.setdataCount,
36 | },
37 | setdata_duration: msToNs(view.setdataDuration),
38 | loadingTime: msToNs(view.loadingTime),
39 | stayTime: msToNs(view.stayTime),
40 | onload2onshow: msToNs(view.onload2onshowTime),
41 | onshow2onready: msToNs(view.onshow2onready),
42 | fpt: msToNs(view.fpt),
43 | fmp: msToNs(view.fmp),
44 | isActive: view.isActive,
45 | apdexLevel,
46 | // longTask: {
47 | // count: view.eventCounts.longTaskCount
48 | // },
49 | resource: {
50 | count: view.eventCounts.resourceCount,
51 | },
52 | timeSpent: msToNs(view.duration),
53 | },
54 | }
55 | return {
56 | rawRumEvent: viewEvent,
57 | startTime: view.startTime,
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/rumEventsCollection/parentContexts.js:
--------------------------------------------------------------------------------
1 | import { ONE_MINUTE, ONE_HOUR } from '../helper/enums'
2 | import { each, now } from '../helper/utils'
3 | import { LifeCycleEventType } from '../core/lifeCycle'
4 | export var VIEW_CONTEXT_TIME_OUT_DELAY = 4 * ONE_HOUR
5 | export var CLEAR_OLD_CONTEXTS_INTERVAL = ONE_MINUTE
6 |
7 | export function startParentContexts(lifeCycle) {
8 | var currentView
9 | var currentAction
10 | var previousViews = []
11 | var previousActions = []
12 | lifeCycle.subscribe(
13 | LifeCycleEventType.VIEW_CREATED,
14 | function (currentContext) {
15 | currentView = currentContext
16 | },
17 | )
18 |
19 | lifeCycle.subscribe(
20 | LifeCycleEventType.VIEW_UPDATED,
21 | function (currentContext) {
22 | // A view can be updated after its end. We have to ensure that the view being updated is the
23 | // most recently created.
24 | if (currentView && currentView.id === currentContext.id) {
25 | currentView = currentContext
26 | }
27 | },
28 | )
29 | lifeCycle.subscribe(LifeCycleEventType.VIEW_ENDED, function (data) {
30 | if (currentView) {
31 | previousViews.unshift({
32 | endTime: data.endClocks,
33 | context: buildCurrentViewContext(),
34 | startTime: currentView.startTime,
35 | })
36 | currentView = undefined
37 | }
38 | })
39 | lifeCycle.subscribe(
40 | LifeCycleEventType.AUTO_ACTION_CREATED,
41 | function (currentContext) {
42 | currentAction = currentContext
43 | },
44 | )
45 |
46 | lifeCycle.subscribe(
47 | LifeCycleEventType.AUTO_ACTION_COMPLETED,
48 | function (action) {
49 | if (currentAction) {
50 | previousActions.unshift({
51 | context: buildCurrentActionContext(),
52 | endTime: currentAction.startClocks + action.duration,
53 | startTime: currentAction.startClocks,
54 | })
55 | }
56 | currentAction = undefined
57 | },
58 | )
59 |
60 | lifeCycle.subscribe(LifeCycleEventType.AUTO_ACTION_DISCARDED, function () {
61 | currentAction = undefined
62 | })
63 | lifeCycle.subscribe(LifeCycleEventType.SESSION_RENEWED, function () {
64 | previousViews = []
65 | previousActions = []
66 | currentView = undefined
67 | currentAction = undefined
68 | })
69 | var clearOldContextsInterval = setInterval(function () {
70 | clearOldContexts(previousViews, VIEW_CONTEXT_TIME_OUT_DELAY)
71 | }, CLEAR_OLD_CONTEXTS_INTERVAL)
72 |
73 | function clearOldContexts(previousContexts, timeOutDelay) {
74 | var oldTimeThreshold = now() - timeOutDelay
75 | while (
76 | previousContexts.length > 0 &&
77 | previousContexts[previousContexts.length - 1].startTime < oldTimeThreshold
78 | ) {
79 | previousContexts.pop()
80 | }
81 | }
82 | function buildCurrentActionContext() {
83 | return { userAction: { id: currentAction.id } }
84 | }
85 | function buildCurrentViewContext() {
86 | return {
87 | page: {
88 | id: currentView.id,
89 | referer:
90 | (previousViews.length &&
91 | previousViews[previousViews.length - 1].context.page.route) ||
92 | undefined,
93 | route: currentView.route,
94 | },
95 | }
96 | }
97 |
98 | function findContext(
99 | buildContext,
100 | previousContexts,
101 | currentContext,
102 | startTime,
103 | ) {
104 | if (startTime === undefined) {
105 | return currentContext ? buildContext() : undefined
106 | }
107 | if (currentContext && startTime >= currentContext.startTime) {
108 | return buildContext()
109 | }
110 | var flag = undefined
111 | each(previousContexts, function (previousContext) {
112 | if (startTime > previousContext.endTime) {
113 | return false
114 | }
115 | if (startTime >= previousContext.startTime) {
116 | flag = previousContext.context
117 | return false
118 | }
119 | })
120 |
121 | return flag
122 | }
123 |
124 | var parentContexts = {
125 | findView: function (startTime) {
126 | return findContext(
127 | buildCurrentViewContext,
128 | previousViews,
129 | currentView,
130 | startTime,
131 | )
132 | },
133 | findAction: function (startTime) {
134 | return findContext(
135 | buildCurrentActionContext,
136 | previousActions,
137 | currentAction,
138 | startTime,
139 | )
140 | },
141 |
142 | stop: function () {
143 | clearInterval(clearOldContextsInterval)
144 | },
145 | }
146 | return parentContexts
147 | }
148 |
--------------------------------------------------------------------------------
/src/rumEventsCollection/performanceCollection.js:
--------------------------------------------------------------------------------
1 | import { LifeCycleEventType } from '../core/lifeCycle'
2 | import { sdk } from '../core/sdk'
3 | export function startPagePerformanceObservable(lifeCycle, configuration) {
4 | if (!!sdk.getPerformance) {
5 | const performance = sdk.getPerformance()
6 | const observer = performance.createObserver((entryList) => {
7 | lifeCycle.notify(
8 | LifeCycleEventType.PERFORMANCE_ENTRY_COLLECTED,
9 | entryList.getEntries(),
10 | )
11 | })
12 | observer.observe({ entryTypes: ['render', 'script', 'navigation'] })
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/rumEventsCollection/requestCollection.js:
--------------------------------------------------------------------------------
1 | import { startXhrProxy } from '../core/xhrProxy'
2 | import { startDownloadProxy } from '../core/downloadProxy'
3 | import { LifeCycleEventType } from '../core/lifeCycle'
4 | import { isObject } from '../helper/utils'
5 | import { isAllowedRequestUrl } from '../rumEventsCollection/resource/resourceUtils'
6 | import {startTracer} from '../rumEventsCollection/tracing/tracer'
7 | var nextRequestIndex = 1
8 |
9 | export function startRequestCollection(lifeCycle, configuration) {
10 | var tracer = startTracer(configuration)
11 | trackXhr(lifeCycle, configuration,tracer)
12 | trackDownload(lifeCycle, configuration)
13 | }
14 | function parseHeader(header) {
15 | // 大小写兼容
16 | if (!isObject(header)) return header
17 | var res = {}
18 | Object.keys(header).forEach(function (key) {
19 | res[key.toLowerCase()] = header[key]
20 | })
21 | return res
22 | }
23 | function getHeaderString(header) {
24 | if (!isObject(header)) return header
25 | var headerStr = ''
26 | Object.keys(header).forEach(function (key) {
27 | headerStr += key + ':' + header[key] + ';'
28 | })
29 | return headerStr
30 | }
31 | export function trackXhr(lifeCycle, configuration,tracer) {
32 | var xhrProxy = startXhrProxy()
33 | xhrProxy.beforeSend(function (context) {
34 | if (isAllowedRequestUrl(configuration, context.url)) {
35 | tracer.traceXhr(context)
36 | context.requestIndex = getNextRequestIndex()
37 | lifeCycle.notify(LifeCycleEventType.REQUEST_STARTED, {
38 | requestIndex: context.requestIndex,
39 | })
40 | }
41 | })
42 | xhrProxy.onRequestComplete(function (context) {
43 | if (isAllowedRequestUrl(configuration, context.url)) {
44 | tracer.clearTracingIfCancelled(context)
45 | lifeCycle.notify(LifeCycleEventType.REQUEST_COMPLETED, {
46 | duration: context.duration,
47 | method: context.method,
48 | requestIndex: context.requestIndex,
49 | performance: context.profile,
50 | response: context.response,
51 | startTime: context.startTime,
52 | status: context.status,
53 | traceId: context.traceId,
54 | spanId: context.spanId,
55 | type: context.type,
56 | url: context.url,
57 | })
58 | }
59 | })
60 | return xhrProxy
61 | }
62 | export function trackDownload(lifeCycle, configuration) {
63 | var dwonloadProxy = startDownloadProxy()
64 | dwonloadProxy.beforeSend(function (context) {
65 | if (isAllowedRequestUrl(configuration, context.url)) {
66 | context.requestIndex = getNextRequestIndex()
67 | lifeCycle.notify(LifeCycleEventType.REQUEST_STARTED, {
68 | requestIndex: context.requestIndex,
69 | })
70 | }
71 | })
72 | dwonloadProxy.onRequestComplete(function (context) {
73 | if (isAllowedRequestUrl(configuration, context.url)) {
74 | lifeCycle.notify(LifeCycleEventType.REQUEST_COMPLETED, {
75 | duration: context.duration,
76 | method: context.method,
77 | requestIndex: context.requestIndex,
78 | performance: context.profile,
79 | response: context.response,
80 | startTime: context.startTime,
81 | status: context.status,
82 | type: context.type,
83 | url: context.url,
84 | })
85 | }
86 | })
87 | return dwonloadProxy
88 | }
89 | function getNextRequestIndex() {
90 | var result = nextRequestIndex
91 | nextRequestIndex += 1
92 | return result
93 | }
94 |
--------------------------------------------------------------------------------
/src/rumEventsCollection/resource/resourceCollection.js:
--------------------------------------------------------------------------------
1 | import {
2 | computePerformanceResourceDuration,
3 | computePerformanceResourceDetails,
4 | computeSize,
5 | } from './resourceUtils'
6 | import { LifeCycleEventType } from '../../core/lifeCycle'
7 | import {
8 | msToNs,
9 | extend2Lev,
10 | urlParse,
11 | getQueryParamsFromUrl,
12 | replaceNumberCharByPath,
13 | jsonStringify,
14 | getStatusGroup,
15 | UUID
16 | } from '../../helper/utils'
17 | import { RumEventType } from '../../helper/enums'
18 | export function startResourceCollection(lifeCycle, configuration) {
19 | lifeCycle.subscribe(LifeCycleEventType.REQUEST_COMPLETED, function (request) {
20 | lifeCycle.notify(
21 | LifeCycleEventType.RAW_RUM_EVENT_COLLECTED,
22 | processRequest(request),
23 | )
24 | })
25 | }
26 |
27 | function processRequest(request) {
28 | var type = request.type
29 | var timing = request.performance
30 | var correspondingTimingOverrides = timing
31 | ? computePerformanceEntryMetrics(timing)
32 | : undefined
33 | var tracingInfo = computeRequestTracingInfo(request)
34 | var urlObj = urlParse(request.url).getParse()
35 | var startTime = request.startTime
36 | var resourceEvent = extend2Lev(
37 | {
38 | date: startTime,
39 | resource: {
40 | type: type,
41 | duration: msToNs(request.duration),
42 | method: request.method,
43 | status: request.status,
44 | statusGroup: getStatusGroup(request.status),
45 | url: request.url,
46 | urlHost: urlObj.Host,
47 | urlPath: urlObj.Path,
48 | urlPathGroup: replaceNumberCharByPath(urlObj.Path),
49 | urlQuery: jsonStringify(getQueryParamsFromUrl(request.url)),
50 | },
51 | type: RumEventType.RESOURCE,
52 | },
53 | tracingInfo,
54 | correspondingTimingOverrides,
55 | )
56 | return { startTime: startTime, rawRumEvent: resourceEvent }
57 | }
58 | function computeRequestTracingInfo(request) {
59 | var hasBeenTraced = request.traceId && request.spanId
60 | if (!hasBeenTraced) {
61 | return undefined
62 | }
63 | return {
64 | _dd: {
65 | spanId: request.spanId,
66 | traceId: request.traceId
67 | },
68 | resource: { id: UUID() }
69 | }
70 | }
71 | function computePerformanceEntryMetrics(timing) {
72 | return {
73 | resource: extend2Lev(
74 | {},
75 | {
76 | load: computePerformanceResourceDuration(timing),
77 | size: computeSize(timing),
78 | },
79 | computePerformanceResourceDetails(timing),
80 | ),
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/rumEventsCollection/setDataCollection.js:
--------------------------------------------------------------------------------
1 | import { LifeCycleEventType } from '../core/lifeCycle'
2 |
3 | export function startSetDataColloction(lifeCycle) {
4 | const originPage = Page
5 | const originComponent = Component
6 | Page = function (page) {
7 | const originPageOnLoad = page['onLoad']
8 | page['onLoad'] = function () {
9 | this.setUpdatePerformanceListener &&
10 | this.setUpdatePerformanceListener({ withDataPaths: true }, (res) => {
11 | lifeCycle.notify(LifeCycleEventType.PAGE_SET_DATA_UPDATE, res)
12 | })
13 | return originPageOnLoad && originPageOnLoad.apply(this, arguments)
14 | }
15 | return originPage(page)
16 | }
17 | Component = function (component) {
18 | let originComponentAttached
19 | if (component.lifetimes) {
20 | originComponentAttached = component.lifetimes['attached']
21 | } else {
22 | // 兼容老版本
23 | originComponentAttached = component['attached']
24 | }
25 | component['attached'] = function () {
26 | this.setUpdatePerformanceListener &&
27 | this.setUpdatePerformanceListener({ withDataPaths: true }, (res) => {
28 | lifeCycle.notify(LifeCycleEventType.PAGE_SET_DATA_UPDATE, res)
29 | })
30 | return originComponentAttached && originComponentAttached.apply(this, arguments)
31 | }
32 | return originComponent(component)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/rumEventsCollection/tracing/ddtraceTracer.js:
--------------------------------------------------------------------------------
1 | // === Generate a random 64-bit number in fixed-length hex format
2 | function randomTraceId() {
3 | const digits = '0123456789abcdef';
4 | let n = '';
5 | for (let i = 0; i < 19; i += 1) {
6 | const rand = Math.floor(Math.random() * 10);
7 | n += digits[rand];
8 | }
9 | return n;
10 | }
11 | /**
12 | *
13 | * @param {*} configuration 配置信息
14 | */
15 | export function DDtraceTracer(configuration) {
16 | this._spanId = randomTraceId()
17 | this._traceId = randomTraceId()
18 | }
19 | DDtraceTracer.prototype = {
20 | isTracingSupported: function() {
21 | return true
22 | },
23 | getSpanId:function() {
24 | return this._spanId
25 | },
26 | getTraceId: function() {
27 | return this._traceId
28 | },
29 | makeTracingHeaders: function() {
30 | return {
31 | 'x-datadog-origin': 'rum',
32 | // 'x-datadog-parent-id': spanId.toDecimalString(),
33 | 'x-datadog-sampled': '1',
34 | 'x-datadog-sampling-priority': '1',
35 | 'x-datadog-trace-id': this.getTraceId()
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/src/rumEventsCollection/tracing/jaegerTracer.js:
--------------------------------------------------------------------------------
1 | // === Generate a random 64-bit number in fixed-length hex format
2 | function randomTraceId() {
3 | const digits = '0123456789abcdef';
4 | let n = '';
5 | for (let i = 0; i < 16; i += 1) {
6 | const rand = Math.floor(Math.random() * 16);
7 | n += digits[rand];
8 | }
9 | return n;
10 | }
11 |
12 | /**
13 | *
14 | * @param {*} configuration 配置信息
15 | */
16 | export function JaegerTracer(configuration) {
17 | const rootSpanId = randomTraceId();
18 | // this._traceId = randomTraceId() + rootSpanId // 默认用128bit,兼容其他配置
19 | if (configuration.traceId128Bit) {
20 | // 128bit生成traceid
21 | this._traceId = randomTraceId() + rootSpanId
22 | } else {
23 | this._traceId = rootSpanId
24 | }
25 | this._spanId = rootSpanId
26 | }
27 | JaegerTracer.prototype = {
28 | isTracingSupported: function() {
29 | return true
30 | },
31 | getSpanId:function() {
32 | return this._spanId
33 | },
34 | getTraceId: function() {
35 | return this._traceId
36 | },
37 | getUberTraceId: function() {
38 | //{trace-id}:{span-id}:{parent-span-id}:{flags}
39 | return this._traceId + ':' + this._spanId + ':' + '0' + ':' + '1'
40 | },
41 | makeTracingHeaders: function() {
42 | return {
43 | 'uber-trace-id': this.getUberTraceId(),
44 | }
45 |
46 | }
47 | }
--------------------------------------------------------------------------------
/src/rumEventsCollection/tracing/skywalkingTracer.js:
--------------------------------------------------------------------------------
1 | import {base64Encode, urlParse , getActivePage} from '../../helper/utils'
2 | // start SkyWalking
3 | function uuid() {
4 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
5 | /* tslint:disable */
6 | const r = (Math.random() * 16) | 0;
7 | /* tslint:disable */
8 | const v = c === 'x' ? r : (r & 0x3) | 0x8;
9 |
10 | return v.toString(16);
11 | });
12 | }
13 |
14 | /**
15 | *
16 | * @param {*} configuration 配置信息
17 | * @param {*} requestUrl 请求的url
18 | */
19 | export function SkyWalkingTracer(configuration, requestUrl) {
20 | this._spanId = uuid()
21 | this._traceId = uuid()
22 | this._applicationId = configuration.applicationId
23 | this._env = configuration.env
24 | this._version = configuration.version
25 | this._urlParse = urlParse(requestUrl).getParse()
26 | }
27 | SkyWalkingTracer.prototype = {
28 | isTracingSupported: function() {
29 | if (this._env && this._version && this._urlParse) return true
30 | return false
31 | },
32 | getSpanId:function() {
33 | return this._spanId
34 | },
35 | getTraceId: function() {
36 | return this._traceId
37 | },
38 | getSkyWalkingSw8:function() {
39 | try {
40 | var traceIdStr = String(base64Encode(this._traceId));
41 | var segmentId = String(base64Encode(this._spanId));
42 | var service = String(base64Encode(this._applicationId + '_rum_' + this.env));
43 | var instance = String(base64Encode(this._version));
44 | var activePage = getActivePage()
45 | var endpointPage = ''
46 | if (activePage && activePage.route) {
47 | endpointPage = activePage.route
48 | }
49 | var endpoint = String(base64Encode(endpointPage));
50 | var peer = String(base64Encode(this._urlParse.Host));
51 | var index = '0'
52 | // var values = `${1}-${traceIdStr}-${segmentId}-${index}-${service}-${instance}-${endpoint}-${peer}`;
53 | return '1-' + traceIdStr + '-'+ segmentId + '-' +index + '-'+ service + '-'+ instance + '-'+ endpoint + '-'+ peer
54 | } catch(err) {
55 | return ''
56 | }
57 | },
58 | makeTracingHeaders: function() {
59 | return {
60 | 'sw8': this.getSkyWalkingSw8(),
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/src/rumEventsCollection/tracing/tracer.js:
--------------------------------------------------------------------------------
1 |
2 | import {each, extend, getOrigin} from '../../helper/utils'
3 | import {TraceType} from '../../helper/enums'
4 | import { DDtraceTracer} from './ddtraceTracer'
5 | import { SkyWalkingTracer} from './skywalkingTracer'
6 | import { JaegerTracer} from './jaegerTracer'
7 | import { ZipkinSingleTracer} from './zipkinSingleTracer'
8 | import { ZipkinMultiTracer} from './zipkinMultiTracer'
9 | import { W3cTraceParentTracer} from './w3cTraceParentTracer'
10 |
11 | export function clearTracingIfCancelled(context) {
12 | if (context.status === 0) {
13 | context.traceId = undefined
14 | context.spanId = undefined
15 | }
16 | }
17 |
18 | export function startTracer(configuration) {
19 | return {
20 | clearTracingIfCancelled: clearTracingIfCancelled,
21 | traceXhr: function (context) {
22 | return injectHeadersIfTracingAllowed(
23 | configuration,
24 | context,
25 | function (tracingHeaders) {
26 | context.option = extend({}, context.option)
27 | var header = {}
28 | if (context.option.header) {
29 | each(context.option.header, function (value, key) {
30 | header[key] = value
31 | })
32 | }
33 | context.option.header = extend(header, tracingHeaders)
34 | }
35 | )
36 | }
37 | }
38 | }
39 | function isAllowedUrl(configuration, requestUrl) {
40 | var requestOrigin = getOrigin(requestUrl)
41 | var flag = false
42 | each(configuration.allowedTracingOrigins, function (allowedOrigin) {
43 | if (
44 | requestOrigin === allowedOrigin ||
45 | (allowedOrigin instanceof RegExp && allowedOrigin.test(requestOrigin))
46 | ) {
47 | flag = true
48 | return false
49 | }
50 | })
51 | return flag
52 | }
53 |
54 | export function injectHeadersIfTracingAllowed(configuration, context, inject) {
55 | if (!isAllowedUrl(configuration, context.url) || !configuration.traceType) {
56 | return
57 | }
58 | var tracer;
59 | switch(configuration.traceType) {
60 | case TraceType.DDTRACE:
61 | tracer = new DDtraceTracer();
62 | break;
63 | case TraceType.SKYWALKING_V3:
64 | tracer = new SkyWalkingTracer(configuration, context.url);
65 | break;
66 | case TraceType.ZIPKIN_MULTI_HEADER:
67 | tracer = new ZipkinMultiTracer(configuration);
68 | break;
69 | case TraceType.JAEGER:
70 | tracer = new JaegerTracer(configuration);
71 | break;
72 | case TraceType.W3C_TRACEPARENT:
73 | tracer = new W3cTraceParentTracer(configuration);
74 | break;
75 | case TraceType.ZIPKIN_SINGLE_HEADER:
76 | tracer = new ZipkinSingleTracer(configuration);
77 | break;
78 | default:
79 | break;
80 | }
81 | if (!tracer || !tracer.isTracingSupported()) {
82 | return
83 | }
84 |
85 | context.traceId = tracer.getTraceId()
86 | context.spanId = tracer.getSpanId()
87 | inject(tracer.makeTracingHeaders())
88 | }
89 |
--------------------------------------------------------------------------------
/src/rumEventsCollection/tracing/w3cTraceParentTracer.js:
--------------------------------------------------------------------------------
1 | // === Generate a random 64-bit number in fixed-length hex format
2 | function randomTraceId() {
3 | const digits = '0123456789abcdef';
4 | let n = '';
5 | for (let i = 0; i < 16; i += 1) {
6 | const rand = Math.floor(Math.random() * 16);
7 | n += digits[rand];
8 | }
9 | return n;
10 | }
11 |
12 | /**
13 | *
14 | * @param {*} configuration 配置信息
15 | */
16 | export function W3cTraceParentTracer(configuration) {
17 | const rootSpanId = randomTraceId();
18 | this._traceId = randomTraceId() + rootSpanId
19 | this._spanId = rootSpanId
20 | }
21 | W3cTraceParentTracer.prototype = {
22 | isTracingSupported: function() {
23 | return true
24 | },
25 | getSpanId:function() {
26 | return this._spanId
27 | },
28 | getTraceId: function() {
29 | return this._traceId
30 | },
31 | getTraceParent: function() {
32 | // '{version}-{traceId}-{spanId}-{sampleDecision}'
33 | return '00-' + this._traceId + '-' + this._spanId + '-01'
34 | },
35 | makeTracingHeaders: function() {
36 | return {
37 | 'traceparent': this.getTraceParent()
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/src/rumEventsCollection/tracing/zipkinMultiTracer.js:
--------------------------------------------------------------------------------
1 | // === Generate a random 64-bit number in fixed-length hex format
2 | function randomTraceId() {
3 | const digits = '0123456789abcdef';
4 | let n = '';
5 | for (let i = 0; i < 16; i += 1) {
6 | const rand = Math.floor(Math.random() * 16);
7 | n += digits[rand];
8 | }
9 | return n;
10 | }
11 |
12 | /**
13 | *
14 | * @param {*} configuration 配置信息
15 | */
16 | export function ZipkinMultiTracer(configuration) {
17 | const rootSpanId = randomTraceId();
18 | if (configuration.traceId128Bit) {
19 | // 128bit生成traceid
20 | this._traceId = randomTraceId() + rootSpanId
21 | } else {
22 | this._traceId = rootSpanId
23 | }
24 | this._spanId = rootSpanId
25 | }
26 | ZipkinMultiTracer.prototype = {
27 | isTracingSupported: function() {
28 | return true
29 | },
30 | getSpanId:function() {
31 | return this._spanId
32 | },
33 | getTraceId: function() {
34 | return this._traceId
35 | },
36 |
37 | makeTracingHeaders: function() {
38 | return {
39 | 'X-B3-TraceId': this.getTraceId(),
40 | 'X-B3-SpanId': this.getSpanId(),
41 | // 'X-B3-ParentSpanId': '',
42 | 'X-B3-Sampled': '1',
43 | // 'X-B3-Flags': '0'
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/src/rumEventsCollection/tracing/zipkinSingleTracer.js:
--------------------------------------------------------------------------------
1 | // === Generate a random 64-bit number in fixed-length hex format
2 | function randomTraceId() {
3 | const digits = '0123456789abcdef';
4 | let n = '';
5 | for (let i = 0; i < 16; i += 1) {
6 | const rand = Math.floor(Math.random() * 16);
7 | n += digits[rand];
8 | }
9 | return n;
10 | }
11 |
12 | /**
13 | *
14 | * @param {*} configuration 配置信息
15 | */
16 | export function ZipkinSingleTracer(configuration) {
17 | const rootSpanId = randomTraceId();
18 | this._traceId = randomTraceId() + rootSpanId
19 | this._spanId = rootSpanId
20 | }
21 | ZipkinSingleTracer.prototype = {
22 | isTracingSupported: function() {
23 | return true
24 | },
25 | getSpanId:function() {
26 | return this._spanId
27 | },
28 | getTraceId: function() {
29 | return this._traceId
30 | },
31 | getB3Str: function() {
32 | //{TraceId}-{SpanId}-{SamplingState}-{ParentSpanId}
33 | return this._traceId + '-' + this._spanId + '-1'
34 | },
35 | makeTracingHeaders: function() {
36 | return {
37 | 'b3': this.getB3Str()
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/src/rumEventsCollection/trackEventCounts.js:
--------------------------------------------------------------------------------
1 | import { noop } from '../helper/utils'
2 | import { RumEventType } from '../helper/enums'
3 | import { LifeCycleEventType } from '../core/lifeCycle'
4 |
5 | export function trackEventCounts(lifeCycle, callback) {
6 | if (typeof callback === 'undefined') {
7 | callback = noop
8 | }
9 | var eventCounts = {
10 | errorCount: 0,
11 | resourceCount: 0,
12 | longTaskCount: 0,
13 | userActionCount: 0,
14 | }
15 |
16 | var subscription = lifeCycle.subscribe(
17 | LifeCycleEventType.RAW_RUM_EVENT_COLLECTED,
18 | function (data) {
19 | var rawRumEvent = data.rawRumEvent
20 | switch (rawRumEvent.type) {
21 | case RumEventType.ERROR:
22 | eventCounts.errorCount += 1
23 | callback(eventCounts)
24 | break
25 | case RumEventType.RESOURCE:
26 | eventCounts.resourceCount += 1
27 | callback(eventCounts)
28 | break
29 | case RumEventType.ACTION:
30 | eventCounts.userActionCount += 1
31 | callback(eventCounts)
32 | break
33 | }
34 | },
35 | )
36 |
37 | return {
38 | stop: function () {
39 | subscription.unsubscribe()
40 | },
41 | eventCounts: eventCounts,
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/rumEventsCollection/trackPageActiveites.js:
--------------------------------------------------------------------------------
1 | import { each, now } from '../helper/utils'
2 | import { LifeCycleEventType } from '../core/lifeCycle'
3 | import { Observable } from '../core/observable'
4 | // Delay to wait for a page activity to validate the tracking process
5 | export var PAGE_ACTIVITY_VALIDATION_DELAY = 100
6 | // Delay to wait after a page activity to end the tracking process
7 | export var PAGE_ACTIVITY_END_DELAY = 100
8 | // Maximum duration of the tracking process
9 | export var PAGE_ACTIVITY_MAX_DURATION = 10000
10 |
11 | export function waitIdlePageActivity(lifeCycle, completionCallback) {
12 | var _trackPageActivities = trackPageActivities(lifeCycle)
13 | var pageActivitiesObservable = _trackPageActivities.observable
14 | var stopPageActivitiesTracking = _trackPageActivities.stop
15 | var _waitPageActivitiesCompletion = waitPageActivitiesCompletion(
16 | pageActivitiesObservable,
17 | stopPageActivitiesTracking,
18 | completionCallback,
19 | )
20 |
21 | var stopWaitPageActivitiesCompletion = _waitPageActivitiesCompletion.stop
22 | function stop() {
23 | stopWaitPageActivitiesCompletion()
24 | stopPageActivitiesTracking()
25 | }
26 |
27 | return { stop: stop }
28 | }
29 |
30 | // Automatic action collection lifecycle overview:
31 | // (Start new trackPageActivities)
32 | // .-------------------'--------------------.
33 | // v v
34 | // [Wait for a page activity ] [Wait for a maximum duration]
35 | // [timeout: VALIDATION_DELAY] [ timeout: MAX_DURATION ]
36 | // / \ |
37 | // v v |
38 | // [No page activity] [Page activity] |
39 | // | |,----------------------. |
40 | // v v | |
41 | // (Discard) [Wait for a page activity] | |
42 | // [ timeout: END_DELAY ] | |
43 | // / \ | |
44 | // v v | |
45 | // [No page activity] [Page activity] | |
46 | // | | | |
47 | // | '------------' |
48 | // '-----------. ,--------------------'
49 | // v
50 | // (End)
51 | //
52 | // Note: because MAX_DURATION > VALIDATION_DELAY, we are sure that if the process is still alive
53 | // after MAX_DURATION, it has been validated.
54 | export function trackPageActivities(lifeCycle) {
55 | var observable = new Observable()
56 | var subscriptions = []
57 | var firstRequestIndex
58 | var pendingRequestsCount = 0
59 |
60 | subscriptions.push(
61 | lifeCycle.subscribe(LifeCycleEventType.PAGE_SET_DATA_UPDATE, function () {
62 | notifyPageActivity()
63 | }),
64 | lifeCycle.subscribe(LifeCycleEventType.PAGE_ALIAS_ACTION, function () {
65 | notifyPageActivity()
66 | }),
67 | )
68 |
69 | subscriptions.push(
70 | lifeCycle.subscribe(
71 | LifeCycleEventType.REQUEST_STARTED,
72 | function (startEvent) {
73 | if (firstRequestIndex === undefined) {
74 | firstRequestIndex = startEvent.requestIndex
75 | }
76 |
77 | pendingRequestsCount += 1
78 | notifyPageActivity()
79 | },
80 | ),
81 | )
82 |
83 | subscriptions.push(
84 | lifeCycle.subscribe(
85 | LifeCycleEventType.REQUEST_COMPLETED,
86 | function (request) {
87 | // If the request started before the tracking start, ignore it
88 | if (
89 | firstRequestIndex === undefined ||
90 | request.requestIndex < firstRequestIndex
91 | ) {
92 | return
93 | }
94 | pendingRequestsCount -= 1
95 | notifyPageActivity()
96 | },
97 | ),
98 | )
99 |
100 | function notifyPageActivity() {
101 | observable.notify({ isBusy: pendingRequestsCount > 0 })
102 | }
103 |
104 | return {
105 | observable: observable,
106 | stop: function () {
107 | each(subscriptions, function (sub) {
108 | sub.unsubscribe()
109 | })
110 | },
111 | }
112 | }
113 |
114 | export function waitPageActivitiesCompletion(
115 | pageActivitiesObservable,
116 | stopPageActivitiesTracking,
117 | completionCallback,
118 | ) {
119 | var idleTimeoutId
120 | var hasCompleted = false
121 |
122 | var validationTimeoutId = setTimeout(function () {
123 | complete({ hadActivity: false })
124 | }, PAGE_ACTIVITY_VALIDATION_DELAY)
125 | var maxDurationTimeoutId = setTimeout(function () {
126 | complete({ hadActivity: true, endTime: now() })
127 | }, PAGE_ACTIVITY_MAX_DURATION)
128 | pageActivitiesObservable.subscribe(function (data) {
129 | var isBusy = data.isBusy
130 | clearTimeout(validationTimeoutId)
131 | clearTimeout(idleTimeoutId)
132 | var lastChangeTime = now()
133 | if (!isBusy) {
134 | idleTimeoutId = setTimeout(function () {
135 | complete({ hadActivity: true, endTime: lastChangeTime })
136 | }, PAGE_ACTIVITY_END_DELAY)
137 | }
138 | })
139 |
140 | function stop() {
141 | hasCompleted = true
142 | clearTimeout(validationTimeoutId)
143 | clearTimeout(idleTimeoutId)
144 | clearTimeout(maxDurationTimeoutId)
145 | stopPageActivitiesTracking()
146 | }
147 |
148 | function complete(params) {
149 | if (hasCompleted) {
150 | return
151 | }
152 | stop()
153 | completionCallback(params)
154 | }
155 |
156 | return { stop: stop }
157 | }
158 |
--------------------------------------------------------------------------------
/src/rumEventsCollection/transport/batch.js:
--------------------------------------------------------------------------------
1 | import { LifeCycleEventType } from '../../core/lifeCycle'
2 | import { Batch, HttpRequest } from '../../core/transport'
3 | import { RumEventType } from '../../helper/enums'
4 | export function startRumBatch(configuration, lifeCycle) {
5 | var batch = makeRumBatch(configuration, lifeCycle)
6 | lifeCycle.subscribe(
7 | LifeCycleEventType.RUM_EVENT_COLLECTED,
8 | function (serverRumEvent) {
9 | if (serverRumEvent.type === RumEventType.VIEW) {
10 | batch.upsert(serverRumEvent, serverRumEvent.page.id)
11 | } else {
12 | batch.add(serverRumEvent)
13 | }
14 | },
15 | )
16 | return {
17 | stop: function () {
18 | batch.stop()
19 | },
20 | }
21 | }
22 |
23 | function makeRumBatch(configuration, lifeCycle) {
24 | var primaryBatch = createRumBatch(configuration.datakitUrl, lifeCycle)
25 |
26 | function createRumBatch(endpointUrl, lifeCycle) {
27 | return new Batch(
28 | new HttpRequest(endpointUrl, configuration.batchBytesLimit),
29 | configuration.maxBatchSize,
30 | configuration.batchBytesLimit,
31 | configuration.maxMessageSize,
32 | configuration.flushTimeout,
33 | lifeCycle,
34 | )
35 | }
36 |
37 | var stopped = false
38 | return {
39 | add: function (message) {
40 | if (stopped) {
41 | return
42 | }
43 | primaryBatch.add(message)
44 | },
45 | stop: function () {
46 | stopped = true
47 | },
48 | upsert: function (message, key) {
49 | if (stopped) {
50 | return
51 | }
52 | primaryBatch.upsert(message, key)
53 | },
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const TerserPlugin = require('terser-webpack-plugin')
3 | module.exports = (env, args) => {
4 | let baseConfig = {
5 | mode: args.mode,
6 | entry: './src/index.js',
7 | output: {
8 | filename: 'dataflux-rum-miniapp.js',
9 | path: path.resolve(__dirname, './demo/miniprogram'),
10 | library: {
11 | type: 'commonjs2',
12 | },
13 | },
14 | devtool: args.mode === 'development' ? 'inline-source-map' : 'source-map',
15 | }
16 | if (args.mode !== 'development') {
17 | baseConfig = Object.assign(baseConfig, {
18 | optimization: {
19 | minimize: true,
20 | minimizer: [
21 | new TerserPlugin({
22 | terserOptions: {
23 | compress: {
24 | drop_console: true,
25 | },
26 | },
27 | }),
28 | ],
29 | },
30 | })
31 | } else {
32 | baseConfig = Object.assign(baseConfig, {
33 | watchOptions: {
34 | ignored: /node_modules|demo/, //忽略不用监听变更的目录
35 | aggregateTimeout: 300, // 文件发生改变后多长时间后再重新编译(Add a delay before rebuilding once the first file changed )
36 | poll: 1000, //每秒询问的文件变更的次数
37 | },
38 | })
39 | }
40 | return baseConfig
41 | }
42 |
--------------------------------------------------------------------------------