keeson-web-report-sdk 1.1.2 → 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 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(o,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((o="undefined"!=typeof globalThis?globalThis:o||self).reportSdk={})}(this,function(o){"use strict";function e(o,e,t){return e&&function(o,e){for(var t=0;t<e.length;t++){var n=e[t];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(o,i(n.key),n)}}(o.prototype,e),Object.defineProperty(o,"prototype",{writable:!1}),o}function t(o,e,t){return(e=i(e))in o?Object.defineProperty(o,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):o[e]=t,o}function n(o,e){var t=Object.keys(o);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(o);e&&(n=n.filter(function(e){return Object.getOwnPropertyDescriptor(o,e).enumerable})),t.push.apply(t,n)}return t}function r(o){for(var e=1;e<arguments.length;e++){var r=null!=arguments[e]?arguments[e]:{};e%2?n(Object(r),!0).forEach(function(e){t(o,e,r[e])}):Object.getOwnPropertyDescriptors?Object.defineProperties(o,Object.getOwnPropertyDescriptors(r)):n(Object(r)).forEach(function(e){Object.defineProperty(o,e,Object.getOwnPropertyDescriptor(r,e))})}return o}function i(o){var e=function(o,e){if("object"!=typeof o||!o)return o;var t=o[Symbol.toPrimitive];if(void 0!==t){var n=t.call(o,e);if("object"!=typeof n)return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(o)}(o,"string");return"symbol"==typeof e?e:e+""}function s(o){var e=o.getEntries();console.log(e),e.forEach(function(o){if("paint"===o.entryType&&(console.log("-------------------------FP/FCP-------------------------"),console.log(o),"first-paint"===o.name&&console.log("白屏时间 (FP):",o.startTime,"毫秒"),"first-contentful-paint"===o.name&&console.log("首屏时间 (FCP):",o.startTime,"毫秒"),console.log("-------------------------FP/FCP-------------------------")),"largest-contentful-paint"===o.entryType&&(console.log("-------------------------lcp-------------------------"),console.log("LCP:",o.startTime,"ms"),console.log("-------------------------lcp-------------------------")),"first-input"===o.entryType&&(console.log("-------------------------fid-------------------------"),console.log("FID:",o.processingStart-o.startTime,"ms"),console.log("-------------------------fid-------------------------")),"layout-shift"!==o.entryType||o.hadRecentInput||(console.log("-------------------------cls-------------------------"),console.log("CLS:",o.value),console.log("is smaller than 0.1:",o.value<=.1),console.log("-------------------------cls-------------------------")),"navigation"===o.entryType){console.log("-------------------------navigation-------------------------");var e=o.domainLookupEnd-o.domainLookupStart;console.log("页面dnsTime",e);var t=o.responseEnd-o.startTime;console.log("页面请求耗时",t),console.log("-------------------------navigation-------------------------")}if("resource"===o.entryType){console.log("-------------------------资源请求-------------------------"),console.log("resource",o,"resource");var n=o.domainLookupEnd-o.domainLookupStart,r=o.responseEnd-o.requestStart,i=o.duration;console.log("dnsTime",n,"serverTime",r,"totalTime",i),console.log("-------------------------资源请求-------------------------")}if("longtask"===o.entryType){console.log("-------------------------长任务-------------------------"),console.log("longtask",o,"longtask");var s=o.duration-50;console.log("检测到长任务,总耗时: ".concat(o.duration," ms, 阻塞时间约: ").concat(s>0?s:0," ms"),o),console.log("-------------------------长任务-------------------------")}})}function l(o){var e=[];return e.push(function(o){var e=history.pushState,t=history.replaceState;history.pushState=function(){for(var t=window.location.href,n=arguments.length,r=new Array(n),i=0;i<n;i++)r[i]=arguments[i];var s=e.apply(this,r),l=window.location.href;return t!==l&&o({to:l,from:t}),s},history.replaceState=function(){for(var e=window.location.href,n=arguments.length,r=new Array(n),i=0;i<n;i++)r[i]=arguments[i];var s=t.apply(this,r),l=window.location.href;return e!==l&&o({to:l,from:e}),s};var n=function(){o({to:window.location.href,from:window.location.href})};return window.addEventListener("popstate",n),function(){window.removeEventListener("popstate",n),history.pushState=e,history.replaceState=t}}(o)),function(){e.forEach(function(o){return o()})}}var c,a={appId:"",reportUrl:"",env:"production",appUrl:window.location.origin,reportError:!0,reportPromiseReject:!0,reportVueError:!0,vueInstance:null,reportPerformance:!0,reportLongTimeRequest:!0,longTimeRequestThreshold:2e3,reportErrorReqest:!0,reportUserNavigate:!0},u=function(){return e(function o(e){!function(o,e){if(!(o instanceof e))throw new TypeError("Cannot call a class as a function")}(this,o),t(this,"records",[]),t(this,"timer",null),t(this,"options",{}),this.options=r(r({},a),e)},[{key:"init",value:function(){var o,e,t,n;if(this.options.reportError&&(o=function(o){var e=o.msg,t=o.url,n=o.line,r=o.col,i=o.error;console.log({msg:e,url:t,line:n,col:r,error:i})},window.onerror=function(e,t,n,r,i){o&&o({msg:e,url:t,line:n,col:r,error:i})}),this.options.reportPromiseReject&&function(o){window.addEventListener("unhandledrejection",function(e){o&&o(e)})}(function(o){console.log("Unhandled Promise Rejection:",o,o.reason)}),this.options.reportVueError&&this.options.vueInstance&&function(o,e){o.config.errorHandler=function(o,t,n){console.error("Error in ".concat(n,":"),o),e&&e({err:o,vm:t,info:n})}}(this.options.vueInstance,function(o){var e=o.err,t=o.vm,n=o.info;console.log("Vue error:",e,t,n)}),this.options.reportPerformance&&(window.onload=function(){if(console.log("window.onload"),window.PerformanceObserver){var o=new PerformanceObserver(s);console.log("supportedEntryTypes",PerformanceObserver.supportedEntryTypes),o.observe({entryTypes:["paint","largest-contentful-paint","first-input","layout-shift","navigation","resource","longtask"],buffered:!0})}window.performance&&(console.log("-----------------------------------"),s(window.performance),console.log("-----------------------------------"))}),this.options.reportLongTimeRequest&&(e=function(o){console.log(o)},t=function(o){console.log(o)},n=XMLHttpRequest.prototype.open,XMLHttpRequest.prototype.open=function(o,r,i,s,l){var c=Date.now();this.addEventListener("load",function(){var t=Date.now();e&&e({url:r,method:o,duration:t-c,status:this.status,statusText:this.statusText,response:this.responseText}),console.log("Response:",this.responseText)}),this.addEventListener("error",function(){console.log("Error:",this.statusText),t&&t({url:r,method:o,duration:duration,status:this.status,statusText:this.statusText,response:this.responseText})}),console.log("Request URL:",r),console.log("Request Method:",o),n.call(this,o,r,i,s,l)},function(o,e){var t=window.fetch;window.fetch=function(n,r){console.log("Fetch URL:",n),console.log("Fetch Options:",r),r&&r.headers&&(r.headers["X-Custom-Header"]="Value");var i=Date.now();return t(n,r).then(function(e){var t=Date.now();return o&&o({url:n,method:method,duration:t-i,status:e.status,statusText:e.statusText,response:e.responseText}),console.log("Response:",e),e}).catch(function(o){console.log("Error:",o),e&&e({url:n,method:method,duration:duration})})}}(function(o){console.log(o)},function(o){console.log(o)})),this.options.reportUserNavigate){var r=window.location.href;console.log("current href:",r),l(function(o){console.log("route change:",o)})}}},{key:"addReportRecord",value:function(o){var e=this;this.timer&&clearTimeout(this.timer),this.timer=setTimeout(function(){try{e.sendReport(JSON.parse(JSON.stringify(e.records))),e.records=[]}catch(o){console.log(o)}},1e3),this.records.push(o)}},{key:"sendReport",value:function(o){navigator.sendBeacon(this.options.reportUrl,JSON.stringify(o))}}])}();o.init=function(){c||(c=new u(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{})),c.init()}});
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.2",
3
+ "version": "1.1.5",
4
4
  "description": "for web/h5",
5
- "main": "bundle.min.js",
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"
@@ -20,7 +28,10 @@
20
28
  "rollup": "^4.55.1"
21
29
  },
22
30
  "files": [
23
- "bundle.min.js"
31
+ "bundle.cjs.js",
32
+ "bundle.esm.js",
33
+ "bundle.min.js",
34
+ "bundle.iife.min.js"
24
35
  ],
25
36
  "dependencies": {
26
37
  }
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
+ };