Initial version
This commit is contained in:
commit
5b86785c2b
|
@ -0,0 +1,4 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
||||||
|
|
||||||
|
.idea
|
|
@ -0,0 +1,16 @@
|
||||||
|
[package]
|
||||||
|
name = "metrics-process-promstyle"
|
||||||
|
authors = ["Olivier 'reivilibre'"]
|
||||||
|
#
|
||||||
|
version = "0.18.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
metrics = "0.18.1"
|
||||||
|
procfs = "0.12.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
metrics-exporter-prometheus = "0.9.0"
|
||||||
|
tokio = { version = "1.17.0", features = ["macros", "rt-multi-thread"] }
|
|
@ -0,0 +1,53 @@
|
||||||
|
# `metrics-process-promstyle`: Prometheus-style process metrics for the `metrics` façade
|
||||||
|
|
||||||
|
This crate provides Prometheus-style ['process metrics'][prom_process_metrics] for the backend-agnostic
|
||||||
|
[`metrics`][metrics_crate] façade.
|
||||||
|
|
||||||
|
Although these metrics use the conventional Prometheus names, they can be used with any `metrics`-compatible
|
||||||
|
exporter!
|
||||||
|
|
||||||
|
Process metrics are the following useful statistics:
|
||||||
|
|
||||||
|
- CPU time
|
||||||
|
- memory usage
|
||||||
|
- start time of the process
|
||||||
|
- number of threads
|
||||||
|
|
||||||
|
[prom_process_metrics]: https://prometheus.io/docs/instrumenting/writing_clientlibs/#process-metrics
|
||||||
|
[metrics_crate]: https://github.com/metrics-rs/metrics
|
||||||
|
|
||||||
|
Unimplemented process metrics:
|
||||||
|
|
||||||
|
- `process_open_fds`
|
||||||
|
- `process_max_fds`
|
||||||
|
- `process_virtual_memory_max_bytes`
|
||||||
|
- `process_heap_bytes`
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Once your metrics exporter is set up, you can describe the process metrics using `describe()`.
|
||||||
|
|
||||||
|
The `metrics` crate only provides for 'push'-style metrics; there's no way right now to emit statistics on demand.
|
||||||
|
For that reason, this crate exposes a function called `emit_now()` which, as the name implies, emits the statistics when called.
|
||||||
|
You'll need to call this occasionally (from a background thread or Tokio task etc, if necessary) to update the metrics.
|
||||||
|
|
||||||
|
(One day, it should be as easy as enabling a feature in this crate and calling the necessary function to set up a timer for you.)
|
||||||
|
|
||||||
|
See the `examples` directory for an example.
|
||||||
|
|
||||||
|
|
||||||
|
## Licence
|
||||||
|
|
||||||
|
See `LICENCE`: it's the MIT Licence.
|
||||||
|
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
For simplicity's sake, this crate will follow the major version number of `metrics`, so it's easy to tell that compatible versions are being used.
|
||||||
|
This means that version `0.18.x` of this crate will be compatible with `metrics` `0.18.x`, and a hypothetical version `1.x.x` of this crate will be compatible with `metrics` `1.x.x`.
|
||||||
|
|
||||||
|
|
||||||
|
## Contributions
|
||||||
|
|
||||||
|
If you feel like you want to make a contribution, I would be very happy to accept.
|
|
@ -0,0 +1,29 @@
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
/// Demo for process-style metrics.
|
||||||
|
/// Open up http://127.0.0.1:3333 whilst it's running to see!
|
||||||
|
/// We must use Tokio as the Prometheus exporter requires to be called in a Tokio context...
|
||||||
|
/// (this seems like a bug in `metrics-exporter-prometheus`...?).
|
||||||
|
#[tokio::main]
|
||||||
|
pub async fn main() {
|
||||||
|
metrics_exporter_prometheus::PrometheusBuilder::new()
|
||||||
|
.with_http_listener(SocketAddr::from_str("127.0.0.1:3333").unwrap())
|
||||||
|
.install()
|
||||||
|
.expect("Can't build Prometheus exporter");
|
||||||
|
|
||||||
|
// Describes the process metrics.
|
||||||
|
metrics_process_promstyle::describe();
|
||||||
|
|
||||||
|
std::thread::spawn(|| {
|
||||||
|
// 5 seconds is maybe too fast in real-life use.
|
||||||
|
std::thread::sleep(Duration::from_secs(5));
|
||||||
|
|
||||||
|
// Emits the process metrics.
|
||||||
|
metrics_process_promstyle::emit_now().expect("Failed to emit process metrics");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Main program: sleep 2 minutes.
|
||||||
|
std::thread::sleep(Duration::from_secs(120));
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
use metrics::{describe_gauge, gauge, Unit};
|
||||||
|
use procfs::process::Process;
|
||||||
|
use procfs::{page_size, ticks_per_second, ProcResult};
|
||||||
|
|
||||||
|
pub fn describe() {
|
||||||
|
describe_gauge!(
|
||||||
|
"process_cpu_seconds_total",
|
||||||
|
Unit::Seconds,
|
||||||
|
"Total user and system CPU time spent in seconds."
|
||||||
|
);
|
||||||
|
// TODO(feature): Getting the number of open file descriptors involves extra work
|
||||||
|
// and I don't care *that* much about the metric for now anyway.
|
||||||
|
// describe_gauge!("process_open_fds", Unit::Count, "Number of open file descriptors.");
|
||||||
|
// TODO(feature): Getting the max number of file descriptors involves reading the limits file,
|
||||||
|
// which is easy enough to implement but I don't care about the limit if there's no count of
|
||||||
|
// open FDs (and may as well reduce the amount of work the collector needs to do).
|
||||||
|
// describe_gauge!("process_max_fds", Unit::Count, "Maximum number of open file descriptors.");
|
||||||
|
describe_gauge!(
|
||||||
|
"process_virtual_memory_bytes",
|
||||||
|
Unit::Bytes,
|
||||||
|
"Virtual memory size in bytes."
|
||||||
|
);
|
||||||
|
// TODO(feature): Couldn't immediately see where to get this one, and I don't usually rely on it.
|
||||||
|
// describe_gauge!("process_virtual_memory_max_bytes", Unit::Bytes, "Maximum amount of virtual memory available in bytes.");
|
||||||
|
describe_gauge!(
|
||||||
|
"process_resident_memory_bytes",
|
||||||
|
Unit::Bytes,
|
||||||
|
"Resident memory size in bytes."
|
||||||
|
);
|
||||||
|
// TODO(feature): counting heap bytes presumably needs access to the allocator.
|
||||||
|
// I can see that breaking depending on which allocator is in use, so won't bother for now.
|
||||||
|
// describe_gauge!("process_heap_bytes", Unit::Bytes, "Process heap size in bytes.");
|
||||||
|
describe_gauge!(
|
||||||
|
"process_start_time_seconds",
|
||||||
|
Unit::Seconds,
|
||||||
|
"Start time of the process since unix epoch in seconds."
|
||||||
|
);
|
||||||
|
describe_gauge!(
|
||||||
|
"process_threads",
|
||||||
|
Unit::Count,
|
||||||
|
"Number of OS threads in the process."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emit_now() -> ProcResult<()> {
|
||||||
|
let boot_time_secs = procfs::boot_time_secs()?;
|
||||||
|
|
||||||
|
let this_process = Process::myself()?;
|
||||||
|
|
||||||
|
let total_time_ticks = this_process.stat.utime + this_process.stat.stime;
|
||||||
|
let ticks_per_second = ticks_per_second()?;
|
||||||
|
|
||||||
|
let rss_bytes = this_process.stat.rss * page_size()?;
|
||||||
|
|
||||||
|
let start_time_secs =
|
||||||
|
(this_process.stat.starttime as f64) / (ticks_per_second as f64) + boot_time_secs as f64;
|
||||||
|
let total_time_secs = (total_time_ticks as f64) / (ticks_per_second as f64);
|
||||||
|
|
||||||
|
let num_threads = this_process.stat.num_threads;
|
||||||
|
|
||||||
|
gauge!("process_cpu_seconds_total", total_time_secs);
|
||||||
|
// gauge!("process_open_fds", );
|
||||||
|
// gauge!("process_max_fds", );
|
||||||
|
gauge!(
|
||||||
|
"process_virtual_memory_bytes",
|
||||||
|
this_process.stat.vsize as f64
|
||||||
|
);
|
||||||
|
// gauge!("process_virtual_memory_max_bytes", );
|
||||||
|
gauge!("process_resident_memory_bytes", rss_bytes as f64);
|
||||||
|
// gauge!("process_heap_bytes", );
|
||||||
|
gauge!("process_start_time_seconds", start_time_secs);
|
||||||
|
gauge!("process_threads", num_threads as f64);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue