What You’ll Build
A real-time monitoring dashboard featuring:- System overview with host information
- Live CPU and memory usage meters
- Disk space monitoring for all partitions
- Network I/O statistics
- Top processes by CPU and memory
- Automatic refresh every few seconds
Prerequisites
Building the Dashboard
Import required packages
Create
main.go with all necessary imports:main.go
package main
import (
"fmt"
"os"
"os/exec"
"runtime"
"sort"
"strings"
"time"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/disk"
"github.com/shirou/gopsutil/v4/host"
"github.com/shirou/gopsutil/v4/mem"
"github.com/shirou/gopsutil/v4/net"
"github.com/shirou/gopsutil/v4/process"
)
Create dashboard state structure
Define structures to hold dashboard data:
main.go
type DashboardData struct {
Hostname string
Platform string
Uptime string
CPUPercent float64
CPUCores int
MemoryUsed uint64
MemoryTotal uint64
MemoryPercent float64
SwapUsed uint64
SwapTotal uint64
SwapPercent float64
DiskPartitions []DiskInfo
NetworkStats []NetworkInfo
TopProcesses []ProcessInfo
LastUpdate time.Time
}
type DiskInfo struct {
Device string
Mountpoint string
Total uint64
Used uint64
Free uint64
UsedPercent float64
}
type NetworkInfo struct {
Interface string
BytesSent uint64
BytesRecv uint64
PacketsSent uint64
PacketsRecv uint64
}
type ProcessInfo struct {
PID int32
Name string
CPUPercent float64
MemPercent float32
MemoryMB uint64
}
Implement data collection functions
Create functions to gather system metrics:
main.go
func collectSystemInfo() (hostname, platform, uptime string) {
hostInfo, err := host.Info()
if err == nil {
hostname = hostInfo.Hostname
platform = fmt.Sprintf("%s %s", hostInfo.Platform, hostInfo.PlatformVersion)
}
uptimeSecs, err := host.Uptime()
if err == nil {
d := time.Duration(uptimeSecs) * time.Second
days := d / (24 * time.Hour)
d -= days * 24 * time.Hour
hours := d / time.Hour
d -= hours * time.Hour
minutes := d / time.Minute
uptime = fmt.Sprintf("%dd %dh %dm", days, hours, minutes)
}
return
}
func collectCPUInfo() (percent float64, cores int) {
percentages, err := cpu.Percent(time.Second, false)
if err == nil && len(percentages) > 0 {
percent = percentages[0]
}
cores, _ = cpu.Counts(true)
return
}
func collectMemoryInfo() (used, total uint64, percent float64,
swapUsed, swapTotal uint64, swapPercent float64) {
vmStat, err := mem.VirtualMemory()
if err == nil {
used = vmStat.Used
total = vmStat.Total
percent = vmStat.UsedPercent
}
swapStat, err := mem.SwapMemory()
if err == nil {
swapUsed = swapStat.Used
swapTotal = swapStat.Total
swapPercent = swapStat.UsedPercent
}
return
}
func collectDiskInfo() []DiskInfo {
var disks []DiskInfo
partitions, err := disk.Partitions(false)
if err != nil {
return disks
}
for _, partition := range partitions {
usageStat, err := disk.Usage(partition.Mountpoint)
if err != nil {
continue
}
disks = append(disks, DiskInfo{
Device: partition.Device,
Mountpoint: partition.Mountpoint,
Total: usageStat.Total,
Used: usageStat.Used,
Free: usageStat.Free,
UsedPercent: usageStat.UsedPercent,
})
}
return disks
}
func collectNetworkInfo() []NetworkInfo {
var networks []NetworkInfo
ioCounters, err := net.IOCounters(true)
if err != nil {
return networks
}
for _, counter := range ioCounters {
// Skip loopback and interfaces with no activity
if counter.Name == "lo" ||
(counter.BytesSent == 0 && counter.BytesRecv == 0) {
continue
}
networks = append(networks, NetworkInfo{
Interface: counter.Name,
BytesSent: counter.BytesSent,
BytesRecv: counter.BytesRecv,
PacketsSent: counter.PacketsSent,
PacketsRecv: counter.PacketsRecv,
})
}
return networks
}
func collectTopProcesses(limit int) []ProcessInfo {
var processes []ProcessInfo
pids, err := process.Pids()
if err != nil {
return processes
}
for _, pid := range pids {
p, err := process.NewProcess(pid)
if err != nil {
continue
}
name, err := p.Name()
if err != nil {
continue
}
cpuPercent, _ := p.CPUPercent()
memPercent, _ := p.MemoryPercent()
memInfo, _ := p.MemoryInfo()
var memMB uint64
if memInfo != nil {
memMB = memInfo.RSS / 1024 / 1024
}
processes = append(processes, ProcessInfo{
PID: pid,
Name: name,
CPUPercent: cpuPercent,
MemPercent: memPercent,
MemoryMB: memMB,
})
}
// Sort by CPU usage
sort.Slice(processes, func(i, j int) bool {
return processes[i].CPUPercent > processes[j].CPUPercent
})
if len(processes) > limit {
processes = processes[:limit]
}
return processes
}
Create the main collection function
Combine all collectors into one function:
main.go
func collectDashboardData() DashboardData {
data := DashboardData{
LastUpdate: time.Now(),
}
// System info
data.Hostname, data.Platform, data.Uptime = collectSystemInfo()
// CPU info
data.CPUPercent, data.CPUCores = collectCPUInfo()
// Memory info
data.MemoryUsed, data.MemoryTotal, data.MemoryPercent,
data.SwapUsed, data.SwapTotal, data.SwapPercent = collectMemoryInfo()
// Disk info
data.DiskPartitions = collectDiskInfo()
// Network info
data.NetworkStats = collectNetworkInfo()
// Top processes
data.TopProcesses = collectTopProcesses(10)
return data
}
Create display formatting functions
Add functions to format and display the data:
main.go
func clearScreen() {
if runtime.GOOS == "windows" {
cmd := exec.Command("cmd", "/c", "cls")
cmd.Stdout = os.Stdout
cmd.Run()
} else {
cmd := exec.Command("clear")
cmd.Stdout = os.Stdout
cmd.Run()
}
}
func formatBytes(bytes uint64) string {
const unit = 1024
if bytes < unit {
return fmt.Sprintf("%d B", bytes)
}
div, exp := uint64(unit), 0
for n := bytes / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
}
func createProgressBar(percent float64, width int) string {
filled := int(percent / 100 * float64(width))
if filled > width {
filled = width
}
bar := strings.Repeat("█", filled)
empty := strings.Repeat("░", width-filled)
return fmt.Sprintf("[%s%s] %.1f%%", bar, empty, percent)
}
func displayDashboard(data DashboardData) {
clearScreen()
fmt.Println("╔═══════════════════════════════════════════════════════════════════════════════╗")
fmt.Println("║ SYSTEM MONITORING DASHBOARD ║")
fmt.Println("╚═══════════════════════════════════════════════════════════════════════════════╝")
fmt.Printf("\nLast Update: %s\n\n", data.LastUpdate.Format("2006-01-02 15:04:05"))
// System Information
fmt.Println("┌─ SYSTEM INFORMATION ─────────────────────────────────────────────────────────┐")
fmt.Printf("│ Hostname: %-66s │\n", data.Hostname)
fmt.Printf("│ Platform: %-66s │\n", data.Platform)
fmt.Printf("│ Uptime: %-66s │\n", data.Uptime)
fmt.Println("└──────────────────────────────────────────────────────────────────────────────┘")
// CPU Information
fmt.Println("\n┌─ CPU ────────────────────────────────────────────────────────────────────────┐")
fmt.Printf("│ Cores: %d\n", data.CPUCores)
fmt.Printf("│ Usage: %s\n", createProgressBar(data.CPUPercent, 50))
fmt.Println("└──────────────────────────────────────────────────────────────────────────────┘")
// Memory Information
fmt.Println("\n┌─ MEMORY ─────────────────────────────────────────────────────────────────────┐")
fmt.Printf("│ RAM: %s / %s\n",
formatBytes(data.MemoryUsed), formatBytes(data.MemoryTotal))
fmt.Printf("│ %s\n", createProgressBar(data.MemoryPercent, 50))
fmt.Printf("│\n")
fmt.Printf("│ Swap: %s / %s\n",
formatBytes(data.SwapUsed), formatBytes(data.SwapTotal))
fmt.Printf("│ %s\n", createProgressBar(data.SwapPercent, 50))
fmt.Println("└──────────────────────────────────────────────────────────────────────────────┘")
// Disk Information
fmt.Println("\n┌─ DISK USAGE ─────────────────────────────────────────────────────────────────┐")
for _, disk := range data.DiskPartitions {
fmt.Printf("│ %s (%s)\n", disk.Mountpoint, disk.Device)
fmt.Printf("│ %s / %s\n",
formatBytes(disk.Used), formatBytes(disk.Total))
fmt.Printf("│ %s\n", createProgressBar(disk.UsedPercent, 50))
fmt.Printf("│\n")
}
fmt.Println("└──────────────────────────────────────────────────────────────────────────────┘")
// Network Information
if len(data.NetworkStats) > 0 {
fmt.Println("\n┌─ NETWORK I/O ────────────────────────────────────────────────────────────────┐")
fmt.Printf("│ %-15s %-15s %-15s %-15s %-12s│\n",
"Interface", "Sent", "Received", "Packets Out", "Packets In")
fmt.Printf("│ %s│\n", strings.Repeat("─", 78))
for _, net := range data.NetworkStats {
fmt.Printf("│ %-15s %-15s %-15s %-15d %-12d│\n",
net.Interface,
formatBytes(net.BytesSent),
formatBytes(net.BytesRecv),
net.PacketsSent,
net.PacketsRecv,
)
}
fmt.Println("└──────────────────────────────────────────────────────────────────────────────┘")
}
// Top Processes
fmt.Println("\n┌─ TOP PROCESSES (by CPU) ─────────────────────────────────────────────────────┐")
fmt.Printf("│ %-8s %-25s %-10s %-10s %-12s│\n",
"PID", "Name", "CPU %", "Mem %", "Memory")
fmt.Printf("│ %s│\n", strings.Repeat("─", 78))
for _, proc := range data.TopProcesses {
name := proc.Name
if len(name) > 25 {
name = name[:22] + "..."
}
fmt.Printf("│ %-8d %-25s %-10.2f %-10.2f %-12s│\n",
proc.PID,
name,
proc.CPUPercent,
proc.MemPercent,
formatBytes(proc.MemoryMB*1024*1024),
)
}
fmt.Println("└──────────────────────────────────────────────────────────────────────────────┘")
fmt.Println("\nPress Ctrl+C to exit")
}
Create the main loop
Implement the main function with continuous refresh:
main.go
func main() {
fmt.Println("Starting System Dashboard...")
fmt.Println("Loading data...\n")
time.Sleep(1 * time.Second)
// Set up refresh ticker
ticker := time.NewTicker(3 * time.Second)
defer ticker.Stop()
// Initial display
data := collectDashboardData()
displayDashboard(data)
// Update loop
for range ticker.C {
data = collectDashboardData()
displayDashboard(data)
}
}
Example Output
╔═══════════════════════════════════════════════════════════════════════════════╗
║ SYSTEM MONITORING DASHBOARD ║
╚═══════════════════════════════════════════════════════════════════════════════╝
Last Update: 2024-03-09 15:30:45
┌─ SYSTEM INFORMATION ─────────────────────────────────────────────────────────┐
│ Hostname: my-server │
│ Platform: ubuntu 22.04 │
│ Uptime: 5d 12h 30m │
└──────────────────────────────────────────────────────────────────────────────┘
┌─ CPU ────────────────────────────────────────────────────────────────────────┐
│ Cores: 8
│ Usage: [███████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 23.4%
└──────────────────────────────────────────────────────────────────────────────┘
┌─ MEMORY ─────────────────────────────────────────────────────────────────────┐
│ RAM: 8.2 GB / 16.0 GB
│ [█████████████████████████░░░░░░░░░░░░░░░░░░░░░░░░░] 51.2%
│
│ Swap: 0 B / 4.0 GB
│ [░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0.0%
└──────────────────────────────────────────────────────────────────────────────┘
┌─ TOP PROCESSES (by CPU) ─────────────────────────────────────────────────────┐
│ PID Name CPU % Mem % Memory │
│ ──────────────────────────────────────────────────────────────────────────────│
│ 1234 chrome 45.32 8.50 1.0 GB │
│ 5678 node 12.45 4.20 512.0 MB │
│ 9012 docker 8.90 3.10 400.0 MB │
└──────────────────────────────────────────────────────────────────────────────┘
Press Ctrl+C to exit
Enhancements
Consider adding these features to your dashboard:Historical Data
Store historical metrics to show trends and create graphs.
Alerts
Add threshold alerts for high CPU, memory, or disk usage.
Export Data
Save metrics to CSV or JSON for analysis.
Web Interface
Create an HTTP server to display the dashboard in a browser.
Key Features
Real-time Updates
Dashboard refreshes automatically every 3 seconds with live data.
Comprehensive View
Monitor all system aspects: CPU, memory, disk, network, and processes.
Visual Indicators
Progress bars provide quick visual understanding of resource usage.
Process Ranking
Automatically shows top resource-consuming processes.
For production use, consider:
- Adding graceful shutdown handling
- Implementing error recovery for data collection failures
- Adding configuration options for refresh rate
- Creating custom views for different monitoring needs
Related Examples
Basic Monitoring
Learn the fundamentals of system monitoring.
Process Monitoring
Dive deeper into process management and monitoring.