[monitoring-plugins] Error summary (#2259)

GitHub git at monitoring-plugins.org
Sat May 30 12:40:12 CEST 2026


    Module: monitoring-plugins
    Branch: master
    Commit: 44e1913da4d227aaabb7b5ccfae65d879082a5e4
    Author: Lorenz Kästle <12514511+RincewindsHat at users.noreply.github.com>
 Committer: GitHub <noreply at github.com>
      Date: Sat May 30 12:30:39 2026 +0200
       URL: https://www.monitoring-plugins.org/repositories/monitoring-plugins/commit/?id=44e1913d

Error summary (#2259)

* lib: properly name function to set summary

* lib: set first non-ok subcheck as the summary for the overall check

* Fetch summarily recursively from failed subchecks

---------

Co-authored-by: Lorenz Kästle <lorenz.kaestle at netways.de>

---

 lib/output.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 lib/output.h |   2 +-
 2 files changed, 130 insertions(+), 12 deletions(-)

diff --git a/lib/output.c b/lib/output.c
index 3c04d63d..9bcd02d9 100644
--- a/lib/output.c
+++ b/lib/output.c
@@ -20,8 +20,53 @@ static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck che
 								 unsigned int indentation);
 static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck);
 
+// mp_compare_state compares two state arguments
+// if *first* is WORSE than *second*, the result is < 0
+// if *first* is equal to *second*, the result is 0
+// if *first* is BETTER (less bad) the result is > 0
+static int mp_compare_state(mp_state_enum first, mp_state_enum second);
+
 // == Implementation ==
 
+// get_subcheck_failed_output retrieves the output of the
+// worst and first leave node in a subcheck tree
+// or NULL if no such message exists
+// the return string is a copy of the original
+static char *get_subcheck_failed_output(const mp_subcheck tree) {
+	if (tree.subchecks == NULL) {
+		// this is a leave node
+		if (mp_compute_subcheck_state(tree) == STATE_OK) {
+			// ALL OK, nothing to return
+			return NULL;
+		}
+
+		char *result = strdup(tree.output);
+		return result;
+	}
+
+	// not a leave node, go through tree
+	mp_subcheck_list *subcheck = tree.subchecks;
+	mp_subcheck *worst_first_node = NULL;
+	mp_state_enum worst_state = STATE_OK;
+	while (subcheck != NULL) {
+		mp_state_enum current = mp_compute_subcheck_state(subcheck->subcheck);
+		if (mp_compare_state(current, worst_state) < 0) {
+			worst_first_node = &subcheck->subcheck;
+		}
+
+		subcheck = subcheck->next;
+	}
+
+	if (worst_first_node == NULL) {
+		// we did not find a failed subcheck, return the output
+		// of the current node
+		char *result = strdup(tree.output);
+		return result;
+	}
+
+	return get_subcheck_failed_output(*worst_first_node);
+}
+
 /*
  * Generate output string for a mp_subcheck object
  */
@@ -164,7 +209,7 @@ int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck subchec
  * Add a manual summary to a mp_check object, effectively replacing
  * the autogenerated one
  */
-void mp_add_summary(mp_check check[static 1], char *summary) { check->summary = summary; }
+void mp_set_summary(mp_check check[static 1], char *summary) { check->summary = strdup(summary); }
 
 /*
  * Generate the summary string of a mp_check object based on its subchecks
@@ -172,31 +217,61 @@ void mp_add_summary(mp_check check[static 1], char *summary) { check->summary =
 char *get_subcheck_summary(mp_check check) {
 	mp_subcheck_list *subchecks = check.subchecks;
 
-	unsigned int ok = 0;
-	unsigned int warning = 0;
-	unsigned int critical = 0;
-	unsigned int unknown = 0;
+	unsigned int ok_count = 0;
+	unsigned int warning_count = 0;
+	unsigned int critical_count = 0;
+	unsigned int unknown_count = 0;
+	char *result = NULL;
 	while (subchecks != NULL) {
 		switch (mp_compute_subcheck_state(subchecks->subcheck)) {
 		case STATE_OK:
-			ok++;
+			ok_count++;
 			break;
 		case STATE_WARNING:
-			warning++;
+			if (critical_count == 0 && unknown_count == 0 && warning_count == 0) {
+				// set summary to first warning subcheck output
+				asprintf(&result, "%s", get_subcheck_failed_output(subchecks->subcheck));
+			}
+			warning_count++;
 			break;
 		case STATE_CRITICAL:
-			critical++;
+			if (critical_count == 0) {
+				// set summary to first critical subcheck output
+				asprintf(&result, "%s", get_subcheck_failed_output(subchecks->subcheck));
+			}
+			critical_count++;
 			break;
 		case STATE_UNKNOWN:
-			unknown++;
+			if (critical_count == 0 && unknown_count == 0) {
+				// set summary to first unknown subcheck output
+				asprintf(&result, "%s", get_subcheck_failed_output(subchecks->subcheck));
+			}
+			unknown_count++;
 			break;
 		default:
 			die(STATE_UNKNOWN, "Unknown state in get_subcheck_summary");
 		}
 		subchecks = subchecks->next;
 	}
-	char *result = NULL;
-	asprintf(&result, "ok=%d, warning=%d, critical=%d, unknown=%d", ok, warning, critical, unknown);
+
+	if (result == NULL) {
+		if (ok_count > 0) {
+			asprintf(&result, "ok=%d", ok_count);
+		}
+
+		if (warning_count > 0) {
+			asprintf(&result, "%swarning=%d", (result == NULL ? "" : ", "), warning_count);
+		}
+
+		if (critical_count > 0) {
+			asprintf(&result, "%scritical=%d", (result == NULL ? "" : ", "), critical_count);
+		}
+
+		if (unknown_count > 0) {
+			asprintf(&result, "%sunknown=%d", (result == NULL ? "" : ", "), unknown_count);
+		}
+	}
+
 	return result;
 }
 
@@ -658,3 +733,46 @@ mp_state_enum mp_eval_unknown(mp_check overall) {
 	(void)overall;
 	return STATE_UNKNOWN;
 }
+
+static int mp_compare_state(mp_state_enum first, mp_state_enum second) {
+	switch (first) {
+	case STATE_OK:
+		switch (second) {
+		case STATE_OK:
+			return 0;
+		case STATE_WARNING:
+		case STATE_UNKNOWN:
+		case STATE_CRITICAL:
+			return 1;
+		}
+	case STATE_WARNING:
+		switch (second) {
+		case STATE_OK:
+			return -1;
+		case STATE_WARNING:
+			return 0;
+		case STATE_UNKNOWN:
+		case STATE_CRITICAL:
+			return 1;
+		}
+	case STATE_UNKNOWN:
+		switch (second) {
+		case STATE_OK:
+		case STATE_WARNING:
+			return -1;
+		case STATE_UNKNOWN:
+			return 0;
+		case STATE_CRITICAL:
+			return 1;
+		}
+	case STATE_CRITICAL:
+		switch (second) {
+		case STATE_OK:
+		case STATE_WARNING:
+		case STATE_UNKNOWN:
+			return -1;
+		case STATE_CRITICAL:
+			return 0;
+		}
+	}
+}
diff --git a/lib/output.h b/lib/output.h
index f5011268..6ca63cfe 100644
--- a/lib/output.h
+++ b/lib/output.h
@@ -87,7 +87,7 @@ int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck);
 
 void mp_add_perfdata_to_subcheck(mp_subcheck check[static 1], mp_perfdata);
 
-void mp_add_summary(mp_check check[static 1], char *summary);
+void mp_set_summary(mp_check check[static 1], char *summary);
 
 mp_state_enum mp_compute_check_state(mp_check);
 mp_state_enum mp_compute_subcheck_state(mp_subcheck);



More information about the Commits mailing list