keeson-web-report-sdk 1.1.1 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/bundle.esm.js +1 -0
- package/bundle.iife.min.js +1 -0
- package/bundle.min.js +1 -1
- package/package.json +15 -4
- package/src/index.js +323 -0
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
```javascript
|
|
2
|
+
export const REPORT_TYPE = {
|
|
3
|
+
ERROR: "error",
|
|
4
|
+
PERFORMANCE: "performance",
|
|
5
|
+
REQUEST: "request",
|
|
6
|
+
USER: "user",
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const ERROR_TYPE = {
|
|
10
|
+
MAIN: "main",
|
|
11
|
+
RESOURCE_ERROR: "resourceError",
|
|
12
|
+
PROMISE_REJECTION: "PromiseRejection",
|
|
13
|
+
VUE_ERROR: "VueError",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const REQUEST_ERROR_TYPE = {
|
|
17
|
+
ERROR: "error",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const PERFORMANCE_TYPE = {
|
|
21
|
+
LOADED: "loaded",
|
|
22
|
+
FP: "paint",
|
|
23
|
+
FCP: "first-contentful-paint",
|
|
24
|
+
LCP: "largest-contentful-paint",
|
|
25
|
+
CLS: "layout-shift",
|
|
26
|
+
FID: "first-input",
|
|
27
|
+
LONGTASK: "longTask",
|
|
28
|
+
RESOURCE: "resource",
|
|
29
|
+
PAGE_RESOURCE: "pageResource",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const USER_TYPE = {
|
|
33
|
+
LOCATION_CHANGE: "LocationChange",
|
|
34
|
+
ROUTE_CHANGE: "RouteChange",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
```
|
package/bundle.esm.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e="error",t="performance",r="user",s="main",o="resourceError",a="PromiseRejection",n="VueError",i="error",p="loaded",u="paint",c="first-contentful-paint",l="largest-contentful-paint",d="layout-shift",m="first-input",h="longTask",g="resource",f="pageResource",y="LocationChange",T="RouteChange";function v(e,t){if(window.PerformanceObserver){const r=new PerformanceObserver(function(r){const s=function(e,t){const r=t.getEntries(),s=[];return r.forEach(t=>{if("paint"===t.entryType&&("first-paint"===t.name&&s.push({type:u,message:`白屏时间 (FP): ${t.startTime+t.duration}ms`,extra:{value:t.startTime+t.duration},value:t.startTime+t.duration}),"first-contentful-paint"===t.name&&s.push({type:c,message:`首屏时间 (FCP): ${t.startTime+t.duration}ms`,extra:{value:t.startTime+t.duration},value:t.startTime+t.duration})),"largest-contentful-paint"===t.entryType){const{renderTime:e,startTime:r,duration:o}=t.renderTime,a=e||r+o;a>2500&&s.push({type:l,message:`元素 ${t.element.outerHTML} LCP 时间${a}毫秒,超过了 2.5 秒`,extra:{element:t.element.outerHTML,value:a},value:a})}if("first-input"===t.entryType&&t.processingStart-t.startTime>100&&s.push({type:m,message:"主线程阻塞时长超过 100 毫秒,用户体验不佳",extra:{value:t.processingStart-t.startTime},value:t.processingStart-t.startTime}),"layout-shift"!==t.entryType||t.hadRecentInput||t.value>.1&&s.push({type:d,message:"CLS 值超过 0.1,用户体验不佳",extra:{value:t.value},value:t.value}),"navigation"===t.entryType){s.push({type:p,message:`加载完成时间 (Load): ${t.loadEventEnd}ms`,value:t.loadEventEnd,extra:{value:t.loadEventEnd}});const r=t.domainLookupEnd-t.domainLookupStart,o=t.responseEnd-t.startTime;o>e.longTimeRequestThreshold&&s.push({type:f,message:`页面${t.name}请求耗时 ${o} 毫秒,超过了 ${e.longTimeRequestThreshold} 毫秒`,extra:{dnsTime:r,value:o,resourceName:t.name},value:o})}if("resource"===t.entryType){const r=t.domainLookupEnd-t.domainLookupStart,o=t.duration;o>e.longTimeRequestThreshold&&s.push({type:g,message:`资源${t.name}请求耗时 ${o} 毫秒,超过了 ${e.longTimeRequestThreshold} 毫秒`,extra:{dnsTime:r,value:o,resourceName:t.name},value:o})}if("longtask"===t.entryType){const e=t.duration-50>0?t.duration-50:0;e>50&&s.push({type:h,message:`检测到长任务,总耗时: ${t.duration} ms, 阻塞时间约: ${e} ms,大于建议的50ms内,请使用控制台performance(性能)进行性能分析`,extra:{duration:t.duration,blockingTime:e,resourceName:t.name},value:e})}}),s}(e,r);return"function"==typeof t&&t(s),s});r.observe({entryTypes:["paint","first-contentful-paint","largest-contentful-paint","first-input","layout-shift","interaction-to-next-paint","navigation","resource","longtask"],buffered:!0})}}function $(e){const t=[];return t.push(function(e){const t=history.pushState,r=history.replaceState;history.pushState=function(...r){const s=window.location.href,o=t.apply(this,r),a=window.location.href;return s!==a&&e({to:a,from:s}),o},history.replaceState=function(...t){const s=window.location.href,o=r.apply(this,t),a=window.location.href;return s!==a&&e({to:a,from:s}),o};const s=()=>{e({to:window.location.href,from:window.location.href})};return window.addEventListener("popstate",s),()=>{window.removeEventListener("popstate",s),history.pushState=t,history.replaceState=r}}(e)),()=>{t.forEach(e=>e())}}const w={reportUrl:"",appId:"",appVersion:"",appUrl:window.location.origin,appEnv:"prod",userAgent:window.navigator.userAgent,reportPath:"",reportError:!0,reportPromiseReject:!0,reportVueError:!0,vueInstance:null,reportPerformance:!0,listenPerformance:!0,longTimeRequestThreshold:2e3,reportErrorReqest:!0,errorRequestFunc:null,reportUserNavigate:!0,appSecretKey:""};class R{records=[];timer=null;options={};constructor(e){this.options={...w,...e}}init(){const p=localStorage.getItem("report-store-date"),u=`${(c=new Date).getFullYear()}-${c.getMonth()+1}-${c.getDate()}`;var c;p!==u&&(localStorage.setItem("report-store-date",u),localStorage.setItem("report-store-list","[]"));const l=JSON.parse(localStorage.getItem("report-store-list"));var d,m,h,g;if(this.options.reportError&&(d=t=>{l.includes(t.message)||(this.addReportRecord({reportType:e,...t}),l.push(t.message),localStorage.setItem("report-store-list",JSON.stringify(l)))},window.addEventListener("error",function(e){if(e.target){if(e.target&&("IMG"===e.target.tagName||"SCRIPT"===e.target.tagName||"LINK"===e.target.tagName||"VIDEO"===e.target.tagName||"AUDIO"===e.target.tagName||"SOURCE"===e.target.tagName)){const t=e.target,r={tagName:t.tagName,src:t.src||t.href,outerHTML:t.outerHTML,time:(new Date).toISOString()};e.preventDefault(),d&&d({type:o,message:`资源加载失败,${r.tagName}: ${r.src}`,extra:r})}}else{const t={message:e.error.stack,filename:e.filename,line:e.lineno,col:e.colno};e.preventDefault(),d&&d({type:s,message:t.message,extra:t})}},!0)),this.options.reportPromiseReject&&function(e){window.addEventListener("unhandledrejection",function(t){e&&e(t)})}(t=>{l.includes(t.reason?.stack)||(this.addReportRecord({reportType:e,type:a,message:t.reason?.stack}),l.push(t.reason?.stack),localStorage.setItem("report-store-list",JSON.stringify(l)))}),this.options.reportVueError&&this.options.vueInstance&&function(e,t){e.config.errorHandler=function(e,r,s){t&&t({err:e,vm:r,info:s})}}(this.options.vueInstance,({err:t})=>{l.includes(t.message)||(this.addReportRecord({reportType:e,type:n,message:t.message}),l.push(t.message),localStorage.setItem("report-store-list",JSON.stringify(l)))}),this.options.listenPerformance&&v(this.options,e=>{e.forEach(e=>{e.extra.resourceName!==this.options.reportUrl&&this.addReportRecord({reportType:t,...e})})}),this.options.reportErrorReqest&&(m=t=>{const{status:r,url:s,method:o,statusText:a}=t,n=`请求报错接口:${s},方法:${o},状态码:${r},状态信息:${a}`;l.includes(n)||(this.addReportRecord({reportType:e,type:i,message:n}),l.push(n),localStorage.setItem("report-store-list",JSON.stringify(l)))},h=this.options,g=XMLHttpRequest.prototype.open,XMLHttpRequest.prototype.open=function(e,t,r,s,o){let a=Date.now();this.addEventListener("load",function(){const r=Date.now()-a;if(200===this.status){if("string"==typeof this.response)try{const s=JSON.parse(this.response);if("function"==typeof h.errorRequestFunc&&h.errorRequestFunc(s))return void(m&&m({message:`请求成功接口:${t},方法:${e},状态码:${this.status},状态信息:${this.statusText}, 请求耗时:${r}ms;服务器处理错误`,extra:{response:s,headers:this.getAllResponseHeaders()}}))}catch(e){}}else m&&m({message:`请求报错接口:${t},方法:${e},状态码:${this.status},状态信息:${this.statusText}`,extra:{headers:this.getAllResponseHeaders()}})}),this.addEventListener("error",function(){m&&m({message:`请求报错接口:${t},方法:${e},状态码:${this.status},状态信息:${this.statusText}`})}),g.call(this,e,t,r,s,o)},function(e,t,r){var s=window.fetch;window.fetch=function(e,o){let a=Date.now();return s(e,o).then(async s=>{const n=Date.now()-a,i=[...s.headers.entries()].map(e=>e.join(": ")).join("\n");if(200===s.status)try{const o=await s.json();if("function"==typeof r.errorRequestFunc&&r.errorRequestFunc(o))return void(t&&t({message:`请求成功接口:${e},方法:${method},状态码:${s.status},状态信息:${s.statusText}, 请求耗时:${n}ms\n服务器处理错误`,extra:{response:o,headers:i}}))}catch(e){}else t&&t({message:`请求报错接口:${e},方法:${o.method},状态码:${s.status},状态信息:${s.statusText}, 耗时:${n}ms`,extra:{headers:i}});return s}).catch(r=>{t&&t({message:`请求报错接口:${e},方法:${o.method},错误信息\n${r.stack}`})})}}(0,t=>{const{url:r,method:s,statusText:o}=t,a=`请求报错接口:${r},方法:${s},状态信息:${o}`;l.includes(a)||(this.addReportRecord({reportType:e,type:i,message:a}),l.push(a),localStorage.setItem("report-store-list",JSON.stringify(l)))},this.options)),this.options.reportUserNavigate){const e=window.location.href;this.addReportRecord({reportType:r,type:y,message:`请求地址:${e}`,extra:JSON.stringify({url:e})}),$(e=>{this.addReportRecord({reportType:r,type:T,message:`路由变化:${e.to}`,extra:JSON.stringify({route:e.to})})})}}addReportRecord(e){this.timer&&clearTimeout(this.timer),this.timer=setTimeout(()=>{try{this.sendReport(JSON.parse(JSON.stringify(this.records))),this.records=[]}catch(e){}},1e3),this.records.push({appId:this.options.appId,appUrl:this.options.appUrl,reportPath:window.location.pathname,userAgent:window.navigator.userAgent,...e,extra:e.extra&&"object"==typeof e.extra?JSON.stringify(e.extra):void 0})}sendReport(e){const t={appSecretKey:this.options.appSecretKey,appId:this.options.appId,data:e},r=new Blob([JSON.stringify(t)],{type:"application/json; charset=UTF-8"});navigator.sendBeacon(this.options.reportUrl,r)}}let S;function x(e={}){S||(S=new R(e)),S.init()}var E={init:x};export{E as default,x as init};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var reportSdk=function(e){"use strict";const t="error",r="performance",s="user",o="main",n="resourceError",a="PromiseRejection",i="VueError",p="error",u="loaded",c="paint",l="first-contentful-paint",d="largest-contentful-paint",m="layout-shift",h="first-input",g="longTask",f="resource",y="pageResource",T="LocationChange",v="RouteChange";function $(e,t){if(window.PerformanceObserver){const r=new PerformanceObserver(function(r){const s=function(e,t){const r=t.getEntries(),s=[];return r.forEach(t=>{if("paint"===t.entryType&&("first-paint"===t.name&&s.push({type:c,message:`白屏时间 (FP): ${t.startTime+t.duration}ms`,extra:{value:t.startTime+t.duration},value:t.startTime+t.duration}),"first-contentful-paint"===t.name&&s.push({type:l,message:`首屏时间 (FCP): ${t.startTime+t.duration}ms`,extra:{value:t.startTime+t.duration},value:t.startTime+t.duration})),"largest-contentful-paint"===t.entryType){const{renderTime:e,startTime:r,duration:o}=t.renderTime,n=e||r+o;n>2500&&s.push({type:d,message:`元素 ${t.element.outerHTML} LCP 时间${n}毫秒,超过了 2.5 秒`,extra:{element:t.element.outerHTML,value:n},value:n})}if("first-input"===t.entryType&&t.processingStart-t.startTime>100&&s.push({type:h,message:"主线程阻塞时长超过 100 毫秒,用户体验不佳",extra:{value:t.processingStart-t.startTime},value:t.processingStart-t.startTime}),"layout-shift"!==t.entryType||t.hadRecentInput||t.value>.1&&s.push({type:m,message:"CLS 值超过 0.1,用户体验不佳",extra:{value:t.value},value:t.value}),"navigation"===t.entryType){s.push({type:u,message:`加载完成时间 (Load): ${t.loadEventEnd}ms`,value:t.loadEventEnd,extra:{value:t.loadEventEnd}});const r=t.domainLookupEnd-t.domainLookupStart,o=t.responseEnd-t.startTime;o>e.longTimeRequestThreshold&&s.push({type:y,message:`页面${t.name}请求耗时 ${o} 毫秒,超过了 ${e.longTimeRequestThreshold} 毫秒`,extra:{dnsTime:r,value:o,resourceName:t.name},value:o})}if("resource"===t.entryType){const r=t.domainLookupEnd-t.domainLookupStart,o=t.duration;o>e.longTimeRequestThreshold&&s.push({type:f,message:`资源${t.name}请求耗时 ${o} 毫秒,超过了 ${e.longTimeRequestThreshold} 毫秒`,extra:{dnsTime:r,value:o,resourceName:t.name},value:o})}if("longtask"===t.entryType){const e=t.duration-50>0?t.duration-50:0;e>50&&s.push({type:g,message:`检测到长任务,总耗时: ${t.duration} ms, 阻塞时间约: ${e} ms,大于建议的50ms内,请使用控制台performance(性能)进行性能分析`,extra:{duration:t.duration,blockingTime:e,resourceName:t.name},value:e})}}),s}(e,r);return"function"==typeof t&&t(s),s});r.observe({entryTypes:["paint","first-contentful-paint","largest-contentful-paint","first-input","layout-shift","interaction-to-next-paint","navigation","resource","longtask"],buffered:!0})}}function w(e){const t=[];return t.push(function(e){const t=history.pushState,r=history.replaceState;history.pushState=function(...r){const s=window.location.href,o=t.apply(this,r),n=window.location.href;return s!==n&&e({to:n,from:s}),o},history.replaceState=function(...t){const s=window.location.href,o=r.apply(this,t),n=window.location.href;return s!==n&&e({to:n,from:s}),o};const s=()=>{e({to:window.location.href,from:window.location.href})};return window.addEventListener("popstate",s),()=>{window.removeEventListener("popstate",s),history.pushState=t,history.replaceState=r}}(e)),()=>{t.forEach(e=>e())}}const R={reportUrl:"",appId:"",appVersion:"",appUrl:window.location.origin,appEnv:"prod",userAgent:window.navigator.userAgent,reportPath:"",reportError:!0,reportPromiseReject:!0,reportVueError:!0,vueInstance:null,reportPerformance:!0,listenPerformance:!0,longTimeRequestThreshold:2e3,reportErrorReqest:!0,errorRequestFunc:null,reportUserNavigate:!0,appSecretKey:""};class S{records=[];timer=null;options={};constructor(e){this.options={...R,...e}}init(){const e=localStorage.getItem("report-store-date"),u=`${(c=new Date).getFullYear()}-${c.getMonth()+1}-${c.getDate()}`;var c;e!==u&&(localStorage.setItem("report-store-date",u),localStorage.setItem("report-store-list","[]"));const l=JSON.parse(localStorage.getItem("report-store-list"));var d,m,h,g;if(this.options.reportError&&(d=e=>{l.includes(e.message)||(this.addReportRecord({reportType:t,...e}),l.push(e.message),localStorage.setItem("report-store-list",JSON.stringify(l)))},window.addEventListener("error",function(e){if(e.target){if(e.target&&("IMG"===e.target.tagName||"SCRIPT"===e.target.tagName||"LINK"===e.target.tagName||"VIDEO"===e.target.tagName||"AUDIO"===e.target.tagName||"SOURCE"===e.target.tagName)){const t=e.target,r={tagName:t.tagName,src:t.src||t.href,outerHTML:t.outerHTML,time:(new Date).toISOString()};e.preventDefault(),d&&d({type:n,message:`资源加载失败,${r.tagName}: ${r.src}`,extra:r})}}else{const t={message:e.error.stack,filename:e.filename,line:e.lineno,col:e.colno};e.preventDefault(),d&&d({type:o,message:t.message,extra:t})}},!0)),this.options.reportPromiseReject&&function(e){window.addEventListener("unhandledrejection",function(t){e&&e(t)})}(e=>{l.includes(e.reason?.stack)||(this.addReportRecord({reportType:t,type:a,message:e.reason?.stack}),l.push(e.reason?.stack),localStorage.setItem("report-store-list",JSON.stringify(l)))}),this.options.reportVueError&&this.options.vueInstance&&function(e,t){e.config.errorHandler=function(e,r,s){t&&t({err:e,vm:r,info:s})}}(this.options.vueInstance,({err:e})=>{l.includes(e.message)||(this.addReportRecord({reportType:t,type:i,message:e.message}),l.push(e.message),localStorage.setItem("report-store-list",JSON.stringify(l)))}),this.options.listenPerformance&&$(this.options,e=>{e.forEach(e=>{e.extra.resourceName!==this.options.reportUrl&&this.addReportRecord({reportType:r,...e})})}),this.options.reportErrorReqest&&(m=e=>{const{status:r,url:s,method:o,statusText:n}=e,a=`请求报错接口:${s},方法:${o},状态码:${r},状态信息:${n}`;l.includes(a)||(this.addReportRecord({reportType:t,type:p,message:a}),l.push(a),localStorage.setItem("report-store-list",JSON.stringify(l)))},h=this.options,g=XMLHttpRequest.prototype.open,XMLHttpRequest.prototype.open=function(e,t,r,s,o){let n=Date.now();this.addEventListener("load",function(){const r=Date.now()-n;if(200===this.status){if("string"==typeof this.response)try{const s=JSON.parse(this.response);if("function"==typeof h.errorRequestFunc&&h.errorRequestFunc(s))return void(m&&m({message:`请求成功接口:${t},方法:${e},状态码:${this.status},状态信息:${this.statusText}, 请求耗时:${r}ms;服务器处理错误`,extra:{response:s,headers:this.getAllResponseHeaders()}}))}catch(e){}}else m&&m({message:`请求报错接口:${t},方法:${e},状态码:${this.status},状态信息:${this.statusText}`,extra:{headers:this.getAllResponseHeaders()}})}),this.addEventListener("error",function(){m&&m({message:`请求报错接口:${t},方法:${e},状态码:${this.status},状态信息:${this.statusText}`})}),g.call(this,e,t,r,s,o)},function(e,t,r){var s=window.fetch;window.fetch=function(e,o){let n=Date.now();return s(e,o).then(async s=>{const a=Date.now()-n,i=[...s.headers.entries()].map(e=>e.join(": ")).join("\n");if(200===s.status)try{const o=await s.json();if("function"==typeof r.errorRequestFunc&&r.errorRequestFunc(o))return void(t&&t({message:`请求成功接口:${e},方法:${method},状态码:${s.status},状态信息:${s.statusText}, 请求耗时:${a}ms\n服务器处理错误`,extra:{response:o,headers:i}}))}catch(e){}else t&&t({message:`请求报错接口:${e},方法:${o.method},状态码:${s.status},状态信息:${s.statusText}, 耗时:${a}ms`,extra:{headers:i}});return s}).catch(r=>{t&&t({message:`请求报错接口:${e},方法:${o.method},错误信息\n${r.stack}`})})}}(0,e=>{const{url:r,method:s,statusText:o}=e,n=`请求报错接口:${r},方法:${s},状态信息:${o}`;l.includes(n)||(this.addReportRecord({reportType:t,type:p,message:n}),l.push(n),localStorage.setItem("report-store-list",JSON.stringify(l)))},this.options)),this.options.reportUserNavigate){const e=window.location.href;this.addReportRecord({reportType:s,type:T,message:`请求地址:${e}`,extra:JSON.stringify({url:e})}),w(e=>{this.addReportRecord({reportType:s,type:v,message:`路由变化:${e.to}`,extra:JSON.stringify({route:e.to})})})}}addReportRecord(e){this.timer&&clearTimeout(this.timer),this.timer=setTimeout(()=>{try{this.sendReport(JSON.parse(JSON.stringify(this.records))),this.records=[]}catch(e){}},1e3),this.records.push({appId:this.options.appId,appUrl:this.options.appUrl,reportPath:window.location.pathname,userAgent:window.navigator.userAgent,...e,extra:e.extra&&"object"==typeof e.extra?JSON.stringify(e.extra):void 0})}sendReport(e){const t={appSecretKey:this.options.appSecretKey,appId:this.options.appId,data:e},r=new Blob([JSON.stringify(t)],{type:"application/json; charset=UTF-8"});navigator.sendBeacon(this.options.reportUrl,r)}}let x;function E(e={}){x||(x=new S(e)),x.init()}var N={init:E};return e.default=N,e.init=E,Object.defineProperty(e,"__esModule",{value:!0}),e}({});
|
package/bundle.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).reportSdk={})}(this,function(e){"use strict";const t="error",r="performance",s="user",o="main",n="resourceError",a="PromiseRejection",i="VueError",p="error",u="loaded",c="paint",l="first-contentful-paint",d="largest-contentful-paint",m="layout-shift",h="first-input",f="longTask",g="resource",y="pageResource",T="LocationChange",v="RouteChange";function $(e,t){if(window.PerformanceObserver){const r=new PerformanceObserver(function(r){const s=function(e,t){const r=t.getEntries(),s=[];return r.forEach(t=>{if("paint"===t.entryType&&("first-paint"===t.name&&s.push({type:c,message:`白屏时间 (FP): ${t.startTime+t.duration}ms`,extra:{value:t.startTime+t.duration},value:t.startTime+t.duration}),"first-contentful-paint"===t.name&&s.push({type:l,message:`首屏时间 (FCP): ${t.startTime+t.duration}ms`,extra:{value:t.startTime+t.duration},value:t.startTime+t.duration})),"largest-contentful-paint"===t.entryType){const{renderTime:e,startTime:r,duration:o}=t.renderTime,n=e||r+o;n>2500&&s.push({type:d,message:`元素 ${t.element.outerHTML} LCP 时间${n}毫秒,超过了 2.5 秒`,extra:{element:t.element.outerHTML,value:n},value:n})}if("first-input"===t.entryType&&t.processingStart-t.startTime>100&&s.push({type:h,message:"主线程阻塞时长超过 100 毫秒,用户体验不佳",extra:{value:t.processingStart-t.startTime},value:t.processingStart-t.startTime}),"layout-shift"!==t.entryType||t.hadRecentInput||t.value>.1&&s.push({type:m,message:"CLS 值超过 0.1,用户体验不佳",extra:{value:t.value},value:t.value}),"navigation"===t.entryType){s.push({type:u,message:`加载完成时间 (Load): ${t.loadEventEnd}ms`,value:t.loadEventEnd,extra:{value:t.loadEventEnd}});const r=t.domainLookupEnd-t.domainLookupStart,o=t.responseEnd-t.startTime;o>e.longTimeRequestThreshold&&s.push({type:y,message:`页面${t.name}请求耗时 ${o} 毫秒,超过了 ${e.longTimeRequestThreshold} 毫秒`,extra:{dnsTime:r,value:o,resourceName:t.name},value:o})}if("resource"===t.entryType){const r=t.domainLookupEnd-t.domainLookupStart,o=t.duration;o>e.longTimeRequestThreshold&&s.push({type:g,message:`资源${t.name}请求耗时 ${o} 毫秒,超过了 ${e.longTimeRequestThreshold} 毫秒`,extra:{dnsTime:r,value:o,resourceName:t.name},value:o})}if("longtask"===t.entryType){const e=t.duration-50>0?t.duration-50:0;e>50&&s.push({type:f,message:`检测到长任务,总耗时: ${t.duration} ms, 阻塞时间约: ${e} ms,大于建议的50ms内,请使用控制台performance(性能)进行性能分析`,extra:{duration:t.duration,blockingTime:e,resourceName:t.name},value:e})}}),s}(e,r);return"function"==typeof t&&t(s),s});r.observe({entryTypes:["paint","first-contentful-paint","largest-contentful-paint","first-input","layout-shift","interaction-to-next-paint","navigation","resource","longtask"],buffered:!0})}}function w(e){const t=[];return t.push(function(e){const t=history.pushState,r=history.replaceState;history.pushState=function(...r){const s=window.location.href,o=t.apply(this,r),n=window.location.href;return s!==n&&e({to:n,from:s}),o},history.replaceState=function(...t){const s=window.location.href,o=r.apply(this,t),n=window.location.href;return s!==n&&e({to:n,from:s}),o};const s=()=>{e({to:window.location.href,from:window.location.href})};return window.addEventListener("popstate",s),()=>{window.removeEventListener("popstate",s),history.pushState=t,history.replaceState=r}}(e)),()=>{t.forEach(e=>e())}}const R={reportUrl:"",appId:"",appVersion:"",appUrl:window.location.origin,appEnv:"prod",userAgent:window.navigator.userAgent,reportPath:"",reportError:!0,reportPromiseReject:!0,reportVueError:!0,vueInstance:null,reportPerformance:!0,listenPerformance:!0,longTimeRequestThreshold:2e3,reportErrorReqest:!0,errorRequestFunc:null,reportUserNavigate:!0,appSecretKey:""};class S{records=[];timer=null;options={};constructor(e){this.options={...R,...e}}init(){const e=localStorage.getItem("report-store-date"),u=`${(c=new Date).getFullYear()}-${c.getMonth()+1}-${c.getDate()}`;var c;e!==u&&(localStorage.setItem("report-store-date",u),localStorage.setItem("report-store-list","[]"));const l=JSON.parse(localStorage.getItem("report-store-list"));var d,m,h,f;if(this.options.reportError&&(d=e=>{l.includes(e.message)||(this.addReportRecord({reportType:t,...e}),l.push(e.message),localStorage.setItem("report-store-list",JSON.stringify(l)))},window.addEventListener("error",function(e){if(e.target){if(e.target&&("IMG"===e.target.tagName||"SCRIPT"===e.target.tagName||"LINK"===e.target.tagName||"VIDEO"===e.target.tagName||"AUDIO"===e.target.tagName||"SOURCE"===e.target.tagName)){const t=e.target,r={tagName:t.tagName,src:t.src||t.href,outerHTML:t.outerHTML,time:(new Date).toISOString()};e.preventDefault(),d&&d({type:n,message:`资源加载失败,${r.tagName}: ${r.src}`,extra:r})}}else{const t={message:e.error.stack,filename:e.filename,line:e.lineno,col:e.colno};e.preventDefault(),d&&d({type:o,message:t.message,extra:t})}},!0)),this.options.reportPromiseReject&&function(e){window.addEventListener("unhandledrejection",function(t){e&&e(t)})}(e=>{l.includes(e.reason?.stack)||(this.addReportRecord({reportType:t,type:a,message:e.reason?.stack}),l.push(e.reason?.stack),localStorage.setItem("report-store-list",JSON.stringify(l)))}),this.options.reportVueError&&this.options.vueInstance&&function(e,t){e.config.errorHandler=function(e,r,s){t&&t({err:e,vm:r,info:s})}}(this.options.vueInstance,({err:e})=>{l.includes(e.message)||(this.addReportRecord({reportType:t,type:i,message:e.message}),l.push(e.message),localStorage.setItem("report-store-list",JSON.stringify(l)))}),this.options.listenPerformance&&$(this.options,e=>{e.forEach(e=>{e.extra.resourceName!==this.options.reportUrl&&this.addReportRecord({reportType:r,...e})})}),this.options.reportErrorReqest&&(m=e=>{const{status:r,url:s,method:o,statusText:n}=e,a=`请求报错接口:${s},方法:${o},状态码:${r},状态信息:${n}`;l.includes(a)||(this.addReportRecord({reportType:t,type:p,message:a}),l.push(a),localStorage.setItem("report-store-list",JSON.stringify(l)))},h=this.options,f=XMLHttpRequest.prototype.open,XMLHttpRequest.prototype.open=function(e,t,r,s,o){let n=Date.now();this.addEventListener("load",function(){const r=Date.now()-n;if(200===this.status){if("string"==typeof this.response)try{const s=JSON.parse(this.response);if("function"==typeof h.errorRequestFunc&&h.errorRequestFunc(s))return void(m&&m({message:`请求成功接口:${t},方法:${e},状态码:${this.status},状态信息:${this.statusText}, 请求耗时:${r}ms;服务器处理错误`,extra:{response:s,headers:this.getAllResponseHeaders()}}))}catch(e){}}else m&&m({message:`请求报错接口:${t},方法:${e},状态码:${this.status},状态信息:${this.statusText}`,extra:{headers:this.getAllResponseHeaders()}})}),this.addEventListener("error",function(){m&&m({message:`请求报错接口:${t},方法:${e},状态码:${this.status},状态信息:${this.statusText}`})}),f.call(this,e,t,r,s,o)},function(e,t,r){var s=window.fetch;window.fetch=function(e,o){let n=Date.now();return s(e,o).then(async s=>{const a=Date.now()-n,i=[...s.headers.entries()].map(e=>e.join(": ")).join("\n");if(200===s.status)try{const o=await s.json();if("function"==typeof r.errorRequestFunc&&r.errorRequestFunc(o))return void(t&&t({message:`请求成功接口:${e},方法:${method},状态码:${s.status},状态信息:${s.statusText}, 请求耗时:${a}ms\n服务器处理错误`,extra:{response:o,headers:i}}))}catch(e){}else t&&t({message:`请求报错接口:${e},方法:${o.method},状态码:${s.status},状态信息:${s.statusText}, 耗时:${a}ms`,extra:{headers:i}});return s}).catch(r=>{t&&t({message:`请求报错接口:${e},方法:${o.method},错误信息\n${r.stack}`})})}}(0,e=>{const{url:r,method:s,statusText:o}=e,n=`请求报错接口:${r},方法:${s},状态信息:${o}`;l.includes(n)||(this.addReportRecord({reportType:t,type:p,message:n}),l.push(n),localStorage.setItem("report-store-list",JSON.stringify(l)))},this.options)),this.options.reportUserNavigate){const e=window.location.href;this.addReportRecord({reportType:s,type:T,message:`请求地址:${e}`,extra:JSON.stringify({url:e})}),w(e=>{this.addReportRecord({reportType:s,type:v,message:`路由变化:${e.to}`,extra:JSON.stringify({route:e.to})})})}}addReportRecord(e){this.timer&&clearTimeout(this.timer),this.timer=setTimeout(()=>{try{this.sendReport(JSON.parse(JSON.stringify(this.records))),this.records=[]}catch(e){}},1e3),this.records.push({appId:this.options.appId,appUrl:this.options.appUrl,reportPath:window.location.pathname,userAgent:window.navigator.userAgent,...e,extra:e.extra&&"object"==typeof e.extra?JSON.stringify(e.extra):void 0})}sendReport(e){const t={appSecretKey:this.options.appSecretKey,appId:this.options.appId,data:e},r=new Blob([JSON.stringify(t)],{type:"application/json; charset=UTF-8"});navigator.sendBeacon(this.options.reportUrl,r)}}let x;function E(e={}){x||(x=new S(e)),x.init()}var N={init:E};e.default=N,e.init=E,Object.defineProperty(e,"__esModule",{value:!0})});
|
package/package.json
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keeson-web-report-sdk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "for web/h5",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"module": "src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"require": "./src/index.js",
|
|
10
|
+
"import": "./src/index.js",
|
|
11
|
+
"browser": "./src/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
6
14
|
"scripts": {
|
|
7
15
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
8
16
|
"build": "rollup --config rollup.config.js"
|
|
@@ -16,12 +24,15 @@
|
|
|
16
24
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
17
25
|
"@rollup/plugin-terser": "^0.4.4",
|
|
18
26
|
"core-js": "^3.47.0",
|
|
27
|
+
"regenerator-runtime": "^0.14.1",
|
|
19
28
|
"rollup": "^4.55.1"
|
|
20
29
|
},
|
|
21
30
|
"files": [
|
|
22
|
-
"bundle.
|
|
31
|
+
"bundle.cjs.js",
|
|
32
|
+
"bundle.esm.js",
|
|
33
|
+
"bundle.min.js",
|
|
34
|
+
"bundle.iife.min.js"
|
|
23
35
|
],
|
|
24
36
|
"dependencies": {
|
|
25
|
-
"regenerator-runtime": "^0.14.1"
|
|
26
37
|
}
|
|
27
38
|
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import * as reportError from "./report-error";
|
|
2
|
+
import * as reportPerformance from "./report-performance";
|
|
3
|
+
import * as reportRequest from "./report-request";
|
|
4
|
+
import * as reportUser from "./report-user";
|
|
5
|
+
import {
|
|
6
|
+
REPORT_TYPE,
|
|
7
|
+
ERROR_TYPE,
|
|
8
|
+
REQUEST_ERROR_TYPE,
|
|
9
|
+
USER_TYPE,
|
|
10
|
+
} from "./consts";
|
|
11
|
+
function formatDate(date) {
|
|
12
|
+
const year = date.getFullYear();
|
|
13
|
+
const month = date.getMonth() + 1;
|
|
14
|
+
const day = date.getDate();
|
|
15
|
+
return `${year}-${month}-${day}`;
|
|
16
|
+
}
|
|
17
|
+
const defaultOptions = {
|
|
18
|
+
reportUrl: "", // 上报请求地址
|
|
19
|
+
|
|
20
|
+
// app基本信息
|
|
21
|
+
appId: "", // appId
|
|
22
|
+
appVersion: "",
|
|
23
|
+
appUrl: window.location.origin, // 站点地址, 对应应用配置网站站点地址
|
|
24
|
+
appEnv: "prod",
|
|
25
|
+
userAgent: window.navigator.userAgent,
|
|
26
|
+
|
|
27
|
+
reportPath: "", // 上报页面路径
|
|
28
|
+
|
|
29
|
+
// 上报记录
|
|
30
|
+
reportError: true,
|
|
31
|
+
reportPromiseReject: true,
|
|
32
|
+
reportVueError: true,
|
|
33
|
+
vueInstance: null,
|
|
34
|
+
|
|
35
|
+
reportPerformance: true, // 是否上报性能数据
|
|
36
|
+
listenPerformance: true, // 是否监听性能数据
|
|
37
|
+
|
|
38
|
+
longTimeRequestThreshold: 2000, // 长请求时间阈值,单位毫秒
|
|
39
|
+
reportErrorReqest: true, // 是否上报报错请求
|
|
40
|
+
errorRequestFunc: null, // 错误请求过滤函数
|
|
41
|
+
// response => {
|
|
42
|
+
// return response.data.code !== 0
|
|
43
|
+
// },
|
|
44
|
+
|
|
45
|
+
reportUserNavigate: true, // 是否上报用户导航行为
|
|
46
|
+
|
|
47
|
+
// repeatRequestTime: 2000, // todo: 2s调用同一接口,同一参数,上报
|
|
48
|
+
appSecretKey: "", // 应用密钥
|
|
49
|
+
// 级别:错误、性能信息、用户导航行为、重复请求
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
class Report {
|
|
53
|
+
records = [];
|
|
54
|
+
timer = null;
|
|
55
|
+
options = {};
|
|
56
|
+
constructor(options) {
|
|
57
|
+
this.options = { ...defaultOptions, ...options };
|
|
58
|
+
}
|
|
59
|
+
init() {
|
|
60
|
+
const storeDate = localStorage.getItem("report-store-date");
|
|
61
|
+
|
|
62
|
+
const today = formatDate(new Date());
|
|
63
|
+
if (storeDate !== today) {
|
|
64
|
+
localStorage.setItem("report-store-date", today);
|
|
65
|
+
localStorage.setItem("report-store-list", "[]");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const reportStoreList = JSON.parse(localStorage.getItem("report-store-list"));
|
|
69
|
+
|
|
70
|
+
// type 类型 (string) Required
|
|
71
|
+
// errorType 错误类型 (string)
|
|
72
|
+
// performanceType 性能上报指标 (string)
|
|
73
|
+
// requestType 请求上报指标 (string)
|
|
74
|
+
// navigationType 导航上报指标 (string)
|
|
75
|
+
// message 上报信息 (string)
|
|
76
|
+
// extra 额外信息 (string)
|
|
77
|
+
// originData 原始数据 (string)
|
|
78
|
+
|
|
79
|
+
// todo: 重复的上报太多,需要过滤
|
|
80
|
+
|
|
81
|
+
// listen and callback
|
|
82
|
+
if (this.options.reportError) {
|
|
83
|
+
// 错误上报
|
|
84
|
+
// 包含:资源加载错误,全局代码错误
|
|
85
|
+
reportError.globalErrorHandler((res) => {
|
|
86
|
+
if(reportStoreList.includes(res.message)) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
this.addReportRecord({
|
|
90
|
+
reportType: REPORT_TYPE.ERROR,
|
|
91
|
+
...res,
|
|
92
|
+
});
|
|
93
|
+
reportStoreList.push(res.message);
|
|
94
|
+
localStorage.setItem("report-store-list", JSON.stringify(reportStoreList));
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (this.options.reportPromiseReject) {
|
|
98
|
+
// 未处理的 Promise 错误上报
|
|
99
|
+
reportError.promiseErrorHandler((event) => {
|
|
100
|
+
if(reportStoreList.includes(event.reason?.stack)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
this.addReportRecord({
|
|
104
|
+
reportType: REPORT_TYPE.ERROR,
|
|
105
|
+
type: ERROR_TYPE.PROMISE_REJECTION,
|
|
106
|
+
message: event.reason?.stack,
|
|
107
|
+
});
|
|
108
|
+
reportStoreList.push(event.reason?.stack);
|
|
109
|
+
localStorage.setItem("report-store-list", JSON.stringify(reportStoreList));
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
if (this.options.reportVueError && this.options.vueInstance) {
|
|
113
|
+
// vue错误上报
|
|
114
|
+
reportError.vueErrorHandler(this.options.vueInstance, ({ err }) => {
|
|
115
|
+
if(reportStoreList.includes(err.message)) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
this.addReportRecord({
|
|
119
|
+
reportType: REPORT_TYPE.ERROR,
|
|
120
|
+
type: ERROR_TYPE.VUE_ERROR,
|
|
121
|
+
message: err.message,
|
|
122
|
+
});
|
|
123
|
+
reportStoreList.push(err.message);
|
|
124
|
+
localStorage.setItem("report-store-list", JSON.stringify(reportStoreList));
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// if (this.options.reportPerformance) {
|
|
129
|
+
// // 性能上报
|
|
130
|
+
// reportPerformance.reportPerformance(options, (performanceList) => {
|
|
131
|
+
// this.addReportRecord({
|
|
132
|
+
// reportType: REPORT_TYPE.PERFORMANCE,
|
|
133
|
+
// type: "performance",
|
|
134
|
+
// message: "",
|
|
135
|
+
// extra: JSON.stringify(performanceList),
|
|
136
|
+
// });
|
|
137
|
+
// });
|
|
138
|
+
// }
|
|
139
|
+
|
|
140
|
+
if (this.options.listenPerformance) {
|
|
141
|
+
console.log("加载性能:fp, fcp, lcp, tbt, cls,等");
|
|
142
|
+
console.log("资源加载性能:dns, tcp, ssl, firstByte, download,等");
|
|
143
|
+
console.log("接口调用时间,需要剔除上报接口!");
|
|
144
|
+
|
|
145
|
+
// reportPerformance.loadedTimestamp((item) => {
|
|
146
|
+
// this.addReportRecord({
|
|
147
|
+
// reportType: REPORT_TYPE.PERFORMANCE,
|
|
148
|
+
// ...item,
|
|
149
|
+
// });
|
|
150
|
+
// });
|
|
151
|
+
|
|
152
|
+
// console.log(this.options,)
|
|
153
|
+
reportPerformance.listenPerformance(this.options, (performanceList) => {
|
|
154
|
+
// 加载性能:fp, fcp, lcp, tbt, cls,等
|
|
155
|
+
// 资源加载性能:dns, tcp, ssl, firstByte, download,等
|
|
156
|
+
// console.log('=============["performanceList"]:',performanceList)
|
|
157
|
+
performanceList.forEach((item) => {
|
|
158
|
+
if (item.extra.resourceName !== this.options.reportUrl) {
|
|
159
|
+
this.addReportRecord({
|
|
160
|
+
reportType: REPORT_TYPE.PERFORMANCE,
|
|
161
|
+
...item,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
// this.addReportRecord({
|
|
166
|
+
// reportType: REPORT_TYPE.PERFORMANCE,
|
|
167
|
+
// type: "performance",
|
|
168
|
+
// message: "",
|
|
169
|
+
// extra: JSON.stringify(performanceList),
|
|
170
|
+
// });
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (this.options.reportErrorReqest) {
|
|
175
|
+
// 请求性能上报
|
|
176
|
+
reportRequest.xhrReportHandle(
|
|
177
|
+
null,
|
|
178
|
+
// (res) => {
|
|
179
|
+
// if (
|
|
180
|
+
// !!this.options.longTimeRequestThreshold &&
|
|
181
|
+
// res.duration > this.options.longTimeRequestThreshold
|
|
182
|
+
// ) {
|
|
183
|
+
// const { duration, url, method } = res;
|
|
184
|
+
// this.addReportRecord({
|
|
185
|
+
// reportType: REPORT_TYPE.REQUEST,
|
|
186
|
+
// type: REQUEST_ERROR_TYPE.LONG,
|
|
187
|
+
// message: `长请求时间接口:${url},方法:${method},耗时:${duration}ms`,
|
|
188
|
+
// extra: JSON.stringify({ duration }),
|
|
189
|
+
// });
|
|
190
|
+
// }
|
|
191
|
+
// },
|
|
192
|
+
(error) => {
|
|
193
|
+
const { status, url, method, statusText } = error;
|
|
194
|
+
const message = `请求报错接口:${url},方法:${method},状态码:${status},状态信息:${statusText}`
|
|
195
|
+
if(reportStoreList.includes(message)) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
this.addReportRecord({
|
|
199
|
+
reportType: REPORT_TYPE.ERROR,
|
|
200
|
+
type: REQUEST_ERROR_TYPE.ERROR,
|
|
201
|
+
message: message,
|
|
202
|
+
});
|
|
203
|
+
reportStoreList.push(message);
|
|
204
|
+
localStorage.setItem("report-store-list", JSON.stringify(reportStoreList));
|
|
205
|
+
},
|
|
206
|
+
this.options,
|
|
207
|
+
);
|
|
208
|
+
reportRequest.fetchReportHandle(
|
|
209
|
+
// (res) => {
|
|
210
|
+
// if (
|
|
211
|
+
// !!this.options.longTimeRequestThreshold &&
|
|
212
|
+
// res.duration > this.options.longTimeRequestThreshold
|
|
213
|
+
// ) {
|
|
214
|
+
// const { duration, url, method } = res;
|
|
215
|
+
// this.addReportRecord({
|
|
216
|
+
// reportType: REPORT_TYPE.REQUEST,
|
|
217
|
+
// type: REQUEST_ERROR_TYPE.LONG,
|
|
218
|
+
// message: `长请求时间接口:${url},方法:${method},耗时:${duration}ms`,
|
|
219
|
+
// extra: JSON.stringify({ duration }),
|
|
220
|
+
// });
|
|
221
|
+
// }
|
|
222
|
+
// },
|
|
223
|
+
null,
|
|
224
|
+
(error) => {
|
|
225
|
+
const { url, method, statusText } = error;
|
|
226
|
+
const message = `请求报错接口:${url},方法:${method},状态信息:${statusText}`
|
|
227
|
+
if(reportStoreList.includes(message)) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
this.addReportRecord({
|
|
231
|
+
reportType: REPORT_TYPE.ERROR,
|
|
232
|
+
type: REQUEST_ERROR_TYPE.ERROR,
|
|
233
|
+
message: message,
|
|
234
|
+
});
|
|
235
|
+
reportStoreList.push(message);
|
|
236
|
+
localStorage.setItem("report-store-list", JSON.stringify(reportStoreList));
|
|
237
|
+
},
|
|
238
|
+
this.options,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (this.options.reportUserNavigate) {
|
|
243
|
+
const href = window.location.href;
|
|
244
|
+
this.addReportRecord({
|
|
245
|
+
reportType: REPORT_TYPE.USER,
|
|
246
|
+
type: USER_TYPE.LOCATION_CHANGE,
|
|
247
|
+
message: `请求地址:${href}`,
|
|
248
|
+
extra: JSON.stringify({
|
|
249
|
+
url: href,
|
|
250
|
+
}),
|
|
251
|
+
});
|
|
252
|
+
reportUser.initRouteListener((record) => {
|
|
253
|
+
this.addReportRecord({
|
|
254
|
+
reportType: REPORT_TYPE.USER,
|
|
255
|
+
type: USER_TYPE.ROUTE_CHANGE,
|
|
256
|
+
message: `路由变化:${record.to}`,
|
|
257
|
+
extra: JSON.stringify({
|
|
258
|
+
route: record.to,
|
|
259
|
+
}),
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
addReportRecord(record) {
|
|
265
|
+
this.timer && clearTimeout(this.timer);
|
|
266
|
+
this.timer = setTimeout(() => {
|
|
267
|
+
try {
|
|
268
|
+
this.sendReport(JSON.parse(JSON.stringify(this.records)));
|
|
269
|
+
this.records = [];
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.log(error);
|
|
272
|
+
}
|
|
273
|
+
}, 1000);
|
|
274
|
+
|
|
275
|
+
this.records.push({
|
|
276
|
+
appId: this.options.appId,
|
|
277
|
+
appUrl: this.options.appUrl,
|
|
278
|
+
reportPath: window.location.pathname,
|
|
279
|
+
userAgent: window.navigator.userAgent,
|
|
280
|
+
...record,
|
|
281
|
+
extra:
|
|
282
|
+
record.extra && typeof record.extra === "object"
|
|
283
|
+
? JSON.stringify(record.extra)
|
|
284
|
+
: undefined,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
sendReport(records) {
|
|
288
|
+
const analyticsData = {
|
|
289
|
+
appSecretKey: this.options.appSecretKey,
|
|
290
|
+
appId: this.options.appId,
|
|
291
|
+
data: records,
|
|
292
|
+
};
|
|
293
|
+
const blob = new Blob(
|
|
294
|
+
[JSON.stringify(analyticsData)], // 将JavaScript对象转换为JSON字符串
|
|
295
|
+
{ type: "application/json; charset=UTF-8" }, // 在这里设置Content-Type
|
|
296
|
+
);
|
|
297
|
+
const success = navigator.sendBeacon(this.options.reportUrl, blob);
|
|
298
|
+
if (success) {
|
|
299
|
+
console.log("数据已成功加入发送队列。");
|
|
300
|
+
console.log(analyticsData);
|
|
301
|
+
} else {
|
|
302
|
+
console.log("数据入队失败,需要降级方案。");
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
let report;
|
|
308
|
+
export function init(options = {}) {
|
|
309
|
+
if (!report) report = new Report(options);
|
|
310
|
+
report.init();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// // 导出所有模块
|
|
314
|
+
// export {
|
|
315
|
+
// reportError,
|
|
316
|
+
// reportPerformance,
|
|
317
|
+
// reportRequest,
|
|
318
|
+
// reportUser,
|
|
319
|
+
// init
|
|
320
|
+
// };
|
|
321
|
+
export default {
|
|
322
|
+
init,
|
|
323
|
+
};
|