From 15cf1963b76ce7348014477f1cc98f5e551e1c71 Mon Sep 17 00:00:00 2001 From: Mark Nellemann Date: Tue, 4 May 2021 12:08:24 +0200 Subject: [PATCH] More work on plugins and added some tests. --- .../java/org/sysmon/agent/Application.java | 2 +- .../groovy/org/sysmon/agent/AppTest.groovy | 9 ++ .../groovy/org/sysmon/test/AppTest.groovy | 12 --- plugins/build.gradle | 25 +++++- plugins/sysmon-aix/build.gradle | 10 --- .../sysmon_aix/AixProcessorExtension.java | 86 +++++++------------ .../plugins/sysmon_aix/AixProcessorStat.java | 61 +++++++++++++ plugins/sysmon-linux/build.gradle | 10 --- .../sysmon_linux/LinuxDiskExtension.java | 2 +- .../sysmon_linux/LinuxMemoryExtension.java | 2 +- .../sysmon_linux/LinuxProcessorExtension.java | 70 ++++++++------- .../java/org/sysmon/shared/AixPlugin.java | 5 -- .../java/org/sysmon/shared/MetricResult.java | 5 +- .../java/org/sysmon/shared/PluginHelper.java | 57 ++++++++++++ 14 files changed, 232 insertions(+), 124 deletions(-) create mode 100644 agent/src/test/groovy/org/sysmon/agent/AppTest.groovy delete mode 100644 agent/src/test/groovy/org/sysmon/test/AppTest.groovy create mode 100644 plugins/sysmon-aix/src/main/java/org/sysmon/plugins/sysmon_aix/AixProcessorStat.java delete mode 100644 shared/src/main/java/org/sysmon/shared/AixPlugin.java create mode 100644 shared/src/main/java/org/sysmon/shared/PluginHelper.java diff --git a/agent/src/main/java/org/sysmon/agent/Application.java b/agent/src/main/java/org/sysmon/agent/Application.java index 8339e9c..f40ad7b 100644 --- a/agent/src/main/java/org/sysmon/agent/Application.java +++ b/agent/src/main/java/org/sysmon/agent/Application.java @@ -37,7 +37,7 @@ public class Application { pluginManager.loadPlugins(); pluginManager.startPlugins(); -*/ + */ /* List plugins = pluginManager.getPlugins(); diff --git a/agent/src/test/groovy/org/sysmon/agent/AppTest.groovy b/agent/src/test/groovy/org/sysmon/agent/AppTest.groovy new file mode 100644 index 0000000..f95a586 --- /dev/null +++ b/agent/src/test/groovy/org/sysmon/agent/AppTest.groovy @@ -0,0 +1,9 @@ +package org.sysmon.agent + +import spock.lang.Specification + +class AppTest extends Specification { + + + +} diff --git a/agent/src/test/groovy/org/sysmon/test/AppTest.groovy b/agent/src/test/groovy/org/sysmon/test/AppTest.groovy deleted file mode 100644 index 8bc2515..0000000 --- a/agent/src/test/groovy/org/sysmon/test/AppTest.groovy +++ /dev/null @@ -1,12 +0,0 @@ -/* - * This Spock specification was generated by the Gradle 'init' task. - */ -package org.sysmon.test - -import spock.lang.Specification - -class AppTest extends Specification { - - - -} diff --git a/plugins/build.gradle b/plugins/build.gradle index 333e3f4..74113f6 100644 --- a/plugins/build.gradle +++ b/plugins/build.gradle @@ -1,5 +1,21 @@ subprojects { - apply plugin: 'java-library' + apply plugin: 'java' + apply plugin: 'groovy' + + dependencies { + + testImplementation 'org.spockframework:spock-core:2.0-M4-groovy-3.0' + testImplementation "org.slf4j:slf4j-api:${slf4jVersion}" + testImplementation project(':shared') + + implementation project(':shared') + implementation(group: 'org.pf4j', name: 'pf4j', version: "${pf4jVersion}") { + exclude(group: "org.slf4j") + } + annotationProcessor(group: 'org.pf4j', name: 'pf4j', version: "${pf4jVersion}") + implementation "org.slf4j:slf4j-api:${slf4jVersion}" + + } jar { manifest { @@ -22,6 +38,13 @@ subprojects { copyJar } + + test { + useJUnitPlatform() + } + + sourceCompatibility = 1.8 + targetCompatibility = 1.8 } task customCleanUp(type:Delete) { diff --git a/plugins/sysmon-aix/build.gradle b/plugins/sysmon-aix/build.gradle index a3d951b..e69de29 100644 --- a/plugins/sysmon-aix/build.gradle +++ b/plugins/sysmon-aix/build.gradle @@ -1,10 +0,0 @@ -dependencies { - // compileOnly important!!! We do not want to put the api into the zip file since the main program has it already! - implementation project(':shared') - implementation(group: 'org.pf4j', name: 'pf4j', version: "${pf4jVersion}") { - exclude(group: "org.slf4j") - } - annotationProcessor(group: 'org.pf4j', name: 'pf4j', version: "${pf4jVersion}") - implementation "org.slf4j:slf4j-api:${slf4jVersion}" - -} diff --git a/plugins/sysmon-aix/src/main/java/org/sysmon/plugins/sysmon_aix/AixProcessorExtension.java b/plugins/sysmon-aix/src/main/java/org/sysmon/plugins/sysmon_aix/AixProcessorExtension.java index 091e4c3..971d67d 100644 --- a/plugins/sysmon-aix/src/main/java/org/sysmon/plugins/sysmon_aix/AixProcessorExtension.java +++ b/plugins/sysmon-aix/src/main/java/org/sysmon/plugins/sysmon_aix/AixProcessorExtension.java @@ -1,12 +1,21 @@ package org.sysmon.plugins.sysmon_aix; import org.pf4j.Extension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.sysmon.shared.MetricExtension; +import org.sysmon.shared.MetricMeasurement; import org.sysmon.shared.MetricResult; +import org.sysmon.shared.PluginHelper; + +import java.util.ArrayList; +import java.util.List; @Extension public class AixProcessorExtension implements MetricExtension { + private static final Logger log = LoggerFactory.getLogger(AixProcessorExtension.class); + @Override public boolean isSupported() { return System.getProperty("os.name").toLowerCase().contains("aix"); @@ -19,59 +28,30 @@ public class AixProcessorExtension implements MetricExtension { @Override public MetricResult getMetrics() { - return null; + + MetricResult result = new MetricResult("processor"); + + List mpstat = PluginHelper.executeCommand("mpstat", "-a"); + List processorStats = processCommandOutput(mpstat); + for(AixProcessorStat stat : processorStats) { + result.addMetricMeasurement(new MetricMeasurement(String.format("cpu%d", stat.getCpuNum()), stat.getUtilizationPercentage())); + } + + return result; } + + protected List processCommandOutput(List inputLines) { + List processorStatList = new ArrayList<>(); + + for(String line : inputLines) { + if(line.matches("^\\s+[0-9]+\\s+.*")) { + processorStatList.add(new AixProcessorStat(line)); + } + } + + return processorStatList; + } + + } - - - - -/* - - -# mpstat -v - -System configuration: lcpu=8 ent=0.5 mode=Uncapped - - -vcpu lcpu us sy wa id pbusy pc VTB(ms) ----- ---- ---- ---- ----- ----- ----- ----- ------- -0 12.26 10.89 0.11 76.74 0.00[ 23.1%] 0.00[ 0.0%] 121967 - 0 10.58 8.53 0.04 5.71 0.00[ 19.1%] 0.00[ 24.9%] - - 1 1.32 1.21 0.05 11.16 0.00[ 2.5%] 0.00[ 13.7%] - - 2 0.22 0.28 0.01 8.24 0.00[ 0.5%] 0.00[ 8.8%] - - 3 0.11 0.19 0.01 11.63 0.00[ 0.3%] 0.00[ 11.9%] - - 4 0.01 0.10 0.00 8.34 0.00[ 0.1%] 0.00[ 8.5%] - - 5 0.00 0.07 0.00 11.69 0.00[ 0.1%] 0.00[ 11.8%] - - 6 0.00 0.13 0.00 8.33 0.00[ 0.1%] 0.00[ 8.5%] - - 7 0.01 0.37 0.00 11.63 0.00[ 0.4%] 0.00[ 12.0%] - - - - - -# mpstat - -System configuration: lcpu=8 ent=0.5 mode=Uncapped - -cpu min maj mpc int cs ics rq mig lpa sysc us sy wa id pc %ec lcs - 0 1489677 9337 2633 2146943 1160666 30547 3 2951 100 8361624 43 35 0 23 0.00 0.0 1646908 - 1 336156 2711 383 266244 25376 5494 0 3401 100 1042507 10 9 0 80 0.00 0.0 230605 - 2 45820 829 377 116004 5984 2326 0 1889 100 474631 3 3 0 94 0.00 0.0 117923 - 3 46812 699 377 115297 6217 2306 0 1746 100 58549 1 2 0 97 0.00 0.0 117011 - 4 2786 39 377 112634 1485 1124 0 1143 100 7432 0 1 0 99 0.00 0.0 114271 - 5 1233 45 377 112032 1369 1111 0 1147 100 7591 0 1 0 99 0.00 0.0 113674 - 6 25415 238 377 112763 1519 1235 0 1126 100 2403 0 2 0 98 0.00 0.0 114479 - 7 3596 124 377 193193 1615 1181 0 1123 100 2572 0 3 0 97 0.00 0.0 195104 - U - - - - - - - - - - - - 0 100 0.50 100.0 - -ALL 1951495 14022 5278 3175110 1204231 45324 3 14526 100 9957309 0 0 0 100 0.00 0.0 2649975 - - -%ec - (Default, -a flag) The percentage of entitled capacity consumed by the logical processor. - The %ec of the ALL CPU row represents the percentage of entitled capacity consumed. - Because the time base over which this data is computed can vary, the entitled capacity - percentage can sometimes exceed 100%. This excess is noticeable only with small sampling intervals. - The attribute is displayed only in a shared partition. - - */ diff --git a/plugins/sysmon-aix/src/main/java/org/sysmon/plugins/sysmon_aix/AixProcessorStat.java b/plugins/sysmon-aix/src/main/java/org/sysmon/plugins/sysmon_aix/AixProcessorStat.java new file mode 100644 index 0000000..815cea7 --- /dev/null +++ b/plugins/sysmon-aix/src/main/java/org/sysmon/plugins/sysmon_aix/AixProcessorStat.java @@ -0,0 +1,61 @@ +package org.sysmon.plugins.sysmon_aix; + +public class AixProcessorStat { + + private final Integer cpuNum; + private final Float userTime; + private final Float systemTime; + private final Float waitTime; + private final Float idleTime; + + AixProcessorStat(String procString) { + + // cpu min maj mpcs mpcr dev soft dec ph cs ics bound rq push S3pull S3grd S0rd S1rd S2rd S3rd S4rd S5rd sysc us sy wa id pc %ec ilcs vlcs S3hrd S4hrd S5hrd %nsp + String[] splitStr = procString.trim().split("\\s+"); + if(splitStr.length != 35) { + throw new UnsupportedOperationException("AIX mpstat CPU string error: " + procString); + } + + this.cpuNum = Integer.parseInt(splitStr[0]); + this.userTime = Float.parseFloat(splitStr[23]); + this.systemTime = Float.parseFloat(splitStr[24]); + this.waitTime = Float.parseFloat(splitStr[25]); + this.idleTime = Float.parseFloat(splitStr[26]); + + + } + + public Integer getCpuNum() { + return cpuNum; + } + + public Float getUserTime() { + return userTime; + } + + public Float getSystemTime() { + return systemTime; + } + + public Float getIdleTime() { + return idleTime; + } + + public Float getWaitTime() { + return waitTime; + } + + + public Float getCombinedWorkTime() { + return userTime + systemTime; + } + + public Float getCombinedTime() { + return getIdleTime() + getCombinedWorkTime(); + } + + public float getUtilizationPercentage() { + return 100 - idleTime; + } + +} diff --git a/plugins/sysmon-linux/build.gradle b/plugins/sysmon-linux/build.gradle index a3d951b..e69de29 100644 --- a/plugins/sysmon-linux/build.gradle +++ b/plugins/sysmon-linux/build.gradle @@ -1,10 +0,0 @@ -dependencies { - // compileOnly important!!! We do not want to put the api into the zip file since the main program has it already! - implementation project(':shared') - implementation(group: 'org.pf4j', name: 'pf4j', version: "${pf4jVersion}") { - exclude(group: "org.slf4j") - } - annotationProcessor(group: 'org.pf4j', name: 'pf4j', version: "${pf4jVersion}") - implementation "org.slf4j:slf4j-api:${slf4jVersion}" - -} diff --git a/plugins/sysmon-linux/src/main/java/org/sysmon/plugins/sysmon_linux/LinuxDiskExtension.java b/plugins/sysmon-linux/src/main/java/org/sysmon/plugins/sysmon_linux/LinuxDiskExtension.java index acdb55f..777a83f 100644 --- a/plugins/sysmon-linux/src/main/java/org/sysmon/plugins/sysmon_linux/LinuxDiskExtension.java +++ b/plugins/sysmon-linux/src/main/java/org/sysmon/plugins/sysmon_linux/LinuxDiskExtension.java @@ -35,7 +35,7 @@ public class LinuxDiskExtension implements MetricExtension { try { copyCurrentValues(); readProcFile(); - result.setMeasurementList(calculate()); + result.setMetricMeasurementList(calculate()); } catch (IOException e) { e.printStackTrace(); } diff --git a/plugins/sysmon-linux/src/main/java/org/sysmon/plugins/sysmon_linux/LinuxMemoryExtension.java b/plugins/sysmon-linux/src/main/java/org/sysmon/plugins/sysmon_linux/LinuxMemoryExtension.java index 47d6617..a5871ec 100644 --- a/plugins/sysmon-linux/src/main/java/org/sysmon/plugins/sysmon_linux/LinuxMemoryExtension.java +++ b/plugins/sysmon-linux/src/main/java/org/sysmon/plugins/sysmon_linux/LinuxMemoryExtension.java @@ -35,7 +35,7 @@ public class LinuxMemoryExtension implements MetricExtension { MetricResult result = new MetricResult("memory"); try { - result.setMeasurementList(readProcFile()); + result.setMetricMeasurementList(readProcFile()); } catch (IOException e) { e.printStackTrace(); } diff --git a/plugins/sysmon-linux/src/main/java/org/sysmon/plugins/sysmon_linux/LinuxProcessorExtension.java b/plugins/sysmon-linux/src/main/java/org/sysmon/plugins/sysmon_linux/LinuxProcessorExtension.java index 12477d5..50fbdcb 100644 --- a/plugins/sysmon-linux/src/main/java/org/sysmon/plugins/sysmon_linux/LinuxProcessorExtension.java +++ b/plugins/sysmon-linux/src/main/java/org/sysmon/plugins/sysmon_linux/LinuxProcessorExtension.java @@ -2,6 +2,8 @@ package org.sysmon.plugins.sysmon_linux; import org.pf4j.Extension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.sysmon.shared.MetricExtension; import org.sysmon.shared.MetricMeasurement; import org.sysmon.shared.MetricResult; @@ -16,58 +18,40 @@ import java.util.List; @Extension public class LinuxProcessorExtension implements MetricExtension { + private static final Logger log = LoggerFactory.getLogger(LinuxProcessorExtension.class); + private List currentProcessorStats; private List previousProcessorStats; + @Override public boolean isSupported() { return System.getProperty("os.name").toLowerCase().contains("linux"); } + @Override public String getGreeting() { return "Welcome from Linux ProcessorMetric"; } + @Override public MetricResult getMetrics() { - MetricResult result = new MetricResult("processor"); - try { - copyCurrentValues(); - readProcFile(); - result.setMeasurementList(calculate()); - } catch (IOException e) { - e.printStackTrace(); - } - - return result; - } - - - private void readProcFile() throws IOException { - - currentProcessorStats = new ArrayList<>(); - List allLines = Files.readAllLines(Paths.get("/proc/stat"), StandardCharsets.UTF_8); - for(String line : allLines) { - if(line.startsWith("cpu")) { - currentProcessorStats.add(new LinuxProcessorStat(line)); - } - } - - } - - - private void copyCurrentValues() { - if(currentProcessorStats != null && currentProcessorStats.size() > 0) { previousProcessorStats = new ArrayList<>(currentProcessorStats); } + MetricResult result = new MetricResult("processor"); + currentProcessorStats = processFileOutput(readProcFile()); + result.setMetricMeasurementList(calculateDifference()); + + return result; } - private List calculate() { + private List calculateDifference() { List measurementList = new ArrayList<>(); @@ -92,6 +76,34 @@ public class LinuxProcessorExtension implements MetricExtension { return measurementList; } + + + protected List readProcFile() { + + List allLines = new ArrayList<>(); + try { + allLines = Files.readAllLines(Paths.get("/proc/stat"), StandardCharsets.UTF_8); + } catch (IOException e) { + log.error(e.getMessage()); + } + return allLines; + + } + + + protected List processFileOutput(List inputLines) { + + List processorStats = new ArrayList<>(); + for(String line : inputLines) { + if(line.matches("^cpu\\d+.*")) { + processorStats.add(new LinuxProcessorStat(line)); + } + } + + return processorStats; + } + + } diff --git a/shared/src/main/java/org/sysmon/shared/AixPlugin.java b/shared/src/main/java/org/sysmon/shared/AixPlugin.java deleted file mode 100644 index 5562dbd..0000000 --- a/shared/src/main/java/org/sysmon/shared/AixPlugin.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.sysmon.shared; - -public interface AixPlugin { - -} diff --git a/shared/src/main/java/org/sysmon/shared/MetricResult.java b/shared/src/main/java/org/sysmon/shared/MetricResult.java index d2abdf3..48252cb 100644 --- a/shared/src/main/java/org/sysmon/shared/MetricResult.java +++ b/shared/src/main/java/org/sysmon/shared/MetricResult.java @@ -14,10 +14,13 @@ public class MetricResult { this.timestamp = Instant.now(); } - public void setMeasurementList(List measurementList) { + public void setMetricMeasurementList(List measurementList) { this.measurementList = measurementList; } + public void addMetricMeasurement(MetricMeasurement measurement) { + measurementList.add(measurement); + } public String toString() { StringBuilder sb = new StringBuilder(String.format("%s - %s\n", timestamp.toString(), name)); diff --git a/shared/src/main/java/org/sysmon/shared/PluginHelper.java b/shared/src/main/java/org/sysmon/shared/PluginHelper.java new file mode 100644 index 0000000..c7c1bd8 --- /dev/null +++ b/shared/src/main/java/org/sysmon/shared/PluginHelper.java @@ -0,0 +1,57 @@ +package org.sysmon.shared; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +public class PluginHelper { + + final static boolean isWindows = System.getProperty("os.name") + .toLowerCase().startsWith("windows"); + + + public static List executeCommand(String... cmd) { + + List outputLines = new ArrayList<>(); + + ProcessBuilder builder = new ProcessBuilder(); + if (isWindows) { + builder.command("cmd.exe", "/c"); + } else { + builder.command("sh", "-c"); + } + + for(String c : cmd) { + builder.command().add(c); + } + + builder.directory(new File(System.getProperty("user.home"))); + + try { + + Process process = builder.start(); + + BufferedReader reader = + new BufferedReader(new InputStreamReader(process.getInputStream())); + + String line; + while ((line = reader.readLine()) != null) { + outputLines.add(line); + } + + int exitCode = process.waitFor(); + System.out.println("\nExited with error code : " + exitCode); + + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + + return outputLines; + } + + + +}