From: Lennart Poettering Date: Mon, 15 Jun 2026 08:19:54 +0000 (+0200) Subject: report-basic: also report load average + swap size in metrics report X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=491ee4afb1601c151d8f12e38b84e75cc9c9b795;p=thirdparty%2Fsystemd.git report-basic: also report load average + swap size in metrics report --- diff --git a/src/report/report-basic.c b/src/report/report-basic.c index cf7a4f0b5ef..56f7065c68d 100644 --- a/src/report/report-basic.c +++ b/src/report/report-basic.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include +#include #include #include "sd-device.h" @@ -142,6 +143,67 @@ static int cpus_online_generate(const MetricFamily *mf, sd_varlink *link, void * /* fields= */ NULL); } +static int load_average_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) { + enum { + LOAD_AVERAGE_FIELD_1MIN, + LOAD_AVERAGE_FIELD_5MIN, + LOAD_AVERAGE_FIELD_15MIN, + _LOAD_AVERAGE_FIELD_MAX, + }; + + int r; + + assert(mf && mf->name); + assert(link); + + /* The classic Linux load average, i.e. the exponentially damped moving average of the number of + * runnable plus uninterruptible tasks over the last 1, 5 and 15 minutes. The kernel exposes these as + * fixed-point numbers shifted left by SI_LOAD_SHIFT bits. */ + + struct sysinfo info; + if (sysinfo(&info) < 0) + return log_debug_errno(errno, "Failed to call sysinfo(): %m"); + + assert_cc(_LOAD_AVERAGE_FIELD_MAX == ELEMENTSOF(info.loads)); + + for (size_t i = 0; i < _LOAD_AVERAGE_FIELD_MAX; i++) { + + r = metric_build_send_double( + mf + i, + link, + /* object= */ NULL, + (double) info.loads[i] / (UINT64_C(1) << SI_LOAD_SHIFT), + /* fields= */ NULL); + if (r < 0) + return r; + } + + return 0; +} + +static int swap_generate(const MetricFamily *mf, sd_varlink *link, void *userdata) { + assert(mf && mf->name); + assert(link); + + /* The total amount of configured swap space, in bytes. */ + + struct sysinfo info; + if (sysinfo(&info) < 0) + return log_debug_errno(errno, "Failed to call sysinfo(): %m"); + + /* Overflow is unrealistic (would need >16 EiB of swap), but use MUL_SAFE to make this obvious to + * static analyzers. */ + uint64_t swap; + assert_se(MUL_SAFE(&swap, (uint64_t) info.totalswap, (uint64_t) info.mem_unit)); + + return metric_build_send_unsigned( + mf, + link, + /* object= */ NULL, + swap, + /* fields= */ NULL); +} + enum { FIELD_PRETTY_NAME, FIELD_NAME, @@ -500,6 +562,25 @@ static const MetricFamily metric_family_table[] = { METRIC_FAMILY_TYPE_STRING, .generate = kernel_version_generate, }, + { + METRIC_IO_SYSTEMD_BASIC_PREFIX "LoadAverage1Min", + "System load average over the last 1 minute", + METRIC_FAMILY_TYPE_GAUGE, + .generate = load_average_generate, + }, + { + METRIC_IO_SYSTEMD_BASIC_PREFIX "LoadAverage5Min", + "System load average over the last 5 minutes", + METRIC_FAMILY_TYPE_GAUGE, + .generate = NULL, + }, + { + METRIC_IO_SYSTEMD_BASIC_PREFIX "LoadAverage15Min", + "System load average over the last 15 minutes", + METRIC_FAMILY_TYPE_GAUGE, + .generate = NULL, + }, + /* Keep those ↑ in sync with load_average_generate(). */ { METRIC_IO_SYSTEMD_BASIC_PREFIX "MachineID", "Machine ID", @@ -568,6 +649,12 @@ static const MetricFamily metric_family_table[] = { SMBIOS_STANDARD_FIELD("ChassisSerialNumber"), SMBIOS_STANDARD_FIELD("ChassisAssetTagNumber"), /* Keep those ↑ in sync with smbios_generate(). */ + { + METRIC_IO_SYSTEMD_BASIC_PREFIX "SwapBytes", + "Total configured swap space in bytes", + METRIC_FAMILY_TYPE_GAUGE, + .generate = swap_generate, + }, { METRIC_IO_SYSTEMD_BASIC_PREFIX "TPM2.Manufacturer", "TPM2 device manufacturer (ID_TPM2_MANUFACTURER property of the tpmrm0 device)", diff --git a/src/shared/metrics.c b/src/shared/metrics.c index 7c50f8ab34e..9a587f32dbb 100644 --- a/src/shared/metrics.c +++ b/src/shared/metrics.c @@ -209,3 +209,23 @@ int metric_build_send_unsigned( return metric_build_send(mf, link, object, v, fields); } + +int metric_build_send_double( + const MetricFamily *mf, + sd_varlink *link, + const char *object, + double value, + sd_json_variant *fields) { + + _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; + int r; + + assert(mf); + assert(link); + + r = sd_json_variant_new_real(&v, value); + if (r < 0) + return log_debug_errno(r, "Failed to allocate JSON real: %m"); + + return metric_build_send(mf, link, object, v, fields); +} diff --git a/src/shared/metrics.h b/src/shared/metrics.h index 7c4afb5de77..63b19464504 100644 --- a/src/shared/metrics.h +++ b/src/shared/metrics.h @@ -38,3 +38,4 @@ int metrics_method_list(const MetricFamily mfs[], sd_varlink *link, sd_json_vari int metric_build_send_string(const MetricFamily* mf, sd_varlink *link, const char *object, const char *value, sd_json_variant *fields); int metric_build_send_unsigned(const MetricFamily* mf, sd_varlink *link, const char *object, uint64_t value, sd_json_variant *fields); +int metric_build_send_double(const MetricFamily* mf, sd_varlink *link, const char *object, double value, sd_json_variant *fields); diff --git a/test/units/TEST-74-AUX-UTILS.report.sh b/test/units/TEST-74-AUX-UTILS.report.sh index f758d24030d..4b733ad611f 100755 --- a/test/units/TEST-74-AUX-UTILS.report.sh +++ b/test/units/TEST-74-AUX-UTILS.report.sh @@ -62,6 +62,24 @@ id1="$(varlinkctl call --more /run/systemd/report/io.systemd.Basic io.systemd.Me id2="$(. /etc/os-release; echo "$ID")" [ "$id1" = "$id2" ] +# test io.systemd.Basic load average and swap metrics +basic_metrics="$(varlinkctl call --more /run/systemd/report/io.systemd.Basic io.systemd.Metrics.List {})" +# NB: '| tostring' turns the numeric value into a raw string, so 'jq -r' prints it cleanly +# (numbers, unlike strings, are otherwise still emitted with the json-seq record separator). +basic_value() { echo "$basic_metrics" | jq --seq -r "select(.name == \"$1\") | .value | tostring"; } + +# The three classic load average fields must be present and numeric (jq's 'numbers' filter +# emits the value only if it is a JSON number, so a non-empty result confirms both). +for field in LoadAverage1Min LoadAverage5Min LoadAverage15Min; do + loadavg="$(echo "$basic_metrics" | jq --seq -r "select(.name == \"io.systemd.Basic.$field\") | .value | numbers | tostring")" + test -n "$loadavg" +done + +# SwapBytes must match the total the kernel reports in /proc/meminfo (which is in kB). +swap_reported="$(basic_value io.systemd.Basic.SwapBytes)" +swap_expected=$(( $(awk '/^SwapTotal:/ { print $2; found=1 } END { if (!found) print 0 }' /proc/meminfo) * 1024 )) +[ "$swap_reported" = "$swap_expected" ] + # test io.systemd.Basic.MachineInfo.* metrics, sourced from /etc/machine-info if [ -e /etc/machine-info ]; then MACHINE_INFO_BACKUP="$(mktemp)"