This post will show you how to create a simple system resource monitor that sends WhatsApp alerts when CPU or memory utilization exceeds a threshold. We created this demo project with the sysinfo and the infobip_sdk crates. The complete code is in the sysmonitor-alert repository.

Rust is an efficient systems programming language. This makes it ideal for resource monitors, profilers, and other low-level, infrastructure-related software. When you deploy an application, you want it to perform at its best without overloading the computer it runs on. For that purpose, resource monitors are used to observe latency and scalability in a system. Ideally, you want your software to use enough processor and memory without saturating the computer. Usage numbers that are too high may mean you need to grow the system or check if something is wrong. This software could run in a company server, or you could simply monitor your laptop to identify inefficient, battery-draining processes.

We’ll guide you through the steps we followed to create this monitor, and we’ll show the most interesting code and how to run the project.

Overview

If you want to create a monitor of your own, you may follow the steps detailed in this guide:

  1. Create a new Rust project
  2. Install dependencies
  3. Create a async main function
  4. Create a sysinfo::System
  5. Read system details in a loop
  6. Check for usage beyond the threshold
  7. Send the alert if usage is high

Create a new Rust project

Create a new project using cargo or your IDE of choice.

cargo new <project-name>

Install Dependencies

We need a few crates for the monitor to work nicely:

[dependencies]
infobip_sdk = "0.5.2"
sysinfo = "0.30.5"
tokio = "1.35.1"
chrono = "0.4.33"
bytesize = "1.3.0"
clap = { version = "4.4.18", features = ["derive"] }

Particularly important are infobip_sdk, which enables the program to send WhatsApp alerts, tokio, which provides the async runtime and the interval loop, and sysinfo, which is our source of system information.

Create an async main function

Because the Infobip Rust SDK is async, we need to create an async main with the help of Tokio:

#[tokio::main]
async fn main() {
}

Create a sysinfo::System object

The System object will help us gather resource usage details. Make sure you check the sysinfo crate, which has a lot of system properties it can monitor.

let sys = System::new_all();

Read system details in a loop

We need to run the System.refresh() function to update system properties and detect anomalies. To refresh at regular intervals, we are using Tokio’s interval, which runs async and can run while we send WhatsApp alerts over the network without stopping the program execution.

    let mut interval = time::interval(Duration::from_secs(args.refresh_interval_secs));

    // ...

    loop {
        // Refresh CPU for accuracy.
        sys.refresh_cpu();

        interval.tick().await;

        sys.refresh_all();

Check for usage beyond threshold and alert

After gathering system info, we check if all CPU cores are within limits. If they are outside limits, we send an alert with the send_alert() function.

    for (i, cpu) in sys.cpus().iter().enumerate() {
        let usage = cpu.cpu_usage();
        if usage > args.cpu_usage_threshold {
            cpus_high_cycles[i] += 1;
            if cpus_high_cycles[i] >= args.cycles_for_alert
                && cpus_ok_cycles[i] >= args.cycles_between_alert
            {
                cpus_ok_cycles[i] = 0;
                handles.push(send_alert(format!(
                    "{ts} {hostname}: High CPU{i} usage: {usage:.1}%",
                )));
            }
        } else {
            cpus_high_cycles[i] = 0;
            cpus_ok_cycles[i] += 1;
        }
    }

To prevent flooding your phone with messages, we implemented a couple of simple counters, cpus_high_cycles and cpus_ok_cycles, that prevent sending alerts after every high usage spike. For an alert to trigger, a number of consecutive high measures must have been taken, and there should be a number of consecutive non-high measures to separate one anomaly from another. For checking memory, we follow the same logic.

Send the alert if usage is high

The special feature of this monitor is implemented in the send_alert() function, which is called when there’s an anomaly. Here’s the code for it:

async fn send_alert(message: String) {
    let client = WhatsappClient::with_configuration(Configuration::from_env_api_key().unwrap());

    let request_body = SendTextRequestBody::new(
        env::var("WA_SENDER").unwrap().as_str(),
        env::var("WA_DESTINATION").unwrap().as_str(),
        TextContent::new(message.as_str()),
    );

    let response = client.send_text(request_body).await.unwrap();

    println!("Alert: {message} => HTTP response: {:?}", response.status);
}

The above code creates a message and then sends it to the server. For this code to work correctly, we need to set four environment variables, which you can retrieve from your account:

IB_API_KEY=<your-api-key>
IB_BASE_URL=<your-base-url>
WA_SENDER=<your-whatsapp-sender-number>
WA_DESTINATION=<your-whatsapp-phone-number>

The IB_API_KEY and IB_BASE_URL variables are needed to initialize the Infobip SDK, while WA_SENDER and WA_DESTINATION are the ones we use for our specific use case.

Run the project

Because we are using simple text WhatsApp messages, we need prior user engagement with the user to send messages. Send a message from the phone to the sender number with any text. Check the endpoint documentation for more information.

Once you send the message, run the project with cargo or with the command line:

cargo run

That’s it! The process will run forever until you kill it.

Customizing execution

The default command will run with the default parameters. We implemented a few command line options to customize the usage thresholds, refresh frequency and the required positive cycles to trigger an alert. Run with -–help to get a summary of the available parameters.

cargo run -- --help

I hope you find this monitor helpful or that it helps you to create your own awesome one. In case you find any errors or you want to know more about response details, please check Infobip Response Status and Error Codes Reference.