Skip to content

Conversation

wentasah
Copy link
Contributor

It prints CPU usage since the last run dissected by systemd cgroups, i.e. by users and services. The output can look like this:

CPU usage in the past 5m:
  Users:
    johndoe               2% [#=======================================]
    root                  4% [##======================================]
  Services:
    apache2               7% [###=====================================]
    cron                 11% [####====================================]
    icinga2               2% [#=======================================]
    munin-node            2% [#=======================================]

If no cgroup reports usage higher than a threshold (by default 1%), the output is:

CPU usage in the past 5m: almost idle

@aaronscode
Copy link
Contributor

Hey, thanks for the contribution! The one thing that caught my eye were the two dependencies.

  1. The only place walkdir is being used, it's being given a min and max depth of 1, which seems like it defeats the point of using walkdir. That is, unless I'm missing something, or you're using it for some other functionality like symlink resolution or something like that?
  2. It looks like users is only being used for a single simple function, how do you feel about just copy pasting that function (with attribution) from the users crate and adding a dependency on the libc crate instead?

Neither of these are deal breakers for getting this merged for me, and if it's not worth it to you I'm happy to merge this as is. Just wanted to ask about both of those

@wentasah
Copy link
Contributor Author

wentasah commented Jan 6, 2025

You're right, that adding the dependencies is not very useful in this case.

I used walkdir more heavily in the initial version of the patch, but in this version it's simpler to use std::fs::read_dir instead. Replacing the users crate required more effort, but I agree that pulling in the whole dependency for just one function is unnecessary and followed your suggestion to copy it in.

I also rebased this on main to resolve the conflict.

};
// Read statistics of system services and shorten too long names, e.g.,
// docker-dcd9a8c71b756de71a4a837c005840f84e0ed92574704ae1c89409c57980aaee.scope
let re = Regex::new(r"\.service|\.scope|\.slice").unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

How about lazy static for these regexes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea. Then even the closure could be implemented as a separate function, which can be tested with unit tests.

@wentasah
Copy link
Contributor Author

wentasah commented Jan 6, 2025

Converted to lazy_static. I also added tests. Not sure how they will behave on non-linux systems.

@MarcelRobitaille
Copy link
Contributor

I don't know of anyone using this on non-linux systems so that's probably fine. 😄

}
fs::write(&self.state_file, toml::to_string(&now)?)
.map_err(|e| CgStatsError::FileError(PathBuf::from(&self.state_file), e))?;
let min_width = INDENT_WIDTH + prepared_cg_stats.max_name_width + 12 + 5;
Copy link
Contributor

Choose a reason for hiding this comment

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

Where does + 12 + 5 come from?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will change it to:

        let min_width = INDENT_WIDTH
            + prepared_cg_stats.max_name_width
            + "100%".len()
            + "[=========]".len()
            + 2; // spaces

};
// Read statistics of system services and shorten too long names, e.g.,
// docker-dcd9a8c71b756de71a4a837c005840f84e0ed92574704ae1c89409c57980aaee.scope
let re = Regex::new(r"\.service|\.scope|\.slice").unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

Couldn't this one also be lazy static?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure.

.unwrap_or((self, Some(Constraints { min_width: None })))
}
async fn print(self: Box<Self>, _global_config: &GlobalConfig, _width: Option<usize>) {
println!("Cgroup Statistics component failed");
Copy link
Contributor

Choose a reason for hiding this comment

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

I find it a bit confusing to have two different structs each implement half of the Component trait. Maybe there is a way to consolidate this into one Component struct that holds the configuration and the prepared data or something like that. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe adding #[serde(skip)] to non-config data would help. Will look into it.

It prints CPU usage since the last run dissected by systemd cgroups,
i.e. by users and services. The output can look like this:

    CPU usage in the past 5m:
      Users:
        johndoe     2% [#=======================================]
	root	    4% [##======================================]
      Services:
	apache2	    7% [###=====================================]
	cron	   11% [####====================================]
	icinga2	    2% [#=======================================]
	munin-node  2% [#=======================================]

If no cgroup reports usage higher than a threshold (e.g. 1%), the
output is:

    CPU usage in the past 5m: almost idle
@wentasah
Copy link
Contributor Author

wentasah commented Jan 8, 2025

Now, there is just one component implementing both prepare and print. The other comments are also addressed.

@MarcelRobitaille
Copy link
Contributor

Thank you very much for implemented the comments. I have to think more about the 1 component thing. I'd prefer something that follows "parse, don't validate". However, I think that would be a bigger refactor that touches other components, so I'd say for now it's fine.

@MarcelRobitaille MarcelRobitaille merged commit cf88a5a into rust-motd:main Jan 9, 2025
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.

3 participants