diff --git a/servers/su/Cargo.lock b/servers/su/Cargo.lock index 36f722d8d..699b7ea1a 100644 --- a/servers/su/Cargo.lock +++ b/servers/su/Cargo.lock @@ -1215,6 +1215,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "http" version = "0.2.9" @@ -2035,6 +2041,52 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "procfs" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4" +dependencies = [ + "bitflags 2.4.0", + "hex", + "lazy_static", + "procfs-core", + "rustix", +] + +[[package]] +name = "procfs-core" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" +dependencies = [ + "bitflags 2.4.0", + "hex", +] + +[[package]] +name = "prometheus" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "libc", + "memchr", + "parking_lot", + "procfs", + "protobuf", + "thiserror", +] + +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + [[package]] name = "quote" version = "1.0.33" @@ -2682,6 +2734,7 @@ dependencies = [ "lazy_static", "log", "lru", + "prometheus", "reqwest", "ring", "rocksdb", diff --git a/servers/su/Cargo.toml b/servers/su/Cargo.toml index 937892bfa..0ee7c1194 100644 --- a/servers/su/Cargo.toml +++ b/servers/su/Cargo.toml @@ -29,6 +29,7 @@ actix-cors = "0.6.0" simd-json = "0.13.10" futures = "0.3.30" rocksdb = "0.22.0" +prometheus = { version = "0.13.4", features = ["process"] } lru = "0.12.4" lazy_static = "1.5.0" avro-rs = "0.13.0" diff --git a/servers/su/src/domain/clients/metrics.rs b/servers/su/src/domain/clients/metrics.rs index c824bbfeb..7059c356f 100644 --- a/servers/su/src/domain/clients/metrics.rs +++ b/servers/su/src/domain/clients/metrics.rs @@ -1,6 +1,6 @@ use super::super::config::AoConfig; use super::super::core::dal::CoreMetrics; -// use prometheus::{HistogramOpts, HistogramVec, IntCounter, Registry}; +use prometheus::{HistogramOpts, HistogramVec, IntCounter, Registry, TextEncoder}; /* Implementation of metrics @@ -11,55 +11,74 @@ use super::super::core::dal::CoreMetrics; pub struct PromMetrics { enabled: bool, - // core_metrics: HistogramVec, - // message_save_failures: IntCounter, + core_metrics: HistogramVec, + message_save_failures: IntCounter, + registry: Registry } impl PromMetrics { pub fn new(config: AoConfig) -> Self { - // let registry = Registry::new(); + let registry = Registry::new(); - // // Define the options for the histogram, with buckets in milliseconds - // let histogram_opts = HistogramOpts::new( - // "core_metrics_duration_milliseconds", - // "Histogram of durations for core metrics functions in milliseconds", - // ) - // .buckets(vec![ - // 0.0, 1.0, 5.0, 10.0, 25.0, 50.0, 100.0, 250.0, 500.0, 1000.0, 2500.0, 5000.0, 5500.0, - // 6000.0, 6500.0, 7000.0, 7500.0, 8000.0, 8500.0, 9000.0, 9500.0, 10000.0, - // ]) - // .namespace("su"); + // Define the options for the histogram, with buckets in milliseconds + let histogram_opts = HistogramOpts::new( + "core_metrics_duration_milliseconds", + "Histogram of durations for core metrics functions in milliseconds", + ) + .buckets(vec![ + 0.0, 1.0, 5.0, 10.0, 25.0, 50.0, 100.0, 250.0, 500.0, 1000.0, 2500.0, 5000.0, 5500.0, + 6000.0, 6500.0, 7000.0, 7500.0, 8000.0, 8500.0, 9000.0, 9500.0, 10000.0, + ]) + .namespace("su"); - // // Create a HistogramVec with labels for the different core metric functions - // let core_metrics = HistogramVec::new(histogram_opts, &["function_name"]).unwrap(); + // Create a HistogramVec with labels for the different core metric functions + let core_metrics = HistogramVec::new(histogram_opts, &["function_name"]).unwrap(); - // // Register the HistogramVec with the provided registry - // registry.register(Box::new(core_metrics.clone())).unwrap(); + // Register the HistogramVec with the provided registry + registry.register(Box::new(core_metrics.clone())).unwrap(); - // let message_save_failures: IntCounter = - // IntCounter::new("message_save_failures", "message save failure count").unwrap(); + let message_save_failures: IntCounter = + IntCounter::new("message_save_failures", "message save failure count").unwrap(); - // // Register the IntCounter with the provided registry - // registry - // .register(Box::new(message_save_failures.clone())) - // .unwrap(); + // Register the IntCounter with the provided registry + registry + .register(Box::new(message_save_failures.clone())) + .unwrap(); PromMetrics { enabled: config.enable_metrics, - // core_metrics, - // message_save_failures, + core_metrics, + message_save_failures, + registry, } } - fn observe_duration(&self, _function_name: &str, _duration: u128) { + fn observe_duration(&self, function_name: &str, duration: u128) { if !self.enabled { return; } // Observe the duration in milliseconds directly - // self.core_metrics - // .with_label_values(&[function_name]) - // .observe(duration as f64); + self.core_metrics + .with_label_values(&[function_name]) + .observe(duration as f64); + } + + pub fn emit_metrics(&self) -> Result { + if !self.enabled { + return Err("Metrics not enabled".to_string()); + } + + let encoder = TextEncoder::new(); + + let metric_families = self.registry.gather(); + + let mut buffer = String::new(); + if let Err(err) = encoder.encode_utf8(&metric_families, &mut buffer) { + return Err(format!("Failed to encode metrics: {}", err)); + } + + Ok(buffer) } } @@ -93,6 +112,6 @@ impl CoreMetrics for PromMetrics { } fn failed_message_save(&self) { - // self.message_save_failures.inc(); + self.message_save_failures.inc(); } } diff --git a/servers/su/src/domain/mod.rs b/servers/su/src/domain/mod.rs index af11f48ab..acaecdd0a 100644 --- a/servers/su/src/domain/mod.rs +++ b/servers/su/src/domain/mod.rs @@ -23,7 +23,7 @@ pub use flows::Deps; pub use local_store::migration::migrate_to_local; pub use store::migrate_to_disk; -pub async fn init_deps(mode: Option) -> Arc { +pub async fn init_deps(mode: Option) -> (Arc, Arc) { let logger: Arc = SuLog::init(); let config = Arc::new(AoConfig::new(mode.clone()).expect("Failed to read configuration")); @@ -100,8 +100,9 @@ pub async fn init_deps(mode: Option) -> Arc { let metrics = Arc::new(PromMetrics::new( AoConfig::new(mode).expect("Failed to read configuration"), )); + let metrics_clone = metrics.clone(); - Arc::new(Deps { + (Arc::new(Deps { data_store: main_data_store, router_data_store, logger, @@ -112,5 +113,5 @@ pub async fn init_deps(mode: Option) -> Arc { wallet, uploader, metrics, - }) + }), metrics_clone) } diff --git a/servers/su/src/main.rs b/servers/su/src/main.rs index 2f71ac3b8..1e4105c25 100644 --- a/servers/su/src/main.rs +++ b/servers/su/src/main.rs @@ -11,7 +11,7 @@ use actix_web::{ use serde::Deserialize; use serde_json::json; -use su::domain::{flows, init_deps, router, Deps}; +use su::domain::{flows, init_deps, router, Deps, PromMetrics}; #[derive(Deserialize)] struct FromTo { @@ -213,8 +213,21 @@ async fn health_check() -> impl Responder { HttpResponse::Ok() } +async fn metrics_route(data: web::Data) -> impl Responder { + let result = data.metrics.emit_metrics(); + match result { + Ok(metrics_str) => HttpResponse::Ok() + .content_type("application/openmetrics-text; version=1.0.0; charset=utf-8") + .body(metrics_str), + Err(err) => HttpResponse::BadRequest() + .content_type("text/plain") + .body(err), + } +} + struct AppState { deps: Arc, + metrics: Arc, } #[actix_web::main] @@ -239,8 +252,8 @@ async fn main() -> io::Result<()> { } }; - let deps = init_deps(mode).await; - let app_state = web::Data::new(AppState { deps }); + let (deps, metrics) = init_deps(mode).await; + let app_state = web::Data::new(AppState { deps, metrics }); let run_deps = app_state.deps.clone(); @@ -266,6 +279,7 @@ async fn main() -> io::Result<()> { .route("/", web::post().to(main_post_route)) .route("/timestamp", web::get().to(timestamp_route)) .route("/health", web::get().to(health_check)) + .route("/metrics", web::get().to(metrics_route)) .route("/{tx_id}", web::get().to(main_get_route)) .route("/processes/{process_id}", web::get().to(read_process_route)) }) diff --git a/servers/su/su b/servers/su/su index ba9bf0186..703b5177a 100644 Binary files a/servers/su/su and b/servers/su/su differ