diff --git a/pom.xml b/pom.xml index 13986c6..ab8acc4 100644 --- a/pom.xml +++ b/pom.xml @@ -49,6 +49,11 @@ spring-retry [2.0.0,3.0.0) + + com.github.oshi + oshi-core + 6.9.2 + com.github.spotbugs spotbugs-annotations diff --git a/src/main/java/io/apitally/common/ApitallyClient.java b/src/main/java/io/apitally/common/ApitallyClient.java index a647811..b664a78 100644 --- a/src/main/java/io/apitally/common/ApitallyClient.java +++ b/src/main/java/io/apitally/common/ApitallyClient.java @@ -72,6 +72,7 @@ public enum HubRequestStatus { public final ValidationErrorCounter validationErrorCounter; public final ServerErrorCounter serverErrorCounter; public final ConsumerRegistry consumerRegistry; + public final ResourceMonitor resourceMonitor; private final Queue syncDataQueue = new ConcurrentLinkedQueue(); private final Random random = new Random(); @@ -87,6 +88,7 @@ public ApitallyClient(String clientId, String env, RequestLoggingConfig requestL this.validationErrorCounter = new ValidationErrorCounter(); this.serverErrorCounter = new ServerErrorCounter(); this.consumerRegistry = new ConsumerRegistry(); + this.resourceMonitor = new ResourceMonitor(); } public boolean isEnabled() { @@ -149,7 +151,8 @@ private void sendSyncData() { requestCounter.getAndResetRequests(), validationErrorCounter.getAndResetValidationErrors(), serverErrorCounter.getAndResetServerErrors(), - consumerRegistry.getAndResetConsumers()); + consumerRegistry.getAndResetConsumers(), + resourceMonitor.getCpuMemoryUsage()); syncDataQueue.offer(data); int i = 0; diff --git a/src/main/java/io/apitally/common/ResourceMonitor.java b/src/main/java/io/apitally/common/ResourceMonitor.java new file mode 100644 index 0000000..0cec7a0 --- /dev/null +++ b/src/main/java/io/apitally/common/ResourceMonitor.java @@ -0,0 +1,34 @@ +package io.apitally.common; + +import io.apitally.common.dto.ResourceUsage; +import oshi.SystemInfo; +import oshi.software.os.OSProcess; + +public class ResourceMonitor { + private final SystemInfo systemInfo = new SystemInfo(); + private OSProcess previousSnapshot; + + public ResourceUsage getCpuMemoryUsage() { + try { + int pid = (int) ProcessHandle.current().pid(); + OSProcess currentProcess = systemInfo.getOperatingSystem().getProcess(pid); + if (currentProcess == null) { + return null; + } + + if (previousSnapshot == null) { + previousSnapshot = currentProcess; + return null; + } + + double cpuPercent = + 100.0 * currentProcess.getProcessCpuLoadBetweenTicks(previousSnapshot); + long memoryRss = currentProcess.getResidentSetSize(); + previousSnapshot = currentProcess; + + return new ResourceUsage(cpuPercent, memoryRss); + } catch (Exception e) { + return null; + } + } +} diff --git a/src/main/java/io/apitally/common/dto/ResourceUsage.java b/src/main/java/io/apitally/common/dto/ResourceUsage.java new file mode 100644 index 0000000..4efff2a --- /dev/null +++ b/src/main/java/io/apitally/common/dto/ResourceUsage.java @@ -0,0 +1,23 @@ +package io.apitally.common.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class ResourceUsage { + private final double cpuPercent; + private final long memoryRss; + + public ResourceUsage(double cpuPercent, long memoryRss) { + this.cpuPercent = cpuPercent; + this.memoryRss = memoryRss; + } + + @JsonProperty("cpu_percent") + public double getCpuPercent() { + return cpuPercent; + } + + @JsonProperty("memory_rss") + public long getMemoryRss() { + return memoryRss; + } +} diff --git a/src/main/java/io/apitally/common/dto/SyncData.java b/src/main/java/io/apitally/common/dto/SyncData.java index bc043a7..6234b12 100644 --- a/src/main/java/io/apitally/common/dto/SyncData.java +++ b/src/main/java/io/apitally/common/dto/SyncData.java @@ -1,6 +1,7 @@ package io.apitally.common.dto; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; import java.util.UUID; @@ -13,13 +14,15 @@ public class SyncData extends BaseDto { private final List validationErrors; private final List serverErrors; private final List consumers; + private final ResourceUsage resources; public SyncData( UUID instanceUuid, List requests, List validationErrors, List serverErrors, - List consumers) { + List consumers, + ResourceUsage resources) { this.timestamp = System.currentTimeMillis() / 1000.0; this.instanceUuid = instanceUuid; this.messageUuid = UUID.randomUUID(); @@ -27,6 +30,7 @@ public SyncData( this.validationErrors = validationErrors; this.serverErrors = serverErrors; this.consumers = consumers; + this.resources = resources; } @JsonProperty("timestamp") @@ -64,6 +68,12 @@ public List getConsumers() { return consumers; } + @JsonProperty("resources") + @JsonInclude(JsonInclude.Include.NON_NULL) + public ResourceUsage getResources() { + return resources; + } + @JsonIgnore public double getAgeInSeconds() { return System.currentTimeMillis() / 1000.0 - timestamp; diff --git a/src/test/java/io/apitally/common/ResourceMonitorTest.java b/src/test/java/io/apitally/common/ResourceMonitorTest.java new file mode 100644 index 0000000..1ace0a5 --- /dev/null +++ b/src/test/java/io/apitally/common/ResourceMonitorTest.java @@ -0,0 +1,26 @@ +package io.apitally.common; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import io.apitally.common.dto.ResourceUsage; +import org.junit.jupiter.api.Test; + +class ResourceMonitorTest { + + @Test + void testGetCpuMemoryUsage() throws InterruptedException { + ResourceMonitor resourceMonitor = new ResourceMonitor(); + + ResourceUsage firstResult = resourceMonitor.getCpuMemoryUsage(); + assertNull(firstResult); + + Thread.sleep(100); + + ResourceUsage secondResult = resourceMonitor.getCpuMemoryUsage(); + assertNotNull(secondResult); + assertTrue(secondResult.getCpuPercent() >= 0); + assertTrue(secondResult.getMemoryRss() > 0); + } +}