From 6872d545ac832f2030eadd244c6ea811b0db9b22 Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Fri, 13 Nov 2015 09:18:21 -0500 Subject: [PATCH] Add system CPU percent to OS stats This commit adds the system CPU percent reflecting the recent CPU usage for the whole system. --- .../org/elasticsearch/monitor/Probes.java | 20 +++++++ .../org/elasticsearch/monitor/os/OsProbe.java | 11 +++- .../org/elasticsearch/monitor/os/OsStats.java | 54 +++++++++++++++---- .../monitor/process/ProcessProbe.java | 13 +---- .../rest/action/cat/RestNodesAction.java | 4 +- .../monitor/os/OsProbeBenchmark.java | 12 +++-- .../monitor/os/OsProbeTests.java | 13 ++--- docs/reference/cluster/nodes-stats.asciidoc | 5 +- docs/reference/migration/migrate_3_0.asciidoc | 12 +++++ 9 files changed, 109 insertions(+), 35 deletions(-) create mode 100644 core/src/main/java/org/elasticsearch/monitor/Probes.java diff --git a/core/src/main/java/org/elasticsearch/monitor/Probes.java b/core/src/main/java/org/elasticsearch/monitor/Probes.java new file mode 100644 index 00000000000..d20a3f58b27 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/monitor/Probes.java @@ -0,0 +1,20 @@ +package org.elasticsearch.monitor; + +import java.lang.management.OperatingSystemMXBean; +import java.lang.reflect.Method; + +public class Probes { + public static short getLoadAndScaleToPercent(Method method, OperatingSystemMXBean osMxBean) { + if (method != null) { + try { + double load = (double) method.invoke(osMxBean); + if (load >= 0) { + return (short) (load * 100); + } + } catch (Throwable t) { + return -1; + } + } + return -1; + } +} diff --git a/core/src/main/java/org/elasticsearch/monitor/os/OsProbe.java b/core/src/main/java/org/elasticsearch/monitor/os/OsProbe.java index 21d4b6b0c72..9e1ba39437a 100644 --- a/core/src/main/java/org/elasticsearch/monitor/os/OsProbe.java +++ b/core/src/main/java/org/elasticsearch/monitor/os/OsProbe.java @@ -20,6 +20,7 @@ package org.elasticsearch.monitor.os; import org.apache.lucene.util.Constants; +import org.elasticsearch.monitor.Probes; import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; @@ -34,6 +35,7 @@ public class OsProbe { private static final Method getFreeSwapSpaceSize; private static final Method getTotalSwapSpaceSize; private static final Method getSystemLoadAverage; + private static final Method getSystemCpuLoad; static { getFreePhysicalMemorySize = getMethod("getFreePhysicalMemorySize"); @@ -41,6 +43,7 @@ public class OsProbe { getFreeSwapSpaceSize = getMethod("getFreeSwapSpaceSize"); getTotalSwapSpaceSize = getMethod("getTotalSwapSpaceSize"); getSystemLoadAverage = getMethod("getSystemLoadAverage"); + getSystemCpuLoad = getMethod("getSystemCpuLoad"); } /** @@ -113,6 +116,10 @@ public class OsProbe { } } + public short getSystemCpuPercent() { + return Probes.getLoadAndScaleToPercent(getSystemCpuLoad, osMxBean); + } + private static class OsProbeHolder { private final static OsProbe INSTANCE = new OsProbe(); } @@ -136,7 +143,9 @@ public class OsProbe { public OsStats osStats() { OsStats stats = new OsStats(); stats.timestamp = System.currentTimeMillis(); - stats.loadAverage = getSystemLoadAverage(); + stats.cpu = new OsStats.Cpu(); + stats.cpu.percent = getSystemCpuPercent(); + stats.cpu.loadAverage = getSystemLoadAverage(); OsStats.Mem mem = new OsStats.Mem(); mem.total = getTotalPhysicalMemorySize(); diff --git a/core/src/main/java/org/elasticsearch/monitor/os/OsStats.java b/core/src/main/java/org/elasticsearch/monitor/os/OsStats.java index 03f262f883b..ebf7d9fafda 100644 --- a/core/src/main/java/org/elasticsearch/monitor/os/OsStats.java +++ b/core/src/main/java/org/elasticsearch/monitor/os/OsStats.java @@ -36,7 +36,7 @@ public class OsStats implements Streamable, ToXContent { long timestamp; - double loadAverage = -1; + Cpu cpu = null; Mem mem = null; @@ -49,10 +49,7 @@ public class OsStats implements Streamable, ToXContent { return timestamp; } - public double getLoadAverage() { - return loadAverage; - } - + public Cpu getCpu() { return cpu; } public Mem getMem() { return mem; @@ -65,6 +62,8 @@ public class OsStats implements Streamable, ToXContent { static final class Fields { static final XContentBuilderString OS = new XContentBuilderString("os"); static final XContentBuilderString TIMESTAMP = new XContentBuilderString("timestamp"); + static final XContentBuilderString CPU = new XContentBuilderString("cpu"); + static final XContentBuilderString PERCENT = new XContentBuilderString("percent"); static final XContentBuilderString LOAD_AVERAGE = new XContentBuilderString("load_average"); static final XContentBuilderString MEM = new XContentBuilderString("mem"); @@ -85,7 +84,12 @@ public class OsStats implements Streamable, ToXContent { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(Fields.OS); builder.field(Fields.TIMESTAMP, getTimestamp()); - builder.field(Fields.LOAD_AVERAGE, getLoadAverage()); + if (cpu != null) { + builder.startObject(Fields.CPU); + builder.field(Fields.PERCENT, cpu.getPercent()); + builder.field(Fields.LOAD_AVERAGE, cpu.getLoadAverage()); + builder.endObject(); + } if (mem != null) { builder.startObject(Fields.MEM); @@ -120,7 +124,7 @@ public class OsStats implements Streamable, ToXContent { @Override public void readFrom(StreamInput in) throws IOException { timestamp = in.readVLong(); - loadAverage = in.readDouble(); + cpu = in.readOptionalStreamable(Cpu::new); if (in.readBoolean()) { mem = Mem.readMem(in); } @@ -132,7 +136,7 @@ public class OsStats implements Streamable, ToXContent { @Override public void writeTo(StreamOutput out) throws IOException { out.writeVLong(timestamp); - out.writeDouble(loadAverage); + out.writeOptionalStreamable(cpu); if (mem == null) { out.writeBoolean(false); } else { @@ -147,6 +151,39 @@ public class OsStats implements Streamable, ToXContent { } } + public static class Cpu implements Streamable { + short percent = -1; + double loadAverage = -1; + + Cpu() {} + + public static Cpu readCpu(StreamInput in) throws IOException { + Cpu cpu = new Cpu(); + cpu.readFrom(in); + return cpu; + } + + @Override + public void readFrom(StreamInput in) throws IOException { + percent = in.readShort(); + loadAverage = in.readDouble(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeShort(percent); + out.writeDouble(loadAverage); + } + + public short getPercent() { + return percent; + } + + public double getLoadAverage() { + return loadAverage; + } + } + public static class Swap implements Streamable { long total = -1; @@ -230,5 +267,4 @@ public class OsStats implements Streamable, ToXContent { private static short calculatePercentage(long used, long max) { return max <= 0 ? 0 : (short) (Math.round((100d * used) / max)); } - } diff --git a/core/src/main/java/org/elasticsearch/monitor/process/ProcessProbe.java b/core/src/main/java/org/elasticsearch/monitor/process/ProcessProbe.java index eca3327e779..4cd005982c2 100644 --- a/core/src/main/java/org/elasticsearch/monitor/process/ProcessProbe.java +++ b/core/src/main/java/org/elasticsearch/monitor/process/ProcessProbe.java @@ -20,6 +20,7 @@ package org.elasticsearch.monitor.process; import org.elasticsearch.bootstrap.BootstrapInfo; +import org.elasticsearch.monitor.Probes; import java.lang.management.ManagementFactory; import java.lang.management.OperatingSystemMXBean; @@ -88,17 +89,7 @@ public class ProcessProbe { * Returns the process CPU usage in percent */ public short getProcessCpuPercent() { - if (getProcessCpuLoad != null) { - try { - double load = (double) getProcessCpuLoad.invoke(osMxBean); - if (load >= 0) { - return (short) (load * 100); - } - } catch (Throwable t) { - return -1; - } - } - return -1; + return Probes.getLoadAndScaleToPercent(getProcessCpuLoad, osMxBean); } /** diff --git a/core/src/main/java/org/elasticsearch/rest/action/cat/RestNodesAction.java b/core/src/main/java/org/elasticsearch/rest/action/cat/RestNodesAction.java index 50f1baa5ad0..208b7e6c374 100644 --- a/core/src/main/java/org/elasticsearch/rest/action/cat/RestNodesAction.java +++ b/core/src/main/java/org/elasticsearch/rest/action/cat/RestNodesAction.java @@ -130,6 +130,7 @@ public class RestNodesAction extends AbstractCatAction { table.addCell("file_desc.percent", "default:false;alias:fdp,fileDescriptorPercent;text-align:right;desc:used file descriptor ratio"); table.addCell("file_desc.max", "default:false;alias:fdm,fileDescriptorMax;text-align:right;desc:max file descriptors"); + table.addCell("cpu", "alias:cpu;text-align:right;desc:recent cpu usage"); table.addCell("load", "alias:l;text-align:right;desc:most recent load avg"); table.addCell("uptime", "default:false;alias:u;text-align:right;desc:node uptime"); table.addCell("node.role", "alias:r,role,dc,nodeRole;desc:d:data node, c:client node"); @@ -258,7 +259,8 @@ public class RestNodesAction extends AbstractCatAction { table.addCell(processStats == null ? null : calculatePercentage(processStats.getOpenFileDescriptors(), processStats.getMaxFileDescriptors())); table.addCell(processStats == null ? null : processStats.getMaxFileDescriptors()); - table.addCell(osStats == null ? null : String.format(Locale.ROOT, "%.2f", osStats.getLoadAverage())); + table.addCell(osStats == null ? null : Short.toString(osStats.getCpu().getPercent())); + table.addCell(osStats == null ? null : String.format(Locale.ROOT, "%.2f", osStats.getCpu().getLoadAverage())); table.addCell(jvmStats == null ? null : jvmStats.getUptime()); table.addCell(node.clientNode() ? "c" : node.dataNode() ? "d" : "-"); table.addCell(masterId == null ? "x" : masterId.equals(node.id()) ? "*" : node.masterNode() ? "m" : "-"); diff --git a/core/src/test/java/org/elasticsearch/benchmark/monitor/os/OsProbeBenchmark.java b/core/src/test/java/org/elasticsearch/benchmark/monitor/os/OsProbeBenchmark.java index c475c10c8f8..de5f0410726 100644 --- a/core/src/test/java/org/elasticsearch/benchmark/monitor/os/OsProbeBenchmark.java +++ b/core/src/test/java/org/elasticsearch/benchmark/monitor/os/OsProbeBenchmark.java @@ -41,12 +41,10 @@ public class OsProbeBenchmark { probe.getTotalSwapSpaceSize(); probe.getFreeSwapSpaceSize(); probe.getSystemLoadAverage(); + probe.getSystemCpuPercent(); } logger.info("--> warmed up"); - - - logger.info("--> testing 'getTotalPhysicalMemorySize' method..."); long start = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { @@ -86,5 +84,13 @@ public class OsProbeBenchmark { } elapsed = System.currentTimeMillis() - start; logger.info("--> total [{}] ms, avg [{}] ms", elapsed, (elapsed / (double)ITERATIONS)); + + logger.info("--> testing 'getSystemCpuPercent' method..."); + start = System.currentTimeMillis(); + for (int i = 0; i < ITERATIONS; i++) { + probe.getSystemCpuPercent(); + } + elapsed = System.currentTimeMillis() - start; + logger.info("--> total [{}] ms, avg [{}] ms", elapsed, (elapsed / (double)ITERATIONS)); } } diff --git a/core/src/test/java/org/elasticsearch/monitor/os/OsProbeTests.java b/core/src/test/java/org/elasticsearch/monitor/os/OsProbeTests.java index 4d1dde5f178..0d59341f1c9 100644 --- a/core/src/test/java/org/elasticsearch/monitor/os/OsProbeTests.java +++ b/core/src/test/java/org/elasticsearch/monitor/os/OsProbeTests.java @@ -22,13 +22,7 @@ package org.elasticsearch.monitor.os; import org.apache.lucene.util.Constants; import org.elasticsearch.test.ESTestCase; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.anyOf; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.greaterThanOrEqualTo; -import static org.hamcrest.Matchers.lessThan; -import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static org.hamcrest.Matchers.*; public class OsProbeTests extends ESTestCase { OsProbe probe = OsProbe.getInstance(); @@ -47,12 +41,13 @@ public class OsProbeTests extends ESTestCase { OsStats stats = probe.osStats(); assertNotNull(stats); assertThat(stats.getTimestamp(), greaterThan(0L)); + assertThat(stats.getCpu().getPercent(), anyOf(equalTo((short) -1), is(both(greaterThanOrEqualTo((short) 0)).and(lessThanOrEqualTo((short) 100))))); if (Constants.WINDOWS) { // Load average is always -1 on Windows platforms - assertThat(stats.getLoadAverage(), equalTo((double) -1)); + assertThat(stats.getCpu().getLoadAverage(), equalTo((double) -1)); } else { // Load average can be negative if not available or not computed yet, otherwise it should be >= 0 - assertThat(stats.getLoadAverage(), anyOf(lessThan((double) 0), greaterThanOrEqualTo((double) 0))); + assertThat(stats.getCpu().getLoadAverage(), anyOf(lessThan((double) 0), greaterThanOrEqualTo((double) 0))); } assertNotNull(stats.getMem()); diff --git a/docs/reference/cluster/nodes-stats.asciidoc b/docs/reference/cluster/nodes-stats.asciidoc index 9890164bff5..cd6c7db2d41 100644 --- a/docs/reference/cluster/nodes-stats.asciidoc +++ b/docs/reference/cluster/nodes-stats.asciidoc @@ -128,7 +128,10 @@ the operating system: `os.timestamp`:: Last time the operating system statistics have been refreshed -`os.load_average`:: +`os.cpu.percent`:: + Recent CPU usage for the whole system, or -1 if not supported + +`os.cpu.load_average`:: System load average for the last minute, or -1 if not supported `os.mem.total_in_bytes`:: diff --git a/docs/reference/migration/migrate_3_0.asciidoc b/docs/reference/migration/migrate_3_0.asciidoc index 15080923b1e..7c2bef79c9d 100644 --- a/docs/reference/migration/migrate_3_0.asciidoc +++ b/docs/reference/migration/migrate_3_0.asciidoc @@ -428,3 +428,15 @@ controls the backing queue for the thread pool and modifying this is an expert s and high risk of being misused. The ability to change the thread pool type for any thread pool has been removed; do note that it is still possible to adjust relevant thread pool parameters for each of the thread pools (e.g., depending on the thread pool type, `keep_alive`, `queue_size`, etc.). + +=== Adding system CPU percent to OS stats + +The recent CPU usage (as a percent) has been added to the OS stats reported under the node stats API and the cat nodes +API. The breaking change here is that there is a new object in the "os" object in the node stats response. This object +is called "cpu" and includes "percent" and "load_average" as fields. This moves the "load_average" field that was +previously a top-level field in the "os" object to the "cpu" object. Additionally, the "cpu" field in the cat nodes API +response is output by default. + +Finally, the API for org.elasticsearch.monitor.os.OsStats has changed. The `getLoadAverage` method has been removed. The +value for this can now be obtained from `OsStats.Cpu#getLoadAverage`. Additionally, the recent CPU usage can be obtained +from `OsStats.Cpu#getPercent`.