Skip to content

Call metrics API for units related stats#158

Merged
cooperlees merged 2 commits intocooperlees:mainfrom
YapingLi04:varlink
Feb 25, 2026
Merged

Call metrics API for units related stats#158
cooperlees merged 2 commits intocooperlees:mainfrom
YapingLi04:varlink

Conversation

@YapingLi04
Copy link
Contributor

This implements support for the new metrics API in monitord.

The metrics API will be available in systemd v260. The zlink support for ANY type is in zlink release 0.4.0.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for collecting systemd unit-related statistics via the new varlink metrics API (systemd v260+), alongside a refactor that centralizes unit state constants.

Changes:

  • Introduces a varlink metrics client (src/varlink/metrics.rs) and a new varlink-based unit stats collector (src/varlink_units.rs).
  • Refactors unit state enums / is_unit_unhealthy into src/unit_constants.rs and re-exports them from src/units.rs.
  • Adds a [varlink] config section and switches collectors to choose varlink vs D-Bus based on config.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
src/varlink_units.rs New varlink-based unit stats collection and parsing logic.
src/varlink/mod.rs Adds varlink module namespace.
src/varlink/metrics.rs Varlink proxy + output types for Metrics interface.
src/units.rs Removes duplicated enums; re-exports from unit_constants.
src/unit_constants.rs New shared enums + is_unit_unhealthy implementation.
src/machines.rs Switches container unit stats collection to varlink when enabled.
src/lib.rs Switches host unit stats collection to varlink when enabled; exposes modules.
src/config.rs Adds VarlinkConfig and embeds into main config struct.
monitord.conf Documents [varlink] enabled = true.
Cargo.toml Adds futures-util and zlink-related dependencies.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Owner

@cooperlees cooperlees left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks a good start.

  • Let's add a section on varlink usage and flow in README.md please
  • Let's remove all .unwrap()s and see if we can throw an Error OR at least move to .expect("Helpful message") for now
  • Consider other suggestions.

Lots of other comments + let's see what copilot says.

anyhow = "1.0"
clap = { version = "4.5", features = ["derive", "std", "help"], default-features = false }
configparser = { version = "3.1.0", features = ["indexmap"] }
futures-util = "0.3"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have been trying to avoid bringing in futures, but at least here we only bring in utils ...

@cooperlees
Copy link
Owner

Would love @ikruglov take a look if you have time please ...

@cooperlees
Copy link
Owner

Also, @YapingLi04 - If you don't have time to do the follow ups say so, and I can merge and chip away at them

@YapingLi04
Copy link
Contributor Author

@cooperlees thanks for the review! I addressed all of your comments. Please let know if anything else is needed.

Copy link
Owner

@cooperlees cooperlees left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome. Thanks for the sweet docs too!

Just so you know this will auto sync to https://monitord.xyz/ when we release.

@cooperlees cooperlees merged commit 6e7b187 into cooperlees:main Feb 25, 2026
3 checks passed
let object_name = metric.object_name();

match metric_name_suffix {
"unit_active_state" => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@YapingLi04, I recall you rename these metrics to make them camelCase in systemd

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops, seems I'm a bit late :-)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can forward fix things if you find stuff - It's not released to crates.io etc. - Only merged to main

if config.varlink.enabled {
join_set.spawn(crate::varlink_units::update_unit_stats(
Arc::clone(&config),
sdc.clone(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does varlink implementation needs dbus?

Comment on lines +210 to +225
match get_unit_stats(&config, &socket_path).await {
Ok(units_stats) => {
let mut machine_stats = locked_machine_stats.write().await;
machine_stats.units = units_stats;
}
Err(err) => {
warn!(
"Varlink units stats failed, falling back to D-Bus: {:?}",
err
);
crate::units::update_unit_stats(Arc::clone(&config), connection, locked_machine_stats)
.await?;
}
}
Ok(())
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd personally prefer the fallback happen in an outer layer to drop any dependency of varlink on dbus. Not sure if it's possible in rust. Not an expert here.

if should_skip_unit(&object_name, config) {
return Ok(());
}
let value = metric.value_as_string("" /* default_value */);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@YapingLi04, why value has a default_value? I recall that value is a mandatory field in systemd. Plus, any of its value types (int, bool, enum, etc) can be converted to string. So, I'd prefer to fail and go to next metric in case of a error rather then silently ignore the failure.

}

/// Returns the int value as u64 or default_value if not present
pub fn value_as_int(&self, default_value: u64) -> u64 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

negative number are completely legit value. Please don't mix gauge vs counter here. Counter is always positive, but gauge can be negative.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, I'm not sure I'm happy with silenty ignoring the "wrong" value and converting them into default. I think we should warn/error about it and move forward. But not silently produce wrong value.

.service_stats
.entry(object_name.to_string())
.or_default()
.nrestarts = metric.value_as_int(0 /* default_value */) as u32;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here in all other similar case. I'm not happy about "default_value". We should warn/error and move forward. But not silently produce 0.

YapingLi04 added a commit to YapingLi04/monitord that referenced this pull request Feb 25, 2026
@YapingLi04
Copy link
Contributor Author

@ikruglov thanks for the review! I put up PR #159 to address all of your comments. Let's continue there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants