Simple Server Monitoring with Java: Track CPU, Disk & Network IO on Linux (Fedora)
Server monitoring is critical for maintaining system health, optimizing performance, and troubleshooting issues. While tools like Prometheus, Grafana, or Nagios offer robust solutions, there are scena
Server monitoring is critical for maintaining system health, optimizing performance, and troubleshooting issues. While tools like Prometheus, Grafana, or Nagios offer robust solutions, there are scenarios where a lightweight, custom Java-based monitor is preferred—e.g., for embedded systems, low-resource environments, or integrating monitoring directly into a Java application.
In this blog, we’ll build a simple yet powerful server monitoring tool using Java to track CPU usage, disk metrics, and network IO on a Linux (Fedora) system. We’ll leverage the OSHI (Operating System and Hardware Information) library, a cross-platform API that abstracts low-level system details, making it easy to fetch hardware and OS metrics without writing OS-specific code.
By the end, you’ll have a Java application that periodically collects and displays key system metrics, with code you can extend for alerts, logging, or integration with dashboards.
Discover more
Linux kernel
File system
scripting
Scripting
Linux Kernel
Linux
Bash
Kernel
open-source
Compilers
Discover more
shell
Kernel
Scripting language
File System
Linux
Linux Kernel
open-source
Bash
Open source
kernel
Table of Contents#
- Prerequisites
- Setting Up the Project
- Monitoring CPU Usage
- 3.1 CPU Cores and Load Average
- 3.2 Per-Core CPU Usage
- Monitoring Disk Usage
- 4.1 Disk Space (Total/Used/Free)
- 4.2 Disk IO (Read/Write Rates)
- Monitoring Network IO
- 5.1 Network Interfaces and Throughput
- 5.2 Calculating Network Rates
- Putting It All Together: A Simple Monitor App
- Troubleshooting
- Conclusion & Enhancements
- References
Discover more
shell
Scripting
Linux kernel
Shell
file system
Open source
Kernel
open-source
Scripting language
Bash
Prerequisites#
Before starting, ensure you have the following tools installed on your Fedora system:
- Java Development Kit (JDK): Version 11 or higher. Install via:
sudo dnf install java-17-openjdk-devel # For JDK 17 (LTS) - Maven: For project management and dependencies. Install via:
sudo dnf install maven - Basic Linux Knowledge: Familiarity with concepts like CPU ticks, disk partitions, and network interfaces will help.
Setting Up the Project#
We’ll use Maven to create a new Java project and include OSHI (Operating System and Hardware Information) as a dependency. OSHI simplifies accessing system metrics by abstracting OS-specific APIs (e.g., /proc on Linux, WMI on Windows).
Step 1: Create a Maven Project#
Run the following command to generate a basic Maven project:
mvn archetype:generate -DgroupId=com.servermonitor -DartifactId=system-monitor -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
This creates a system-monitor directory with a standard Maven structure.
Step 2: Add OSHI Dependency#
Open pom.xml and add OSHI to the <dependencies> section. As of 2024, the latest stable version is 6.4.0:
<dependencies> <!-- OSHI: System metrics library --> <dependency> <groupId>com.github.oshi</groupId> <artifactId>oshi-core</artifactId> <version>6.4.0</version> </dependency> <!-- SLF4J Simple (for logging; optional but helpful) --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>2.0.9</version> </dependency> </dependencies>
OSHI requires SLF4J for logging, so we include slf4j-simple for simplicity.
Monitoring CPU Usage#
CPU metrics help identify bottlenecks (e.g., high usage, uneven core distribution). We’ll track:
- Total CPU usage percentage
- CPU core count (physical/logical)
- System load average (1/5/15-minute)
- Per-core CPU usage
3.1 CPU Cores and Load Average#
OSHI’s SystemInfo class provides access to hardware and OS details. Use CentralProcessor to fetch CPU data:
import oshi.SystemInfo; import oshi.hardware.CentralProcessor; public class CpuMonitor { private final CentralProcessor cpu; public CpuMonitor() { SystemInfo systemInfo = new SystemInfo(); this.cpu = systemInfo.getHardware().getProcessor(); } // Get CPU core info public void printCpuCores() { System.out.println("CPU Cores:"); System.out.println(" Physical: " + cpu.getPhysicalProcessorCount()); System.out.println(" Logical: " + cpu.getLogicalProcessorCount()); } // Get system load average (1/5/15 min) public void printLoadAverage() { double[] loadAvg = cpu.getSystemLoadAverage(3); // 3 samples: 1,5,15 min System.out.println("\nLoad Average:"); System.out.printf(" 1 min: %.2f | 5 min: %.2f | 15 min: %.2f%n", loadAvg[0], loadAvg[1], loadAvg[2]); } }
3.2 Total and Per-Core CPU Usage#
CPU usage is calculated by comparing "ticks" (time spent in user, system, idle, etc.) over a period. OSHI provides getSystemCpuLoad() for total usage, but we’ll also calculate per-core usage manually:
// Inside CpuMonitor class private long[] prevTicks; // Initialize previous ticks for usage calculation public void initCpuTicks() { prevTicks = cpu.getSystemCpuLoadTicks(); } // Calculate total CPU usage (%) over a interval public double getTotalCpuUsage() { long[] currTicks = cpu.getSystemCpuLoadTicks(); double usage = cpu.getSystemCpuLoadBetweenTicks(prevTicks) * 100; prevTicks = currTicks; // Update for next calculation return usage; } // Print per-core CPU usage (%) public void printPerCoreUsage() { System.out.println("\nPer-Core Usage (%):"); double[] coreLoads = cpu.getProcessorCpuLoadBetweenTicks(); // Uses prev ticks for (int i = 0; i < coreLoads.length; i++) { System.out.printf(" Core %d: %.1f%%%n", i, coreLoads[i] * 100); } }
How it works:
getSystemCpuLoadTicks()returns an array of ticks for user, nice, system, idle, etc.getSystemCpuLoadBetweenTicks(prevTicks)computes usage as(total - idle) / totalover the interval.
Monitoring Disk Usage#
Disk metrics include space usage (total/used/free) and IO performance (read/write rates, operations per second).
4.1 Disk Space (Total/Used/Free)#
OSHI’s FileSystem class lists mounted filesystems and their usage. We’ll focus on local filesystems (e.g., /, /home):
import oshi.hardware.HardwareAbstractionLayer; import oshi.software.os.FileSystem; import oshi.software.os.OSFileStore; public class DiskMonitor { private final FileSystem fileSystem; public DiskMonitor() { SystemInfo systemInfo = new SystemInfo(); this.fileSystem = systemInfo.getOperatingSystem().getFileSystem(); } // Print disk space for local filesystems public void printDiskSpace() { System.out.println("\nDisk Space Usage:"); for (OSFileStore store : fileSystem.getFileStores()) { // Skip non-local or temporary filesystems if (store.getType().equals("tmpfs") || store.getMount().startsWith("//")) { continue; } long total = store.getTotalSpace(); long used = store.getTotalSpace() - store.getFreeSpace(); long free = store.getFreeSpace(); System.out.printf(" Mount: %s%n", store.getMount()); System.out.printf(" Total: %.2f GB | Used: %.2f GB (%.1f%%) | Free: %.2f GB%n", total / (1e9), used / (1e9), (used * 100.0) / total, free / (1e9)); } } }
4.2 Disk IO (Read/Write Rates)#
To track disk IO, use HWDiskStore to get cumulative read/write bytes and operations. Calculate rates by sampling over time:
import oshi.hardware.HWDiskStore; public class DiskMonitor { // ... (previous code) private HWDiskStore[] prevDiskStats; // Initialize disk stats for IO calculation public void initDiskStats() { HardwareAbstractionLayer hal = new SystemInfo().getHardware(); prevDiskStats = hal.getDiskStores(); } // Print disk IO rates (per second) public void printDiskIoRates(int intervalSec) { HardwareAbstractionLayer hal = new SystemInfo().getHardware(); HWDiskStore[] currDiskStats = hal.getDiskStores(); System.out.println("\nDisk IO Rates:"); for (int i = 0; i < currDiskStats.length; i++) { HWDiskStore prev = prevDiskStats[i]; HWDiskStore curr = currDiskStats[i]; long readBytes = curr.getReadBytes() - prev.getReadBytes(); long writeBytes = curr.getWriteBytes() - prev.getWriteBytes(); long readOps = curr.getReads() - prev.getReads(); long writeOps = curr.getWrites() - prev.getWrites(); System.out.printf(" Disk: %s%n", curr.getName()); System.out.printf(" Read: %.2f MB/s | Write: %.2f MB/s%n", readBytes / (1e6 * intervalSec), writeBytes / (1e6 * intervalSec)); System.out.printf(" Read Ops: %d/s | Write Ops: %d/s%n", readOps / intervalSec, writeOps / intervalSec); } prevDiskStats = currDiskStats; // Update for next interval } }
Monitoring Network IO#
Network metrics include throughput (bytes sent/received per second) and interface status (e.g., up/down).
5.1 Network Interfaces and Throughput#
OSHI’s NetworkIF class provides network interface details. We’ll track non-loopback interfaces (e.g., eth0, wlan0):
import oshi.hardware.NetworkIF; import java.util.List; public class NetworkMonitor { private List<NetworkIF> prevNetStats; public NetworkMonitor() { SystemInfo systemInfo = new SystemInfo(); this.prevNetStats = systemInfo.getHardware().getNetworkIFs(); // Initialize stats (first read) for (NetworkIF iface : prevNetStats) { iface.updateAttributes(); } } // Print network throughput (bytes sent/received per second) public void printNetworkRates(int intervalSec) { SystemInfo systemInfo = new SystemInfo(); List<NetworkIF> currNetStats = systemInfo.getHardware().getNetworkIFs(); System.out.println("\nNetwork Throughput:"); for (NetworkIF curr : currNetStats) { // Skip loopback and down interfaces if (curr.isLoopback() || !curr.isUp()) { continue; } // Find previous stats for this interface NetworkIF prev = prevNetStats.stream() .filter(p -> p.getName().equals(curr.getName())) .findFirst().orElse(null); if (prev == null) continue; long recvBytes = curr.getBytesRecv() - prev.getBytesRecv(); long sentBytes = curr.getBytesSent() - prev.getBytesSent(); System.out.printf(" Interface: %s%n", curr.getName()); System.out.printf(" Received: %.2f MB/s | Sent: %.2f MB/s%n", recvBytes / (1e6 * intervalSec), sentBytes / (1e6 * intervalSec)); } prevNetStats = currNetStats; // Update for next interval } }
Putting It All Together: A Simple Monitor App#
Combine the monitors into a single application that runs in a loop, collecting metrics every 5 seconds:
public class SystemMonitorApp { private static final int INTERVAL_SEC = 5; // Collect metrics every 5s public static void main(String[] args) throws InterruptedException { CpuMonitor cpuMonitor = new CpuMonitor(); DiskMonitor diskMonitor = new DiskMonitor(); NetworkMonitor networkMonitor = new NetworkMonitor(); // Initialize CPU and disk ticks for usage calculation cpuMonitor.initCpuTicks(); diskMonitor.initDiskStats(); while (true) { System.out.println("\n=== Metrics Collected at " + java.time.LocalTime.now() + " ==="); // CPU Metrics cpuMonitor.printCpuCores(); cpuMonitor.printLoadAverage(); System.out.printf("Total CPU Usage: %.1f%%%n", cpuMonitor.getTotalCpuUsage()); cpuMonitor.printPerCoreUsage(); // Disk Metrics diskMonitor.printDiskSpace(); diskMonitor.printDiskIoRates(INTERVAL_SEC); // Network Metrics networkMonitor.printNetworkRates(INTERVAL_SEC); Thread.sleep(INTERVAL_SEC * 1000); // Wait for next interval } } }
Run the Application#
-
Navigate to the project directory:
cd system-monitor -
Run with Maven:
mvn exec:java -Dexec.mainClass="com.servermonitor.SystemMonitorApp"
Sample Output:
=== Metrics Collected at 14:30:00 ===
CPU Cores:
Physical: 4 | Logical: 8
Load Average:
1 min: 0.85 | 5 min: 0.72 | 15 min: 0.68
Total CPU Usage: 23.5%
Per-Core Usage (%):
Core 0: 18.2% | Core 1: 25.1% | ...
Disk Space Usage:
Mount: /
Total: 465.76 GB | Used: 120.45 GB (25.9%) | Free: 345.31 GB
Disk IO Rates:
Disk: sda
Read: 0.52 MB/s | Write: 2.10 MB/s
Read Ops: 12/s | Write Ops: 35/s
Network Throughput:
Interface: eth0
Received: 1.25 MB/s | Sent: 0.82 MB/s
Troubleshooting#
- OSHI Not Detecting Hardware: Ensure you’re using the latest OSHI version. Some metrics require read access to
/proc(default on Linux). - High CPU Usage in Monitor: The monitor itself uses minimal resources, but reduce the interval (e.g., 10 seconds) if needed.
- Missing Dependencies: Run
mvn clean installto resolve missing libraries.
Conclusion & Enhancements#
This blog covered building a Java monitor for CPU, disk, and network metrics on Fedora Linux using OSHI. You can extend it by:
- Sending metrics to a database (e.g., InfluxDB) or dashboard (Grafana).
- Adding alerts (e.g., email/Slack when CPU > 90%).
- Supporting Windows/macOS (OSHI is cross-platform).
- Adding memory usage (use
oshi.hardware.GlobalMemory).
Discover more
Scripting language
Kernel
Bash
compiler
Compiler
Linux kernel
shell
Scripting
kernel
scripting
References#
- OSHI GitHub (official docs and examples)
- OSHI Javadoc
- Fedora System Monitoring Guide
- Java ManagementFactory (for basic JVM metrics)
更多推荐

所有评论(0)