git: 61156558d2a5 - stable/12 - prometheus_sysctl_exporter(8): filter output using a regex

From: Robert Wing <rew_at_FreeBSD.org>
Date: Sun, 10 Oct 2021 18:18:29 UTC
The branch stable/12 has been updated by rew:

URL: https://cgit.FreeBSD.org/src/commit/?id=61156558d2a53e88c139561458420f2e62f6aa06

commit 61156558d2a53e88c139561458420f2e62f6aa06
Author:     Robert Wing <rew@FreeBSD.org>
AuthorDate: 2021-10-08 01:00:27 +0000
Commit:     Robert Wing <rew@FreeBSD.org>
CommitDate: 2021-10-10 18:18:29 +0000

    prometheus_sysctl_exporter(8): filter output using a regex
    
    Add two options, -i and -e, for filtering prometheus metrics.
    
    Each option takes a regular expression as an argument. The provided
    regex will be tested against the prometheus metric name.
    
    The -i option includes metrics matching the given regex.
    The -e option excludes metrics matching the given regex.
    
    Sponsored by:   Modirum MDPay
    Sponsored by:   Klara Inc.
    Reviewed by:    0mp, debdrup
    Differential Revision:  https://reviews.freebsd.org/D32269
    
    (cherry picked from commit 8ff2b52241400f2b5c2287b91e6e12b97ffd5773)
---
 .../prometheus_sysctl_exporter.8                   | 18 ++++-
 .../prometheus_sysctl_exporter.c                   | 88 ++++++++++++++++------
 2 files changed, 80 insertions(+), 26 deletions(-)

diff --git a/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.8 b/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.8
index f4ac44891ecb..7d73e3cab8cd 100644
--- a/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.8
+++ b/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.8
@@ -22,7 +22,7 @@
 .\" SUCH DAMAGE.
 .\"
 .\" $FreeBSD$
-.Dd December 18, 2016
+.Dd October 7, 2021
 .Dt PROMETHEUS_SYSCTL_EXPORTER 8
 .Os
 .Sh NAME
@@ -31,6 +31,8 @@
 .Sh SYNOPSIS
 .Nm prometheus_sysctl_exporter
 .Op Fl dgh
+.Op Fl e Ar pattern
+.Op Fl i Ar pattern
 .Op Ar prefix ...
 .Sh DESCRIPTION
 Prometheus is a monitoring system that gathers metrics from its targets
@@ -74,12 +76,26 @@ The following options are available:
 .Bl -tag -width indent
 .It Fl d
 Print descriptions of metrics when available.
+.It Fl e Ar pattern
+Same as
+.Fl i ,
+except exclude metrics that match
+.Ar pattern .
 .It Fl g
 Gzip compresses the HTTP response body.
 .It Fl h
 Precede the output with a HTTP response header.
 This flag is required when running this utility through
 .Xr inetd 8 .
+.It Fl i Ar pattern
+If specified, include metrics that match
+.Ar pattern .
+The format of
+.Ar pattern
+is to be a regular expression as described in
+.Xr re_format 7 .
+The provided regular expression is tested against the Prometheus
+metric name.
 .El
 .Sh SEE ALSO
 .Xr cron 8 ,
diff --git a/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.c b/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.c
index b1f24ddf203a..25e60f5dddbe 100644
--- a/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.c
+++ b/usr.sbin/prometheus_sysctl_exporter/prometheus_sysctl_exporter.c
@@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
 #include <err.h>
 #include <errno.h>
 #include <math.h>
+#include <regex.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -44,6 +45,10 @@ __FBSDID("$FreeBSD$");
 #include <unistd.h>
 #include <zlib.h>
 
+/* Regular expressions for filtering output. */
+static regex_t inc_regex;
+static regex_t exc_regex;
+
 /*
  * Cursor for iterating over all of the system's sysctl OIDs.
  */
@@ -370,25 +375,27 @@ oid_get_name(const struct oid *o, struct oidname *on)
 	on->oid = *o;
 }
 
-/* Prints the name and labels of an OID to a file stream. */
+/* Populates the name and labels of an OID to a buffer. */
 static void
-oidname_print(const struct oidname *on, const struct oidformat *of,
-    FILE *fp)
+oid_get_metric(const struct oidname *on, const struct oidformat *of,
+    char *metric, size_t mlen)
 {
 	const char *name, *label;
 	size_t i;
-	char separator;
+	char separator, buf[BUFSIZ];
 
 	/* Print the name of the metric. */
-	fprintf(fp, "sysctl");
+	snprintf(metric, mlen, "%s", "sysctl");
 	name = on->names;
 	label = on->labels;
 	for (i = 0; i < on->oid.len; ++i) {
 		if (*label == '\0') {
-			fputc('_', fp);
+			strlcat(metric, "_", mlen);
 			while (*name != '\0') {
 				/* Map unsupported characters to underscores. */
-				fputc(isalnum(*name) ? *name : '_', fp);
+				snprintf(buf, sizeof(buf), "%c",
+				    isalnum(*name) ? *name : '_');
+				strlcat(metric, buf, mlen);
 				++name;
 			}
 		}
@@ -396,9 +403,9 @@ oidname_print(const struct oidname *on, const struct oidformat *of,
 		label += strlen(label) + 1;
 	}
 	if (oidformat_is_temperature(of))
-		fprintf(fp, "_celcius");
+		strlcat(metric, "_celcius", mlen);
 	else if (oidformat_is_timeval(of))
-		fprintf(fp, "_seconds");
+		strlcat(metric, "_seconds", mlen);
 
 	/* Print the labels of the metric. */
 	name = on->names;
@@ -410,21 +417,23 @@ oidname_print(const struct oidname *on, const struct oidformat *of,
 			    "abcdefghijklmnopqrstuvwxyz"
 			    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 			    "0123456789_")] == '\0');
-			fprintf(fp, "%c%s=\"", separator, label);
+			snprintf(buf, sizeof(buf), "%c%s=\"", separator, label);
+			strlcat(metric, buf, mlen);
 			while (*name != '\0') {
 				/* Escape backslashes and double quotes. */
 				if (*name == '\\' || *name == '"')
-					fputc('\\', fp);
-				fputc(*name++, fp);
+					strlcat(metric, "\\", mlen);
+				snprintf(buf, sizeof(buf), "%c", *name++);
+				strlcat(metric, buf, mlen);
 			}
-			fputc('"', fp);
+			strlcat(metric, "\"", mlen);
 			separator = ',';
 		}
 		name += strlen(name) + 1;
 		label += strlen(label) + 1;
 	}
 	if (separator != '{')
-		fputc('}', fp);
+		strlcat(metric, "}", mlen);
 }
 
 /* Returns whether the OID name has any labels associated to it. */
@@ -483,16 +492,25 @@ oiddescription_print(const struct oiddescription *od, FILE *fp)
 
 static void
 oid_print(const struct oid *o, struct oidname *on, bool print_description,
-    FILE *fp)
+    bool exclude, bool include, FILE *fp)
 {
 	struct oidformat of;
 	struct oidvalue ov;
 	struct oiddescription od;
+	char metric[BUFSIZ];
 
 	if (!oid_get_format(o, &of) || !oid_get_value(o, &of, &ov))
 		return;
 	oid_get_name(o, on);
 
+	oid_get_metric(on, &of, metric, sizeof(metric));
+
+	if (exclude && regexec(&exc_regex, metric, 0, NULL, 0) == 0)
+		return;
+
+	if (include && regexec(&inc_regex, metric, 0, NULL, 0) != 0)
+		return;
+
 	/*
 	 * Print the line with the description. Prometheus expects a
 	 * single unique description for every metric, which cannot be
@@ -502,14 +520,14 @@ oid_print(const struct oid *o, struct oidname *on, bool print_description,
 	if (print_description && !oidname_has_labels(on) &&
 	    oid_get_description(o, &od)) {
 		fprintf(fp, "# HELP ");
-		oidname_print(on, &of, fp);
+		fprintf(fp, "%s", metric);
 		fputc(' ', fp);
 		oiddescription_print(&od, fp);
 		fputc('\n', fp);
 	}
 
 	/* Print the line with the value. */
-	oidname_print(on, &of, fp);
+	fprintf(fp, "%s", metric);
 	fputc(' ', fp);
 	oidvalue_print(&ov, fp);
 	fputc('\n', fp);
@@ -539,8 +557,9 @@ static void
 usage(void)
 {
 
-	fprintf(stderr,
-	    "usage: prometheus_sysctl_exporter [-dgh] [prefix ...]\n");
+	fprintf(stderr, "%s",
+	    "usage: prometheus_sysctl_exporter [-dgh] [-e pattern] [-i pattern]\n"
+	    "\t[prefix ...]\n");
 	exit(1);
 }
 
@@ -551,22 +570,41 @@ main(int argc, char *argv[])
 	char *http_buf;
 	FILE *fp;
 	size_t http_buflen;
-	int ch;
-	bool gzip_mode, http_mode, print_descriptions;
+	int ch, error;
+	bool exclude, include, gzip_mode, http_mode, print_descriptions;
+	char errbuf[BUFSIZ];
 
 	/* Parse command line flags. */
-	gzip_mode = http_mode = print_descriptions = false;
-	while ((ch = getopt(argc, argv, "dgh")) != -1) {
+	include = exclude = gzip_mode = http_mode = print_descriptions = false;
+	while ((ch = getopt(argc, argv, "de:ghi:")) != -1) {
 		switch (ch) {
 		case 'd':
 			print_descriptions = true;
 			break;
+		case 'e':
+			error = regcomp(&exc_regex, optarg, REG_EXTENDED);
+			if (error != 0) {
+				regerror(error, &exc_regex, errbuf, sizeof(errbuf));
+				errx(1, "bad regular expression '%s': %s",
+				    optarg, errbuf);
+			}
+			exclude = true;
+			break;
 		case 'g':
 			gzip_mode = true;
 			break;
 		case 'h':
 			http_mode = true;
 			break;
+		case 'i':
+			error = regcomp(&inc_regex, optarg, REG_EXTENDED);
+			if (error != 0) {
+				regerror(error, &inc_regex, errbuf, sizeof(errbuf));
+				errx(1, "bad regular expression '%s': %s",
+				    optarg, errbuf);
+			}
+			include = true;
+			break;
 		default:
 			usage();
 		}
@@ -590,7 +628,7 @@ main(int argc, char *argv[])
 		/* Print all OIDs. */
 		oid_get_root(&o);
 		do {
-			oid_print(&o, &on, print_descriptions, fp);
+			oid_print(&o, &on, print_descriptions, exclude, include, fp);
 		} while (oid_get_next(&o, &o));
 	} else {
 		int i;
@@ -602,7 +640,7 @@ main(int argc, char *argv[])
 			oid_get_by_name(&root, argv[i]);
 			o = root;
 			do {
-				oid_print(&o, &on, print_descriptions, fp);
+				oid_print(&o, &on, print_descriptions, exclude, include, fp);
 			} while (oid_get_next(&o, &o) &&
 			    oid_is_beneath(&o, &root));
 		}