use std::sync::Arc; use crate::structs::v3::MetricOutput; use serde_json::{Map, Value}; use prometheus::Gauge; use tracing::error; use prometheus::Opts; 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 ) -> Option> { trace!("fn gauge_from_number is running"); if let Some(status) = metric.status { let vec = GaugeVec::new(opts!(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( 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 gauge_from_map_metrics( map: &Map, service: &str, endpoint: &str ) -> Option { let map = map.clone(); let help: String = map.keys() .enumerate() .map(|(idx, key)| { if idx == 1 { return key.to_owned(); } "".to_owned() }) .collect(); let name = format!("{}_{}_{}", service, endpoint, &help); if map.len() > 1 { // tagged if map.len() > 2 { error!("Cannot create Gauge {}. It can be only 1 tag", &name); } else { let mut label_name = String::new(); let mut label_value = String::new(); let mut metric_value = 0.0; map.iter() .enumerate() .for_each(|(idx, (key, value))|{ if idx == 0 { label_name = key.to_owned(); label_value = value.as_str() .unwrap_or("") .to_owned(); } else { metric_value = value.as_f64().unwrap_or(0.0) } }); let opts = Opts::new(&name, &help); let gauge_vec = GaugeVec::new(opts, &[&label_name]); match gauge_vec { Ok(vec) => { match vec.get_metric_with_label_values(&[&label_value]) { Ok(metric) => { metric.set(metric_value); return Some(metric.clone()); }, Err(er) => { error!("Cannot create Gauge {} due to {}", &name, er); }, } }, Err(er) => error!("Cannot create Gauge {} due to {}", &name, er), } } } else { // not-tagged let metric = Gauge::new(&name, &help); match metric { Ok(gauge) => { let mut value = 0.0; map.values() .map(|val| val.clone().as_f64()) .for_each(|val| { value = val.unwrap_or(0.0) }); gauge.set(value); return Some(gauge); }, Err(er) => { error!("Cannot create Gauge {} due to {}", &name, er); } } } None } pub fn get_value_as_vec_map(metrics: &MetricOutput) -> Vec>{ let mut vec: Vec> = Vec::new(); let arr = metrics.value.as_array().unwrap(); arr.iter() .for_each(|a| { vec.push(serde_json::from_value(a.clone()).unwrap()); }); vec } 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_valid(metrics: &PrometheusMetrics) -> bool { // false // } 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() } } trait IsTaggedArray { fn is_tagged_array(&self) -> bool; } impl IsTaggedArray for Value { fn is_tagged_array(&self) -> bool { if let Some(arr) = self.as_array() { return arr[0].get("tag_name").is_some(); } false } }