Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: process collector #232

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ authors = ["Max Inden <[email protected]>"]
edition = "2021"
description = "Open Metrics client library allowing users to natively instrument applications."
license = "Apache-2.0 OR MIT"
keywords = ["openmetrics", "prometheus", "metrics", "instrumentation", "monitoring"]
keywords = [
"openmetrics",
"prometheus",
"metrics",
"instrumentation",
"monitoring",
]
repository = "https://github.com/prometheus/client_rust"
homepage = "https://github.com/prometheus/client_rust"
documentation = "https://docs.rs/prometheus-client"
Expand All @@ -16,6 +22,7 @@ protobuf = ["dep:prost", "dep:prost-types", "dep:prost-build"]

[workspace]
members = ["derive-encode"]
exclude = ["process-collector"]

[dependencies]
dtoa = "1.0"
Expand All @@ -35,7 +42,12 @@ quickcheck = "1"
rand = "0.8.4"
tide = "0.16"
actix-web = "4"
tokio = { version = "1", features = ["rt-multi-thread", "net", "macros", "signal"] }
tokio = { version = "1", features = [
"rt-multi-thread",
"net",
"macros",
"signal",
] }
hyper = { version = "1.3.1", features = ["server", "http1"] }
hyper-util = { version = "0.1.3", features = ["tokio"] }
http-body-util = "0.1.1"
Expand Down
2 changes: 2 additions & 0 deletions process-collector/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/target
Cargo.lock
10 changes: 10 additions & 0 deletions process-collector/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "process-collector"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
procfs = "0.17.0"
prometheus-client = { path = "../" }
62 changes: 62 additions & 0 deletions process-collector/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use prometheus_client::{
collector::Collector,
encoding::{DescriptorEncoder, EncodeMetric},
metrics::gauge::ConstGauge,
registry::Unit,
};
use std::time::{SystemTime, UNIX_EPOCH};

mod linux;

#[derive(Debug)]
pub struct ProcessCollector {
namespace: Option<String>,
#[cfg(target_os = "linux")]
system: linux::System,
}

impl ProcessCollector {
pub fn new(namespace: Option<String>) -> Self {
#[cfg(target_os = "linux")]
let system = linux::System {};

ProcessCollector {
namespace,
#[cfg(target_os = "linux")]
system,
}
}
}

impl Collector for ProcessCollector {
fn encode(&self, mut encoder: DescriptorEncoder) -> Result<(), std::fmt::Error> {
let start_time_from_epoch = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_err(|e| std::fmt::Error)?;
let start_time = ConstGauge::new(start_time_from_epoch.as_secs_f64());
let start_time_metric = encoder.encode_descriptor(
"process_start_time_seconds",
"Start time of the process since unix epoch in seconds.",
Some(&Unit::Seconds),
start_time.metric_type(),
)?;
start_time.encode(start_time_metric)?;

#[cfg(target_os = "linux")]
self.system.encode(encoder)?;

Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;
use prometheus_client::registry::Registry;

#[test]
fn register_process_collector() {
let mut registry = Registry::default();
registry.register_collector(Box::new(ProcessCollector::new(None)))
}
}
101 changes: 101 additions & 0 deletions process-collector/src/linux.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use procfs::process::{LimitValue, Process};
use prometheus_client::{
collector::Collector,
encoding::EncodeMetric,
metrics::{counter::ConstCounter, gauge::ConstGauge},
registry::Unit,
};

#[derive(Debug)]
pub(crate) struct System {}

impl Collector for System {
fn encode(
&self,
mut encoder: prometheus_client::encoding::DescriptorEncoder,
) -> Result<(), std::fmt::Error> {
let tps = procfs::ticks_per_second();

// TODO: handle errors
let proc = match Process::myself() {
Ok(proc) => proc,
Err(_) => {
return Ok(());
}
};
let stat = match proc.stat() {
Ok(stat) => stat,
Err(_) => {
return Ok(());
}
};

let cpu_time = (stat.stime + stat.utime) / tps;
let counter = ConstCounter::new(cpu_time);
let metric_encoder = encoder.encode_descriptor(
"process_cpu_seconds_total",
"Total user and system CPU time spent in seconds.",
Some(&Unit::Seconds),
counter.metric_type(),
)?;
counter.encode(metric_encoder)?;

if let Ok(limits) = proc.limits() {
let max_open_files = limits.max_open_files;
let max_fds = match max_open_files.soft_limit {
LimitValue::Unlimited => match max_open_files.hard_limit {
LimitValue::Unlimited => 0,
LimitValue::Value(hard) => hard,
},
LimitValue::Value(soft) => soft,
};
let gauge = ConstGauge::new(max_fds as i64);
let metric_encoder = encoder.encode_descriptor(
"process_max_fds",
"Maximum number of open file descriptors.",
None,
gauge.metric_type(),
)?;
gauge.encode(metric_encoder)?;

let max_address_space = limits.max_address_space;
let max_virtual_memory = match max_address_space.soft_limit {
LimitValue::Unlimited => match max_address_space.hard_limit {
LimitValue::Unlimited => 0,
LimitValue::Value(hard) => hard,
},
LimitValue::Value(soft) => soft,
};
let gauge = ConstGauge::new(max_virtual_memory as i64);
let metric_encoder = encoder.encode_descriptor(
"process_virtual_memory_max_bytes",
"Maximum amount of virtual memory available in bytes.",
None,
gauge.metric_type(),
)?;
gauge.encode(metric_encoder)?;
}

let vm_bytes = ConstGauge::new(stat.vsize as i64);
let vme = encoder.encode_descriptor(
"process_virtual_memory_bytes",
"Virtual memory size in bytes",
Some(&Unit::Bytes),
vm_bytes.metric_type(),
)?;
vm_bytes.encode(vme)?;

// TODO: add rss_bytes (fix self.page_size)
//
// let rss_bytes = ConstGauge::new((stat.rss * self.page_size) as i64);
// let rsse = encoder.encode_descriptor(
// "process_resident_memory_bytes",
// "Resident memory size in bytes.",
// Some(&Unit::Bytes),
// rss_bytes.metric_type(),
// )?;
// rss_bytes.encode(rsse)?;

Ok(())
}
}