diff options
| author | Hazel Atkinson <yellowsink@riseup.net> | 2025-04-06 21:01:33 +0100 |
|---|---|---|
| committer | Hazel Atkinson <yellowsink@riseup.net> | 2025-04-06 21:01:33 +0100 |
| commit | a61ac43c3ae5baaede67449dde84ca86cbbc3873 (patch) | |
| tree | 3ae14e652f45f82502711c9ead611a0b4cb877f0 /src/main.rs | |
| parent | 6ccc65ab237a9926e6e0f43e3bc1ef357ff16240 (diff) | |
| download | containerspy-a61ac43c3ae5baaede67449dde84ca86cbbc3873.tar.gz containerspy-a61ac43c3ae5baaede67449dde84ca86cbbc3873.tar.bz2 containerspy-a61ac43c3ae5baaede67449dde84ca86cbbc3873.zip | |
impl more stats
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 84 |
1 files changed, 3 insertions, 81 deletions
diff --git a/src/main.rs b/src/main.rs index 6a6d61f..2bbfe3f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,15 @@ use std::{collections::BTreeMap, sync::Arc, time::Duration}; use anyhow::Result; -use bollard::{container::StatsOptions, Docker}; -use bollard::models::ContainerSummary; -use opentelemetry::KeyValue; +use bollard::Docker; use config::CONFIG; -use opentelemetry::metrics::MeterProvider; use opentelemetry_otlp::{MetricExporter, Protocol, WithExportConfig}; use opentelemetry_sdk::metrics::SdkMeterProvider; use tokio::task::JoinHandle; -use tokio_stream::StreamExt; use tokio_util::sync::CancellationToken; mod config; +mod stats_task; fn setup_otlp() -> Result<SdkMeterProvider> { let metric_exporter = @@ -105,7 +102,7 @@ async fn main() -> Result<()> { let id_string = cont.id.as_ref().unwrap(); if !tasks.contains_key(id_string) { // all this string cloning hurts me - tasks.insert(id_string.clone(), launch_stats_task(cont, docker.clone(), meter_provider.clone())); + tasks.insert(id_string.clone(), stats_task::launch_stats_task(cont, docker.clone(), meter_provider.clone())); } } } @@ -118,79 +115,4 @@ async fn main() -> Result<()> { println!("clean shutdown."); Ok(()) -} - -// I do not enjoy taking a bunch of Rcs but tokio needs ownership so fine. -fn launch_stats_task(container: ContainerSummary, docker: Arc<Docker>, meter_provider: Arc<SdkMeterProvider>) -> JoinHandle<()> { - tokio::spawn(async move { - // extract some container info - let container_id = container.id.unwrap(); - let container_name = container.names.iter().flatten().next().map(|n| n.trim_start_matches("/").to_owned()); - - let mut stats_stream = - docker.stats(container_id.as_str(), Some(StatsOptions { - stream: true, - one_shot: false - })); - - // drop the first read - loop { - match stats_stream.next().await { - None => return, - Some(Ok(_)) => break, - Some(Err(err)) => { - // TODO: use json logging or syslog so loki can understand this lol - println!("Failed to get stats for container {container_id}!: {err:?}"); - } - } - } - - // container labels shared for all metrics - let mut shared_labels = vec![ - KeyValue::new("id", container_id.to_owned()), - KeyValue::new("image", container.image.unwrap_or(container.image_id.unwrap())) - ]; - - if let Some(name) = container_name { - shared_labels.push(KeyValue::new("name", name)); - } - - if let Some(docker_labels) = &container.labels { - for (key, value) in docker_labels { - shared_labels.push(KeyValue::new("container_label_".to_string() + key, value.clone())) - } - } - - // free space and make mutable - shared_labels.shrink_to_fit(); - let shared_labels = &shared_labels[..]; - - //println!("Starting reporting for container: {shared_labels:?}"); - - // create meters - let meter_container_cpu_usage_seconds_total = meter_provider.meter("test_meter").f64_counter("container_cpu_usage_seconds_total").with_unit("s").with_description("Cumulative cpu time consumed").build(); - - while let Some(val) = stats_stream.next().await { - if let Ok(stats) = val { - meter_container_cpu_usage_seconds_total.add( - cpu_delta_from_docker(stats.cpu_stats.cpu_usage.total_usage, stats.precpu_stats.cpu_usage.total_usage).as_secs_f64(), - shared_labels); - } - else { - // failed to get stats, log as such: - // TODO: use json logging or syslog so loki can understand this lol - println!("Failed to get stats for container {container_id}!: {:?}", val.unwrap_err()); - } - } - }) -} - -fn cpu_delta_from_docker(cpu_usage: u64, precpu_usage: u64) -> Duration { - let delta = cpu_usage - precpu_usage; - - // https://docs.docker.com/reference/api/engine/version/v1.48/#tag/Container/operation/ContainerStats - // see response schema > cpu_stats > cpu_usage > total_usage - let delta_ns = if cfg!(windows) { delta * 100 } else { delta }; - - Duration::from_nanos(delta_ns) }
\ No newline at end of file |
