UNPKG

bodhi-node-profiler

Version:

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

252 lines (224 loc) 7.92 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 updateSystemMetrics = (metrics) => { // Update CPU Usage const cpuElement = document.getElementById('cpu-usage'); const cpuValue = metrics.cpu.percentage.toFixed(1); cpuElement.textContent = `${cpuValue}%`; updateStatus('cpu-status', cpuValue); // Update Memory Usage const memoryElement = document.getElementById('memory-usage'); const memoryValue = metrics.memory.percentage; memoryElement.textContent = `${memoryValue}%`; updateStatus('memory-status', memoryValue); // Update Event Loop Delay const eventLoopElement = document.getElementById('event-loop-delay'); const eventLoopValue = metrics.eventLoop.latency.toFixed(1); eventLoopElement.textContent = `${eventLoopValue} ms`; updateStatus('event-loop-status', eventLoopValue); // Update API Response const apiElement = document.getElementById('api-response'); const apiValue = metrics.api.responseTime.toFixed(1); apiElement.textContent = `${apiValue} ms`; updateStatus('api-status', apiValue); // Update System Information const totalMemoryElement = document.getElementById('total-memory'); totalMemoryElement.textContent = `${metrics.memory.heapTotal} MB`; const uptimeElement = document.getElementById('uptime'); const uptime = parseInt(metrics.uptime); let uptimeText = ''; if (uptime < 60) { uptimeText = `${uptime}s`; } else if (uptime < 3600) { uptimeText = `${Math.floor(uptime / 60)}m ${uptime % 60}s`; } else { const hours = Math.floor(uptime / 3600); const minutes = Math.floor((uptime % 3600) / 60); uptimeText = `${hours}h ${minutes}m`; } uptimeElement.textContent = uptimeText; const lastUpdatedElement = document.getElementById('last-updated'); const date = new Date(metrics.timestamp); const timeStr = date.toLocaleTimeString(); lastUpdatedElement.textContent = timeStr; // Hide all "Collecting data..." messages document.querySelectorAll('.collecting-data').forEach(el => { el.style.display = 'none'; }); }; const updateStatus = (id, value) => { const statusEl = document.getElementById(id); let status; const numValue = parseFloat(value); if (numValue >= 80) { status = { class: 'bg-red-100 text-red-800', text: 'High' }; } else if (numValue >= 60) { 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; }; const updateCharts = (metrics) => { const timestamp = new Date(metrics.timestamp); // Update CPU chart charts.cpu.data.labels.push(formatTime(timestamp)); charts.cpu.data.datasets[0].data.push(metrics.cpu.percentage); if (charts.cpu.data.labels.length > 20) { charts.cpu.data.labels.shift(); charts.cpu.data.datasets[0].data.shift(); } charts.cpu.update('none'); // Update Memory chart charts.memory.data.labels.push(formatTime(timestamp)); charts.memory.data.datasets[0].data.push(metrics.memory.percentage); if (charts.memory.data.labels.length > 20) { charts.memory.data.labels.shift(); charts.memory.data.datasets[0].data.shift(); } charts.memory.update('none'); // Update Event Loop chart charts.eventLoop.data.labels.push(formatTime(timestamp)); charts.eventLoop.data.datasets[0].data.push(metrics.eventLoop.latency); if (charts.eventLoop.data.labels.length > 20) { charts.eventLoop.data.labels.shift(); charts.eventLoop.data.datasets[0].data.shift(); } charts.eventLoop.update('none'); // Update API Response Time chart charts.api.data.labels.push(formatTime(timestamp)); charts.api.data.datasets[0].data.push(metrics.api.responseTime); if (charts.api.data.labels.length > 20) { charts.api.data.labels.shift(); charts.api.data.datasets[0].data.shift(); } charts.api.update('none'); }; const formatTime = (date) => { return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`; }; const fetchMetrics = async () => { try { console.log('Fetching metrics...'); const response = await fetch('/profiler/stats'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const metrics = await response.json(); console.log('Received metrics:', metrics); updateCharts(metrics); updateSystemMetrics(metrics); } catch (error) { console.error('Error fetching metrics:', error); } }; // Initialize document.addEventListener('DOMContentLoaded', () => { console.log('Initializing dashboard...'); initializeCharts(); fetchMetrics(); // Initial fetch setInterval(fetchMetrics, 1000); // Poll every second });