More work on packages, plugins and tests.
This commit is contained in:
parent
088d49c90c
commit
bc43d687a0
|
@ -1,15 +1,14 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'application'
|
id 'application'
|
||||||
|
|
||||||
id "com.github.johnrengelman.shadow" version "6.1.0"
|
id "com.github.johnrengelman.shadow" version "7.0.0"
|
||||||
id "net.nemerosa.versioning" version "2.14.0"
|
id "net.nemerosa.versioning" version "2.14.0"
|
||||||
id "nebula.ospackage" version "8.4.1"
|
id "nebula.ospackage" version "8.5.6"
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation project(':shared')
|
testImplementation project(':shared')
|
||||||
implementation project(':shared')
|
implementation project(':shared')
|
||||||
implementation project(':plugins')
|
|
||||||
|
|
||||||
annotationProcessor(group: 'org.pf4j', name: 'pf4j', version: "${pf4jVersion}")
|
annotationProcessor(group: 'org.pf4j', name: 'pf4j', version: "${pf4jVersion}")
|
||||||
implementation group: 'org.pf4j', name: 'pf4j', version: "${pf4jVersion}"
|
implementation group: 'org.pf4j', name: 'pf4j', version: "${pf4jVersion}"
|
||||||
|
@ -48,7 +47,7 @@ ospackage {
|
||||||
user = 'root'
|
user = 'root'
|
||||||
packager = "Mark Nellemann <mark.nellemann@gmail.com>"
|
packager = "Mark Nellemann <mark.nellemann@gmail.com>"
|
||||||
|
|
||||||
into '/opt/sysmon-client'
|
into '/opt/sysmon/client'
|
||||||
|
|
||||||
from(shadowJar.outputs.files) {
|
from(shadowJar.outputs.files) {
|
||||||
into 'lib'
|
into 'lib'
|
||||||
|
@ -90,3 +89,10 @@ jar {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shadowJar {
|
||||||
|
archiveBaseName.set('sysmon-client')
|
||||||
|
archiveClassifier.set('')
|
||||||
|
archiveVersion.set('')
|
||||||
|
mergeServiceFiles() // Tell plugin to merge duplicate service files
|
||||||
|
}
|
|
@ -8,10 +8,12 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import picocli.CommandLine;
|
import picocli.CommandLine;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.Properties;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
@CommandLine.Command(name = "sysmon-client", mixinStandardHelpOptions = true)
|
@CommandLine.Command(name = "sysmon-client", mixinStandardHelpOptions = true)
|
||||||
|
@ -22,9 +24,11 @@ public class Application implements Callable<Integer> {
|
||||||
@CommandLine.Option(names = { "-s", "--server-url" }, description = "Server URL (default: ${DEFAULT-VALUE}).", defaultValue = "http://127.0.0.1:9925/metrics", paramLabel = "<url>")
|
@CommandLine.Option(names = { "-s", "--server-url" }, description = "Server URL (default: ${DEFAULT-VALUE}).", defaultValue = "http://127.0.0.1:9925/metrics", paramLabel = "<url>")
|
||||||
private URL serverUrl;
|
private URL serverUrl;
|
||||||
|
|
||||||
@CommandLine.Option(names = { "-n", "--hostname" }, description = "Client hostname.", paramLabel = "<name>")
|
@CommandLine.Option(names = { "-n", "--hostname" }, description = "Client hostname (default: <hostname>).", paramLabel = "<name>")
|
||||||
private String hostname;
|
private String hostname;
|
||||||
|
|
||||||
|
@CommandLine.Option(names = { "-p", "--plugins" }, description = "Plugin jar path (default: ${DEFAULT-VALUE}).", paramLabel = "<path>", defaultValue = "/opt/sysmon/plugins")
|
||||||
|
private File plugins;
|
||||||
|
|
||||||
public static void main(String... args) {
|
public static void main(String... args) {
|
||||||
int exitCode = new CommandLine(new Application()).execute(args);
|
int exitCode = new CommandLine(new Application()).execute(args);
|
||||||
|
|
|
@ -11,7 +11,11 @@ import org.slf4j.LoggerFactory;
|
||||||
import org.sysmon.shared.MetricExtension;
|
import org.sysmon.shared.MetricExtension;
|
||||||
import org.sysmon.shared.MetricResult;
|
import org.sysmon.shared.MetricResult;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
public class ClientRouteBuilder extends RouteBuilder {
|
public class ClientRouteBuilder extends RouteBuilder {
|
||||||
|
|
||||||
|
@ -22,17 +26,29 @@ public class ClientRouteBuilder extends RouteBuilder {
|
||||||
|
|
||||||
Registry registry = getContext().getRegistry();
|
Registry registry = getContext().getRegistry();
|
||||||
|
|
||||||
PluginManager pluginManager = new JarPluginManager();
|
Path[] pluginpaths = { new File("/opt/sysmon/plugins").toPath() };
|
||||||
|
|
||||||
|
PluginManager pluginManager = new JarPluginManager(pluginpaths);
|
||||||
pluginManager.loadPlugins();
|
pluginManager.loadPlugins();
|
||||||
pluginManager.startPlugins();
|
pluginManager.startPlugins();
|
||||||
|
|
||||||
|
List<String> providers = new ArrayList<>();
|
||||||
List<MetricExtension> metricExtensions = pluginManager.getExtensions(MetricExtension.class);
|
List<MetricExtension> metricExtensions = pluginManager.getExtensions(MetricExtension.class);
|
||||||
for (MetricExtension ext : metricExtensions) {
|
for (MetricExtension ext : metricExtensions) {
|
||||||
|
|
||||||
if(ext.isSupported()) {
|
if(ext.isSupported()) {
|
||||||
|
|
||||||
|
String provides = ext.getProvides();
|
||||||
|
if(providers.contains(provides)) {
|
||||||
|
log.warn("Skipping extension (already provided): " + ext.getName());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
log.info(">>> Enabling extension: " + ext.getDescription());
|
log.info(">>> Enabling extension: " + ext.getDescription());
|
||||||
|
providers.add(provides);
|
||||||
|
|
||||||
// Setup Camel route for this extension
|
// Setup Camel route for this extension
|
||||||
from("timer:collect?period=30000")
|
from("timer:collect?fixedRate=true&period=30s")
|
||||||
.bean(ext, "getMetrics")
|
.bean(ext, "getMetrics")
|
||||||
//.doTry()
|
//.doTry()
|
||||||
.process(new MetricEnrichProcessor(registry))
|
.process(new MetricEnrichProcessor(registry))
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
group=org.sysmon
|
||||||
|
version=0.0.1-SNAPSHOT
|
||||||
|
|
||||||
pf4jVersion=3.6.0
|
pf4jVersion=3.6.0
|
||||||
slf4jVersion=1.7.30
|
slf4jVersion=1.7.30
|
||||||
camelVersion=3.7.3
|
camelVersion=3.7.4
|
||||||
picocliVersion=4.6.1
|
picocliVersion=4.6.1
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
plugins {
|
||||||
|
id "nebula.ospackage" version "8.5.6"
|
||||||
|
}
|
||||||
|
|
||||||
subprojects {
|
subprojects {
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
apply plugin: 'groovy'
|
apply plugin: 'groovy'
|
||||||
|
@ -49,4 +53,29 @@ task customCleanUp(type:Delete) {
|
||||||
delete "output"
|
delete "output"
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.clean.dependsOn(tasks.customCleanUp)
|
tasks.clean.dependsOn(tasks.customCleanUp)
|
||||||
|
|
||||||
|
|
||||||
|
apply plugin: 'nebula.ospackage'
|
||||||
|
ospackage {
|
||||||
|
packageName = 'sysmon-plugins'
|
||||||
|
release = '1'
|
||||||
|
user = 'root'
|
||||||
|
packager = "Mark Nellemann <mark.nellemann@gmail.com>"
|
||||||
|
|
||||||
|
into '/opt/sysmon/plugins'
|
||||||
|
|
||||||
|
from('output/') {
|
||||||
|
into ''
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
buildRpm {
|
||||||
|
dependsOn assemble
|
||||||
|
os = "LINUX"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildDeb {
|
||||||
|
dependsOn assemble
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,11 @@ public class AixDiskExtension implements MetricExtension {
|
||||||
return "aix-disk";
|
return "aix-disk";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProvides() {
|
||||||
|
return "disk";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return "AIX Disk Metrics (TODO)";
|
return "AIX Disk Metrics (TODO)";
|
||||||
|
|
|
@ -8,7 +8,7 @@ import org.sysmon.shared.MetricExtension;
|
||||||
import org.sysmon.shared.MetricResult;
|
import org.sysmon.shared.MetricResult;
|
||||||
import org.sysmon.shared.PluginHelper;
|
import org.sysmon.shared.PluginHelper;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.io.File;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -19,7 +19,19 @@ public class AixProcessorExtension implements MetricExtension {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSupported() {
|
public boolean isSupported() {
|
||||||
return System.getProperty("os.name").toLowerCase().contains("aix");
|
|
||||||
|
String osArch = System.getProperty("os.arch").toLowerCase();
|
||||||
|
if(!osArch.startsWith("ppc64")) {
|
||||||
|
log.warn("Wrong os arch: " + osArch);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!PluginHelper.canExecute("lparstat")) {
|
||||||
|
log.warn("No lparstat command found.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -27,6 +39,11 @@ public class AixProcessorExtension implements MetricExtension {
|
||||||
return "aix-processor";
|
return "aix-processor";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProvides() {
|
||||||
|
return "processor";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return "AIX Processor Metrics";
|
return "AIX Processor Metrics";
|
||||||
|
@ -35,7 +52,7 @@ public class AixProcessorExtension implements MetricExtension {
|
||||||
@Override
|
@Override
|
||||||
public MetricResult getMetrics() {
|
public MetricResult getMetrics() {
|
||||||
|
|
||||||
List<String> vmstat = PluginHelper.executeCommand("/usr/bin/lparstat 1 1");
|
List<String> vmstat = PluginHelper.executeCommand("lparstat 1 1");
|
||||||
AixProcessorStat processorStat = processCommandOutput(vmstat);
|
AixProcessorStat processorStat = processCommandOutput(vmstat);
|
||||||
|
|
||||||
Map<String, String> tagsMap = processorStat.getTags();
|
Map<String, String> tagsMap = processorStat.getTags();
|
||||||
|
|
|
@ -13,7 +13,12 @@ public class AixProcessorStat {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(AixProcessorStat.class);
|
private static final Logger log = LoggerFactory.getLogger(AixProcessorStat.class);
|
||||||
|
|
||||||
private final Pattern pattern = Pattern.compile("^System configuration: type=(\\S+) mode=(\\S+) smt=(\\d+) lcpu=(\\d+) mem=(\\d+)MB psize=(\\d+) ent=(\\d+\\.?\\d*)");
|
// System configuration: type=Shared mode=Uncapped smt=8 lcpu=8 mem=4096MB psize=19 ent=0.50
|
||||||
|
private final Pattern patternAix = Pattern.compile("^System configuration: type=(\\S+) mode=(\\S+) smt=(\\d+) lcpu=(\\d+) mem=(\\d+)MB psize=(\\d+) ent=(\\d+\\.?\\d*)");
|
||||||
|
|
||||||
|
// type=Shared mode=Uncapped smt=8 lcpu=4 mem=4101120 kB cpus=24 ent=4.00
|
||||||
|
private final Pattern patternLinux = Pattern.compile("^type=(\\S+) mode=(\\S+) smt=(\\d+) lcpu=(\\d+) mem=(\\d+) kB cpus=(\\d+) ent=(\\d+\\.?\\d*)");
|
||||||
|
|
||||||
|
|
||||||
private String type;
|
private String type;
|
||||||
private String mode;
|
private String mode;
|
||||||
|
@ -31,34 +36,44 @@ public class AixProcessorStat {
|
||||||
private final Float lbusy; // Indicates the percentage of logical processor(s) utilization that occurred while executing at the user and system level.
|
private final Float lbusy; // Indicates the percentage of logical processor(s) utilization that occurred while executing at the user and system level.
|
||||||
|
|
||||||
|
|
||||||
/*
|
AixProcessorStat(List<String> lines) {
|
||||||
|
|
||||||
System configuration: type=Shared mode=Uncapped smt=8 lcpu=8 mem=4096MB psize=19 ent=0.50
|
Pattern p;
|
||||||
|
for (String line : lines) {
|
||||||
|
|
||||||
|
if (line.startsWith("System configuration:")) {
|
||||||
%user %sys %wait %idle physc %entc lbusy vcsw phint %nsp %utcyc
|
p = patternAix;
|
||||||
----- ----- ------ ------ ----- ----- ------ ----- ----- ----- ------
|
Matcher matcher = patternAix.matcher(line);
|
||||||
0.1 0.0 0.0 99.9 0.00 0.2 1.9 37441986 316 149 33.06
|
if (matcher.find() && matcher.groupCount() == 7) {
|
||||||
*/
|
type = matcher.group(1);
|
||||||
AixProcessorStat(List<String> vmstatLines) {
|
mode = matcher.group(2);
|
||||||
|
smt = Integer.parseInt(matcher.group(3));
|
||||||
for(String line : vmstatLines) {
|
lcpu = Integer.parseInt(matcher.group(4));
|
||||||
Matcher matcher = pattern.matcher(line);
|
psize = Integer.parseInt(matcher.group(5));
|
||||||
if (matcher.find() && matcher.groupCount() == 7) {
|
ent = Float.parseFloat(matcher.group(7));
|
||||||
type=matcher.group(1);
|
}
|
||||||
mode=matcher.group(2);
|
|
||||||
smt = Integer.parseInt(matcher.group(3));
|
|
||||||
lcpu = Integer.parseInt(matcher.group(4));
|
|
||||||
psize = Integer.parseInt(matcher.group(5));
|
|
||||||
ent = Float.parseFloat(matcher.group(7));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (line.startsWith("type=")) {
|
||||||
|
//type=Shared mode=Uncapped smt=8 lcpu=4 mem=4101120 kB cpus=24 ent=4.00
|
||||||
|
|
||||||
|
Matcher matcher = patternLinux.matcher(line);
|
||||||
|
if (matcher.find() && matcher.groupCount() == 7) {
|
||||||
|
type = matcher.group(1);
|
||||||
|
mode = matcher.group(2);
|
||||||
|
smt = Integer.parseInt(matcher.group(3));
|
||||||
|
lcpu = Integer.parseInt(matcher.group(4));
|
||||||
|
psize = Integer.parseInt(matcher.group(6));
|
||||||
|
ent = Float.parseFloat(matcher.group(7));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String vmstat = vmstatLines.get(vmstatLines.size() -1);
|
String lparstat = lines.get(lines.size() -1);
|
||||||
String[] splitStr = vmstat.trim().split("\\s+");
|
String[] splitStr = lparstat.trim().split("\\s+");
|
||||||
if(splitStr.length != 11) {
|
if(splitStr.length < 9) {
|
||||||
throw new UnsupportedOperationException("vmstat string error: " + splitStr.length);
|
throw new UnsupportedOperationException("lparstat string error: " + lparstat);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.user = Float.parseFloat(splitStr[0]);
|
this.user = Float.parseFloat(splitStr[0]);
|
||||||
|
|
|
@ -4,10 +4,10 @@ import spock.lang.Specification
|
||||||
|
|
||||||
class AixProcessorTest extends Specification {
|
class AixProcessorTest extends Specification {
|
||||||
|
|
||||||
void "test lparstat output processing"() {
|
void "test AIX lparstat output processing"() {
|
||||||
|
|
||||||
setup:
|
setup:
|
||||||
def testFile = new File(getClass().getResource('/lparstat.txt').toURI())
|
def testFile = new File(getClass().getResource('/lparstat-aix.txt').toURI())
|
||||||
List<String> lines = testFile.readLines("UTF-8")
|
List<String> lines = testFile.readLines("UTF-8")
|
||||||
|
|
||||||
when:
|
when:
|
||||||
|
@ -24,4 +24,25 @@ class AixProcessorTest extends Specification {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void "test Linux lparstat output processing"() {
|
||||||
|
|
||||||
|
setup:
|
||||||
|
def testFile = new File(getClass().getResource('/lparstat-linux.txt').toURI())
|
||||||
|
List<String> lines = testFile.readLines("UTF-8")
|
||||||
|
|
||||||
|
when:
|
||||||
|
AixProcessorExtension extension = new AixProcessorExtension()
|
||||||
|
AixProcessorStat stats = extension.processCommandOutput(lines)
|
||||||
|
|
||||||
|
then:
|
||||||
|
stats.getUser() == 0.03f
|
||||||
|
stats.getSys() == 0.0f
|
||||||
|
stats.getWait() == 0.0f
|
||||||
|
stats.getIdle() == 99.97f
|
||||||
|
stats.getFields().get("ent") == 4.00f
|
||||||
|
stats.getTags().get("mode") == "Uncapped"
|
||||||
|
stats.getTags().get("type") == "Shared"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
7
plugins/sysmon-aix/src/test/resources/lparstat-linux.txt
Normal file
7
plugins/sysmon-aix/src/test/resources/lparstat-linux.txt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
|
||||||
|
System Configuration
|
||||||
|
type=Shared mode=Uncapped smt=8 lcpu=4 mem=4101120 kB cpus=24 ent=4.00
|
||||||
|
|
||||||
|
%user %sys %wait %idle physc %entc lbusy vcsw phint
|
||||||
|
----- ----- ----- ----- ----- ----- ----- ----- -----
|
||||||
|
0.03 0.00 0.00 99.97 0.000000 0.000000 0.03 445478301 18863
|
|
@ -27,7 +27,8 @@ public class LinuxDiskExtension implements MetricExtension {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSupported() {
|
public boolean isSupported() {
|
||||||
return System.getProperty("os.name").toLowerCase().contains("linux");
|
//return System.getProperty("os.name").toLowerCase().contains("linux");
|
||||||
|
return false; // TODO: Not ready yet.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -35,6 +36,11 @@ public class LinuxDiskExtension implements MetricExtension {
|
||||||
return "linux-disk";
|
return "linux-disk";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProvides() {
|
||||||
|
return "disk";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return "Linux Disk Metrics";
|
return "Linux Disk Metrics";
|
||||||
|
|
|
@ -6,6 +6,8 @@ import org.sysmon.shared.MetricExtension;
|
||||||
import org.sysmon.shared.MetricResult;
|
import org.sysmon.shared.MetricResult;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
@ -31,6 +33,11 @@ public class LinuxMemoryExtension implements MetricExtension {
|
||||||
return "linux-memory";
|
return "linux-memory";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProvides() {
|
||||||
|
return "memory";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return "Linux Memory Metrics";
|
return "Linux Memory Metrics";
|
||||||
|
@ -42,7 +49,7 @@ public class LinuxMemoryExtension implements MetricExtension {
|
||||||
|
|
||||||
MetricResult result = new MetricResult("memory");
|
MetricResult result = new MetricResult("memory");
|
||||||
try {
|
try {
|
||||||
result.setMeasurement(readProcFile());
|
result.setMeasurement(processProcFile(readProcFile()));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
@ -51,27 +58,51 @@ public class LinuxMemoryExtension implements MetricExtension {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Measurement readProcFile() throws IOException {
|
protected List<String> readProcFile() throws IOException {
|
||||||
|
List<String> allLines = Files.readAllLines(Paths.get("/proc/meminfo"), StandardCharsets.UTF_8);
|
||||||
|
return allLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Measurement processProcFile(List<String> lines) {
|
||||||
|
|
||||||
Map<String, String> tagsMap = new HashMap<>();
|
Map<String, String> tagsMap = new HashMap<>();
|
||||||
Map<String, Object> fieldsMap = new HashMap<>();
|
Map<String, Object> fieldsMap = new HashMap<>();
|
||||||
|
|
||||||
List<String> allLines = Files.readAllLines(Paths.get("/proc/meminfo"), StandardCharsets.UTF_8);
|
Long total = null;
|
||||||
for (String line : allLines) {
|
Long available = null;
|
||||||
|
|
||||||
|
for (String line : lines) {
|
||||||
|
|
||||||
if (line.startsWith("Mem")) {
|
if (line.startsWith("Mem")) {
|
||||||
|
|
||||||
Matcher matcher = pattern.matcher(line);
|
Matcher matcher = pattern.matcher(line);
|
||||||
if (matcher.find() && matcher.groupCount() == 2) {
|
if (matcher.find() && matcher.groupCount() == 2) {
|
||||||
|
|
||||||
String key = matcher.group(1).substring(3).toLowerCase(); // remove "Mem" and lowercase
|
String key = matcher.group(1).substring(3).toLowerCase(); // remove "Mem" and lowercase
|
||||||
Object value = matcher.group(2);
|
String value = matcher.group(2);
|
||||||
fieldsMap.put(key, value);
|
|
||||||
|
switch (key) {
|
||||||
|
case "total":
|
||||||
|
total = Long.parseLong(value);
|
||||||
|
fieldsMap.put(key, total);
|
||||||
|
break;
|
||||||
|
case "available":
|
||||||
|
available = Long.parseLong(value);
|
||||||
|
fieldsMap.put(key, available);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(total != null && available != null) {
|
||||||
|
BigDecimal usage = BigDecimal.valueOf(((float)(total - available) / total) * 100);
|
||||||
|
fieldsMap.put("usage", usage.setScale(2, RoundingMode.HALF_EVEN));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Measurement(tagsMap, fieldsMap);
|
return new Measurement(tagsMap, fieldsMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -35,6 +35,11 @@ public class LinuxProcessorExtension implements MetricExtension {
|
||||||
return "linux-processor";
|
return "linux-processor";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProvides() {
|
||||||
|
return "processor";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return "Linux Processor Metrics";
|
return "Linux Processor Metrics";
|
||||||
|
|
23
plugins/sysmon-linux/src/test/groovy/LinuxMemoryTest.groovy
Normal file
23
plugins/sysmon-linux/src/test/groovy/LinuxMemoryTest.groovy
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import org.sysmon.plugins.sysmon_linux.LinuxMemoryExtension
|
||||||
|
import org.sysmon.shared.Measurement
|
||||||
|
import spock.lang.Specification
|
||||||
|
|
||||||
|
class LinuxMemoryTest extends Specification {
|
||||||
|
|
||||||
|
void "test proc file processing"() {
|
||||||
|
|
||||||
|
setup:
|
||||||
|
def testFile = new File(getClass().getResource('/meminfo.txt').toURI())
|
||||||
|
List<String> lines = testFile.readLines("UTF-8")
|
||||||
|
|
||||||
|
when:
|
||||||
|
LinuxMemoryExtension extension = new LinuxMemoryExtension()
|
||||||
|
Measurement m = extension.processProcFile(lines);
|
||||||
|
|
||||||
|
then:
|
||||||
|
m.getFields().get("total") == 16069616
|
||||||
|
m.getFields().get("available") == 7968744
|
||||||
|
m.getFields().get("usage") == 50.41
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
51
plugins/sysmon-linux/src/test/resources/meminfo.txt
Normal file
51
plugins/sysmon-linux/src/test/resources/meminfo.txt
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
MemTotal: 16069616 kB
|
||||||
|
MemFree: 1587092 kB
|
||||||
|
MemAvailable: 7968744 kB
|
||||||
|
Buffers: 463364 kB
|
||||||
|
Cached: 6808540 kB
|
||||||
|
SwapCached: 2156 kB
|
||||||
|
Active: 9179808 kB
|
||||||
|
Inactive: 4369880 kB
|
||||||
|
Active(anon): 6366532 kB
|
||||||
|
Inactive(anon): 761912 kB
|
||||||
|
Active(file): 2813276 kB
|
||||||
|
Inactive(file): 3607968 kB
|
||||||
|
Unevictable: 270200 kB
|
||||||
|
Mlocked: 48 kB
|
||||||
|
SwapTotal: 3985404 kB
|
||||||
|
SwapFree: 3974652 kB
|
||||||
|
Dirty: 9708 kB
|
||||||
|
Writeback: 0 kB
|
||||||
|
AnonPages: 6545944 kB
|
||||||
|
Mapped: 1948448 kB
|
||||||
|
Shmem: 852772 kB
|
||||||
|
KReclaimable: 302640 kB
|
||||||
|
Slab: 502784 kB
|
||||||
|
SReclaimable: 302640 kB
|
||||||
|
SUnreclaim: 200144 kB
|
||||||
|
KernelStack: 21376 kB
|
||||||
|
PageTables: 52856 kB
|
||||||
|
NFS_Unstable: 0 kB
|
||||||
|
Bounce: 0 kB
|
||||||
|
WritebackTmp: 0 kB
|
||||||
|
CommitLimit: 12020212 kB
|
||||||
|
Committed_AS: 14750600 kB
|
||||||
|
VmallocTotal: 34359738367 kB
|
||||||
|
VmallocUsed: 42828 kB
|
||||||
|
VmallocChunk: 0 kB
|
||||||
|
Percpu: 8320 kB
|
||||||
|
HardwareCorrupted: 0 kB
|
||||||
|
AnonHugePages: 0 kB
|
||||||
|
ShmemHugePages: 0 kB
|
||||||
|
ShmemPmdMapped: 0 kB
|
||||||
|
FileHugePages: 0 kB
|
||||||
|
FilePmdMapped: 0 kB
|
||||||
|
HugePages_Total: 0
|
||||||
|
HugePages_Free: 0
|
||||||
|
HugePages_Rsvd: 0
|
||||||
|
HugePages_Surp: 0
|
||||||
|
Hugepagesize: 2048 kB
|
||||||
|
Hugetlb: 0 kB
|
||||||
|
DirectMap4k: 410796 kB
|
||||||
|
DirectMap2M: 10795008 kB
|
||||||
|
DirectMap1G: 6291456 kB
|
|
@ -1,7 +1,7 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'application'
|
id 'application'
|
||||||
|
|
||||||
id "com.github.johnrengelman.shadow" version "6.1.0"
|
id "com.github.johnrengelman.shadow" version "7.0.0"
|
||||||
id "net.nemerosa.versioning" version "2.14.0"
|
id "net.nemerosa.versioning" version "2.14.0"
|
||||||
id "nebula.ospackage" version "8.5.6"
|
id "nebula.ospackage" version "8.5.6"
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ ospackage {
|
||||||
user = 'root'
|
user = 'root'
|
||||||
packager = "Mark Nellemann <mark.nellemann@gmail.com>"
|
packager = "Mark Nellemann <mark.nellemann@gmail.com>"
|
||||||
|
|
||||||
into '/opt/sysmon-server'
|
into '/opt/sysmon/server'
|
||||||
|
|
||||||
from(shadowJar.outputs.files) {
|
from(shadowJar.outputs.files) {
|
||||||
into 'lib'
|
into 'lib'
|
||||||
|
@ -69,7 +69,8 @@ buildDeb {
|
||||||
dependsOn startShadowScripts
|
dependsOn startShadowScripts
|
||||||
}
|
}
|
||||||
|
|
||||||
task aixRpm(type: Rpm) {
|
task buildRpmAix(type: Rpm) {
|
||||||
|
dependsOn startShadowScripts
|
||||||
os "AIX"
|
os "AIX"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +84,14 @@ jar {
|
||||||
'Build-Version' : versioning.info.tag ?: (versioning.info.branch + "-" + versioning.info.build),
|
'Build-Version' : versioning.info.tag ?: (versioning.info.branch + "-" + versioning.info.build),
|
||||||
'Build-Revision' : versioning.info.commit,
|
'Build-Revision' : versioning.info.commit,
|
||||||
'Build-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ss.SSSZ").toString(),
|
'Build-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ss.SSSZ").toString(),
|
||||||
|
'Add-Opens' : 'java.base/java.lang.invoke' // To ignore "Illegal reflective access by retrofit2.Platform" warnings
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shadowJar {
|
||||||
|
archiveBaseName.set('sysmon-server')
|
||||||
|
archiveClassifier.set('')
|
||||||
|
archiveVersion.set('')
|
||||||
|
mergeServiceFiles() // Tell plugin to merge duplicate service files
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
package org.sysmon.server;
|
package org.sysmon.server;
|
||||||
|
|
||||||
|
import org.apache.camel.CamelContext;
|
||||||
import org.apache.camel.main.Main;
|
import org.apache.camel.main.Main;
|
||||||
|
import org.apache.camel.support.DefaultRegistry;
|
||||||
|
import org.apache.camel.support.SimpleRegistry;
|
||||||
import org.influxdb.InfluxDB;
|
import org.influxdb.InfluxDB;
|
||||||
import org.influxdb.InfluxDBFactory;
|
import org.influxdb.InfluxDBFactory;
|
||||||
import picocli.CommandLine;
|
import picocli.CommandLine;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.Properties;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
@CommandLine.Command(name = "sysmon-server", mixinStandardHelpOptions = true)
|
@CommandLine.Command(name = "sysmon-server", mixinStandardHelpOptions = true)
|
||||||
|
@ -21,8 +25,11 @@ public class Application implements Callable<Integer> {
|
||||||
@CommandLine.Option(names = { "-p", "--influxdb-pass" }, description = "InfluxDB Password (default: ${DEFAULT-VALUE}).", defaultValue = "", paramLabel = "<pass>")
|
@CommandLine.Option(names = { "-p", "--influxdb-pass" }, description = "InfluxDB Password (default: ${DEFAULT-VALUE}).", defaultValue = "", paramLabel = "<pass>")
|
||||||
private String influxPass;
|
private String influxPass;
|
||||||
|
|
||||||
@CommandLine.Option(names = { "-s", "--server-port" }, description = "Server port (default: ${DEFAULT-VALUE}).", defaultValue = "9925", paramLabel = "<port>")
|
@CommandLine.Option(names = { "-H", "--server-host" }, description = "Server listening address (default: ${DEFAULT-VALUE}).", paramLabel = "<addr>")
|
||||||
private String listenPort;
|
private String listenHost = "0.0.0.0";
|
||||||
|
|
||||||
|
@CommandLine.Option(names = { "-P", "--server-port" }, description = "Server listening port (default: ${DEFAULT-VALUE}).", paramLabel = "<port>")
|
||||||
|
private Integer listenPort = 9925;
|
||||||
|
|
||||||
|
|
||||||
public static void main(String... args) {
|
public static void main(String... args) {
|
||||||
|
@ -34,14 +41,19 @@ public class Application implements Callable<Integer> {
|
||||||
@Override
|
@Override
|
||||||
public Integer call() throws IOException {
|
public Integer call() throws IOException {
|
||||||
|
|
||||||
|
Properties properties = new Properties();
|
||||||
|
properties.put("http.host", listenHost);
|
||||||
|
properties.put("http.port", listenPort);
|
||||||
|
|
||||||
InfluxDB influxConnectionBean = InfluxDBFactory.connect(influxUrl.toString(), influxUser, influxPass);
|
InfluxDB influxConnectionBean = InfluxDBFactory.connect(influxUrl.toString(), influxUser, influxPass);
|
||||||
|
|
||||||
Main main = new Main();
|
Main main = new Main();
|
||||||
main.bind("myInfluxConnection", influxConnectionBean);
|
main.bind("myInfluxConnection", influxConnectionBean);
|
||||||
main.bind("myListenPort", Integer.parseInt(listenPort));
|
main.bind("http.host", listenHost);
|
||||||
|
main.bind("http.port", listenPort);
|
||||||
|
main.bind("properties", properties);
|
||||||
main.configure().addRoutesBuilder(ServerRouteBuilder.class);
|
main.configure().addRoutesBuilder(ServerRouteBuilder.class);
|
||||||
|
|
||||||
|
|
||||||
// now keep the application running until the JVM is terminated (ctrl + c or sigterm)
|
// now keep the application running until the JVM is terminated (ctrl + c or sigterm)
|
||||||
try {
|
try {
|
||||||
main.run();
|
main.run();
|
||||||
|
|
|
@ -5,6 +5,8 @@ import org.apache.camel.model.rest.RestBindingMode;
|
||||||
import org.apache.camel.spi.Registry;
|
import org.apache.camel.spi.Registry;
|
||||||
import org.sysmon.shared.MetricResult;
|
import org.sysmon.shared.MetricResult;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
public class ServerRouteBuilder extends RouteBuilder {
|
public class ServerRouteBuilder extends RouteBuilder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -14,8 +16,8 @@ public class ServerRouteBuilder extends RouteBuilder {
|
||||||
|
|
||||||
restConfiguration().component("jetty")
|
restConfiguration().component("jetty")
|
||||||
.bindingMode(RestBindingMode.auto)
|
.bindingMode(RestBindingMode.auto)
|
||||||
.host("127.0.0.1")
|
.host(registry.lookupByNameAndType("http.host", String.class))
|
||||||
.port((Integer) registry.lookupByName("myListenPort"));
|
.port(registry.lookupByNameAndType("http.port", Integer.class));
|
||||||
|
|
||||||
rest()
|
rest()
|
||||||
.get("/")
|
.get("/")
|
||||||
|
@ -39,7 +41,7 @@ public class ServerRouteBuilder extends RouteBuilder {
|
||||||
.log(">>> metric: ${header.hostname} - ${body}")
|
.log(">>> metric: ${header.hostname} - ${body}")
|
||||||
.doTry()
|
.doTry()
|
||||||
.process(new MetricResultToPointProcessor())
|
.process(new MetricResultToPointProcessor())
|
||||||
.to("influxdb://myInfluxConnection?databaseName=sysmon&retentionPolicy=autogen")
|
.to("influxdb://ref.myInfluxConnection?databaseName=sysmon&retentionPolicy=autogen")
|
||||||
.doCatch(Exception.class)
|
.doCatch(Exception.class)
|
||||||
.log("Error storing metric to InfluxDB: ${exception}")
|
.log("Error storing metric to InfluxDB: ${exception}")
|
||||||
.end();
|
.end();
|
||||||
|
|
|
@ -27,7 +27,7 @@ camel.main.name = sysmon-server
|
||||||
#camel.main.beanIntrospectionLoggingLevel=INFO
|
#camel.main.beanIntrospectionLoggingLevel=INFO
|
||||||
|
|
||||||
# run in lightweight mode to be tiny as possible
|
# run in lightweight mode to be tiny as possible
|
||||||
camel.main.lightweight = true
|
#camel.main.lightweight = true
|
||||||
# and eager load classes
|
# and eager load classes
|
||||||
#camel.main.eager-classloading = true
|
#camel.main.eager-classloading = true
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ public interface MetricExtension extends ExtensionPoint {
|
||||||
boolean isSupported();
|
boolean isSupported();
|
||||||
|
|
||||||
String getName();
|
String getName();
|
||||||
|
String getProvides();
|
||||||
String getDescription();
|
String getDescription();
|
||||||
|
|
||||||
MetricResult getMetrics();
|
MetricResult getMetrics();
|
||||||
|
|
|
@ -7,8 +7,12 @@ import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class PluginHelper {
|
public class PluginHelper {
|
||||||
|
|
||||||
|
@ -58,5 +62,10 @@ public class PluginHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean canExecute(String cmd) {
|
||||||
|
return Stream.of(System.getenv("PATH").split(Pattern.quote(File.pathSeparator)))
|
||||||
|
.map(Paths::get)
|
||||||
|
.anyMatch(path -> Files.exists(path.resolve(cmd)));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue