UNPKG

bodhi-node-profiler

Version:

A lightweight, zero-configuration performance profiler for Node.js applications with real-time dashboard

231 lines (207 loc) 6.94 kB
let charts = { cpu: null, memory: null, eventLoop: null, api: null }; const startTime = Date.now(); const createChart = (ctx, label, options = {}) => { return new Chart(ctx, { type: 'line', data: { labels: [], datasets: [{ label: label, data: [], borderColor: 'rgb(99, 102, 241)', backgroundColor: 'rgba(99, 102, 241, 0.1)', borderWidth: 2, fill: true, tension: 0.4, pointRadius: 0, pointHitRadius: 20 }] }, options: { responsive: true, maintainAspectRatio: false, animation: { duration: 0 }, plugins: { legend: { display: false }, tooltip: { mode: 'index', intersect: false, callbacks: { label: function(context) { return `${label}: ${context.parsed.y.toFixed(2)}${options.unit || ''}`; } } } }, scales: { x: { grid: { display: false } }, y: { beginAtZero: true, grid: { color: 'rgba(0, 0, 0, 0.05)' }, ...options.scales?.y } } } }); }; const initializeCharts = () => { charts.cpu = createChart(document.getElementById('cpuChart').getContext('2d'), 'CPU Usage', { unit: '%', scales: { y: { max: 100, ticks: { callback: value => value + '%' } } } }); charts.memory = createChart(document.getElementById('memoryChart').getContext('2d'), 'Memory', { unit: ' MB', scales: { y: { ticks: { callback: value => value.toFixed(0) + ' MB' } } } }); charts.eventLoop = createChart(document.getElementById('eventLoopChart').getContext('2d'), 'Delay', { unit: ' ms', scales: { y: { ticks: { callback: value => value.toFixed(1) + ' ms' } } } }); charts.api = createChart(document.getElementById('apiChart').getContext('2d'), 'Response Time', { unit: ' ms', scales: { y: { ticks: { callback: value => value.toFixed(0) + ' ms' } } } }); }; const updateCharts = (metrics) => { const timestamp = moment(metrics.timestamp).format('HH:mm:ss'); // Update CPU Chart charts.cpu.data.labels.push(timestamp); charts.cpu.data.datasets[0].data.push(parseFloat(metrics.cpu.percentage)); if (charts.cpu.data.labels.length > 30) { charts.cpu.data.labels.shift(); charts.cpu.data.datasets[0].data.shift(); } charts.cpu.update(); // Update Memory Chart const memoryUsageMB = metrics.memory.heapUsed / (1024 * 1024); charts.memory.data.labels.push(timestamp); charts.memory.data.datasets[0].data.push(memoryUsageMB); if (charts.memory.data.labels.length > 30) { charts.memory.data.labels.shift(); charts.memory.data.datasets[0].data.shift(); } charts.memory.update(); // Update Event Loop Chart charts.eventLoop.data.labels.push(timestamp); charts.eventLoop.data.datasets[0].data.push(metrics.eventLoopDelay); if (charts.eventLoop.data.labels.length > 30) { charts.eventLoop.data.labels.shift(); charts.eventLoop.data.datasets[0].data.shift(); } charts.eventLoop.update(); // Update API Chart if (metrics.api) { charts.api.data.labels.push(timestamp); charts.api.data.datasets[0].data.push(metrics.api.responseTime); if (charts.api.data.labels.length > 30) { charts.api.data.labels.shift(); charts.api.data.datasets[0].data.shift(); } charts.api.update(); } }; const updateMetricCard = (id, value, unit, trend, thresholds) => { const statusEl = document.getElementById(`${id}Status`); const valueEl = document.getElementById(`${id}Value`); const trendEl = document.getElementById(`${id}Trend`); valueEl.textContent = `${value.toFixed(1)}${unit}`; let status; if (value >= thresholds.high) { status = { class: 'bg-red-100 text-red-800', text: 'High' }; } else if (value >= thresholds.warning) { status = { class: 'bg-yellow-100 text-yellow-800', text: 'Warning' }; } else { status = { class: 'bg-green-100 text-green-800', text: 'Good' }; } statusEl.className = `px-3 py-1 rounded-full text-sm font-medium ${status.class}`; statusEl.textContent = status.text; if (trend) { const trendText = trend > 0 ? `↑ ${trend.toFixed(1)}% from last min` : `↓ ${Math.abs(trend).toFixed(1)}% from last min`; trendEl.textContent = trendText; } }; const updateSystemMetrics = (metrics) => { // CPU Metrics updateMetricCard('cpu', parseFloat(metrics.cpu.percentage), '%', null, { warning: 60, high: 80 }); // Memory Metrics const memoryUsage = (metrics.memory.heapUsed / metrics.memory.heapTotal) * 100; updateMetricCard('memory', memoryUsage, '%', null, { warning: 70, high: 85 }); // Event Loop Metrics updateMetricCard('eventLoop', metrics.eventLoopDelay, ' ms', null, { warning: 50, high: 100 }); // API Metrics if (metrics.api) { updateMetricCard('api', metrics.api.responseTime, ' ms', null, { warning: 500, high: 1000 }); } // System Info document.getElementById('totalMemory').textContent = `${(metrics.memory.heapTotal / (1024 * 1024)).toFixed(0)} MB`; document.getElementById('uptime').textContent = moment.duration(Date.now() - startTime).humanize(); document.getElementById('lastUpdated').textContent = moment(metrics.timestamp).format('HH:mm:ss'); }; const fetchMetrics = async () => { try { const response = await fetch('/profiler/stats'); const metrics = await response.json(); updateCharts(metrics); updateSystemMetrics(metrics); } catch (error) { console.error('Error fetching metrics:', error); } }; // Initialize document.addEventListener('DOMContentLoaded', () => { initializeCharts(); fetchMetrics(); // Initial fetch setInterval(fetchMetrics, 1000); });