|
|
@@ -0,0 +1,286 @@
|
|
|
+// 内部监控平台SDK基础配置
|
|
|
+
|
|
|
+class MonitorSDK {
|
|
|
+ constructor(options = {}) {
|
|
|
+ this.config = {
|
|
|
+ // 基础配置
|
|
|
+ appId: options.appId || '',
|
|
|
+ apiUrl: options.apiUrl || '',
|
|
|
+ enableErrorTracking: options.enableErrorTracking !== false,
|
|
|
+ enablePerformanceTracking: options.enablePerformanceTracking !== false,
|
|
|
+ enableUserBehaviorTracking: options.enableUserBehaviorTracking !== false,
|
|
|
+ enableResourceTracking: options.enableResourceTracking !== false,
|
|
|
+ sampleRate: options.sampleRate || 1.0, // 采样率,默认100%
|
|
|
+ reportInterval: options.reportInterval || 5000, // 上报间隔,默认5秒
|
|
|
+ ...options
|
|
|
+ };
|
|
|
+
|
|
|
+ this.init();
|
|
|
+ }
|
|
|
+
|
|
|
+ init() {
|
|
|
+ console.log('MonitorSDK initialized');
|
|
|
+
|
|
|
+ // 初始化各种监控模块
|
|
|
+ if (this.config.enableErrorTracking) {
|
|
|
+ this.initErrorTracking();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.config.enablePerformanceTracking) {
|
|
|
+ this.initPerformanceTracking();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.config.enableUserBehaviorTracking) {
|
|
|
+ this.initUserBehaviorTracking();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.config.enableResourceTracking) {
|
|
|
+ this.initResourceTracking();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 定期上报数据
|
|
|
+ this.startReporting();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 错误监控
|
|
|
+ initErrorTracking() {
|
|
|
+ // 监控JavaScript错误
|
|
|
+ window.addEventListener('error', (event) => {
|
|
|
+ this.reportError({
|
|
|
+ type: 'javascript_error',
|
|
|
+ message: event.message,
|
|
|
+ filename: event.filename,
|
|
|
+ lineno: event.lineno,
|
|
|
+ colno: event.colno,
|
|
|
+ stack: event.error ? event.error.stack : null,
|
|
|
+ timestamp: Date.now()
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 监控未处理的Promise错误
|
|
|
+ window.addEventListener('unhandledrejection', (event) => {
|
|
|
+ this.reportError({
|
|
|
+ type: 'promise_rejection',
|
|
|
+ message: event.reason && event.reason.message ? event.reason.message : String(event.reason),
|
|
|
+ stack: event.reason && event.reason.stack ? event.reason.stack : null,
|
|
|
+ timestamp: Date.now()
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 性能监控
|
|
|
+ initPerformanceTracking() {
|
|
|
+ // 页面加载性能
|
|
|
+ if (performance && performance.timing) {
|
|
|
+ setTimeout(() => {
|
|
|
+ const perfData = {
|
|
|
+ type: 'performance',
|
|
|
+ navigationStart: performance.timing.navigationStart,
|
|
|
+ // DNS查询耗时
|
|
|
+ dnsTime: performance.timing.domainLookupEnd - performance.timing.domainLookupStart,
|
|
|
+ // TCP连接耗时
|
|
|
+ tcpTime: performance.timing.connectEnd - performance.timing.connectStart,
|
|
|
+ // 请求响应耗时
|
|
|
+ responseTime: performance.timing.responseEnd - performance.timing.requestStart,
|
|
|
+ // DOM解析耗时
|
|
|
+ domParseTime: performance.timing.domComplete - performance.timing.domLoading,
|
|
|
+ // DOM内容加载完成耗时
|
|
|
+ domContentLoadedTime: performance.timing.domContentLoadedEventEnd - performance.timing.navigationStart,
|
|
|
+ // 页面加载完成总耗时
|
|
|
+ loadCompleteTime: performance.timing.loadEventEnd - performance.timing.navigationStart,
|
|
|
+ timestamp: Date.now()
|
|
|
+ };
|
|
|
+ this.reportData(perfData);
|
|
|
+ }, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 监控Web Vitals
|
|
|
+ this.measureWebVitals();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 用户行为监控
|
|
|
+ initUserBehaviorTracking() {
|
|
|
+ // 监控页面点击事件
|
|
|
+ document.addEventListener('click', (event) => {
|
|
|
+ const target = event.target;
|
|
|
+ this.reportData({
|
|
|
+ type: 'user_behavior',
|
|
|
+ action: 'click',
|
|
|
+ element: target.tagName,
|
|
|
+ elementId: target.id,
|
|
|
+ elementClass: target.className,
|
|
|
+ pageUrl: window.location.href,
|
|
|
+ timestamp: Date.now()
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ // 监控页面停留时间
|
|
|
+ let pageVisibleTime = Date.now();
|
|
|
+ document.addEventListener('visibilitychange', () => {
|
|
|
+ if (document.visibilityState === 'hidden') {
|
|
|
+ this.reportData({
|
|
|
+ type: 'page_stay',
|
|
|
+ duration: Date.now() - pageVisibleTime,
|
|
|
+ pageUrl: window.location.href,
|
|
|
+ timestamp: Date.now()
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ pageVisibleTime = Date.now();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 资源加载监控
|
|
|
+ initResourceTracking() {
|
|
|
+ if (performance && performance.getEntriesByType) {
|
|
|
+ // 监控资源加载
|
|
|
+ const resources = performance.getEntriesByType('resource');
|
|
|
+ resources.forEach(resource => {
|
|
|
+ this.reportData({
|
|
|
+ type: 'resource_load',
|
|
|
+ name: resource.name,
|
|
|
+ duration: resource.duration,
|
|
|
+ entryType: resource.entryType,
|
|
|
+ timestamp: Date.now()
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 上报错误
|
|
|
+ reportError(errorData) {
|
|
|
+ // 检查采样率
|
|
|
+ if (Math.random() > this.config.sampleRate) {
|
|
|
+ return; // 根据采样率决定是否上报
|
|
|
+ }
|
|
|
+
|
|
|
+ this.reportData({
|
|
|
+ type: 'error',
|
|
|
+ ...errorData
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 上报数据
|
|
|
+ reportData(data) {
|
|
|
+ // 检查采样率
|
|
|
+ if (Math.random() > this.config.sampleRate) {
|
|
|
+ return; // 根据采样率决定是否上报
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加通用信息
|
|
|
+ data.sessionId = this.getSessionId();
|
|
|
+ data.userAgent = navigator.userAgent;
|
|
|
+ data.url = window.location.href;
|
|
|
+ data.timestamp = Date.now();
|
|
|
+
|
|
|
+ // 发送到服务端
|
|
|
+ if (this.config.apiUrl) {
|
|
|
+ try {
|
|
|
+ // 使用navigator.sendBeacon发送数据,即使页面关闭也能发送
|
|
|
+ if (navigator.sendBeacon) {
|
|
|
+ navigator.sendBeacon(
|
|
|
+ this.config.apiUrl,
|
|
|
+ JSON.stringify(data)
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ // 备用方案:使用fetch
|
|
|
+ fetch(this.config.apiUrl, {
|
|
|
+ method: 'POST',
|
|
|
+ body: JSON.stringify(data),
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
+ }
|
|
|
+ }).catch(err => {
|
|
|
+ console.error('MonitorSDK: Failed to send data:', err);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('MonitorSDK: Error sending data:', error);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 开发模式下打印到控制台
|
|
|
+ console.log('MonitorSDK: Data to send:', data);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 开始定期上报
|
|
|
+ startReporting() {
|
|
|
+ setInterval(() => {
|
|
|
+ // 这里可以实现批量上报逻辑
|
|
|
+ }, this.config.reportInterval);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取会话ID
|
|
|
+ getSessionId() {
|
|
|
+ if (!this.sessionId) {
|
|
|
+ this.sessionId = 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
|
|
+ }
|
|
|
+ return this.sessionId;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 测量Web Vitals
|
|
|
+ measureWebVitals() {
|
|
|
+ // 这里可以集成web-vitals库来测量核心Web指标
|
|
|
+ // 如CLS (Cumulative Layout Shift), FID (First Input Delay), LCP (Largest Contentful Paint)
|
|
|
+ if ('PerformanceObserver' in window) {
|
|
|
+ // LCP (Largest Contentful Paint)
|
|
|
+ new PerformanceObserver((entryList) => {
|
|
|
+ const entries = entryList.getEntries();
|
|
|
+ const lastEntry = entries[entries.length - 1];
|
|
|
+ if (lastEntry) {
|
|
|
+ this.reportData({
|
|
|
+ type: 'web_vitals',
|
|
|
+ metric: 'LCP',
|
|
|
+ value: lastEntry.startTime,
|
|
|
+ timestamp: Date.now()
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }).observe({ entryTypes: ['largest-contentful-paint'] });
|
|
|
+
|
|
|
+ // FID (First Input Delay)
|
|
|
+ new PerformanceObserver((entryList) => {
|
|
|
+ const firstInput = entryList.getEntries()[0];
|
|
|
+ if (firstInput) {
|
|
|
+ const fid = firstInput.processingStart - firstInput.startTime;
|
|
|
+ this.reportData({
|
|
|
+ type: 'web_vitals',
|
|
|
+ metric: 'FID',
|
|
|
+ value: fid,
|
|
|
+ timestamp: Date.now()
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }).observe({ entryTypes: ['first-input'] });
|
|
|
+
|
|
|
+ // CLS (Cumulative Layout Shift)
|
|
|
+ let clsValue = 0;
|
|
|
+ new PerformanceObserver((entryList) => {
|
|
|
+ for (const entry of entryList.getEntries()) {
|
|
|
+ if (!entry.hadRecentInput) {
|
|
|
+ clsValue += entry.value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.reportData({
|
|
|
+ type: 'web_vitals',
|
|
|
+ metric: 'CLS',
|
|
|
+ value: clsValue,
|
|
|
+ timestamp: Date.now()
|
|
|
+ });
|
|
|
+ }).observe({ entryTypes: ['layout-shift'] });
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 初始化监控SDK
|
|
|
+const monitor = new MonitorSDK({
|
|
|
+ appId: 'monitor-app',
|
|
|
+ apiUrl: '/api/monitor', // 这里应该是实际的API地址
|
|
|
+ enableErrorTracking: true,
|
|
|
+ enablePerformanceTracking: true,
|
|
|
+ enableUserBehaviorTracking: true,
|
|
|
+ enableResourceTracking: true,
|
|
|
+ sampleRate: 1.0
|
|
|
+});
|
|
|
+
|
|
|
+// 将monitor实例暴露到全局,方便调用
|
|
|
+window.MonitorSDK = MonitorSDK;
|
|
|
+window.monitorInstance = monitor;
|