|
|
!function(){"use strict";const e=document.querySelector(".tool-workspace");if(!e)return;const t=(t,a)=>(a||e).querySelector(t),a=(t,a)=>Array.from((a||e).querySelectorAll(t)),n=(e,t,a)=>{const n=document.createElement(e);return t&&(n.className=t),void 0!==a&&(n.textContent=a),n},r=e=>new Intl.NumberFormat("zh-CN",{maximumFractionDigits:2}).format(e),o=(e,t)=>e.length?e[Math.min(e.length-1,Math.ceil(e.length*t)-1)]:0,l=(e,t)=>{const a=URL.createObjectURL(new Blob([t],{type:"text/markdown;charset=utf-8"})),n=document.createElement("a");n.href=a,n.download=e,n.click(),setTimeout((()=>URL.revokeObjectURL(a)),1e3)},i=async(e,t)=>{try{await navigator.clipboard.writeText(e)}catch(t){const a=n("textarea");a.value=e,a.style.position="fixed",a.style.opacity="0",document.body.appendChild(a),a.select(),document.execCommand("copy"),a.remove()}if(t){const e=t.textContent;t.textContent="已复制",setTimeout((()=>{t.textContent=e}),1200)}},s=()=>{const e=t("[data-results]"),a=t("[data-empty]");e&&(e.hidden=!1),a&&(a.hidden=!0)},c=()=>{const e=t("[data-results]"),a=t("[data-empty]");e&&(e.hidden=!0),a&&(a.hidden=!1)};function d(e){return e.map((e=>{const t=n("div","tool-metric");return t.append(n("span","",e.label),n("strong","",e.value)),t}))}function u(e){return e.map((e=>{const t=n("div","tool-finding tool-finding--"+e.level);return t.append(n("strong","",e.title),document.createTextNode(e.text)),t}))}function m(){const e=t("[data-input]"),a=t("[data-file-input]"),n=t("[data-dropzone]");let o=null;if(!e||!a||!n)return{input:e,getFile:()=>null,clearFile:()=>{}};const l=t=>{t&&(o=t,e.value="",e.placeholder=t.name+"("+r(t.size/1024/1024)+" MiB,分析时读取)",n.querySelector("strong").textContent=t.name,n.querySelector("small").textContent=r(t.size/1024/1024)+" MiB · 文件仍在本地")};return a.addEventListener("change",(()=>l(a.files[0]))),e.addEventListener("input",(()=>{o=null})),["dragenter","dragover"].forEach((e=>n.addEventListener(e,(e=>{e.preventDefault(),n.classList.add("is-dragging")})))),["dragleave","drop"].forEach((e=>n.addEventListener(e,(e=>{e.preventDefault(),n.classList.remove("is-dragging")})))),n.addEventListener("drop",(e=>l(e.dataTransfer.files[0]))),{input:e,getFile:()=>o,clearFile:()=>{o=null,a.value="",e.placeholder="",n.querySelector("strong").textContent="gc-file"===n.htmlFor?"拖入 GC 日志":"拖入线程快照",n.querySelector("small").textContent="或点击选择 .log / .txt 文件"}}}function p(e,t,a){return new Promise(((n,r)=>{const o=new Worker("/tools/parser-worker.js"),l=Date.now()+"-"+Math.random();o.onmessage=e=>{e.data.requestId===l&&("progress"===e.data.type&&a("读取文件 "+e.data.value+"%"),"result"===e.data.type&&(o.terminate(),n(e.data.result)),"error"===e.data.type&&(o.terminate(),r(new Error(e.data.message))))},o.onerror=e=>{o.terminate(),r(e)},o.postMessage({type:e,source:t,requestId:l})}))}function v(e){const a=function(e){const t=e.events.map((e=>e.durationMs)).sort(((e,t)=>e-t)),a=t.reduce(((e,t)=>e+t),0),n=e.events.filter((e=>"Full GC"===e.kind)),r=e.events.reduce(((e,t)=>e+(t.reclaimed||0)),0),l=[];return e.events.length?(n.length&&l.push({level:"danger",title:"检测到 Full GC",text:n.length+" 次 Full GC,需要结合触发原因、老年代增长和分配速率继续排查。"}),o(t,.99)>200&&l.push({level:"warn",title:"长尾暂停偏高",text:"P99 已超过 200ms,建议关注最慢事件及其前后的堆占用变化。"}),Math.max(...t)<=50&&!n.length&&l.push({level:"ok",title:"样本内暂停较平稳",text:"没有发现 Full GC,且最大可识别暂停不超过 50ms。仍需确认日志覆盖了真实高峰。"}),e.events.length<5&&l.push({level:"info",title:"样本较少",text:"事件数量不足以代表长期运行情况,建议提供覆盖业务高峰的完整日志。"})):l.push({level:"warn",title:"没有识别出 GC 暂停",text:"请确认日志包含 GC pause 行,并使用常见 JDK 8 或 JDK 9+ 格式。"}),{durations:t,total:a,full:n,reclaimed:r,findings:l}}(e);return t("[data-metrics]").replaceChildren(...d([{label:"可识别事件",value:String(e.events.length)},{label:"Full GC",value:String(a.full.length)},{label:"平均暂停",value:r(a.total/Math.max(1,e.events.length))+" ms"},{label:"P99 暂停",value:r(o(a.durations,.99))+" ms"},{label:"累计回收",value:r(a.reclaimed)+" MiB"}])),t("[data-findings]").replaceChildren(...u(a.findings)),t("[data-events]").replaceChildren(...e.events.slice().sort(((e,t)=>t.durationMs-e.durationMs)).slice(0,10).map((e=>{const t=document.createElement("tr");return[null===e.time?"#"+e.id:e.time+"s",e.kind,r(e.durationMs)+"ms",null===e.before?"-":r(e.before)+" -> "+r(e.after)+" MiB"].forEach((e=>t.append(n("td","",e)))),t}))),function(e){const a=t("[data-chart]");if(!a)return;const n=Math.max(320,a.clientWidth),o=window.devicePixelRatio||1;a.width=n*o,a.height=220*o;const l=a.getContext("2d");l.scale(o,o),l.clearRect(0,0,n,220);const i=n-44-10,s=Math.max(...e.map((e=>e.durationMs)),1);l.strokeStyle="#dfe4e8",l.fillStyle="#71808b",l.font="10px sans-serif";for(let e=0;e<=4;e+=1){const t=12+180*e/4;l.beginPath(),l.moveTo(44,t),l.lineTo(n-10,t),l.stroke(),l.fillText(r(s*(4-e)/4),2,t+3)}const c=Math.max(1,Math.min(4,i/e.length*.2)),d=Math.max(1,i/e.length-c);e.forEach(((e,t)=>{const a=Math.max(2,e.durationMs/s*180);l.fillStyle="Full GC"===e.kind?"#bf4d3e":e.durationMs>=100?"#bc7a20":"#2f7891",l.fillRect(44+t*(d+c),192-a,d,a)})),l.fillStyle="#71808b",l.fillText("事件序号",n-48,213)}(e.events),s(),"# GC 日志分析报告\n\n- 格式:"+e.format+"\n- 事件:"+e.events.length+"\n- Full GC:"+a.full.length+"\n- 平均暂停:"+r(a.total/Math.max(1,e.events.length))+" ms\n- P99:"+r(o(a.durations,.99))+" ms\n- 最大暂停:"+r(a.durations[a.durations.length-1]||0)+" ms\n"}const g=[[/^-Xms/i,"初始 Java 堆大小"],[/^-Xmx/i,"最大 Java 堆大小"],[/^-Xss/i,"每个 Java 线程的栈大小"],[/MaxMetaspaceSize/,"Metaspace 上限"],[/MaxDirectMemorySize/,"直接内存上限"],[/MaxRAMPercentage/,"最大堆占可用内存百分比"],[/UseG1GC/,"使用 G1 垃圾收集器"],[/UseParallelGC/,"使用 Parallel GC"],[/UseZGC/,"使用 ZGC"],[/UseShenandoahGC/,"使用 Shenandoah GC"],[/HeapDumpOnOutOfMemoryError/,"发生 OOM 时生成 Heap Dump"],[/HeapDumpPath/,"Heap Dump 输出路径"],[/ExitOnOutOfMemoryError/,"发生 OOM 后退出进程"],[/^-Xlog:gc/,"JDK 9+ Unified GC Logging"],[/^-Xloggc/,"JDK 8 GC 日志路径"],[/PrintGCDetails/,"JDK 8 GC 详细日志"],[/UseContainerSupport/,"启用容器资源感知"],[/^-D/,"Java 系统属性"]],h=e=>{const t=String(e||"").match(/^([0-9.]+)([kmgt])?$/i);if(!t)return null;const a={k:1/1024,m:1,g:1024,t:1048576}[(t[2]||"m").toLowerCase()];return Number(t[1])*a},x=/\bat (?:java\.|javax\.|jdk\.|sun\.|org\.springframework\.|org\.apache\.|com\.fasterxml\.|reactor\.|io\.netty\.)/,f={heap:"Java Heap",meta:"Metaspace",direct:"Direct Memory",stacks:"线程栈",code:"Code Cache",native:"其他 Native",headroom:"剩余空间"};function C(){const o=function(){const e={};a("[data-memory]").forEach((t=>{e[t.dataset.memory]=Math.max(0,Number(t.value)||0)})),e.stacks=e.threads*e.xss/1024;const t=e.heap+e.meta+e.direct+e.stacks+e.code+e.native,n=e.limit-t,r=t/Math.max(1,e.limit);return{values:e,used:t,headroom:n,ratio:r,status:r>1?"预算已经超出限制":r>.9?"余量不足,OOM 风险高":r>.8?"预算偏紧,需要压测验证":"预算保留了合理余量",level:r>1||r>.9?"danger":r>.8?"warn":"ok"}}();t("[data-percent]").textContent=Math.round(100*o.ratio)+"%",t("[data-status]").textContent=o.status,t("[data-summary]").textContent="估算使用 "+r(o.used)+" MiB,剩余 "+r(o.headroom)+" MiB。线程栈预算为 "+r(o.values.stacks)+" MiB。",t("[data-ring]").style.borderColor="danger"===o.level?"#bf4d3e":"warn"===o.level?"#bd7a1d":"#26785e";const l=["heap","meta","direct","stacks","code","native","headroom"];t("[data-memory-bar]").replaceChildren(...l.map((e=>{const t="headroom"===e?Math.max(0,o.headroom):o.values[e],a=n("span","tool-memory-segment");return a.style.width=t/Math.max(o.values.limit,o.used)*100+"%",a.title=f[e]+": "+r(t)+" MiB",a}))),t("[data-breakdown]").replaceChildren(...l.map((e=>{const t="headroom"===e?o.headroom:o.values[e],a=document.createElement("tr");return a.append(n("td","",f[e]),n("td","",r(t)+" MiB"),n("td","",r(t/Math.max(1,o.values.limit)*100)+"%")),a})));const i=[{level:o.level,title:o.status,text:o.ratio>.8?"建议降低 Heap、线程数或单线程栈,并为 JVM 内部结构和峰值波动保留更多空间。":"仍需通过 Native Memory Tracking、容器 RSS 和压力测试校准估算。"}];o.values.stacks>.15*o.values.limit&&i.push({level:"warn",title:"线程栈占比较高",text:"线程数与 -Xss 共同占用 "+r(o.values.stacks)+" MiB,可检查线程池上限和是否需要较大的调用栈。"}),0===o.values.direct&&i.push({level:"info",title:"Direct Memory 未计入",text:"Netty、NIO 或零拷贝应用通常需要单独预留 Direct Memory。"}),t("[data-findings]").replaceChildren(...u(i)),e.dataset.memoryReport="JVM 内存预算\n总限制:"+o.values.limit+" MiB\n估算使用:"+r(o.used)+" MiB\n剩余:"+r(o.headroom)+" MiB\n状态:"+o.status,s()}const M={"gc-log":function(){const e=m(),a=t("[data-progress]");let n="";t('[data-action="sample"]').addEventListener("click",(()=>{e.clearFile(),e.input.value="[0.231s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 128M->34M(512M) 8.420ms\n[1.927s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 226M->61M(512M) 14.870ms\n[4.112s][info][gc] GC(2) Pause Young (Concurrent Start) (G1 Humongous Allocation) 318M->105M(512M) 31.280ms\n[7.641s][info][gc] GC(3) Pause Remark 244M->244M(512M) 5.610ms\n[12.880s][info][gc] GC(4) Pause Full (G1 Compaction Pause) 486M->172M(512M) 286.740ms\n[18.214s][info][gc] GC(5) Pause Young (Normal) (G1 Evacuation Pause) 350M->188M(512M) 22.130ms"})),t('[data-action="clear"]').addEventListener("click",(()=>{e.input.value="",e.clearFile(),a.textContent="",c()})),t('[data-action="analyze"]').addEventListener("click",(async t=>{const o=t.currentTarget,l=e.getFile()||e.input.value.trim();if(l){o.disabled=!0,a.textContent="正在解析...";try{const e=await p("gc",l,(e=>{a.textContent=e}));n=v(e),a.textContent="完成,共扫描 "+r(e.lines)+" 行"}catch(e){a.textContent="解析失败:"+e.message}o.disabled=!1}else a.textContent="请先提供日志"})),t('[data-action="export"]').addEventListener("click",(()=>l("gc-analysis.md",n)))},"thread-dump":function(){const e=m(),a=t("[data-progress]");let o="";t('[data-action="sample"]').addEventListener("click",(()=>{e.clearFile(),e.input.value='"http-nio-8080-exec-1" #41 prio=5 os_prio=0 tid=0x1 nid=0x20 waiting for monitor entry\n java.lang.Thread.State: BLOCKED (on object monitor)\n at com.example.order.StockService.reserve(StockService.java:84)\n - waiting to lock <0x0000000711111111>\n at com.example.order.OrderService.create(OrderService.java:51)\n\n"http-nio-8080-exec-2" #42 prio=5 os_prio=0 tid=0x2 nid=0x21 waiting for monitor entry\n java.lang.Thread.State: BLOCKED (on object monitor)\n at com.example.order.StockService.reserve(StockService.java:84)\n - waiting to lock <0x0000000711111111>\n at com.example.order.OrderService.create(OrderService.java:51)\n\n"HikariPool-1 housekeeper" #18 daemon prio=5 os_prio=0 tid=0x3 nid=0x12 waiting on condition\n java.lang.Thread.State: TIMED_WAITING (parking)\n at jdk.internal.misc.Unsafe.park(Native Method)\n at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:252)\n at com.zaxxer.hikari.pool.HikariPool$HouseKeeper.run(HikariPool.java:1198)\n\n"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x4 nid=0x3 runnable\n java.lang.Thread.State: RUNNABLE\n at java.lang.ref.Reference.waitForReferencePendingList(Native Method)'})),t('[data-action="clear"]').addEventListener("click",(()=>{e.input.value="",e.clearFile(),a.textContent="",c()})),t('[data-action="analyze"]').addEventListener("click",(async l=>{const i=l.currentTarget,c=e.getFile()||e.input.value.trim();if(c){i.disabled=!0,a.textContent="正在解析...";try{const e=await p("thread",c,(e=>{a.textContent=e}));o=function(e){t("[data-metrics]").replaceChildren(...d([{label:"线程总数",value:String(e.threads)},{label:"RUNNABLE",value:String(e.states.RUNNABLE||0)},{label:"BLOCKED",value:String(e.blocked)},{label:"等待线程",value:String(e.waiting)},{label:"调用栈分组",value:String(e.groups.length)}]));const a=Math.max(...Object.values(e.states),1);t("[data-states]").replaceChildren(...Object.entries(e.states).sort(((e,t)=>t[1]-e[1])).map((([e,t])=>{const r=n("div","tool-state-row"),o=n("div","tool-state-track"),l=n("div","tool-state-fill");return l.style.width=t/a*100+"%",o.append(l),r.append(n("span","",e),o,n("b","",String(t))),r})));const r=[];return e.deadlockMarker&&r.push({level:"danger",title:"日志包含死锁标记",text:"JVM 已报告 Java-level deadlock,需要核对后续锁对象和线程链。"}),e.blocked>0&&r.push({level:e.blocked>=Math.max(3,.2*e.threads)?"warn":"info",title:"存在 BLOCKED 线程",text:e.blocked+" 个线程正在等待 monitor,优先检查高频调用栈中的共同锁点。"}),e.threads||r.push({level:"warn",title:"没有识别到线程",text:"标准 Thread Dump 的线程头通常以双引号包围的线程名开始。"}),e.threads&&!r.length&&r.push({level:"ok",title:"未发现明显死锁信号",text:"当前快照没有死锁标记或 BLOCKED 线程;单次快照不能排除瞬时问题。"}),t("[data-findings]").replaceChildren(...u(r)),t("[data-groups]").replaceChildren(...e.groups.map((e=>{const t=n("div","tool-stack-group");return t.append(n("strong","",e.names.join(", ")),n("span","",e.count+" 个线程"),n("code","",e.signature.replace(/\n/g," <- "))),t}))),s(),"# Thread Dump 分析报告\n\n- 线程总数:"+e.threads+"\n- BLOCKED:"+e.blocked+"\n- 等待线程:"+e.waiting+"\n- 死锁标记:"+(e.deadlockMarker?"是":"否")+"\n\n## 高频调用栈\n\n"+e.groups.map((e=>"- "+e.count+" 个线程:`"+e.signature.split("\n")[0]+"`")).join("\n")}(e),a.textContent="完成,共扫描 "+r(e.lines)+" 行"}catch(e){a.textContent="解析失败:"+e.message}i.disabled=!1}else a.textContent="请先提供线程快照"})),t('[data-action="export"]').addEventListener("click",(()=>l("thread-dump-analysis.md",o)))},"jvm-options":function(){const e=t("[data-input]");let a="";t('[data-action="sample"]').addEventListener("click",(()=>{e.value="java -Xms2g -Xmx4g -Xss1m -XX:+UseG1GC -XX:MaxMetaspaceSize=384m -XX:MaxDirectMemorySize=512m -XX:+HeapDumpOnOutOfMemoryError -Xlog:gc*:file=logs/gc.log:time,uptime,level,tags -jar app.jar"})),t('[data-action="clear"]').addEventListener("click",(()=>{e.value="",c()})),t('[data-action="analyze"]').addEventListener("click",(()=>{if(!e.value.trim())return;const r=Number(t("[data-jdk]").value),o=Number(t("[data-host-memory]").value)||0,l=function(e,t,a){const n=(e.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)||[]).filter((e=>/^-(?:X|XX:|D|verbose:)/.test(e))),r=n.map((e=>{const t=e.match(/^([^=]+)=(.*)$/),a=t?t[2]:/^-X(?:ms|mx|ss)/i.test(e)?e.replace(/^-X(?:ms|mx|ss)/i,""):/^-XX:[+-]/.test(e)?e.includes(":+")?"启用":"禁用":"-",n=g.find((([t])=>t.test(e)));return{option:t?t[1]:e,value:a,help:n?n[1]:"自定义或未收录参数,请结合对应 JDK 文档确认"}})),o=[],l=e=>{const t=n.slice().reverse().find((t=>t.toLowerCase().startsWith(e.toLowerCase())));return t?t.slice(e.length):null},i=h(l("-Xms")),s=h(l("-Xmx"));i&&s&&i>s?o.push({level:"danger",title:"-Xms 大于 -Xmx",text:"JVM 无法使用初始堆大于最大堆的配置启动。"}):s?a&&s>1024*a*.75&&o.push({level:"warn",title:"Heap 占用预算偏高",text:"-Xmx 已超过可用内存的 75%,线程栈、Direct Memory 和 Native Memory 可能没有足够余量。"}):o.push({level:"warn",title:"没有显式设置 -Xmx",text:"最大堆将由 JVM ergonomics 推导,容器中应确认最终值是否符合预算。"});const c=n.filter((e=>/-XX:\+Use(?:G1|Parallel|Z|Shenandoah|ConcMarkSweep)GC/.test(e)));c.length>1&&o.push({level:"danger",title:"配置了多个 GC 收集器",text:c.join("、")+" 不能同时作为主收集器。"}),n.some((e=>/UseConcMarkSweepGC/.test(e)))&&t>=14&&o.push({level:"danger",title:"CMS 已被移除",text:"UseConcMarkSweepGC 在 JDK 14 起不可用,建议评估 G1 或其他现代收集器。"}),n.some((e=>/(?:Max)?PermSize/.test(e)))&&t>=8&&o.push({level:"warn",title:"PermGen 参数已失效",text:"JDK 8 使用 Metaspace,PermSize/MaxPermSize 不再生效。"}),n.some((e=>/^-Xlog:gc/.test(e)))&&8===t&&o.push({level:"danger",title:"日志参数版本不兼容",text:"-Xlog:gc 属于 JDK 9+ Unified Logging;JDK 8 使用 -Xloggc 与 PrintGCDetails。"}),n.some((e=>/^-Xloggc/.test(e)))&&t>=9&&o.push({level:"warn",title:"使用了旧 GC 日志参数",text:"JDK 9+ 建议迁移到 -Xlog:gc*。"});const d=Object.entries(n.reduce(((e,t)=>{const a=(e=>{const t=e.match(/^-XX:(?:[+-])?([^=]+)/);if(t)return"-XX:"+t[1];const a=e.match(/^(-X(?:ms|mx|ss))/i);if(a)return a[1].toLowerCase();const n=e.match(/^(-D[^=]+)/);return n?n[1]:e.replace(/=.*/,"")})(t);return e[a]=(e[a]||0)+1,e}),{})).filter((([,e])=>e>1));return d.length&&o.push({level:"warn",title:"存在重复参数",text:d.map((([e])=>e)).join("、")+" 出现多次,通常以后出现的值为准。"}),n.some((e=>/HeapDumpOnOutOfMemoryError/.test(e)))||o.push({level:"info",title:"可以保留 OOM 现场",text:"生产环境可评估增加 -XX:+HeapDumpOnOutOfMemoryError,并确保输出目录有足够空间。"}),o.some((e=>"danger"===e.level||"warn"===e.level))||o.unshift({level:"ok",title:"未发现明显冲突",text:"静态检查通过;还需用 java -XX:+PrintFlagsFinal 和真实压测确认最终生效值。"}),{rows:r,findings:o,xms:i,xmx:s}}(e.value,r,o);t("[data-findings]").replaceChildren(...u(l.findings)),t("[data-options]").replaceChildren(...l.rows.map((e=>{const t=document.createElement("tr"),a=n("td");return a.append(n("code","",e.option)),t.append(a,n("td","",e.value),n("td","",e.help)),t}))),a="JVM 参数检查(JDK "+r+")\n\n"+l.findings.map((e=>"["+e.level.toUpperCase()+"] "+e.title+":"+e.text)).join("\n")+"\n\n"+l.rows.map((e=>e.option+" = "+e.value+":"+e.help)).join("\n"),s()})),t('[data-action="copy"]').addEventListener("click",(e=>i(a,e.currentTarget)))},stacktrace:function(){const e=t("[data-input]");let r="compact",o=null;const l=()=>{e.value.trim()&&(o=function(e,t){const a=e.split(/\r?\n/).map((e=>e.replace(/\s+$/,""))).filter(Boolean),n=a.filter((e=>/^(?:Caused by:|[\w.$]+(?:Exception|Error)(?::|$))/.test(e.trim()))).map((e=>e.trim().replace(/^Caused by:\s*/,""))),r=n[n.length-1]||a[0]||"未识别异常";if(!t)return{output:a.join("\n"),causes:n,rootCause:r};const o=[];let l=0;const i=()=>{l&&o.push(" ... 已折叠 "+l+" 个框架调用"),l=0};return a.forEach((e=>{x.test(e)?l+=1:(i(),o.push(e))})),i(),{output:o.join("\n"),causes:n,rootCause:r}}(e.value,"compact"===r),t("[data-root-title]").textContent=o.rootCause,t("[data-causes]").replaceChildren(...o.causes.map(((e,t)=>n("span","",(t===o.causes.length-1?"ROOT · ":"")+e)))),t("[data-output]").textContent=o.output,s())};t('[data-action="sample"]').addEventListener("click",(()=>{e.value="java.lang.IllegalStateException: 创建订单失败\n at com.example.order.OrderService.create(OrderService.java:72)\n at com.example.order.OrderController.submit(OrderController.java:41)\n at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:190)\nCaused by: org.springframework.dao.DataAccessResourceFailureException: database unavailable\n at org.springframework.jdbc.support.SQLExceptionSubclassTranslator.doTranslate(SQLExceptionSubclassTranslator.java:80)\n at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:650)\nCaused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms\n at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:696)\n at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:181)\n at com.example.order.OrderRepository.save(OrderRepository.java:58)\n ... 24 common frames omitted"})),t('[data-action="clear"]').addEventListener("click",(()=>{e.value="",o=null,c()})),t('[data-action="analyze"]').addEventListener("click",l),a("[data-mode]").forEach((e=>e.addEventListener("click",(()=>{r=e.dataset.mode,a("[data-mode]").forEach((t=>t.classList.toggle("is-active",t===e))),o&&l()})))),t('[data-action="copy"]').addEventListener("click",(e=>{o&&i("## 根因\n\n`"+o.rootCause+"`\n\n```text\n"+o.output+"\n```",e.currentTarget)}))},memory:function(){t('[data-action="calculate"]').addEventListener("click",C),t('[data-action="sample"]').addEventListener("click",(()=>{const e={limit:8192,heap:4096,meta:256,direct:512,threads:300,xss:1024,code:240,native:512};a("[data-memory]").forEach((t=>{t.value=e[t.dataset.memory]})),C()})),t('[data-action="copy"]').addEventListener("click",(t=>i(e.dataset.memoryReport||"",t.currentTarget))),C()}};M[e.dataset.tool]&&M[e.dataset.tool]()}();
|