org.elasticsearch.monitor.os.OsStats Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch - Open Source, Distributed, RESTful Search Engine
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.monitor.os;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.TransportVersion;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.unit.ByteSizeValue;
import org.elasticsearch.xcontent.ToXContentFragment;
import org.elasticsearch.xcontent.XContentBuilder;
import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
public class OsStats implements Writeable, ToXContentFragment {
private final long timestamp;
private final Cpu cpu;
private final Mem mem;
private final Swap swap;
private final Cgroup cgroup;
public OsStats(final long timestamp, final Cpu cpu, final Mem mem, final Swap swap, final Cgroup cgroup) {
this.timestamp = timestamp;
this.cpu = Objects.requireNonNull(cpu);
this.mem = Objects.requireNonNull(mem);
this.swap = Objects.requireNonNull(swap);
this.cgroup = cgroup;
}
public OsStats(StreamInput in) throws IOException {
this.timestamp = in.readVLong();
this.cpu = new Cpu(in);
this.mem = new Mem(in);
this.swap = new Swap(in);
this.cgroup = in.readOptionalWriteable(Cgroup::new);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeVLong(timestamp);
cpu.writeTo(out);
mem.writeTo(out);
swap.writeTo(out);
out.writeOptionalWriteable(cgroup);
}
public long getTimestamp() {
return timestamp;
}
public Cpu getCpu() {
return cpu;
}
public Mem getMem() {
return mem;
}
public Swap getSwap() {
return swap;
}
public Cgroup getCgroup() {
return cgroup;
}
static final class Fields {
static final String OS = "os";
static final String TIMESTAMP = "timestamp";
static final String CPU = "cpu";
static final String PERCENT = "percent";
static final String LOAD_AVERAGE = "load_average";
static final String LOAD_AVERAGE_1M = "1m";
static final String LOAD_AVERAGE_5M = "5m";
static final String LOAD_AVERAGE_15M = "15m";
static final String MEM = "mem";
static final String SWAP = "swap";
static final String FREE = "free";
static final String FREE_IN_BYTES = "free_in_bytes";
static final String USED = "used";
static final String USED_IN_BYTES = "used_in_bytes";
static final String TOTAL = "total";
static final String TOTAL_IN_BYTES = "total_in_bytes";
static final String ADJUSTED_TOTAL = "adjusted_total";
static final String ADJUSTED_TOTAL_IN_BYTES = "adjusted_total_in_bytes";
static final String FREE_PERCENT = "free_percent";
static final String USED_PERCENT = "used_percent";
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(Fields.OS);
builder.field(Fields.TIMESTAMP, getTimestamp());
cpu.toXContent(builder, params);
mem.toXContent(builder, params);
swap.toXContent(builder, params);
if (cgroup != null) {
cgroup.toXContent(builder, params);
}
builder.endObject();
return builder;
}
public static class Cpu implements Writeable, ToXContentFragment {
private final short percent;
private final double[] loadAverage;
public Cpu(short systemCpuPercent, double[] systemLoadAverage) {
this.percent = systemCpuPercent;
this.loadAverage = systemLoadAverage;
}
public Cpu(StreamInput in) throws IOException {
this.percent = in.readShort();
if (in.readBoolean()) {
this.loadAverage = in.readDoubleArray();
} else {
this.loadAverage = null;
}
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeShort(percent);
if (loadAverage == null) {
out.writeBoolean(false);
} else {
out.writeBoolean(true);
out.writeDoubleArray(loadAverage);
}
}
public short getPercent() {
return percent;
}
public double[] getLoadAverage() {
return loadAverage;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(Fields.CPU);
builder.field(Fields.PERCENT, getPercent());
if (getLoadAverage() != null && Arrays.stream(getLoadAverage()).anyMatch(load -> load != -1)) {
builder.startObject(Fields.LOAD_AVERAGE);
if (getLoadAverage()[0] != -1) {
builder.field(Fields.LOAD_AVERAGE_1M, getLoadAverage()[0]);
}
if (getLoadAverage()[1] != -1) {
builder.field(Fields.LOAD_AVERAGE_5M, getLoadAverage()[1]);
}
if (getLoadAverage()[2] != -1) {
builder.field(Fields.LOAD_AVERAGE_15M, getLoadAverage()[2]);
}
builder.endObject();
}
builder.endObject();
return builder;
}
}
public static class Swap implements Writeable, ToXContentFragment {
private static final Logger logger = LogManager.getLogger(Swap.class);
private final long total;
private final long free;
public Swap(long total, long free) {
assert total >= 0 : "expected total swap to be positive, got: " + total;
assert free >= 0 : "expected free swap to be positive, got: " + total;
this.total = total;
this.free = free;
}
public Swap(StreamInput in) throws IOException {
this.total = in.readLong();
assert this.total >= 0 : "expected total swap to be positive, got: " + total;
this.free = in.readLong();
assert this.free >= 0 : "expected free swap to be positive, got: " + total;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeLong(total);
out.writeLong(free);
}
public ByteSizeValue getFree() {
return ByteSizeValue.ofBytes(free);
}
public ByteSizeValue getUsed() {
if (total == 0) {
// The work in https://github.com/elastic/elasticsearch/pull/42725 established that total memory
// can be reported as negative in some cases. Swap can similarly be reported as negative and in
// those cases, we force it to zero in which case we can no longer correctly report the used swap
// as (total-free) and should report it as zero.
//
// We intentionally check for (total == 0) rather than (total - free < 0) so as not to hide
// cases where (free > total) which would be a different bug.
if (free > 0) {
logger.debug("cannot compute used swap when total swap is 0 and free swap is " + free);
}
return ByteSizeValue.ZERO;
}
return ByteSizeValue.ofBytes(total - free);
}
public ByteSizeValue getTotal() {
return ByteSizeValue.ofBytes(total);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(Fields.SWAP);
builder.humanReadableField(Fields.TOTAL_IN_BYTES, Fields.TOTAL, getTotal());
builder.humanReadableField(Fields.FREE_IN_BYTES, Fields.FREE, getFree());
builder.humanReadableField(Fields.USED_IN_BYTES, Fields.USED, getUsed());
builder.endObject();
return builder;
}
}
public static class Mem implements Writeable, ToXContentFragment {
private static final Logger logger = LogManager.getLogger(Mem.class);
private final long total;
private final long adjustedTotal;
private final long free;
public Mem(long total, long adjustedTotal, long free) {
assert total >= 0 : "expected total memory to be positive, got: " + total;
assert adjustedTotal >= 0 : "expected adjusted total memory to be positive, got: " + adjustedTotal;
assert free >= 0 : "expected free memory to be positive, got: " + free;
// Extra layer of protection for when assertions are disabled
if (total < 0) {
logger.error("negative total memory [{}] found in memory stats", total);
total = 0;
}
if (adjustedTotal < 0) {
logger.error("negative adjusted total memory [{}] found in memory stats", total);
adjustedTotal = 0;
}
if (free < 0) {
logger.error("negative free memory [{}] found in memory stats", total);
free = 0;
}
this.total = total;
this.adjustedTotal = adjustedTotal;
this.free = free;
}
public Mem(StreamInput in) throws IOException {
long total = in.readLong();
assert total >= 0 : "expected total memory to be positive, got: " + total;
// Extra layer of protection for when assertions are disabled
if (total < 0) {
logger.error("negative total memory [{}] deserialized in memory stats", total);
total = 0;
}
this.total = total;
if (in.getTransportVersion().onOrAfter(TransportVersion.V_8_0_0)) {
long adjustedTotal = in.readLong();
assert adjustedTotal >= 0 : "expected adjusted total memory to be positive, got: " + adjustedTotal;
if (adjustedTotal < 0) {
logger.error("negative adjusted total memory [{}] deserialized in memory stats", adjustedTotal);
adjustedTotal = 0;
}
this.adjustedTotal = adjustedTotal;
} else {
this.adjustedTotal = total;
}
long free = in.readLong();
assert free >= 0 : "expected free memory to be positive, got: " + free;
if (free < 0) {
logger.error("negative free memory [{}] deserialized in memory stats", free);
free = 0;
}
this.free = free;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeLong(total);
if (out.getTransportVersion().onOrAfter(TransportVersion.V_8_0_0)) {
out.writeLong(adjustedTotal);
}
out.writeLong(free);
}
public ByteSizeValue getTotal() {
return ByteSizeValue.ofBytes(total);
}
public ByteSizeValue getAdjustedTotal() {
return ByteSizeValue.ofBytes(adjustedTotal);
}
public ByteSizeValue getUsed() {
if (total == 0) {
// The work in https://github.com/elastic/elasticsearch/pull/42725 established that total memory
// can be reported as negative in some cases. In those cases, we force it to zero in which case
// we can no longer correctly report the used memory as (total-free) and should report it as zero.
//
// We intentionally check for (total == 0) rather than (total - free < 0) so as not to hide
// cases where (free > total) which would be a different bug.
if (free > 0) {
logger.debug("cannot compute used memory when total memory is 0 and free memory is " + free);
}
return ByteSizeValue.ZERO;
}
return ByteSizeValue.ofBytes(total - free);
}
public short getUsedPercent() {
return calculatePercentage(getUsed().getBytes(), total);
}
public ByteSizeValue getFree() {
return ByteSizeValue.ofBytes(free);
}
public short getFreePercent() {
return calculatePercentage(free, total);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(Fields.MEM);
builder.humanReadableField(Fields.TOTAL_IN_BYTES, Fields.TOTAL, getTotal());
builder.humanReadableField(Fields.ADJUSTED_TOTAL_IN_BYTES, Fields.ADJUSTED_TOTAL, getAdjustedTotal());
builder.humanReadableField(Fields.FREE_IN_BYTES, Fields.FREE, getFree());
builder.humanReadableField(Fields.USED_IN_BYTES, Fields.USED, getUsed());
builder.field(Fields.FREE_PERCENT, getFreePercent());
builder.field(Fields.USED_PERCENT, getUsedPercent());
builder.endObject();
return builder;
}
}
/**
* Encapsulates basic cgroup statistics.
*/
public static class Cgroup implements Writeable, ToXContentFragment {
private final String cpuAcctControlGroup;
private final long cpuAcctUsageNanos;
private final String cpuControlGroup;
private final long cpuCfsPeriodMicros;
private final long cpuCfsQuotaMicros;
private final CpuStat cpuStat;
private final String memoryControlGroup;
private final String memoryLimitInBytes;
private final String memoryUsageInBytes;
/**
* The control group for the {@code cpuacct} subsystem.
*
* @return the control group
*/
public String getCpuAcctControlGroup() {
return cpuAcctControlGroup;
}
/**
* The total CPU time consumed by all tasks in the
* {@code cpuacct} control group from
* {@link Cgroup#cpuAcctControlGroup}.
*
* @return the total CPU time in nanoseconds
*/
public long getCpuAcctUsageNanos() {
return cpuAcctUsageNanos;
}
/**
* The control group for the {@code cpu} subsystem.
*
* @return the control group
*/
public String getCpuControlGroup() {
return cpuControlGroup;
}
/**
* The period of time for how frequently the control group from
* {@link Cgroup#cpuControlGroup} has its access to CPU
* resources reallocated.
*
* @return the period of time in microseconds
*/
public long getCpuCfsPeriodMicros() {
return cpuCfsPeriodMicros;
}
/**
* The total amount of time for which all tasks in the control
* group from {@link Cgroup#cpuControlGroup} can run in one
* period as represented by {@link Cgroup#cpuCfsPeriodMicros}.
*
* @return the total amount of time in microseconds
*/
public long getCpuCfsQuotaMicros() {
return cpuCfsQuotaMicros;
}
/**
* The CPU time statistics. See {@link CpuStat}.
*
* @return the CPU time statistics.
*/
public CpuStat getCpuStat() {
return cpuStat;
}
/**
* The control group for the {@code memory} subsystem.
*
* @return the control group
*/
public String getMemoryControlGroup() {
return memoryControlGroup;
}
/**
* The maximum amount of user memory (including file cache).
* This is stored as a String
because the value can be too big to fit in a
* long
. (The alternative would have been BigInteger
but then
* it would not be possible to index the OS stats document into Elasticsearch without
* losing information, as BigInteger
is not a supported Elasticsearch type.)
*
* @return the maximum amount of user memory (including file cache).
*/
public String getMemoryLimitInBytes() {
return memoryLimitInBytes;
}
/**
* The total current memory usage by processes in the cgroup (in bytes).
* This is stored as a String
for consistency with memoryLimitInBytes
.
*
* @return the total current memory usage by processes in the cgroup (in bytes).
*/
public String getMemoryUsageInBytes() {
return memoryUsageInBytes;
}
public Cgroup(
final String cpuAcctControlGroup,
final long cpuAcctUsageNanos,
final String cpuControlGroup,
final long cpuCfsPeriodMicros,
final long cpuCfsQuotaMicros,
final CpuStat cpuStat,
final String memoryControlGroup,
final String memoryLimitInBytes,
final String memoryUsageInBytes
) {
this.cpuAcctControlGroup = Objects.requireNonNull(cpuAcctControlGroup);
this.cpuAcctUsageNanos = cpuAcctUsageNanos;
this.cpuControlGroup = Objects.requireNonNull(cpuControlGroup);
this.cpuCfsPeriodMicros = cpuCfsPeriodMicros;
this.cpuCfsQuotaMicros = cpuCfsQuotaMicros;
this.cpuStat = Objects.requireNonNull(cpuStat);
this.memoryControlGroup = memoryControlGroup;
this.memoryLimitInBytes = memoryLimitInBytes;
this.memoryUsageInBytes = memoryUsageInBytes;
}
Cgroup(final StreamInput in) throws IOException {
cpuAcctControlGroup = in.readString();
cpuAcctUsageNanos = in.readLong();
cpuControlGroup = in.readString();
cpuCfsPeriodMicros = in.readLong();
cpuCfsQuotaMicros = in.readLong();
cpuStat = new CpuStat(in);
memoryControlGroup = in.readOptionalString();
memoryLimitInBytes = in.readOptionalString();
memoryUsageInBytes = in.readOptionalString();
}
@Override
public void writeTo(final StreamOutput out) throws IOException {
out.writeString(cpuAcctControlGroup);
out.writeLong(cpuAcctUsageNanos);
out.writeString(cpuControlGroup);
out.writeLong(cpuCfsPeriodMicros);
out.writeLong(cpuCfsQuotaMicros);
cpuStat.writeTo(out);
out.writeOptionalString(memoryControlGroup);
out.writeOptionalString(memoryLimitInBytes);
out.writeOptionalString(memoryUsageInBytes);
}
@Override
public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException {
builder.startObject("cgroup");
{
builder.startObject("cpuacct");
{
builder.field("control_group", cpuAcctControlGroup);
builder.field("usage_nanos", cpuAcctUsageNanos);
}
builder.endObject();
builder.startObject("cpu");
{
builder.field("control_group", cpuControlGroup);
builder.field("cfs_period_micros", cpuCfsPeriodMicros);
builder.field("cfs_quota_micros", cpuCfsQuotaMicros);
cpuStat.toXContent(builder, params);
}
builder.endObject();
if (memoryControlGroup != null) {
builder.startObject("memory");
{
builder.field("control_group", memoryControlGroup);
if (memoryLimitInBytes != null) {
builder.field("limit_in_bytes", memoryLimitInBytes);
}
if (memoryUsageInBytes != null) {
builder.field("usage_in_bytes", memoryUsageInBytes);
}
}
builder.endObject();
}
}
builder.endObject();
return builder;
}
/**
* Encapsulates CPU time statistics.
*/
public static class CpuStat implements Writeable, ToXContentFragment {
private final long numberOfElapsedPeriods;
private final long numberOfTimesThrottled;
private final long timeThrottledNanos;
/**
* The number of elapsed periods.
*
* @return the number of elapsed periods as measured by
* {@code cpu.cfs_period_us}
*/
public long getNumberOfElapsedPeriods() {
return numberOfElapsedPeriods;
}
/**
* The number of times tasks in the control group have been
* throttled.
*
* @return the number of times
*/
public long getNumberOfTimesThrottled() {
return numberOfTimesThrottled;
}
/**
* The total time duration for which tasks in the control
* group have been throttled.
*
* @return the total time in nanoseconds
*/
public long getTimeThrottledNanos() {
return timeThrottledNanos;
}
public CpuStat(final long numberOfElapsedPeriods, final long numberOfTimesThrottled, final long timeThrottledNanos) {
this.numberOfElapsedPeriods = numberOfElapsedPeriods;
this.numberOfTimesThrottled = numberOfTimesThrottled;
this.timeThrottledNanos = timeThrottledNanos;
}
CpuStat(final StreamInput in) throws IOException {
numberOfElapsedPeriods = in.readLong();
numberOfTimesThrottled = in.readLong();
timeThrottledNanos = in.readLong();
}
@Override
public void writeTo(final StreamOutput out) throws IOException {
out.writeLong(numberOfElapsedPeriods);
out.writeLong(numberOfTimesThrottled);
out.writeLong(timeThrottledNanos);
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject("stat");
{
builder.field("number_of_elapsed_periods", numberOfElapsedPeriods);
builder.field("number_of_times_throttled", numberOfTimesThrottled);
builder.field("time_throttled_nanos", timeThrottledNanos);
}
builder.endObject();
return builder;
}
}
}
public static short calculatePercentage(long used, long max) {
return max <= 0 ? 0 : (short) (Math.round((100d * used) / max));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy