diff --git a/src/collectd.conf.in b/src/collectd.conf.in
index 18dd82ef61..283b065cae 100644
--- a/src/collectd.conf.in
+++ b/src/collectd.conf.in
@@ -1748,10 +1748,10 @@
#
# ReportByDevice false
-# ReportBytes true
-# ValuesAbsolute true
-# ValuesPercentage false
+# ReportUsage true
+# ReportUtilization false
# ReportIO true
+# ReportBytes false
#
#
diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod
index b8b9457a99..0dc7ba4291 100644
--- a/src/collectd.conf.pod
+++ b/src/collectd.conf.pod
@@ -9333,18 +9333,20 @@ This option is only available if the I can read C
=item B B|B
-When enabled, the I is reported in bytes. When disabled, the default,
-I is reported in pages. This option is available under Linux only.
+When enabled, the I is reported in bytes. When disabled, I
+is reported in pages. Defaults to B. This option is available under
+Linux and NetBSD only.
-=item B B|B
+
+=item B B|B
Enables or disables reporting of absolute swap metrics, i.e. number of I
available and used. Defaults to B.
-=item B B|B
+=item B B|B
-Enables or disables reporting of relative swap metrics, i.e. I
-available and free. Defaults to B.
+Enables or disables reporting of relative swap metrics, i.e. the ratio of used
+and free swap space. Defaults to B.
This is useful for deploying I in a heterogeneous environment, where
swap sizes differ and you want to specify generic thresholds or similar.
diff --git a/src/swap.c b/src/swap.c
index 979d265ed9..a358251977 100644
--- a/src/swap.c
+++ b/src/swap.c
@@ -114,10 +114,24 @@ static int pagesize;
#error "No applicable input method."
#endif /* HAVE_LIBSTATGRAB */
-static bool values_absolute = true;
-static bool values_percentage;
+static bool report_usage = true;
+static bool report_utilization;
static bool report_io = true;
+static char const *const label_device = "system.device";
+static char const *const label_state = "system.paging.state";
+
+static char const *const state_free = "free";
+static char const *const state_used = "used";
+
+enum {
+ FAM_SWAP_USAGE = 0,
+ FAM_SWAP_UTILIZATION,
+ FAM_SWAP_OPS,
+ FAM_SWAP_IO,
+ FAM_SWAP_MAX,
+};
+
static int swap_config(oconfig_item_t *ci) /* {{{ */
{
for (int i = 0; i < ci->children_num; i++) {
@@ -127,7 +141,7 @@ static int swap_config(oconfig_item_t *ci) /* {{{ */
cf_util_get_boolean(child, &report_bytes);
#else
WARNING("swap plugin: The \"ReportBytes\" option "
- "is only valid under Linux. "
+ "is only valid under Linux and NetBSD. "
"The option is going to be ignored.");
#endif
else if (strcasecmp("ReportByDevice", child->key) == 0)
@@ -138,10 +152,13 @@ static int swap_config(oconfig_item_t *ci) /* {{{ */
"is not supported on this platform. "
"The option is going to be ignored.");
#endif /* SWAP_HAVE_REPORT_BY_DEVICE */
- else if (strcasecmp("ValuesAbsolute", child->key) == 0)
- cf_util_get_boolean(child, &values_absolute);
- else if (strcasecmp("ValuesPercentage", child->key) == 0)
- cf_util_get_boolean(child, &values_percentage);
+ /* ValuesAbsolute and ValuesPercentage are for collectd 5 compatibility. */
+ else if (strcasecmp("ReportUsage", child->key) == 0 ||
+ strcasecmp("ValuesAbsolute", child->key) == 0)
+ cf_util_get_boolean(child, &report_usage);
+ else if (strcasecmp("ReportUtilization", child->key) == 0 ||
+ strcasecmp("ValuesPercentage", child->key) == 0)
+ cf_util_get_boolean(child, &report_utilization);
else if (strcasecmp("ReportIO", child->key) == 0)
cf_util_get_boolean(child, &report_io);
else
@@ -195,44 +212,79 @@ static int swap_init(void) /* {{{ */
return 0;
} /* }}} int swap_init */
-static void swap_submit_usage(char const *plugin_instance, /* {{{ */
- gauge_t used, gauge_t free,
- char const *other_name, gauge_t other_value) {
- value_list_t vl = VALUE_LIST_INIT;
-
- vl.values = &(value_t){.gauge = NAN};
- vl.values_len = 1;
- sstrncpy(vl.plugin, "swap", sizeof(vl.plugin));
- if (plugin_instance != NULL)
- sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
- sstrncpy(vl.type, "swap", sizeof(vl.type));
-
- if (values_absolute)
- plugin_dispatch_multivalue(&vl, false, DS_TYPE_GAUGE, "used", used, "free",
- free, other_name, other_value, NULL);
- if (values_percentage)
- plugin_dispatch_multivalue(&vl, true, DS_TYPE_GAUGE, "used", used, "free",
- free, other_name, other_value, NULL);
-} /* }}} void swap_submit_usage */
+static void swap_append_usage3(metric_family_t *fams, char const *device,
+ gauge_t total, gauge_t used, gauge_t free,
+ char const *other_name, gauge_t other) {
+ metric_family_t *fam_usage = &fams[FAM_SWAP_USAGE];
+ metric_family_t *fam_utilization = &fams[FAM_SWAP_UTILIZATION];
+
+ metric_t m = {0};
+ if (device != NULL) {
+ metric_label_set(&m, label_device, device);
+ }
+
+ bool have_other = (other_name != NULL) && !isnan(other);
+
+ if (report_usage) {
+ if (have_other) {
+ metric_family_append(fam_usage, label_state, other_name,
+ (value_t){.gauge = other}, &m);
+ }
+
+ metric_family_append(fam_usage, label_state, state_used,
+ (value_t){.gauge = used}, &m);
+ metric_family_append(fam_usage, label_state, state_free,
+ (value_t){.gauge = free}, &m);
+ }
+
+ if (report_utilization) {
+ if (have_other) {
+ metric_family_append(fam_utilization, label_state, other_name,
+ (value_t){.gauge = other / total}, &m);
+ }
+
+ metric_family_append(fam_utilization, label_state, state_used,
+ (value_t){.gauge = used / total}, &m);
+ metric_family_append(fam_utilization, label_state, state_free,
+ (value_t){.gauge = free / total}, &m);
+ }
+
+ metric_reset(&m);
+} /* void swap_append_usage3 */
+
+static void swap_append_usage(metric_family_t *fams, char *device,
+ gauge_t total, gauge_t used, gauge_t free) {
+ swap_append_usage3(fams, device, total, used, free, NULL, NAN);
+}
#if KERNEL_LINUX || HAVE_PERFSTAT || KERNEL_NETBSD
-__attribute__((nonnull(1))) static void
-swap_submit_derive(char const *type_instance, /* {{{ */
- derive_t value) {
- value_list_t vl = VALUE_LIST_INIT;
-
- vl.values = &(value_t){.derive = value};
- vl.values_len = 1;
- sstrncpy(vl.plugin, "swap", sizeof(vl.plugin));
- sstrncpy(vl.type, "swap_io", sizeof(vl.type));
- sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
-
- plugin_dispatch_values(&vl);
-} /* }}} void swap_submit_derive */
+static char const *const label_direction = "system.paging.direction";
+
+static char const *const direction_in = "in";
+static char const *const direction_out = "out";
+
+static void swap_append_io(metric_family_t *fams, counter_t in, counter_t out,
+ derive_t pagesize) {
+ if (!report_io) {
+ return;
+ }
+
+ metric_family_t *fam = &fams[FAM_SWAP_OPS];
+ if (report_bytes) {
+ fam = &fams[FAM_SWAP_IO];
+ in = in * (counter_t)pagesize;
+ out = out * (counter_t)pagesize;
+ }
+
+ metric_family_append(fam, label_direction, direction_in,
+ (value_t){.counter = in}, NULL);
+ metric_family_append(fam, label_direction, direction_out,
+ (value_t){.counter = out}, NULL);
+} /* void swap_append_io */
#endif
#if KERNEL_LINUX
-static int swap_read_separate(void) /* {{{ */
+static int swap_read_separate(metric_family_t *fams) /* {{{ */
{
FILE *fh;
char buffer[1024];
@@ -257,7 +309,6 @@ static int swap_read_separate(void) /* {{{ */
continue;
sstrncpy(path, fields[0], sizeof(path));
- escape_slashes(path, sizeof(path));
errno = 0;
endptr = NULL;
@@ -274,7 +325,8 @@ static int swap_read_separate(void) /* {{{ */
if (total < used)
continue;
- swap_submit_usage(path, used * 1024.0, (total - used) * 1024.0, NULL, NAN);
+ swap_append_usage(fams, path, total * 1024.0, used * 1024.0,
+ (total - used) * 1024.0);
}
fclose(fh);
@@ -282,7 +334,7 @@ static int swap_read_separate(void) /* {{{ */
return 0;
} /* }}} int swap_read_separate */
-static int swap_read_combined(void) /* {{{ */
+static int swap_read_combined(metric_family_t *fams) /* {{{ */
{
FILE *fh;
char buffer[1024];
@@ -329,13 +381,13 @@ static int swap_read_combined(void) /* {{{ */
if (swap_used < 0.0)
return EINVAL;
- swap_submit_usage(NULL, swap_used * 1024.0, swap_free * 1024.0,
- isnan(swap_cached) ? NULL : "cached",
- isnan(swap_cached) ? NAN : swap_cached * 1024.0);
+ swap_append_usage3(fams, NULL, swap_total * 1024.0, swap_used * 1024.0,
+ swap_free * 1024.0, "cached", swap_cached * 1024.0);
+
return 0;
} /* }}} int swap_read_combined */
-static int swap_read_io(void) /* {{{ */
+static int swap_read_io(metric_family_t *fams) /* {{{ */
{
char buffer[1024];
@@ -367,32 +419,27 @@ static int swap_read_io(void) /* {{{ */
fclose(fh);
- if (have_data != 0x03)
+ if (have_data != 0x03) {
return ENOENT;
-
- if (report_bytes) {
- swap_in = swap_in * pagesize;
- swap_out = swap_out * pagesize;
}
- swap_submit_derive("in", swap_in);
- swap_submit_derive("out", swap_out);
+ swap_append_io(fams, swap_in, swap_out, pagesize);
return 0;
} /* }}} int swap_read_io */
-static int swap_read(void) /* {{{ */
+static int swap_read_fam(metric_family_t *fams) /* {{{ */
{
if (report_by_device)
- swap_read_separate();
+ swap_read_separate(fams);
else
- swap_read_combined();
+ swap_read_combined(fams);
if (report_io)
- swap_read_io();
+ swap_read_io(fams);
return 0;
-} /* }}} int swap_read */
+} /* }}} int swap_read_fam */
/* #endif KERNEL_LINUX */
/*
@@ -406,7 +453,7 @@ static int swap_read(void) /* {{{ */
*/
#elif 0 && HAVE_LIBKSTAT
/* kstat-based read function */
-static int swap_read_kstat(void) /* {{{ */
+static int swap_read_kstat(metric_family_t *fams) /* {{{ */
{
gauge_t swap_alloc;
gauge_t swap_resv;
@@ -444,14 +491,16 @@ static int swap_read_kstat(void) /* {{{ */
swap_resv = (gauge_t)((ai.ani_resv + ai.ani_free - ai.ani_max) * pagesize);
swap_avail = (gauge_t)((ai.ani_max - ai.ani_resv) * pagesize);
- swap_submit_usage(NULL, swap_alloc, swap_avail, "reserved", swap_resv);
+ swap_append_usage3(fams, NULL, (swap_alloc + swap_resv + swap_avail),
+ swap_alloc, swap_avail, "reserved", swap_resv);
+
return 0;
} /* }}} int swap_read_kstat */
/* #endif 0 && HAVE_LIBKSTAT */
#elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS
/* swapctl-based read function */
-static int swap_read(void) /* {{{ */
+static int swap_read_fam(metric_family_t *fams) /* {{{ */
{
swaptbl_t *s;
char *s_paths;
@@ -526,9 +575,9 @@ static int swap_read(void) /* {{{ */
}
sstrncpy(path, s->swt_ent[i].ste_path, sizeof(path));
- escape_slashes(path, sizeof(path));
- swap_submit_usage(path, this_total - this_avail, this_avail, NULL, NAN);
+ swap_append_usage(fams, path, this_total, this_total - this_avail,
+ this_avail);
} /* for (swap_num) */
if (total < avail) {
@@ -542,27 +591,26 @@ static int swap_read(void) /* {{{ */
/* If the "separate" option was specified (report_by_device == true) all
* values have already been dispatched from within the loop. */
- if (!report_by_device)
- swap_submit_usage(NULL, total - avail, avail, NULL, NAN);
+ if (!report_by_device) {
+ swap_append_usage(fams, NULL, total, total - avail, avail);
+ }
sfree(s_paths);
sfree(s);
return 0;
-} /* }}} int swap_read */
+} /* }}} int swap_read_fam */
/* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS */
#elif HAVE_SWAPCTL && HAVE_SWAPCTL_THREE_ARGS
#if KERNEL_NETBSD
#include
-static int swap_read_io(void) /* {{{ */
+static int swap_read_io(metric_family_t *fams) /* {{{ */
{
static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP2};
struct uvmexp_sysctl uvmexp;
- size_t ssize;
- derive_t swap_in, swap_out;
- ssize = sizeof(uvmexp);
+ size_t ssize = sizeof(uvmexp);
memset(&uvmexp, 0, ssize);
if (sysctl(uvmexp_mib, __arraycount(uvmexp_mib), &uvmexp, &ssize, NULL, 0) ==
-1) {
@@ -572,22 +620,16 @@ static int swap_read_io(void) /* {{{ */
return (-1);
}
- swap_in = uvmexp.pgswapin;
- swap_out = uvmexp.pgswapout;
+ counter_t swap_in = uvmexp.pgswapin;
+ counter_t swap_out = uvmexp.pgswapout;
- if (report_bytes) {
- swap_in = swap_in * pagesize;
- swap_out = swap_out * pagesize;
- }
-
- swap_submit_derive("in", swap_in);
- swap_submit_derive("out", swap_out);
+ swap_append_io(fams, swap_in, swap_out, pagesize);
return (0);
} /* }}} */
#endif
-static int swap_read(void) /* {{{ */
+static int swap_read_fam(metric_family_t *fams) /* {{{ */
{
struct swapent *swap_entries;
int swap_num;
@@ -643,9 +685,9 @@ static int swap_read(void) /* {{{ */
}
sstrncpy(path, swap_entries[i].se_path, sizeof(path));
- escape_slashes(path, sizeof(path));
- swap_submit_usage(path, this_used, this_total - this_used, NULL, NAN);
+ swap_append_usage(fams, path, this_total, this_used,
+ this_total - this_used);
} /* for (swap_num) */
if (total < used) {
@@ -658,19 +700,20 @@ static int swap_read(void) /* {{{ */
/* If the "separate" option was specified (report_by_device == 1), all
* values have already been dispatched from within the loop. */
- if (!report_by_device)
- swap_submit_usage(NULL, used, total - used, NULL, NAN);
+ if (!report_by_device) {
+ swap_append_usage(fams, NULL, total, used, total - used);
+ }
sfree(swap_entries);
#if KERNEL_NETBSD
- swap_read_io();
+ swap_read_io(fams);
#endif
return 0;
-} /* }}} int swap_read */
+} /* }}} int swap_read_fam */
/* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_THREE_ARGS */
#elif defined(VM_SWAPUSAGE)
-static int swap_read(void) /* {{{ */
+static int swap_read_fam(metric_family_t *fams) /* {{{ */
{
int mib[3];
size_t mib_len;
@@ -687,22 +730,19 @@ static int swap_read(void) /* {{{ */
return -1;
/* The returned values are bytes. */
- swap_submit_usage(NULL, (gauge_t)sw_usage.xsu_used,
- (gauge_t)sw_usage.xsu_avail, NULL, NAN);
+ swap_append_usage(fams, NULL, (gauge_t)sw_usage.xsu_total,
+ (gauge_t)sw_usage.xsu_used, (gauge_t)sw_usage.xsu_avail);
return 0;
-} /* }}} int swap_read */
+} /* }}} int swap_read_fam */
/* #endif VM_SWAPUSAGE */
#elif HAVE_LIBKVM_GETSWAPINFO
-static int swap_read(void) /* {{{ */
+static int swap_read_fam(metric_family_t *fams) /* {{{ */
{
struct kvm_swap data_s;
int status;
- gauge_t used;
- gauge_t total;
-
if (kvm_obj == NULL)
return -1;
@@ -711,20 +751,20 @@ static int swap_read(void) /* {{{ */
if (status == -1)
return -1;
- total = (gauge_t)data_s.ksw_total;
- used = (gauge_t)data_s.ksw_used;
+ gauge_t total = (gauge_t)data_s.ksw_total;
+ gauge_t used = (gauge_t)data_s.ksw_used;
total *= (gauge_t)kvm_pagesize;
used *= (gauge_t)kvm_pagesize;
- swap_submit_usage(NULL, used, total - used, NULL, NAN);
+ swap_append_usage(fams, NULL, total, used, total - used);
return 0;
-} /* }}} int swap_read */
+} /* }}} int swap_read_fam */
/* #endif HAVE_LIBKVM_GETSWAPINFO */
#elif HAVE_LIBSTATGRAB
-static int swap_read(void) /* {{{ */
+static int swap_read_fam(metric_family_t *fams) /* {{{ */
{
sg_swap_stats *swap;
@@ -732,44 +772,91 @@ static int swap_read(void) /* {{{ */
if (swap == NULL)
return -1;
- swap_submit_usage(NULL, (gauge_t)swap->used, (gauge_t)swap->free, NULL, NAN);
+ swap_append_usage(fams, NULL, (gauge_t)swap->total, (gauge_t)swap->used,
+ (gauge_t)swap->free);
return 0;
-} /* }}} int swap_read */
+} /* }}} int swap_read_fam */
/* #endif HAVE_LIBSTATGRAB */
#elif HAVE_PERFSTAT
-static int swap_read(void) /* {{{ */
+static int swap_read_fam(metric_family_t *fams) /* {{{ */
{
perfstat_memory_total_t pmemory = {0};
- int status;
-
- gauge_t total;
- gauge_t free;
- gauge_t reserved;
- status =
+ int status =
perfstat_memory_total(NULL, &pmemory, sizeof(perfstat_memory_total_t), 1);
if (status < 0) {
WARNING("swap plugin: perfstat_memory_total failed: %s", STRERRNO);
return -1;
}
- total = (gauge_t)(pmemory.pgsp_total * pagesize);
- free = (gauge_t)(pmemory.pgsp_free * pagesize);
- reserved = (gauge_t)(pmemory.pgsp_rsvd * pagesize);
+ gauge_t total = (gauge_t)(pmemory.pgsp_total * pagesize);
+ gauge_t free = (gauge_t)(pmemory.pgsp_free * pagesize);
+ gauge_t reserved = (gauge_t)(pmemory.pgsp_rsvd * pagesize);
- swap_submit_usage(NULL, total - free, free, "reserved", reserved);
+ swap_append_usage3(fams, NULL, total, total - free, free, "reserved",
+ reserved);
- if (report_io) {
- swap_submit_derive("in", (derive_t)pmemory.pgspins * pagesize);
- swap_submit_derive("out", (derive_t)pmemory.pgspouts * pagesize);
- }
+ counter_t swap_in = pmemory.pgspins;
+ counter_t swap_out = pmemory.pgspouts;
+
+ swap_append_io(fams, swap_in, swap_out, pagesize);
return 0;
-} /* }}} int swap_read */
+} /* }}} int swap_read_fam */
#endif /* HAVE_PERFSTAT */
+static int swap_read(void) {
+ metric_family_t fams[] = {
+ [FAM_SWAP_USAGE] =
+ {
+ .name = "system.paging.usage",
+ .help = "Unix swap usage",
+ .unit = "By",
+ .type = METRIC_TYPE_GAUGE,
+ },
+ [FAM_SWAP_UTILIZATION] =
+ {
+ .name = "system.paging.utilization",
+ .help = "Unix swap utilization",
+ .unit = "1",
+ .type = METRIC_TYPE_GAUGE,
+ },
+ [FAM_SWAP_OPS] =
+ {
+ /* used when report_io && !report_bytes */
+ .name = "system.paging.operations",
+ .unit = "{operation}",
+ .type = METRIC_TYPE_COUNTER,
+ },
+ [FAM_SWAP_IO] =
+ {
+ /* used when report_io && report_bytes */
+ .name = "system.paging.io",
+ .unit = "By",
+ .type = METRIC_TYPE_COUNTER,
+ },
+ };
+
+ int status = swap_read_fam(fams);
+ if (status != 0) {
+ return status;
+ }
+
+ for (size_t i = 0; i < FAM_SWAP_MAX; i++) {
+ metric_family_t *fam = &fams[i];
+ int status = plugin_dispatch_metric_family(fam);
+ if (status != 0) {
+ ERROR("swap plugin: plugin_dispatch_metric_family failed: %s",
+ STRERROR(status));
+ }
+ metric_family_metric_reset(fam);
+ }
+
+ return 0;
+}
+
void module_register(void) {
plugin_register_complex_config("swap", swap_config);
plugin_register_init("swap", swap_init);