UNPKG

memory-monitor-sdk

Version:

A powerful JavaScript memory monitoring SDK for Web and mobile applications. Features real-time monitoring, data visualization, automatic reporting, and memory leak detection.

28 lines (25 loc) 13.4 kB
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class M{constructor(){this.memoryLogs=new Map,this.intervalId=null,this.lastMemoryMB=0,this.peakThresholdMB=20,this.appStartTime=Date.now(),this.currentPage="unknown",this.pageHistory=new Map,this.memoryPageLogs=new Map,this.SIMULATED_LIMIT_MB=300,this.displayElement=null,this.isDisplayVisible=!1,this.shouldShowDisplay=!1}static getInstance(){return M.instance||(M.instance=new M),M.instance}startMonitoring(e=2e3,t=300,o=20,s=!0){this.intervalId&&clearInterval(this.intervalId),this.SIMULATED_LIMIT_MB=t,this.peakThresholdMB=o,this.appStartTime=Date.now(),this.shouldShowDisplay=s&&process.env.NODE_ENV==="development",this.intervalId=setInterval(()=>{this.logMemoryUsage()},e),console.log(`🔍 内存监控已启动(采样间隔: ${e}ms,模拟上限: ${this.SIMULATED_LIMIT_MB}MB,峰值阈值: ${o}MB)`),this.shouldShowDisplay&&console.log("📱 DOM面板将在首次采集到内存数据时创建")}setCurrentPage(e){if(this.currentPage=e,this.pageHistory.set(Date.now(),e),this.pageHistory.size>100){const t=Math.min(...this.pageHistory.keys());this.pageHistory.delete(t)}}getAppRuntimeMinutes(){return Math.round((Date.now()-this.appStartTime)/1e3/60)}getAppRuntimeSeconds(){return Math.round((Date.now()-this.appStartTime)/1e3)}formatRuntime(){const e=this.getAppRuntimeSeconds(),t=Math.floor(e/60),o=e%60;return`${t}分${o}秒`}getCurrentPageInfo(){if(typeof window<"u"){if(window.location){const e=window.location.pathname||window.location.href;return e!==this.currentPage&&(this.currentPage=e,this.pageHistory.set(Date.now(),e)),e}if(typeof getCurrentPages=="function"){const e=getCurrentPages();if(e.length>0){const t=e[e.length-1],o=t.route||t.$page?.route||"unknown";return o!==this.currentPage&&(this.currentPage=o,this.pageHistory.set(Date.now(),o)),o}}}return this.currentPage}getPageAtTime(e){if(this.memoryPageLogs.has(e))return this.memoryPageLogs.get(e)||"unknown";const t=Array.from(this.pageHistory.keys()).sort();let o=this.currentPage;for(let s=0;s<t.length&&t[s]<=e;s++)o=this.pageHistory.get(t[s])||this.currentPage;return o}stopMonitoring(){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null),this.removeDisplayElement(),console.log("⏹️ 内存监控已停止")}createDisplayElement(){if(typeof window>"u"||this.displayElement)return;const e=document.createElement("div");e.id="memory-monitor-display",e.style.cssText=` position: fixed; top: 20px; right: 20px; z-index: 9999; background: rgba(0, 0, 0, 0.8); color: #fff; font-family: 'Courier New', monospace; font-size: 14px; border-radius: 6px; padding: 8px 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.1); user-select: none; cursor: move; transition: all 0.3s ease; `;const t=document.createElement("div");t.id="memory-monitor-content",t.style.cssText=` text-align: center; font-weight: bold; `,e.appendChild(t),document.body.appendChild(e),this.displayElement=e,this.isDisplayVisible=!0,this.addDragFunctionality(e,e)}removeDisplayElement(){if(this.displayElement&&this.displayElement.parentNode){const e=this.displayElement._dragListeners;e&&(e.handle.removeEventListener("mousedown",e.onMouseDown),document.removeEventListener("mousemove",e.onMouseMove),document.removeEventListener("mouseup",e.onMouseUp),e.handle.removeEventListener("touchstart",e.onTouchStart),document.removeEventListener("touchmove",e.onTouchMove),document.removeEventListener("touchend",e.onTouchEnd)),this.displayElement.parentNode.removeChild(this.displayElement),this.displayElement=null,this.isDisplayVisible=!1}}addDragFunctionality(e,t){let o=!1,s=0,n=0,a=0,l=0;t.style.cursor="move",t.style.userSelect="none",t.style.touchAction="none";const r=(i,p)=>{o=!0,s=i,n=p;const f=e.getBoundingClientRect();a=f.left,l=f.top,e.style.transition="none",e.classList.add("memory-monitor-dragging")},m=(i,p)=>{if(!o)return;const f=i-s,L=p-n;e.style.left=a+f+"px",e.style.top=l+L+"px",e.style.right="auto"},g=()=>{o&&(o=!1,e.style.transition="all 0.3s ease",e.classList.remove("memory-monitor-dragging"))},h=i=>{i.button===0&&(r(i.clientX,i.clientY),i.preventDefault())},c=i=>{o&&(m(i.clientX,i.clientY),i.preventDefault())},d=i=>{o&&(g(),i.preventDefault())},u=i=>{if(i.touches.length===1){const p=i.touches[0];r(p.clientX,p.clientY),i.preventDefault()}},y=i=>{if(o&&i.touches.length===1){const p=i.touches[0];m(p.clientX,p.clientY),i.preventDefault()}},S=i=>{o&&(g(),i.preventDefault())};t.addEventListener("mousedown",h),t.addEventListener("touchstart",u),document.addEventListener("mousemove",c),document.addEventListener("touchmove",y,{passive:!1}),document.addEventListener("mouseup",d),document.addEventListener("touchend",S),e._dragListeners={onMouseDown:h,onMouseMove:c,onMouseUp:d,onTouchStart:u,onTouchMove:y,onTouchEnd:S,handle:t}}updateDisplayContent(e,t,o,s,n){if(!this.displayElement||!this.isDisplayVisible)return;const a=this.displayElement.querySelector("#memory-monitor-content");if(!a)return;let l="#4CAF50";switch(s){case"danger":l="#F44336";break;case"warning":l="#FF9800";break;case"caution":l="#FFC107";break;case"peak":l="#9C27B0";break}a.innerHTML=` <div style="color: ${l};"> ${e}MB ${o}% </div> `}async logMemoryUsage(){const e=Date.now();let t=0,o=0,s=this.SIMULATED_LIMIT_MB,n=0;try{if("memory"in performance){const c=performance.memory;t=Math.round(c.usedJSHeapSize/1024/1024),o=Math.round(c.totalJSHeapSize/1024/1024),n=Math.round(t/s*100),this.memoryLogs.set(e,c);const d=this.getCurrentPageInfo();this.memoryPageLogs.set(e,d)}else{console.warn("⚠️ 当前环境不支持 JS 内存检测,仅支持 PC Chrome");return}}catch(c){console.error("内存检测失败:",c);return}if(this.memoryLogs.size>100){const c=Math.min(...this.memoryLogs.keys());this.memoryLogs.delete(c),this.memoryPageLogs.delete(c)}const a=this.lastMemoryMB===0?0:Math.abs(t-this.lastMemoryMB),l=this.lastMemoryMB!==0&&a>=this.peakThresholdMB;let r="normal",m="🟢",g="正常!内存使用率健康,无需担心!",h=!1;if(n>=80?(r="danger",m="🚨",g="危险!内存使用率过高,立即处理!",h=!0):n>=60?(r="warning",m="⚠️",g="警告!内存使用率较高,需要优化!",h=!0):n>=40&&(r="caution",m="🟡",g="注意!内存使用率中等,开始监控!",h=!1),l&&(h=!0,r==="normal"&&(r="peak",m="📈",g=`峰值变化!内存使用变化 ${a}MB,可能存在大量对象创建`)),this.isDisplayVisible?this.updateDisplayContent(t,o,n,r,g):this.shouldShowDisplay&&!this.displayElement&&(this.createDisplayElement(),this.updateDisplayContent(t,o,n,r,g)),h){const c=r==="danger"?"error":r==="warning"||r==="peak"?"warn":"log",d=this.formatRuntime(),u=this.getCurrentPageInfo(),y={used:`${t}MB`,total:`${o}MB`,limit:`${s}MB (模拟手机上限)`,usage:`${n}%`,status:g,level:r,runtime:d,page:u};l&&(y.change=`+${a}MB`,y.timestamp=new Date(e).toLocaleTimeString()),console[c](`${m} 内存使用情况:`,y),(r==="danger"||r==="warning")&&(console.group(r==="danger"?"🚨 内存危险 - 自动生成详细报告":"⚠️ 内存警告 - 自动生成详细报告"),this.generateDetailedReport(r),console.groupEnd())}this.lastMemoryMB=t}getMemoryTrend(){if(this.memoryLogs.size<2)return{trend:"insufficient_data",message:"数据不足,无法分析趋势"};const e=Array.from(this.memoryLogs.keys()).sort(),t=this.memoryLogs.get(e[0]),o=this.memoryLogs.get(e[e.length-1]);if(t.usedJSHeapSize===0)return{trend:"insufficient_data",message:"初始内存为0,无法分析趋势"};const s=o.usedJSHeapSize-t.usedJSHeapSize,n=s/t.usedJSHeapSize*100;return n>10?{trend:"increasing",message:`内存使用增长 ${n.toFixed(2)}%,可能存在内存泄漏`,growth:`${Math.round(s/1024/1024)}MB`}:n<-5?{trend:"decreasing",message:`内存使用减少 ${Math.abs(n).toFixed(2)}%`,growth:`${Math.round(s/1024/1024)}MB`}:{trend:"stable",message:`内存使用稳定,变化 ${n.toFixed(2)}%`,growth:`${Math.round(s/1024/1024)}MB`}}generateReport(){const e=this.getMemoryTrend(),t=Array.from(this.memoryLogs.keys()).sort(),o=t.length>0?this.memoryLogs.get(t[t.length-1]):null;return console.group("📋 内存监控报告"),console.log("当前内存使用:",o?{used:`${Math.round(o.usedJSHeapSize/1024/1024)}MB`,total:`${Math.round(o.totalJSHeapSize/1024/1024)}MB`,limit:`${this.SIMULATED_LIMIT_MB}MB (模拟手机上限)`}:"无数据"),console.log("内存趋势:",e.message),console.groupEnd(),{currentMemory:o,trend:e}}generateDetailedReport(e="warning"){const t=this.getMemoryTrend(),o=Array.from(this.memoryLogs.keys()).sort(),s=o.length>0?this.memoryLogs.get(o[o.length-1]):null,n=this.getPeakChanges(),a=this.formatRuntime(),l=this.getCurrentPageInfo(),r=e==="danger"?"error":"warn",m=e==="danger"?"🚨":"⚠️",g=s?Math.round(s.usedJSHeapSize/1024/1024):0,h=s?Math.round(s.totalJSHeapSize/1024/1024):0,c=Math.round(g/this.SIMULATED_LIMIT_MB*100);console[r](`${m} 应用运行状态:`,{runtime:a,currentPage:l,startTime:new Date(this.appStartTime).toLocaleString(),memory:{used:`${g}MB`,total:`${h}MB`,limit:`${this.SIMULATED_LIMIT_MB}MB (模拟手机上限)`,usage:`${c}%`,status:e==="danger"?"危险":"警告"}}),console[r](`${m} 内存趋势分析:`,t.message),n.length>0?console[r](`${m} 峰值变化记录:`,n.map(u=>({time:u.timeFromStart,runtime:u.runtimeFromStart,page:u.page,change:`${u.change>0?"+":""}${u.change}MB`,memory:`${u.memory}MB`}))):console.log("✅ 无显著峰值变化");const d=o.map(u=>{const y=this.memoryLogs.get(u),S=this.memoryPageLogs.get(u)||"unknown",i=u-this.appStartTime,p=Math.floor(i/1e3/60),f=Math.floor(i%(1e3*60)/1e3),L=`${p}分${f}秒`;return{time:new Date(u).toLocaleTimeString(),runtime:L,page:S,used:`${Math.round(y.usedJSHeapSize/1024/1024)}MB`}});return console[r](`${m} 内存采样记录:`,d),console.log("💾 保存数据命令: memoryMonitor.saveDataToFile()"),console.log("📊 可视化命令: 打开 memory-data/index.html 查看图表"),{runtime:a,currentPage:l,currentMemory:s,trend:t,peakChanges:n,recentLogs:d,level:e}}async saveDataToFile(){try{const t=Array.from(this.memoryLogs.keys()).sort().map(s=>{const n=this.memoryLogs.get(s),a=this.memoryPageLogs.get(s)||"unknown",l=Math.round(n.usedJSHeapSize/1024/1024),r=Math.round(n.totalJSHeapSize/1024/1024),m=s-this.appStartTime,g=Math.floor(m/1e3/60),h=Math.floor(m%(1e3*60)/1e3),c=`${g}:${h.toString().padStart(2,"0")}`;return{timestamp:s,time:new Date(s).toLocaleTimeString(),runtime:c,page:a,used:l,total:r,usage:Math.round(l/this.SIMULATED_LIMIT_MB*100)}}),o={sessionId:this.generateSessionId(),startTime:this.appStartTime,endTime:Date.now(),duration:this.formatRuntime(),totalSamples:t.length,peakChanges:this.getPeakChanges(),trend:this.getMemoryTrend(),data:t,config:{simulatedLimitMB:this.SIMULATED_LIMIT_MB,peakThresholdMB:this.peakThresholdMB}};if(typeof window<"u"){const s=new Blob([JSON.stringify(o,null,2)],{type:"application/json"}),n=URL.createObjectURL(s),a=document.createElement("a");a.href=n,a.download=`memory-data-${o.sessionId}.json`,document.body.appendChild(a),a.click(),document.body.removeChild(a),URL.revokeObjectURL(n),console.log(`💾 内存数据已保存: memory-data-${o.sessionId}.json`),console.log(`📊 包含 ${t.length} 个采样点,运行时长 ${o.duration}`)}return o}catch(e){return console.error("保存数据失败:",e),null}}generateSessionId(){const t=new Date().toISOString().slice(0,19).replace(/[-:T]/g,""),o=Math.random().toString(36).substr(2,6);return`${t}_${o}`}shouldSaveData(){return this.memoryLogs.size>0}clearLogs(){this.memoryLogs.clear(),this.memoryPageLogs.clear(),this.pageHistory.clear(),this.lastMemoryMB=0,this.appStartTime=Date.now(),console.log("🧹 监控数据已清理")}showDisplay(){process.env.NODE_ENV==="development"&&this.createDisplayElement()}hideDisplay(){this.removeDisplayElement()}toggleDisplayPanel(){this.isDisplayVisible?this.hideDisplay():this.showDisplay()}getPeakChanges(){const e=[],t=Array.from(this.memoryLogs.keys()).sort();for(let o=1;o<t.length;o++){const s=t[o-1],n=t[o],a=this.memoryLogs.get(s),l=this.memoryLogs.get(n),r=Math.round((l.usedJSHeapSize-a.usedJSHeapSize)/1024/1024);if(r>=20){const m=n-this.appStartTime,g=Math.floor(m/1e3/60),h=Math.floor(m%(1e3*60)/1e3),c=`${g}分${h}秒`,d=this.getPageAtTime(n),u=new Date(n).toLocaleTimeString();e.push({timestamp:n,change:r,memory:Math.round(l.usedJSHeapSize/1024/1024),runtimeFromStart:c,page:d,timeFromStart:u})}}return e}}const D=M.getInstance();process.env.NODE_ENV==="development"&&setTimeout(()=>{D.clearLogs(),D.startMonitoring(3e3,300,20,!0),console.log("🔍 内存监控已启动!"),console.log("📱 DOM面板已创建(右上角浮动面板)"),console.log("💾 手动保存: memoryMonitor.saveDataToFile()"),console.log("📊 查看图表: 打开 memory-data/index.html"),console.log("📋 生成报告: memoryMonitor.generateDetailedReport()"),console.log("ℹ️ 自动保存条件: 内存使用率≥40% | 峰值变化 | 增长趋势 | 运行≥5分钟 | 内存≥150MB")},1e3);window.MemoryMonitor=M;exports.memoryMonitor=D;