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