use crate::structs::v3::MetricOutput; use serde_json::{Map, Value}; use prometheus::Gauge; use tracing::error; use prometheus::{opts, GaugeVec, core::Collector}; use tracing::{debug, trace}; #[derive(Debug)] pub enum MetricsValueType { Number, Array, TaggedArray, ArrayOfStrings, None, } pub struct MetricsProcesser; impl MetricsProcesser { pub fn get_type_of_value(metrics: &MetricOutput) -> MetricsValueType { trace!("defining metric type"); if Self::is_number(metrics) { debug!("processing Number"); return MetricsValueType::Number; } else if Self::is_array(metrics) { if Self::is_tagged_array(metrics) { debug!("processing TaggedArray"); return MetricsValueType::TaggedArray; } if Self::is_array_of_string_values(metrics) { debug!("processing ArrayOfStrings"); return MetricsValueType::ArrayOfStrings; } debug!("processing Array"); return MetricsValueType::Array; } debug!("processing undefined type"); MetricsValueType::None } // TODO: i64 and f63 support pub fn gauge_from_number( metric: &MetricOutput, metric_name: &str, metric_desc: &str, prefix: &str ) -> Option> { trace!("fn gauge_from_number is running"); if let Some(status) = metric.status { if let Some(device) = metric.device { if let Some(source_id) = &metric.source_id { let vec = GaugeVec::new(opts!(format!("{}_{}", prefix, metric.name), metric_desc), &["status", "device", "source_id"]).unwrap(); vec.with_label_values(&[&status.to_string(), &device.to_string(), &source_id.to_string()]).set(metric.value.as_f64().unwrap_or_else(|| 0.0)); debug!("processed metric: {:?}", &vec); return Some(Box::new(vec)); } else { let vec = GaugeVec::new(opts!(format!("{}_{}", prefix, metric.name), metric_desc), &["status", "device"]).unwrap(); vec.with_label_values(&[&status.to_string(), &device.to_string()]).set(metric.value.as_f64().unwrap_or_else(|| 0.0)); debug!("processed metric: {:?}", &vec); return Some(Box::new(vec)); } } else { let vec = GaugeVec::new(opts!(format!("{}_{}", prefix, metric.name), metric_desc), &["status"]).unwrap(); vec.with_label_values(&[&status.to_string()]).set(metric.value.as_f64().unwrap_or_else(|| 0.0)); debug!("processed metric: {:?}", &vec); return Some(Box::new(vec)); } } let gauge = Gauge::new( format!("{}_{}", prefix, metric.name), metric_desc ); match gauge { Ok(gauge) => { let val = match metric.value.as_number() { Some(val) => { val.as_f64().unwrap_or_else(|| 0.0 ) }, None => { error!("Cannot convert {} metric value to f64 type. Value was set to 0.0", &metric_name); 0.0 }, }; gauge.set(val); debug!("processed metric: {:?}", &gauge); return Some(Box::new(gauge)); }, Err(er) => error!("Cannot create Gauge metric {} due to {}", &metric_name, er), } None } pub fn is_array_of_string_values(metrics: &MetricOutput) -> bool { let arr = metrics.value.clone(); let arr = arr.as_array().unwrap(); let map: Map = serde_json::from_value( arr[0].clone() ).unwrap(); map.values() .all(|val| val.is_string()) } fn is_array(metrics: &MetricOutput) -> bool { metrics.value.is_array() } fn is_tagged_array(metrics: &MetricOutput) -> bool { let arr = metrics.value.as_array().unwrap(); let map: Map = serde_json::from_value(arr[0].clone()).unwrap(); map.len() > 1 } fn is_number(metrics: &MetricOutput) -> bool { metrics .value.is_number() } }