]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
report: add per-service metrics to the varlink Metrics API
authorYaping Li <202858510+YapingLi04@users.noreply.github.com>
Thu, 19 Mar 2026 21:10:49 +0000 (14:10 -0700)
committerYaping Li <202858510+YapingLi04@users.noreply.github.com>
Fri, 3 Apr 2026 04:45:00 +0000 (21:45 -0700)
Added these metrics:

  - ActiveTimestamp: active state transition timestamps (enter/exit)
  - InactiveExitTimestamp: when the unit last left inactive state
  - NRestarts: restart count
  - StateChangeTimestamp: last state change timestamp
  - StatusErrno: service errno status

Per-service cgroup metrics (CpuUsage, MemoryUsage, IOReadBytes,
IOReadOperations, TasksCurrent) are not included here as they are
gathered by the kernel and will be served by a separate process that
reads cgroup files directly, minimizing PID1 involvement.

src/core/varlink-metrics.c

index 68560387f30327e8575bdeb8469c6a8352cf388b..82bc3cf4cba15e40ff3f59165e23fe83770e6c92 100644 (file)
 #include "unit.h"
 #include "varlink-metrics.h"
 
+static int active_timestamp_build_json(MetricFamilyContext *context, void *userdata) {
+        Manager *manager = ASSERT_PTR(userdata);
+        Unit *unit;
+        char *key;
+        int r;
+
+        assert(context);
+
+        _cleanup_(sd_json_variant_unrefp) sd_json_variant *enter_fields = NULL;
+        r = sd_json_buildo(&enter_fields, SD_JSON_BUILD_PAIR_STRING("event", "enter"));
+        if (r < 0)
+                return r;
+
+        _cleanup_(sd_json_variant_unrefp) sd_json_variant *exit_fields = NULL;
+        r = sd_json_buildo(&exit_fields, SD_JSON_BUILD_PAIR_STRING("event", "exit"));
+        if (r < 0)
+                return r;
+
+        HASHMAP_FOREACH_KEY(unit, key, manager->units) {
+                /* ignore aliases */
+                if (key != unit->id)
+                        continue;
+
+                r = metric_build_send_unsigned(
+                                context,
+                                unit->id,
+                                unit->active_enter_timestamp.realtime,
+                                enter_fields);
+                if (r < 0)
+                        return r;
+
+                r = metric_build_send_unsigned(
+                                context,
+                                unit->id,
+                                unit->active_exit_timestamp.realtime,
+                                exit_fields);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int inactive_exit_timestamp_build_json(MetricFamilyContext *context, void *userdata) {
+        Manager *manager = ASSERT_PTR(userdata);
+        Unit *unit;
+        char *key;
+        int r;
+
+        assert(context);
+
+        HASHMAP_FOREACH_KEY(unit, key, manager->units) {
+                /* ignore aliases */
+                if (key != unit->id)
+                        continue;
+
+                r = metric_build_send_unsigned(
+                                context,
+                                unit->id,
+                                unit->inactive_exit_timestamp.realtime,
+                                /* fields= */ NULL);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int state_change_timestamp_build_json(MetricFamilyContext *context, void *userdata) {
+        Manager *manager = ASSERT_PTR(userdata);
+        Unit *unit;
+        char *key;
+        int r;
+
+        assert(context);
+
+        HASHMAP_FOREACH_KEY(unit, key, manager->units) {
+                /* ignore aliases */
+                if (key != unit->id)
+                        continue;
+
+                r = metric_build_send_unsigned(
+                                context,
+                                unit->id,
+                                unit->state_change_timestamp.realtime,
+                                /* fields= */ NULL);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int status_errno_build_json(MetricFamilyContext *context, void *userdata) {
+        Manager *manager = ASSERT_PTR(userdata);
+        int r;
+
+        assert(context);
+
+        LIST_FOREACH(units_by_type, unit, manager->units_by_type[UNIT_SERVICE]) {
+                r = metric_build_send_unsigned(
+                                context,
+                                unit->id,
+                                (uint64_t) SERVICE(unit)->status_errno,
+                                /* fields= */ NULL);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 static int unit_active_state_build_json(MetricFamilyContext *context, void *userdata) {
         Manager *manager = ASSERT_PTR(userdata);
         Unit *unit;
@@ -228,6 +340,18 @@ static int units_total_build_json(MetricFamilyContext *context, void *userdata)
 
 static const MetricFamily metric_family_table[] = {
         /* Keep metrics ordered alphabetically */
+        {
+                .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "ActiveTimestamp",
+                .description = "Per unit metric: timestamp of active state transitions in microseconds; 0 indicates the transition has not occurred",
+                .type = METRIC_FAMILY_TYPE_GAUGE,
+                .generate = active_timestamp_build_json,
+        },
+        {
+                .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "InactiveExitTimestamp",
+                .description = "Per unit metric: timestamp when the unit last exited the inactive state in microseconds; 0 indicates the transition has not occurred",
+                .type = METRIC_FAMILY_TYPE_GAUGE,
+                .generate = inactive_exit_timestamp_build_json,
+        },
         {
                 .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "JobsQueued",
                 .description = "Number of jobs currently queued",
@@ -240,6 +364,18 @@ static const MetricFamily metric_family_table[] = {
                 .type = METRIC_FAMILY_TYPE_COUNTER,
                 .generate = nrestarts_build_json,
         },
+        {
+                .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "StateChangeTimestamp",
+                .description = "Per unit metric: timestamp of the last state change in microseconds; 0 indicates no state change has occurred",
+                .type = METRIC_FAMILY_TYPE_GAUGE,
+                .generate = state_change_timestamp_build_json,
+        },
+        {
+                .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "StatusErrno",
+                .description = "Per service metric: errno status of the service",
+                .type = METRIC_FAMILY_TYPE_GAUGE,
+                .generate = status_errno_build_json,
+        },
         {
                 .name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "SystemState",
                 .description = "Overall system state",