Traceroute Analysis Tool

Traceroute Analysis Tool

Paste Traceroute Output

Destination
Total Hops
Timeouts
Avg. Latency

Hop Details

Hop Host IP Avg T1 T2 T3

Analysis History

Your analysis history will appear here.

Analyzed ${escapeHTML(log.dest)} (${log.hops} hops, ${log.timeouts} timeouts)

`).join(''); } function logEntry(dest, hops, timeouts) { const timestamp = new Date().toLocaleString('en-US'); lookupLog.unshift({ timestamp, dest, hops, timeouts }); if (lookupLog.length > 50) lookupLog.pop(); // Limit log size saveState(); renderLog(); } function showMessage(message, isError = true) { ui.errorMsg.textContent = message; ui.errorMsg.style.display = 'block'; } // --- PARSING & ANALYSIS --- function parseTimes(timeString) { const timeRegex = /([\d\.]+|\*)\s+ms/g; const times = (timeString.match(timeRegex) || []).map(t => t.replace(' ms', '')); return [times[0] || '*', times[1] || '*', times[2] || '*']; } function analyzeTrace() { ui.loader.style.display = 'block'; ui.resultsContainer.style.display = 'none'; ui.errorMsg.style.display = 'none'; const text = ui.inputText.value.trim(); if (!text) { showMessage("Input is empty."); ui.loader.style.display = 'none'; return; } const lines = text.split('\n'); currentResults = { destination: { host: '', ip: '' }, hops: [], summary: {} }; let hopCount = 0; let timeoutCount = 0; let totalLatency = 0; let validPings = 0; // Parse destination from first line const firstLineMatch = lines[0].match(/to\s+(.*?)\s+\((.*?)\)/); if (firstLineMatch) { currentResults.destination = { host: firstLineMatch[1], ip: firstLineMatch[2] }; } else { currentResults.destination = { host: 'N/A', ip: 'N/A' }; } const timeoutRegex = /^\s*(\d+)\s+\*\s+\*\s+\*$/; // Regex for: 1 host.com (1.2.3.4) ...times... const hostIpRegex = /^\s*(\d+)\s+(.+?)\s+\((.*?)\)\s+(.*)$/; // Regex for: 1 1.2.3.4 ...times... const ipOnlyRegex = /^\s*(\d+)\s+([\d\.:a-fA-F0-9]+)\s+(.*)$/; for (const line of lines.slice(1)) { const timeoutMatch = line.match(timeoutRegex); if (timeoutMatch) { currentResults.hops.push({ hop: timeoutMatch[1], host: '*', ip: '*', t1: '*', t2: '*', t3: '*', avg: 'N/A', isTimeout: true }); timeoutCount++; continue; } let hop, host, ip, timeString; const dataMatch = line.match(hostIpRegex); const ipOnlyMatch = line.match(ipOnlyRegex); if (dataMatch) { [_, hop, host, ip, timeString] = dataMatch; } else if (ipOnlyMatch) { [_, hop, host, timeString] = ipOnlyMatch; ip = host; // Host is the IP } else { continue; // Not a line we recognize } const [t1, t2, t3] = parseTimes(timeString); const times = [t1, t2, t3].map(parseFloat).filter(t => !isNaN(t)); let avg = 'N/A'; if (times.length > 0) { const sum = times.reduce((a, b) => a + b, 0); avg = sum / times.length; totalLatency += sum; validPings += times.length; } currentResults.hops.push({ hop, host: host.trim(), ip: ip.trim(), t1, t2, t3, avg, isTimeout: false }); } hopCount = currentResults.hops.length; const totalAvg = validPings > 0 ? (totalLatency / validPings) : 0; currentResults.summary = { hopCount, timeoutCount, totalAvg }; logEntry(currentResults.destination.host || 'N/A', hopCount, timeoutCount); renderResults(); ui.loader.style.display = 'none'; } function renderResults() { const { summary, hops } = currentResults; ui.summaryDest.textContent = `${currentResults.destination.host} (${currentResults.destination.ip})`; ui.summaryHops.textContent = summary.hopCount; ui.summaryTimeouts.textContent = summary.timeoutCount; ui.summaryAvg.textContent = `${summary.totalAvg.toFixed(2)} ms`; ui.resultsTbody.innerHTML = ''; hops.forEach(hop => { const row = document.createElement('tr'); let avgClass = ''; if (hop.isTimeout) { row.className = 'timeout'; } else if (hop.avg > 100) { avgClass = 'latency-danger'; } else if (hop.avg > 50) { avgClass = 'latency-warn'; } row.innerHTML = ` ${hop.hop} ${escapeHTML(hop.host)} ${escapeHTML(hop.ip)} ${hop.avg === 'N/A' ? 'N/A' : hop.avg.toFixed(2)} ${hop.t1} ${hop.t2} ${hop.t3} `; ui.resultsTbody.appendChild(row); }); ui.resultsContainer.style.display = 'block'; } // --- PDF LOGIC --- function downloadReportPDF(data, title, filename) { const { jsPDF } = window.jspdf; if (!jsPDF || !window.jspdf.autoTable) { alert("PDF library not loaded!"); return; } const doc = new jsPDF(); doc.setFontSize(18); doc.text(title, 14, 22); if (data.summary) { // Single Report doc.setFontSize(11); doc.setTextColor(100); doc.text(`Report for: ${data.destination.host} (${data.destination.ip})`, 14, 28); doc.autoTable({ startY: 35, theme: 'grid', head: [['Metric', 'Value']], body: [ ['Total Hops', data.summary.hopCount], ['Timeouts', data.summary.timeoutCount], ['Overall Avg. Latency', `${data.summary.totalAvg.toFixed(2)} ms`], ] }); doc.autoTable({ startY: doc.autoTable.previous.finalY + 10, head: [['Hop', 'Host', 'IP', 'Avg (ms)', 'T1', 'T2', 'T3']], body: data.hops.map(h => [h.hop, h.host, h.ip, h.avg === 'N/A' ? 'N/A' : h.avg.toFixed(2), h.t1, h.t2, h.t3]), theme: 'striped', headStyles: { fillColor: [29, 78, 216] } // var(--primary-color) }); } else { // Log Report doc.autoTable({ startY: 30, head: [['Timestamp', 'Destination', 'Hops', 'Timeouts']], body: data.map(log => [log.timestamp, log.dest, log.hops, log.timeouts]), theme: 'striped', headStyles: { fillColor: [29, 78, 216] } }); } doc.save(filename); } // --- EVENT LISTENERS --- ui.analyzeBtn.addEventListener('click', analyzeTrace); ui.downloadReportBtn.addEventListener('click', () => { if (currentResults.hops.length === 0) { alert("Please analyze a traceroute before downloading."); return; } downloadReportPDF(currentResults, "Traceroute Report", `Traceroute_${currentResults.destination.host}.pdf`); }); ui.downloadLogBtn.addEventListener('click', () => { if (lookupLog.length === 0) { alert("Log is empty."); return; } downloadReportPDF(lookupLog, "Traceroute Analysis Log", "Traceroute_Log.pdf"); }); ui.clearLogBtn.addEventListener('click', () => { lookupLog = []; saveState(); renderLog(); }); // --- TABBING LOGIC --- function switchTab(targetTabId) { ui.tabContents.forEach(c => c.classList.remove('active')); ui.tabButtons.forEach(b => b.classList.remove('active')); document.getElementById(`tat-tab-${targetTabId}`).classList.add('active'); document.querySelector(`.tat-tab-button[data-tab="${targetTabId}"]`).classList.add('active'); } function navigateTabs(dir) { const tabs = Array.from(ui.tabButtons); const active = tabs.find(t => t.classList.contains('active')); let index = tabs.indexOf(active) + dir; if (index < 0) index = 0; if (index >= tabs.length) index = tabs.length - 1; switchTab(tabs[index].dataset.tab); } ui.tabButtons.forEach(button => button.addEventListener('click', () => switchTab(button.dataset.tab))); ui.nextTabBtn.addEventListener('click', () => navigateTabs(1)); ui.prevTabBtn.addEventListener('click', () => navigateTabs(-1)); // --- UTILITY --- function escapeHTML(str) { const p = document.createElement('p'); p.textContent = str; return p.innerHTML; } // --- INITIALIZATION --- loadState(); renderLog(); });
Scroll to Top