소스 검색

sanitising fields that may be null, configurable data path, initialising non-primitive members, fixed validity checks

Grega Bremec 2 년 전
부모
커밋
3643db26c7

+ 4 - 1
exporter/README.adoc

@@ -1,3 +1,6 @@
 = metrics-exporter Project =
 
-TBD
+Configurable property:
+
+ - `exporter.data.path`: where to read incoming data from
+ 

+ 22 - 0
exporter/data/done/20221105/psacct-20221105-163432

@@ -0,0 +1,22 @@
+     138       0.24re       0.00u       0.00s     65682min          0maj          0swp 
+      10       0.07re       0.00u       0.01s     14859min          0maj          0swp    runc
+       2       0.03re       0.01u       0.00s      2099min          0maj          0swp    etcdctl
+      20       0.00re       0.00u       0.00s      3366min          0maj          0swp    md5sum
+      20       0.00re       0.00u       0.00s      4019min          0maj          0swp    awk
+      20       0.00re       0.00u       0.00s      2903min          0maj          0swp    entrypoint.sh*
+      12       2.50re       0.00u       0.00s      1712min          0maj          0swp    sleep
+      10       0.10re       0.00u       0.00s      4287min          0maj          0swp    gmain
+      10       0.02re       0.00u       0.00s     21918min          0maj          0swp    runc:[0:PARENT]
+      10       0.00re       0.00u       0.00s       589min          0maj          0swp    runc:[1:CHILD]*
+       4       0.03re       0.00u       0.00s       460min          0maj          0swp    grep
+       4       0.02re       0.00u       0.00s      3001min          0maj          0swp    grpc_health_pro
+       3       0.00re       0.00u       0.00s       524min          0maj          0swp    date
+       2       0.06re       0.00u       0.00s      1330min          0maj          0swp    bash
+       2       0.04re       0.00u       0.00s      1272min          0maj          0swp    sh
+       2       0.03re       0.00u       0.00s      1208min          0maj          0swp    test
+       2       0.01re       0.00u       0.00s       989min          0maj          0swp    curl
+       1       0.01re       0.00u       0.00s       494min          0maj          0swp    sadf
+       1       0.00re       0.00u       0.00s       157min          0maj          0swp    iptables
+       1       0.00re       0.00u       0.00s       160min          0maj          0swp    ip6tables
+       1       0.00re       0.00u       0.00s       249min          0maj          0swp    sadc
+       1       0.00re       0.00u       0.00s        86min          0maj          0swp    accton

+ 18 - 0
exporter/data/done/20221105/sysstat-20221105-162658

@@ -0,0 +1,18 @@
+{"sysstat": {
+	"hosts": [
+		{
+			"nodename": "master02",
+			"sysname": "Linux",
+			"release": "4.18.0-305.40.2.el8_4.x86_64",
+			"machine": "x86_64",
+			"number-of-cpus": 4,
+			"file-date": "2022-11-05",
+			"file-utc-time": "14:42:05",
+			"timezone": "UTC",
+			"statistics": [
+			],
+			"restarts": [
+			]
+		}
+	]
+}}

+ 103 - 0
exporter/data/done/20221105/sysstat-20221105-162716

@@ -0,0 +1,103 @@
+{"sysstat": {
+	"hosts": [
+		{
+			"nodename": "master02",
+			"sysname": "Linux",
+			"release": "4.18.0-305.40.2.el8_4.x86_64",
+			"machine": "x86_64",
+			"number-of-cpus": 4,
+			"file-date": "2022-11-05",
+			"file-utc-time": "14:42:05",
+			"timezone": "UTC",
+			"statistics": [
+				{
+					"timestamp": {"date": "2022-11-05", "time": "14:47:16", "utc": 1, "interval": 10},
+					"cpu-load": [
+						{"cpu": "all", "usr": 7.77, "nice": 0.00, "sys": 4.29, "iowait": 0.58, "steal": 0.00, "irq": 1.31, "soft": 0.78, "guest": 0.00, "gnice": 0.00, "idle": 85.27},
+						{"cpu": "0", "usr": 7.96, "nice": 0.00, "sys": 4.23, "iowait": 0.40, "steal": 0.00, "irq": 1.31, "soft": 0.60, "guest": 0.00, "gnice": 0.00, "idle": 85.50},
+						{"cpu": "1", "usr": 7.89, "nice": 0.00, "sys": 4.25, "iowait": 0.51, "steal": 0.00, "irq": 1.42, "soft": 0.51, "guest": 0.00, "gnice": 0.00, "idle": 85.43},
+						{"cpu": "2", "usr": 7.96, "nice": 0.00, "sys": 3.93, "iowait": 0.91, "steal": 0.00, "irq": 1.11, "soft": 0.60, "guest": 0.00, "gnice": 0.00, "idle": 85.50},
+						{"cpu": "3", "usr": 7.27, "nice": 0.00, "sys": 4.75, "iowait": 0.51, "steal": 0.00, "irq": 1.41, "soft": 1.41, "guest": 0.00, "gnice": 0.00, "idle": 84.65}
+					],
+					"process-and-context-switch": {"proc": 33.87, "cswch": 20118.48},
+					"swap-pages": {"pswpin": 0.00, "pswpout": 0.00},
+					"paging": {"pgpgin": 0.00, "pgpgout": 384.12, "fault": 7580.32, "majflt": 0.00, "pgfree": 10954.15, "pgscank": 0.00, "pgscand": 0.00, "pgsteal": 0.00, "vmeff-percent": 0.00},
+					"io": {"tps": 41.56, "io-reads": {"rtps": 0.00, "bread": 0.00}, "io-writes": {"wtps": 41.56, "bwrtn": 768.33}, "io-discard": {"dtps": 0.00, "bdscd": 0.00}},
+					"memory": {"memfree": 6679328, "avail": 17005388, "memused": 6841136, "memused-percent": 27.73, "buffers": 1076, "cached": 10533500, "commit": 14153540, "commit-percent": 57.38, "active": 589664, "inactive": 16452856, "dirty": 1504, "anonpg": 4907356, "slab": 611840, "kstack": 15312, "pgtbl": 35584, "vmused": 0, "swpfree": 0, "swpused": 0, "swpused-percent": 0.00, "swpcad": 0, "swpcad-percent": 0.00},
+					"hugepages": {"hugfree": 0, "hugused": 0, "hugused-percent": 0.00, "hugrsvd": 0, "hugsurp": 0},
+					"kernel": {"dentunusd": 202300, "file-nr": 6048, "inode-nr": 195396, "pty-nr": 0},
+					"queue": {"runq-sz": 0, "plist-sz": 954, "ldavg-1": 1.35, "ldavg-5": 1.00, "ldavg-15": 0.91, "blocked": 0},
+					"serial": [
+						{"line": 0, "rcvin": 0.00, "xmtin": 0.00, "framerr": 0.00, "prtyerr": 0.00, "brk": 0.00, "ovrun": 0.00}
+					],
+					"disk": [
+						{"disk-device": "vda", "tps": 41.56, "rd_sec": 0.00, "wr_sec": 768.33, "dc_sec": 0.00, "rkB": 0.00, "wkB": 384.17, "dkB": 0.00, "avgrq-sz": 18.49, "areq-sz": 9.24, "avgqu-sz": 0.04, "aqu-sz": 0.04, "await": 1.00, "util-percent": 4.45},
+						{"disk-device": "sr0", "tps": 0.00, "rd_sec": 0.00, "wr_sec": 0.00, "dc_sec": 0.00, "rkB": 0.00, "wkB": 0.00, "dkB": 0.00, "avgrq-sz": 0.00, "areq-sz": 0.00, "avgqu-sz": 0.00, "aqu-sz": 0.00, "await": 0.00, "util-percent": 0.00}
+					],
+					"network": {
+						"net-dev": [
+							{"iface": "vethc43ef5a1", "rxpck": 15.38, "txpck": 15.08, "rxkB": 2.55, "txkB": 2.10, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "ovs-system", "rxpck": 0.00, "txpck": 0.00, "rxkB": 0.00, "txkB": 0.00, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "veth25ac4d77", "rxpck": 0.30, "txpck": 0.30, "rxkB": 0.02, "txkB": 0.02, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "vethdc847798", "rxpck": 0.50, "txpck": 0.50, "rxkB": 0.03, "txkB": 0.09, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "tun0", "rxpck": 48.65, "txpck": 48.35, "rxkB": 8.26, "txkB": 10.15, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "vxlan_sys_4789", "rxpck": 23.98, "txpck": 17.58, "rxkB": 3.20, "txkB": 9.96, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "veth4eaaa653", "rxpck": 2.40, "txpck": 3.20, "rxkB": 1.04, "txkB": 2.13, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "veth6bc1fc67", "rxpck": 4.00, "txpck": 4.60, "rxkB": 1.51, "txkB": 1.62, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "vethf5f75ba5", "rxpck": 2.90, "txpck": 3.30, "rxkB": 1.52, "txkB": 0.54, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "veth68af4193", "rxpck": 25.27, "txpck": 26.27, "rxkB": 9.14, "txkB": 3.98, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "vethbfaafe62", "rxpck": 0.50, "txpck": 0.90, "rxkB": 0.03, "txkB": 0.06, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "br0", "rxpck": 0.00, "txpck": 0.00, "rxkB": 0.00, "txkB": 0.00, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "vethb2364cfe", "rxpck": 1.00, "txpck": 1.00, "rxkB": 0.14, "txkB": 0.09, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "veth3e633a4f", "rxpck": 0.50, "txpck": 0.70, "rxkB": 0.24, "txkB": 0.36, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "veth3cdacabf", "rxpck": 1.00, "txpck": 1.00, "rxkB": 0.10, "txkB": 0.09, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "vethd0019ea6", "rxpck": 7.39, "txpck": 9.99, "rxkB": 1.51, "txkB": 0.73, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "veth9300bca0", "rxpck": 0.40, "txpck": 0.60, "rxkB": 0.05, "txkB": 0.05, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "lo", "rxpck": 433.07, "txpck": 433.07, "rxkB": 233.09, "txkB": 233.09, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "veth6bd46351", "rxpck": 0.40, "txpck": 0.40, "rxkB": 0.03, "txkB": 0.03, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "veth11dafa0f", "rxpck": 0.40, "txpck": 0.40, "rxkB": 0.03, "txkB": 0.03, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "ens3", "rxpck": 709.69, "txpck": 704.90, "rxkB": 323.77, "txkB": 340.42, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00},
+							{"iface": "veth49557dc0", "rxpck": 2.00, "txpck": 2.20, "rxkB": 0.70, "txkB": 1.26, "rxcmp": 0.00, "txcmp": 0.00, "rxmcst": 0.00, "ifutil-percent": 0.00}
+						],
+						"net-edev": [
+							{"iface": "vethc43ef5a1", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "ovs-system", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "veth25ac4d77", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "vethdc847798", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "tun0", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "vxlan_sys_4789", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "veth4eaaa653", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "veth6bc1fc67", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "vethf5f75ba5", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "veth68af4193", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "vethbfaafe62", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "br0", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "vethb2364cfe", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "veth3e633a4f", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "veth3cdacabf", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "vethd0019ea6", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "veth9300bca0", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "lo", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "veth6bd46351", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "veth11dafa0f", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "ens3", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00},
+							{"iface": "veth49557dc0", "rxerr": 0.00, "txerr": 0.00, "coll": 0.00, "rxdrop": 0.00, "txdrop": 0.00, "txcarr": 0.00, "rxfram": 0.00, "rxfifo": 0.00, "txfifo": 0.00}
+						],
+						"net-nfs": {"call": 0.80, "retrans": 0.00, "read": 0.10, "write": 0.00, "access": 0.00, "getatt": 0.20},
+						"net-nfsd": {"scall": 0.00, "badcall": 0.00, "packet": 0.00, "udp": 0.00, "tcp": 0.00, "hit": 0.00, "miss": 0.00, "sread": 0.00, "swrite": 0.00, "saccess": 0.00, "sgetatt": 0.00},
+						"net-sock": {"totsck": 1943, "tcpsck": 548, "udpsck": 6, "rawsck": 0, "ip-frag": 0, "tcp-tw": 39},
+						"softnet": [
+							{"cpu": "all", "total": 1368.63, "dropd": 0.00, "squeezd": 0.00, "rx_rps": 0.00, "flw_lim": 0.00},
+							{"cpu": "0", "total": 124.18, "dropd": 0.00, "squeezd": 0.00, "rx_rps": 0.00, "flw_lim": 0.00},
+							{"cpu": "1", "total": 146.35, "dropd": 0.00, "squeezd": 0.00, "rx_rps": 0.00, "flw_lim": 0.00},
+							{"cpu": "2", "total": 154.65, "dropd": 0.00, "squeezd": 0.00, "rx_rps": 0.00, "flw_lim": 0.00},
+							{"cpu": "3", "total": 943.46, "dropd": 0.00, "squeezd": 0.00, "rx_rps": 0.00, "flw_lim": 0.00}
+						]
+					}
+				}
+			],
+			"restarts": [
+			]
+		}
+	]
+}}

+ 81 - 44
exporter/src/main/java/net/p0f/openshift/metrics/exporter/SysstatMetrics.java

@@ -21,56 +21,28 @@ public class SysstatMetrics {
 
     SysstatMeasurement lastMeasurement = null;
 
-    public static boolean isRecordValid(SysstatMeasurement sm) {
-        LOG.fine("Checking record validity of " + sm);
-
-        String nullMetrics = "";
+    public void processMetricRecord(SysstatMeasurement sm) {
+        LOG.fine("Updating sysstat metrics records...");
 
-        if (sm.getCpuLoad() == null) {
-            nullMetrics += "CpuLoad, ";
-        }
-        if (sm.getDisk() == null) {
-            nullMetrics += "Disk, ";
+        // sanitize fields that may be null
+        // cpuload may not be null
+        // process-and-context-switch may not be null
+        if (sm.getSwapPages() == null) {
+            sm.setSwapPages(new SysstatMeasurement.SwapPages());
         }
+        // paging may not be null
+        // io may not be null
+        // memory may not be null
         if (sm.getHugepages() == null) {
-            nullMetrics += "Hugepages, ";
-        }
-        if (sm.getIo() == null) {
-            nullMetrics += "Io, ";
-        }
-        if (sm.getKernel() == null) {
-            nullMetrics += "Kernel, ";
-        }
-        if (sm.getMemory() == null) {
-            nullMetrics += "Memory, ";
-        }
-        if (sm.getNetwork() == null) {
-            nullMetrics += "Network, ";
-        }
-        if (sm.getPaging() == null) {
-            nullMetrics += "Paging, ";
-        }
-        if (sm.getProcessAndContextSwitch() == null) {
-            nullMetrics += "ProcessAndContextSwitch, ";
+            sm.setHugepages(new SysstatMeasurement.Hugepages());
         }
+        // kernel may not be null
+        // queue may not be null
+        // disk may not be null
+        // network may not be null
         if (sm.getPsi() == null) {
-            nullMetrics += "Psi, ";
-        }
-        if (sm.getQueue() == null) {
-            nullMetrics += "Queue, ";
+            sm.setPsi(new SysstatMeasurement.Psi());
         }
-        if (sm.getSwapPages() == null) {
-            nullMetrics += "SwapPages, ";
-        }
-
-        nullMetrics.replaceFirst(", $", "");
-        LOG.fine("Validity check result: \"" + nullMetrics + "\"");
-
-        return nullMetrics.length() == 0;
-    }
-
-    public void processMetricRecord(SysstatMeasurement sm) {
-        LOG.fine("Updating sysstat metrics records...");
 
         if (this.lastMeasurement == null) {
             LOG.fine("Initialising sysstat metrics for " + sm.getHostname());
@@ -574,4 +546,69 @@ public class SysstatMetrics {
         }
     }
 
+    public static boolean isRecordValid(SysstatMeasurement sm) {
+        LOG.fine("Checking record validity of " + sm);
+
+        String nullMetrics = "";
+        String warnMetrics = "";
+
+        // cpuload may not be null
+        if (sm.getCpuLoad() == null) {
+            nullMetrics += "CpuLoad, ";
+        }
+        // process-and-context-switch may not be null
+        if (sm.getProcessAndContextSwitch() == null) {
+            nullMetrics += "ProcessAndContextSwitch, ";
+        }
+        // WARNING ONLY
+        if (sm.getSwapPages() == null) {
+            warnMetrics += "SwapPages, ";
+        }
+        // paging may not be null
+        if (sm.getPaging() == null) {
+            nullMetrics += "Paging, ";
+        }
+        // io may not be null
+        if (sm.getIo() == null) {
+            nullMetrics += "Io, ";
+        }
+        // memory may not be null
+        if (sm.getMemory() == null) {
+            nullMetrics += "Memory, ";
+        }
+        // WARNING ONLY
+        if (sm.getHugepages() == null) {
+            warnMetrics += "Hugepages, ";
+        }
+        // kernel may not be null
+        if (sm.getKernel() == null) {
+            nullMetrics += "Kernel, ";
+        }
+        // queue may not be null
+        if (sm.getQueue() == null) {
+            nullMetrics += "Queue, ";
+        }
+        // disk may not be null
+        if (sm.getDisk() == null) {
+            nullMetrics += "Disk, ";
+        }
+        // network may not be null
+        if (sm.getNetwork() == null) {
+            nullMetrics += "Network, ";
+        }
+        // WARNING ONLY
+        if (sm.getPsi() == null) {
+            warnMetrics += "Psi, ";
+        }
+        nullMetrics = nullMetrics.replaceFirst(", $", "");
+        warnMetrics = warnMetrics.replaceFirst(", $", "");
+
+        LOG.fine("Validity check result: ERRORS: \"" + nullMetrics + "\"");
+
+        if (warnMetrics.length() != 0) {
+            LOG.warning("Some sysstat fields are null: \"" + warnMetrics + "\"");
+        }
+
+        return nullMetrics.length() == 0;
+    }
 }

+ 19 - 0
exporter/src/main/java/net/p0f/openshift/metrics/model/SysstatMeasurement.java

@@ -1,6 +1,7 @@
 package net.p0f.openshift.metrics.model;
 
 import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.logging.Logger;
 
@@ -465,6 +466,11 @@ public class SysstatMeasurement implements Serializable {
         IoWrites ioWrites;
         @JsonProperty("io-discard")
         IoDiscard ioDiscard;
+        public Io() {
+            this.ioReads = new IoReads();
+            this.ioWrites = new IoWrites();
+            this.ioDiscard = new IoDiscard();
+        }
         public float getTps() {
             return tps;
         }
@@ -1069,6 +1075,14 @@ public class SysstatMeasurement implements Serializable {
         NetSock netSock;
         @JsonProperty
         List<Softnet> softnet;
+        public Network() {
+            this.netDev = new ArrayList<>();
+            this.netEDev = new ArrayList<>();
+            this.netNfs = new NetNfs();
+            this.netNfsd = new NetNfsd();
+            this.netSock = new NetSock();
+            this.softnet = new ArrayList<>();
+        }
         public List<NetDev> getNetDev() {
             return netDev;
         }
@@ -1647,6 +1661,11 @@ public class SysstatMeasurement implements Serializable {
         PsiIoAndMem psiIo;
         @JsonProperty("psi-mem")
         PsiIoAndMem psiMem;
+        public Psi() {
+            this.psiCpu = new PsiCpu();
+            this.psiIo = new PsiIoAndMem();
+            this.psiMem = new PsiIoAndMem();
+        }
         public PsiCpu getPsiCpu() {
             return psiCpu;
         }

+ 8 - 1
exporter/src/main/java/net/p0f/openshift/metrics/processor/PsacctToCsv.java

@@ -1,11 +1,14 @@
 package net.p0f.openshift.metrics.processor;
 
 import java.util.StringTokenizer;
+import java.util.logging.Logger;
 
 import org.apache.camel.Exchange;
 import org.apache.camel.Processor;
 
 public class PsacctToCsv implements Processor {
+    private static final Logger LOG = Logger.getLogger(PsacctToCsv.class.getName());
+
     @Override
     public void process(Exchange exchange) throws Exception {
         exchange.getMessage().setBody(
@@ -15,10 +18,14 @@ public class PsacctToCsv implements Processor {
                     .replaceFirst(",$", ",ANONYMOUS")
                     .replaceAll("(re|u|s|min|maj|swp),", ",")
         );
+        LOG.fine("Converting to CSV: " + exchange.getMessage().getBody(String.class));
     }
 
     public static boolean isRecordValid(String body) {
-        StringTokenizer st = new StringTokenizer(body, ",");
+        StringTokenizer st = new StringTokenizer(
+                                    body.replaceAll(" +", " ").replaceFirst(" $", " x"),
+                                    " ");
+        LOG.fine("Checking record: \"" + body + "\": got " + st.countTokens() + " tokens.");
         return st.countTokens() == 8;
     }
 }

+ 8 - 1
exporter/src/main/java/net/p0f/openshift/metrics/routes/PsacctConsumer.java

@@ -1,20 +1,26 @@
 package net.p0f.openshift.metrics.routes;
 
+import javax.enterprise.context.ApplicationScoped;
+
 import org.apache.camel.LoggingLevel;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.dataformat.bindy.csv.BindyCsvDataFormat;
 import org.apache.camel.spi.DataFormat;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
 
 import net.p0f.openshift.metrics.model.ProcessAccountingRecord;
 import net.p0f.openshift.metrics.processor.PsacctToCsv;
 
+@ApplicationScoped
 public class PsacctConsumer extends RouteBuilder {
+    @ConfigProperty(defaultValue = "/metrics", name = "exporter.data.path")
+    String dataPath;
 
     @Override
     public void configure() throws Exception {
         PsacctToCsv toCsv = new PsacctToCsv();
         DataFormat fromCsv = new BindyCsvDataFormat(ProcessAccountingRecord.class);
-        from("file:/metrics?" +
+        from("file:" + dataPath + "?" +
                 "fileName=psacct-dump-all&" +
                 "readLock=changed&" +
                 "readLockCheckInterval=250&" +
@@ -27,6 +33,7 @@ public class PsacctConsumer extends RouteBuilder {
             .split().tokenize("\n").parallelProcessing()
             .log(LoggingLevel.DEBUG, "Split Psacct Record: ${body}")
             .setHeader("X-Is-Record-Valid", method(PsacctToCsv.class, "isRecordValid"))
+            .log(LoggingLevel.DEBUG, "Validity check: ${header.X-Is-Record-Valid}")
             .choice()
                 .when(bodyAs(String.class).isEqualTo(""))
                     .log(LoggingLevel.DEBUG, "Skipping empty record.")

+ 8 - 1
exporter/src/main/java/net/p0f/openshift/metrics/routes/SysstatConsumer.java

@@ -1,17 +1,23 @@
 package net.p0f.openshift.metrics.routes;
 
+import javax.enterprise.context.ApplicationScoped;
+
 import org.apache.camel.LoggingLevel;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.jackson.JacksonDataFormat;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
 
 import net.p0f.openshift.metrics.exporter.SysstatMetrics;
 import net.p0f.openshift.metrics.model.SysstatMeasurement;
 
+@ApplicationScoped
 public class SysstatConsumer extends RouteBuilder {
+    @ConfigProperty(defaultValue = "/metrics", name = "exporter.data.path")
+    String dataPath;
 
     @Override
     public void configure() throws Exception {
-        from("file:/metrics?" +
+        from("file:" + dataPath + "?" +
                 "fileName=sysstat-dump.json&" +
                 "readLock=changed&" +
                 "readLockCheckInterval=250&" +
@@ -38,6 +44,7 @@ public class SysstatConsumer extends RouteBuilder {
             .unmarshal(new JacksonDataFormat(SysstatMeasurement.class))
             .log(LoggingLevel.INFO, "Unmarshaled Sysstat: ${body}")
             .setHeader("X-Is-Record-Valid", method(SysstatMetrics.class, "isRecordValid"))
+            .log(LoggingLevel.DEBUG, "Validity check: ${header.X-Is-Record-Valid}")
             .choice()
                 .when(header("X-Is-Record-Valid").isEqualTo(false))
                     .log(LoggingLevel.WARN, "Illegal record: ${body}")