бла бла бла
This commit is contained in:
@@ -1,66 +1,89 @@
|
||||
|
||||
package com.htopweb.system;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import oshi.SystemInfo;
|
||||
import oshi.hardware.CentralProcessor;
|
||||
import oshi.hardware.GlobalMemory;
|
||||
import oshi.hardware.HardwareAbstractionLayer;
|
||||
import oshi.software.os.OperatingSystem;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class SystemMetricsService {
|
||||
|
||||
private final SystemInfo _systemInfo;
|
||||
private final HardwareAbstractionLayer _hardware;
|
||||
private static final Path HOST_ROOT = Path.of("/host/root");
|
||||
private static final Path HOST_PROC = HOST_ROOT.resolve("proc");
|
||||
|
||||
private final OperatingSystem _operatingSystem;
|
||||
private final CentralProcessor _processor;
|
||||
private final GlobalMemory _memory;
|
||||
|
||||
private long[] _previousCpuTicks;
|
||||
private CpuTicks _previousHostCpuTicks;
|
||||
|
||||
private static final Logger log =
|
||||
LoggerFactory.getLogger(SystemMetricsService.class);
|
||||
|
||||
public SystemMetricsService() {
|
||||
_systemInfo = new SystemInfo();
|
||||
_hardware = _systemInfo.getHardware();
|
||||
_operatingSystem = _systemInfo.getOperatingSystem();
|
||||
_processor = _hardware.getProcessor();
|
||||
_memory = _hardware.getMemory();
|
||||
SystemInfo systemInfo = new SystemInfo();
|
||||
HardwareAbstractionLayer hardware = systemInfo.getHardware();
|
||||
|
||||
_previousCpuTicks = _processor.getSystemCpuLoadTicks();
|
||||
_operatingSystem = systemInfo.getOperatingSystem();
|
||||
_processor = hardware.getProcessor();
|
||||
_memory = hardware.getMemory();
|
||||
|
||||
_previousHostCpuTicks = readHostCpuTicks();
|
||||
}
|
||||
|
||||
public synchronized SystemMetricsResponse getMetrics() {
|
||||
long[] currentCpuTicks = _processor.getSystemCpuLoadTicks();
|
||||
CpuTicks currentTicks = readHostCpuTicks();
|
||||
double cpuUsage = 0.0;
|
||||
|
||||
double cpuUsage = _processor.getSystemCpuLoadBetweenTicks(
|
||||
_previousCpuTicks,
|
||||
currentCpuTicks
|
||||
);
|
||||
if (currentTicks != null && _previousHostCpuTicks != null) {
|
||||
long totalDelta = currentTicks.total() - _previousHostCpuTicks.total();
|
||||
long idleDelta = currentTicks.idle() - _previousHostCpuTicks.idle();
|
||||
|
||||
_previousCpuTicks = currentCpuTicks;
|
||||
|
||||
if (Double.isNaN(cpuUsage) || Double.isInfinite(cpuUsage)) {
|
||||
cpuUsage = 0.0;
|
||||
if (totalDelta > 0 && idleDelta >= 0 && idleDelta <= totalDelta) {
|
||||
cpuUsage = (double) (totalDelta - idleDelta) / totalDelta;
|
||||
}
|
||||
}
|
||||
|
||||
// Task Manager displays system CPU usage in the 0–100% range.
|
||||
cpuUsage = Math.max(0.0, Math.min(1.0, cpuUsage));
|
||||
if (currentTicks != null) {
|
||||
_previousHostCpuTicks = currentTicks;
|
||||
}
|
||||
|
||||
Map<String, Long> hostMemory = readHostMemory();
|
||||
|
||||
long totalMemory = hostMemory.getOrDefault("MemTotal", _memory.getTotal());
|
||||
long availableMemory = hostMemory.getOrDefault(
|
||||
"MemAvailable",
|
||||
_memory.getAvailable()
|
||||
);
|
||||
|
||||
availableMemory = Math.max(0L, Math.min(totalMemory, availableMemory));
|
||||
|
||||
long totalMemory = _memory.getTotal();
|
||||
long availableMemory = _memory.getAvailable();
|
||||
long usedMemory = Math.max(0L, totalMemory - availableMemory);
|
||||
|
||||
double memoryUsage = totalMemory > 0
|
||||
? (double) usedMemory / totalMemory * 100.0
|
||||
: 0.0;
|
||||
|
||||
Map<String, String> osRelease = readHostOsRelease();
|
||||
|
||||
return new SystemMetricsResponse(
|
||||
_operatingSystem.getFamily(),
|
||||
_operatingSystem.getVersionInfo().getVersion(),
|
||||
_operatingSystem.getNetworkParams().getHostName(),
|
||||
getHostOsFamily(osRelease),
|
||||
getHostOsVersion(osRelease),
|
||||
readHostHostname(),
|
||||
_processor.getProcessorIdentifier().getName(),
|
||||
_processor.getLogicalProcessorCount(),
|
||||
getLogicalProcessorCount(),
|
||||
round(cpuUsage * 100.0),
|
||||
totalMemory,
|
||||
availableMemory,
|
||||
@@ -69,7 +92,187 @@ public class SystemMetricsService {
|
||||
);
|
||||
}
|
||||
|
||||
private CpuTicks readHostCpuTicks() {
|
||||
Path statFile = HOST_PROC.resolve("stat");
|
||||
|
||||
try {
|
||||
if (!Files.isRegularFile(statFile)
|
||||
|| !Files.isReadable(statFile)) {
|
||||
log.warn("Host CPU statistics are unavailable: {}", statFile);
|
||||
return null;
|
||||
}
|
||||
|
||||
for (String line : Files.readAllLines(statFile)) {
|
||||
if (!line.startsWith("cpu ")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] fields = line.trim().split("\\s+");
|
||||
if (fields.length < 5) {
|
||||
log.warn("Invalid host CPU statistics in {}", statFile);
|
||||
return null;
|
||||
}
|
||||
|
||||
long total = 0L;
|
||||
|
||||
for (int i = 1; i < fields.length; i++) {
|
||||
total += Long.parseLong(fields[i]);
|
||||
}
|
||||
|
||||
long idle = Long.parseLong(fields[4]);
|
||||
|
||||
if (fields.length > 5) {
|
||||
idle += Long.parseLong(fields[5]);
|
||||
}
|
||||
|
||||
return new CpuTicks(total, idle);
|
||||
}
|
||||
|
||||
log.warn("Aggregate CPU line not found in {}", statFile);
|
||||
} catch (IOException | NumberFormatException e) {
|
||||
log.warn("Failed to read host CPU statistics: {}", e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Map<String, Long> readHostMemory() {
|
||||
Map<String, Long> values = new HashMap<>();
|
||||
Path meminfoFile = HOST_PROC.resolve("meminfo");
|
||||
|
||||
try {
|
||||
if (!Files.isRegularFile(meminfoFile)) {
|
||||
return values;
|
||||
}
|
||||
|
||||
for (String line : Files.readAllLines(meminfoFile)) {
|
||||
int separator = line.indexOf(':');
|
||||
|
||||
if (separator < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String key = line.substring(0, separator).trim();
|
||||
|
||||
if (!key.equals("MemTotal") && !key.equals("MemAvailable")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] parts = line.substring(separator + 1).trim().split("\\s+");
|
||||
|
||||
if (parts.length > 0) {
|
||||
// Linux meminfo values are reported in kB.
|
||||
values.put(key, Long.parseLong(parts[0]) * 1024L);
|
||||
}
|
||||
}
|
||||
} catch (IOException | NumberFormatException ignored) {
|
||||
// Use OSHI values when host meminfo is unavailable.
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private int getLogicalProcessorCount() {
|
||||
Path statFile = HOST_PROC.resolve("stat");
|
||||
|
||||
try {
|
||||
if (Files.isRegularFile(statFile)) {
|
||||
int count = 0;
|
||||
|
||||
for (String line : Files.readAllLines(statFile)) {
|
||||
if (line.matches("^cpu\\d+\\s+.*")) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
// Fall back to OSHI.
|
||||
}
|
||||
|
||||
return _processor.getLogicalProcessorCount();
|
||||
}
|
||||
|
||||
private String readHostHostname() {
|
||||
try {
|
||||
Path file = HOST_ROOT.resolve("etc/hostname");
|
||||
|
||||
if (Files.isRegularFile(file)) {
|
||||
String hostname = Files.readString(file).trim();
|
||||
|
||||
if (!hostname.isBlank()) {
|
||||
return hostname;
|
||||
}
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
// Fall back to OSHI.
|
||||
}
|
||||
|
||||
return _operatingSystem.getNetworkParams().getHostName();
|
||||
}
|
||||
|
||||
private Map<String, String> readHostOsRelease() {
|
||||
Map<String, String> values = new HashMap<>();
|
||||
|
||||
try {
|
||||
Path file = HOST_ROOT.resolve("etc/os-release");
|
||||
|
||||
if (!Files.isRegularFile(file)) {
|
||||
return values;
|
||||
}
|
||||
|
||||
for (String line : Files.readAllLines(file)) {
|
||||
int separator = line.indexOf('=');
|
||||
|
||||
if (separator <= 0 || line.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String key = line.substring(0, separator).trim();
|
||||
String value = line.substring(separator + 1).trim();
|
||||
|
||||
if (value.length() >= 2
|
||||
&& ((value.startsWith("\"") && value.endsWith("\""))
|
||||
|| (value.startsWith("'") && value.endsWith("'")))) {
|
||||
value = value.substring(1, value.length() - 1);
|
||||
}
|
||||
|
||||
values.put(key, value);
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
// Fall back to OSHI.
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
private String getHostOsFamily(Map<String, String> osRelease) {
|
||||
String name = osRelease.get("NAME");
|
||||
|
||||
return name != null && !name.isBlank()
|
||||
? name
|
||||
: _operatingSystem.getFamily();
|
||||
}
|
||||
|
||||
private String getHostOsVersion(Map<String, String> osRelease) {
|
||||
String version = osRelease.get("VERSION");
|
||||
|
||||
if (version == null || version.isBlank()) {
|
||||
version = osRelease.get("VERSION_ID");
|
||||
}
|
||||
|
||||
return version != null && !version.isBlank()
|
||||
? version
|
||||
: _operatingSystem.getVersionInfo().getVersion();
|
||||
}
|
||||
|
||||
private double round(double value) {
|
||||
return Math.round(value * 10.0) / 10.0;
|
||||
}
|
||||
|
||||
private record CpuTicks(long total, long idle) {
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user