From 981853fbf06dfb876a500be42fce905f4f5ece98 Mon Sep 17 00:00:00 2001 From: fborello-lambda Date: Mon, 16 Dec 2024 14:35:32 -0300 Subject: [PATCH 01/13] wip --- Cargo.lock | 36 +++++++++++++++++++++++ cmd/ethrex/Cargo.toml | 2 ++ cmd/ethrex/cli.rs | 6 ++++ cmd/ethrex/ethrex.rs | 17 ++++++++++- crates/blockchain/Cargo.toml | 13 ++++---- crates/blockchain/metrics/Cargo.toml | 22 ++++++++++++++ crates/blockchain/metrics/metrics.rs | 44 ++++++++++++++++++++++++++++ crates/blockchain/payload.rs | 7 +++++ crates/l2/Makefile | 10 +++++-- 9 files changed, 147 insertions(+), 10 deletions(-) create mode 100644 crates/blockchain/metrics/Cargo.toml create mode 100644 crates/blockchain/metrics/metrics.rs diff --git a/Cargo.lock b/Cargo.lock index 6e675e405..991c63279 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2043,6 +2043,7 @@ dependencies = [ "ethrex-core", "ethrex-dev", "ethrex-l2", + "ethrex-metrics", "ethrex-net", "ethrex-rlp", "ethrex-rpc", @@ -2068,6 +2069,7 @@ dependencies = [ "bytes", "cfg-if", "ethrex-core", + "ethrex-metrics", "ethrex-rlp", "ethrex-storage", "ethrex-vm", @@ -2173,6 +2175,19 @@ dependencies = [ "walkdir", ] +[[package]] +name = "ethrex-metrics" +version = "0.1.0" +dependencies = [ + "axum", + "prometheus", + "serde", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tracing", +] + [[package]] name = "ethrex-net" version = "0.1.0" @@ -4412,6 +4427,21 @@ dependencies = [ "unicode-ident", ] +[[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", + "memchr", + "parking_lot 0.12.3", + "protobuf", + "thiserror 1.0.69", +] + [[package]] name = "proptest" version = "1.5.0" @@ -4455,6 +4485,12 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + [[package]] name = "puffin" version = "0.19.1" diff --git a/cmd/ethrex/Cargo.toml b/cmd/ethrex/Cargo.toml index 27303b318..e805dae8a 100644 --- a/cmd/ethrex/Cargo.toml +++ b/cmd/ethrex/Cargo.toml @@ -34,6 +34,7 @@ redb = { workspace = true, optional = true } cfg-if = "1.0.0" ethrex-dev = { path = "../../crates/blockchain/dev", optional = true } +ethrex-metrics = { path = "../../crates/blockchain/metrics", optional = true } [[bin]] name = "ethrex" @@ -42,6 +43,7 @@ path = "./ethrex.rs" [features] default = ["dep:ethrex-storage", "libmdbx"] dev = ["dep:ethrex-dev"] +metrics = ["dep:ethrex-metrics"] libmdbx = ["dep:libmdbx", "ethrex-storage/libmdbx"] redb = ["dep:redb", "ethrex-storage/redb"] l2 = ["ethrex-vm/l2"] diff --git a/cmd/ethrex/cli.rs b/cmd/ethrex/cli.rs index bcab7d078..c71be7395 100644 --- a/cmd/ethrex/cli.rs +++ b/cmd/ethrex/cli.rs @@ -116,6 +116,12 @@ pub fn cli() -> Command { .required(false) .value_name("BLOCKS_DIR_PATH"), ) + .arg( + Arg::new("metrics.port") + .long("metrics.port") + .required(false) + .value_name("PROMETHEUS_METRICS_PORT"), + ) .subcommand( Command::new("removedb").about("Remove the database").arg( Arg::new("datadir") diff --git a/cmd/ethrex/ethrex.rs b/cmd/ethrex/ethrex.rs index 3dfd651b3..5f10e74d2 100644 --- a/cmd/ethrex/ethrex.rs +++ b/cmd/ethrex/ethrex.rs @@ -208,6 +208,20 @@ async fn main() { tracker.spawn(rpc_api); + // Start the prometheus /metrics api + cfg_if::cfg_if! { + if #[cfg(feature = "metrics")] { + use ethrex_metrics; + + let metrics_port = matches + .get_one::("metrics.port") + .map_or("3000".to_string(), |v| v.clone()); + + let metrics_api = ethrex_metrics::start_prometheus_metrics_api(metrics_port); + tracker.spawn(metrics_api); + } + } + // We do not want to start the networking module if the l2 feature is enabled. cfg_if::cfg_if! { if #[cfg(feature = "l2")] { @@ -225,7 +239,8 @@ async fn main() { let url = format!("http://{authrpc_socket_addr}"); let block_producer_engine = ethrex_dev::block_producer::start_block_producer(url, authrpc_jwtsecret.into(), head_block_hash, max_tries, 1000, ethrex_core::Address::default()); tracker.spawn(block_producer_engine); - } else { + } + else { let networking = ethrex_net::start_network( udp_socket_addr, tcp_socket_addr, diff --git a/crates/blockchain/Cargo.toml b/crates/blockchain/Cargo.toml index 90aa8c6d1..9747ba211 100644 --- a/crates/blockchain/Cargo.toml +++ b/crates/blockchain/Cargo.toml @@ -19,6 +19,8 @@ ethrex-vm = { path = "../vm", default-features = false } k256 = { version = "0.13.3", features = ["ecdh"] } +ethrex-metrics = { path = "./metrics", optional = true } + [dev-dependencies] serde_json.workspace = true hex = "0.4.3" @@ -27,11 +29,8 @@ hex = "0.4.3" path = "./blockchain.rs" [features] -default = ["c-kzg"] -libmdbx = [ - "ethrex-core/libmdbx", - "ethrex-storage/default", - "ethrex-vm/libmdbx", -] +default = ["c-kzg", "metrics"] +libmdbx = ["ethrex-core/libmdbx", "ethrex-storage/default", "ethrex-vm/libmdbx"] levm = ["ethrex-vm/levm"] -c-kzg =["ethrex-core/c-kzg"] +c-kzg = ["ethrex-core/c-kzg"] +metrics = ["dep:ethrex-metrics"] diff --git a/crates/blockchain/metrics/Cargo.toml b/crates/blockchain/metrics/Cargo.toml new file mode 100644 index 000000000..f7d79f6ef --- /dev/null +++ b/crates/blockchain/metrics/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "ethrex-metrics" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tokio.workspace = true +tracing.workspace = true +thiserror.workspace = true +serde_json.workspace = true +serde.workspace = true + +prometheus = "0.13.4" + +# TODO: remove? +axum = "0.7.9" + + +[lib] +path = "./metrics.rs" diff --git a/crates/blockchain/metrics/metrics.rs b/crates/blockchain/metrics/metrics.rs new file mode 100644 index 000000000..65802bda3 --- /dev/null +++ b/crates/blockchain/metrics/metrics.rs @@ -0,0 +1,44 @@ +use std::sync::{Arc, LazyLock, Mutex}; + +use axum::{routing::get, Router}; +use prometheus::{Encoder, IntCounter, Registry, TextEncoder}; + +pub static TRANSACTION_COUNTER: LazyLock>> = LazyLock::new(|| { + Arc::new(Mutex::new( + IntCounter::new( + "transactions_counter", + "keeps track of the executed transactions", + ) + .unwrap(), + )) +}); + +pub async fn start_prometheus_metrics_api(port: String) { + let app = Router::new() + .route("/metrics", get(get_metrics)) + .route("/health", get("Service Up")); + + // Start the axum app + let listener = tokio::net::TcpListener::bind(&format!("0.0.0.0:{port}")) + .await + .unwrap(); + axum::serve(listener, app).await.unwrap(); +} + +async fn get_metrics() -> String { + let r = Registry::new(); + let tx_counter = TRANSACTION_COUNTER.clone(); + r.register(Box::new(tx_counter.lock().unwrap().clone())) + .unwrap(); + + let encoder = TextEncoder::new(); + let metric_families = r.gather(); + + let mut buffer = Vec::new(); + encoder.encode(&metric_families, &mut buffer).unwrap(); + + let str = String::from_utf8(buffer).unwrap(); + tracing::info!("{str}"); + + str +} diff --git a/crates/blockchain/payload.rs b/crates/blockchain/payload.rs index 287889084..5d4d60a86 100644 --- a/crates/blockchain/payload.rs +++ b/crates/blockchain/payload.rs @@ -20,6 +20,8 @@ use ethrex_vm::{ }; use sha3::{Digest, Keccak256}; +use ethrex_metrics::TRANSACTION_COUNTER; + use crate::{ constants::{ GAS_LIMIT_BOUND_DIVISOR, GAS_PER_BLOB, MAX_BLOB_GAS_PER_BLOCK, MIN_GAS_LIMIT, @@ -319,6 +321,11 @@ pub fn fill_transactions(context: &mut PayloadBuildContext) -> Result<(), ChainE )?; continue; } + // Increment the transaction counter + // TODO: differentiate between total, failed and succedded + let tx_counter = TRANSACTION_COUNTER.lock().unwrap(); + tx_counter.inc(); + // Execute tx let receipt = match apply_transaction(&head_tx, context) { Ok(receipt) => { diff --git a/crates/l2/Makefile b/crates/l2/Makefile index 344157131..ed12113c8 100644 --- a/crates/l2/Makefile +++ b/crates/l2/Makefile @@ -38,17 +38,22 @@ L1_AUTH_PORT=8551 # Used in the .env file. Ensure the same port is used for `ENGINE_API_RPC_URL`. L2_AUTH_PORT=8552 +L1_PROMETHEUS_METRICS_PORT = 3701 +L2_PROMETHEUS_METRICS_PORT = 3702 + + # Local L1 init-local-l1: ## ๐Ÿš€ Initializes an L1 Lambda ethrex Client with Docker (Used with make init) docker compose -f ${ethrex_DEV_DOCKER_COMPOSE_PATH} up -d init-l1: ## ๐Ÿš€ Initializes an L1 Lambda ethrex Client - cargo run --release --manifest-path ../../Cargo.toml --bin ethrex --features dev -- \ + cargo run --release --manifest-path ../../Cargo.toml --bin ethrex --features "dev,metrics" -- \ --network ${L1_GENESIS_FILE_PATH} \ --http.port ${L1_PORT} \ --http.addr 0.0.0.0 \ --authrpc.port ${L1_AUTH_PORT} \ + --metrics.port ${L1_PROMETHEUS_METRICS_PORT} \ --datadir ${ethrex_L1_DEV_LIBMDBX} down-local-l1: ## ๐Ÿ›‘ Shuts down the L1 Lambda ethrex Client @@ -74,11 +79,12 @@ deploy-l1: ## ๐Ÿ“œ Deploys the L1 contracts # L2 init-l2: ## ๐Ÿš€ Initializes an L2 Lambda ethrex Client - cargo run --release --manifest-path ../../Cargo.toml --bin ethrex --features l2 -- \ + cargo run --release --manifest-path ../../Cargo.toml --bin ethrex --features "l2,metrics" -- \ --network ${L2_GENESIS_FILE_PATH} \ --http.port ${L2_PORT} \ --http.addr 0.0.0.0 \ --authrpc.port ${L2_AUTH_PORT} \ + --metrics.port ${L2_PROMETHEUS_METRICS_PORT} \ --datadir ${ethrex_L2_DEV_LIBMDBX} down-l2: ## ๐Ÿ›‘ Shuts down the L2 Lambda ethrex Client From 0f42c9b050613a613a38eed620463546505968af Mon Sep 17 00:00:00 2001 From: fborello-lambda Date: Mon, 16 Dec 2024 16:14:27 -0300 Subject: [PATCH 02/13] test with L2 node --- cmd/ethrex_l2/src/commands/test.rs | 12 +- .../metrics/docker-compose-metrics.yaml | 17 ++ .../dashboards/dashboard.yaml | 11 + .../dashboards/demo_dashboards/dashboard.json | 211 ++++++++++++++++++ .../datasources/datasource.yaml | 13 ++ .../metrics/provisioning/prometheus.yaml | 8 + crates/l2/Makefile | 20 +- 7 files changed, 280 insertions(+), 12 deletions(-) create mode 100644 crates/blockchain/metrics/docker-compose-metrics.yaml create mode 100644 crates/blockchain/metrics/provisioning/grafana_provisioning/dashboards/dashboard.yaml create mode 100644 crates/blockchain/metrics/provisioning/grafana_provisioning/dashboards/demo_dashboards/dashboard.json create mode 100644 crates/blockchain/metrics/provisioning/grafana_provisioning/datasources/datasource.yaml create mode 100644 crates/blockchain/metrics/provisioning/prometheus.yaml diff --git a/cmd/ethrex_l2/src/commands/test.rs b/cmd/ethrex_l2/src/commands/test.rs index 3551fa423..059972345 100644 --- a/cmd/ethrex_l2/src/commands/test.rs +++ b/cmd/ethrex_l2/src/commands/test.rs @@ -72,11 +72,15 @@ async fn transfer_from( let client = EthClient::new(&cfg.network.l2_rpc_url); let private_key = SecretKey::from_slice(pk.parse::().unwrap().as_bytes()).unwrap(); - let mut buffer = [0u8; 32]; - let public_key = private_key.public_key(secp256k1::SECP256K1).serialize(); - buffer.copy_from_slice(&public_key[1..]); + let public_key = private_key + .public_key(secp256k1::SECP256K1) + .serialize_uncompressed(); + let hash = keccak(&public_key[1..]); - let address = H160::from(keccak(buffer)); + // Get the last 20 bytes of the hash + let address_bytes: [u8; 20] = hash.as_ref().get(12..32).unwrap().try_into().unwrap(); + + let address = Address::from(address_bytes); let nonce = client.get_nonce(address).await.unwrap(); let mut retries = 0; diff --git a/crates/blockchain/metrics/docker-compose-metrics.yaml b/crates/blockchain/metrics/docker-compose-metrics.yaml new file mode 100644 index 000000000..2822cf8d1 --- /dev/null +++ b/crates/blockchain/metrics/docker-compose-metrics.yaml @@ -0,0 +1,17 @@ +services: + prometheus: + image: prom/prometheus + command: --config.file=/etc/prometheus/prometheus.yaml + volumes: + - ./provisioning/prometheus.yaml:/etc/prometheus/prometheus.yaml + ports: + - "9090:9090" + grafana: + image: grafana/grafana + volumes: + - ./provisioning/grafana_provisioning/dashboards:/etc/grafana/provisioning/dashboards + - ./provisioning/grafana_provisioning/datasources:/etc/grafana/provisioning/datasources + ports: + - "3700:3000" + depends_on: + - prometheus diff --git a/crates/blockchain/metrics/provisioning/grafana_provisioning/dashboards/dashboard.yaml b/crates/blockchain/metrics/provisioning/grafana_provisioning/dashboards/dashboard.yaml new file mode 100644 index 000000000..95021bafd --- /dev/null +++ b/crates/blockchain/metrics/provisioning/grafana_provisioning/dashboards/dashboard.yaml @@ -0,0 +1,11 @@ +apiVersion: 1 + +providers: + - name: "Custom Prometheus Dashboards" + orgId: 1 + folder: "" + type: file + disableDeletion: true + editable: true + options: + path: /etc/grafana/provisioning/dashboards/demo_dashboards diff --git a/crates/blockchain/metrics/provisioning/grafana_provisioning/dashboards/demo_dashboards/dashboard.json b/crates/blockchain/metrics/provisioning/grafana_provisioning/dashboards/demo_dashboards/dashboard.json new file mode 100644 index 000000000..560e6c171 --- /dev/null +++ b/crates/blockchain/metrics/provisioning/grafana_provisioning/dashboards/demo_dashboards/dashboard.json @@ -0,0 +1,211 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "prom-001" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prom-001" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "transactions_counter", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Total Transactions", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prom-001" + }, + "description": "Simple demo\n", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prom-001" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "rate(transactions_counter[$__rate_interval])", + "format": "table", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Transactions Per Second", + "transparent": true, + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 40, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Ethrex - Transactions", + "uid": "ae6t2gxjoyx34c", + "version": 1, + "weekStart": "" +} diff --git a/crates/blockchain/metrics/provisioning/grafana_provisioning/datasources/datasource.yaml b/crates/blockchain/metrics/provisioning/grafana_provisioning/datasources/datasource.yaml new file mode 100644 index 000000000..7feaf24d0 --- /dev/null +++ b/crates/blockchain/metrics/provisioning/grafana_provisioning/datasources/datasource.yaml @@ -0,0 +1,13 @@ +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + uid: prom-001 + access: proxy + url: http://prometheus:9090 + isDefault: true + jsonData: + httpMethod: POST + readOnly: false + editable: true diff --git a/crates/blockchain/metrics/provisioning/prometheus.yaml b/crates/blockchain/metrics/provisioning/prometheus.yaml new file mode 100644 index 000000000..a2913052d --- /dev/null +++ b/crates/blockchain/metrics/provisioning/prometheus.yaml @@ -0,0 +1,8 @@ +global: + scrape_interval: 15s + +scrape_configs: + - job_name: "ethrex" + static_configs: + # Use the name defined in the docker-compose.yaml + - targets: ["host.docker.internal:3701"] diff --git a/crates/l2/Makefile b/crates/l2/Makefile index ed12113c8..cff45e4a9 100644 --- a/crates/l2/Makefile +++ b/crates/l2/Makefile @@ -10,7 +10,7 @@ help: ## ๐Ÿ“š Show help for each of the Makefile recipes init: init-local-l1 deploy-l1 init-l2 ## ๐Ÿš€ Initializes a localnet with Lambda ethrex client as both L1 and L2 -down: down-local-l1 down-l2 ## ๐Ÿ›‘ Shuts down the localnet +down: down-local-l1 down-l2 down-metrics## ๐Ÿ›‘ Shuts down the localnet clean: clean-contract-deps ## ๐Ÿงน Cleans the localnet @@ -25,6 +25,7 @@ ethrex_PATH=$(shell pwd)/../.. ethrex_BIN_PATH=$(ethrex_PATH)/target/release/ethrex ethrex_DEV_DOCKER_COMPOSE_PATH=$(ethrex_PATH)/crates/blockchain/dev/docker-compose-dev.yaml ethrex_L2_DOCKER_COMPOSE_PATH=./docker-compose-l2.yaml +ethrex_METRICS_DOCKER_COMPOSE_PATH=$(ethrex_PATH)/crates/blockchain/metrics/docker-compose-metrics.yaml ethrex_L2_CONTRACTS_PATH=./contracts L1_RPC_URL=http://localhost:8545 @@ -38,9 +39,7 @@ L1_AUTH_PORT=8551 # Used in the .env file. Ensure the same port is used for `ENGINE_API_RPC_URL`. L2_AUTH_PORT=8552 -L1_PROMETHEUS_METRICS_PORT = 3701 -L2_PROMETHEUS_METRICS_PORT = 3702 - +PROMETHEUS_METRICS_PORT = 3701 # Local L1 @@ -48,12 +47,11 @@ init-local-l1: ## ๐Ÿš€ Initializes an L1 Lambda ethrex Client with Docker (Used docker compose -f ${ethrex_DEV_DOCKER_COMPOSE_PATH} up -d init-l1: ## ๐Ÿš€ Initializes an L1 Lambda ethrex Client - cargo run --release --manifest-path ../../Cargo.toml --bin ethrex --features "dev,metrics" -- \ + cargo run --release --manifest-path ../../Cargo.toml --bin ethrex --features "dev" -- \ --network ${L1_GENESIS_FILE_PATH} \ --http.port ${L1_PORT} \ --http.addr 0.0.0.0 \ --authrpc.port ${L1_AUTH_PORT} \ - --metrics.port ${L1_PROMETHEUS_METRICS_PORT} \ --datadir ${ethrex_L1_DEV_LIBMDBX} down-local-l1: ## ๐Ÿ›‘ Shuts down the L1 Lambda ethrex Client @@ -78,14 +76,20 @@ deploy-l1: ## ๐Ÿ“œ Deploys the L1 contracts # L2 -init-l2: ## ๐Ÿš€ Initializes an L2 Lambda ethrex Client +init-l2: init-metrics ## ๐Ÿš€ Initializes an L2 Lambda ethrex Client cargo run --release --manifest-path ../../Cargo.toml --bin ethrex --features "l2,metrics" -- \ --network ${L2_GENESIS_FILE_PATH} \ --http.port ${L2_PORT} \ --http.addr 0.0.0.0 \ --authrpc.port ${L2_AUTH_PORT} \ - --metrics.port ${L2_PROMETHEUS_METRICS_PORT} \ + --metrics.port ${PROMETHEUS_METRICS_PORT} \ --datadir ${ethrex_L2_DEV_LIBMDBX} + +init-metrics: ## ๐Ÿš€ Initializes Grafana and Prometheus with containers + docker compose -f ${ethrex_METRICS_DOCKER_COMPOSE_PATH} up -d + +down-metrics: ## ๐Ÿ›‘ Shuts down the metrics' containers + docker compose -f ${ethrex_METRICS_DOCKER_COMPOSE_PATH} down down-l2: ## ๐Ÿ›‘ Shuts down the L2 Lambda ethrex Client pkill -f ethrex || exit 0 From 7e5ba04ee3054f7a868e9fed8b0dcb436743c44d Mon Sep 17 00:00:00 2001 From: fborello-lambda Date: Mon, 16 Dec 2024 17:48:13 -0300 Subject: [PATCH 03/13] refactor: Makefile --- cmd/ethrex/ethrex.rs | 3 +-- crates/blockchain/dev/Dockerfile | 2 +- crates/blockchain/dev/docker-compose-dev.yaml | 8 +++++++- .../docker-compose-metrics-l1-dev.overrides.yaml | 9 +++++++++ .../docker-compose-metrics-l2.overrides.yaml | 9 +++++++++ .../metrics/docker-compose-metrics.yaml | 10 ++++------ .../prometheus_l1_dev.yaml} | 4 ++-- .../provisioning/prometheus/prometheus_l2.yaml | 8 ++++++++ crates/l2/Makefile | 16 ++++++++++------ 9 files changed, 51 insertions(+), 18 deletions(-) create mode 100644 crates/blockchain/metrics/docker-compose-metrics-l1-dev.overrides.yaml create mode 100644 crates/blockchain/metrics/docker-compose-metrics-l2.overrides.yaml rename crates/blockchain/metrics/provisioning/{prometheus.yaml => prometheus/prometheus_l1_dev.yaml} (63%) create mode 100644 crates/blockchain/metrics/provisioning/prometheus/prometheus_l2.yaml diff --git a/cmd/ethrex/ethrex.rs b/cmd/ethrex/ethrex.rs index 5f10e74d2..d58fa8f47 100644 --- a/cmd/ethrex/ethrex.rs +++ b/cmd/ethrex/ethrex.rs @@ -239,8 +239,7 @@ async fn main() { let url = format!("http://{authrpc_socket_addr}"); let block_producer_engine = ethrex_dev::block_producer::start_block_producer(url, authrpc_jwtsecret.into(), head_block_hash, max_tries, 1000, ethrex_core::Address::default()); tracker.spawn(block_producer_engine); - } - else { + } else { let networking = ethrex_net::start_network( udp_socket_addr, tcp_socket_addr, diff --git a/crates/blockchain/dev/Dockerfile b/crates/blockchain/dev/Dockerfile index 466580b4b..a39bde1d7 100644 --- a/crates/blockchain/dev/Dockerfile +++ b/crates/blockchain/dev/Dockerfile @@ -22,7 +22,7 @@ COPY --from=planner /ethrex/recipe.json recipe.json RUN cargo chef cook --release --recipe-path recipe.json COPY . . -RUN cargo build --release --features dev +RUN cargo build --release --features "dev,metrics" FROM ubuntu:24.04 WORKDIR /usr/local/bin diff --git a/crates/blockchain/dev/docker-compose-dev.yaml b/crates/blockchain/dev/docker-compose-dev.yaml index b95dce07c..b45aeed2f 100644 --- a/crates/blockchain/dev/docker-compose-dev.yaml +++ b/crates/blockchain/dev/docker-compose-dev.yaml @@ -1,3 +1,9 @@ +# If this dev container is run in the same machine as the L2 node +# we have to run this docker-compose with the .overrides file too +# example: docker compose -f docker-compose-dev.yaml -f docker-compose-metrics-l1.override.yaml up +include: + - ../metrics/docker-compose-metrics.yaml + services: ethrex: restart: always @@ -10,4 +16,4 @@ services: - 127.0.0.1:8545:8545 volumes: - ../../../test_data/genesis-l1.json:/genesis-l1.json - command: --network /genesis-l1.json --http.addr 0.0.0.0 --http.port 8545 + command: --network /genesis-l1.json --http.addr 0.0.0.0 --http.port 8545 --metrics.port 3701 diff --git a/crates/blockchain/metrics/docker-compose-metrics-l1-dev.overrides.yaml b/crates/blockchain/metrics/docker-compose-metrics-l1-dev.overrides.yaml new file mode 100644 index 000000000..1bdafe6b5 --- /dev/null +++ b/crates/blockchain/metrics/docker-compose-metrics-l1-dev.overrides.yaml @@ -0,0 +1,9 @@ +services: + prometheus: + volumes: + - ../metrics/provisioning/prometheus/prometheus_l1_dev.yaml:/etc/prometheus/prometheus.yaml + ports: + - "9091:9090" + grafana: + ports: + - "3801:3000" diff --git a/crates/blockchain/metrics/docker-compose-metrics-l2.overrides.yaml b/crates/blockchain/metrics/docker-compose-metrics-l2.overrides.yaml new file mode 100644 index 000000000..578142ec1 --- /dev/null +++ b/crates/blockchain/metrics/docker-compose-metrics-l2.overrides.yaml @@ -0,0 +1,9 @@ +services: + prometheus: + volumes: + - ../metrics/provisioning/prometheus/prometheus_l2.yaml:/etc/prometheus/prometheus.yaml + ports: + - "9092:9090" + grafana: + ports: + - "3802:3000" diff --git a/crates/blockchain/metrics/docker-compose-metrics.yaml b/crates/blockchain/metrics/docker-compose-metrics.yaml index 2822cf8d1..a7b3890cc 100644 --- a/crates/blockchain/metrics/docker-compose-metrics.yaml +++ b/crates/blockchain/metrics/docker-compose-metrics.yaml @@ -1,17 +1,15 @@ +# example: docker compose -f docker-compose-metrics.yaml -f docker-compose-metrics-l2.override.yaml up services: prometheus: image: prom/prometheus command: --config.file=/etc/prometheus/prometheus.yaml - volumes: - - ./provisioning/prometheus.yaml:/etc/prometheus/prometheus.yaml - ports: - - "9090:9090" + #volumes: defined in the .overrides file + #ports: defined in the .overrides file grafana: image: grafana/grafana volumes: - ./provisioning/grafana_provisioning/dashboards:/etc/grafana/provisioning/dashboards - ./provisioning/grafana_provisioning/datasources:/etc/grafana/provisioning/datasources - ports: - - "3700:3000" + #ports: defined in the .overrides file depends_on: - prometheus diff --git a/crates/blockchain/metrics/provisioning/prometheus.yaml b/crates/blockchain/metrics/provisioning/prometheus/prometheus_l1_dev.yaml similarity index 63% rename from crates/blockchain/metrics/provisioning/prometheus.yaml rename to crates/blockchain/metrics/provisioning/prometheus/prometheus_l1_dev.yaml index a2913052d..a699fd87d 100644 --- a/crates/blockchain/metrics/provisioning/prometheus.yaml +++ b/crates/blockchain/metrics/provisioning/prometheus/prometheus_l1_dev.yaml @@ -2,7 +2,7 @@ global: scrape_interval: 15s scrape_configs: - - job_name: "ethrex" + - job_name: "ethrex L1" static_configs: # Use the name defined in the docker-compose.yaml - - targets: ["host.docker.internal:3701"] + - targets: ["ethrex:3701"] diff --git a/crates/blockchain/metrics/provisioning/prometheus/prometheus_l2.yaml b/crates/blockchain/metrics/provisioning/prometheus/prometheus_l2.yaml new file mode 100644 index 000000000..d391f0945 --- /dev/null +++ b/crates/blockchain/metrics/provisioning/prometheus/prometheus_l2.yaml @@ -0,0 +1,8 @@ +global: + scrape_interval: 15s + +scrape_configs: + - job_name: "ethrex L2" + static_configs: + # Use the name defined in the docker-compose.yaml + - targets: ["host.docker.internal:3702"] diff --git a/crates/l2/Makefile b/crates/l2/Makefile index cff45e4a9..d0b14bdc7 100644 --- a/crates/l2/Makefile +++ b/crates/l2/Makefile @@ -26,6 +26,9 @@ ethrex_BIN_PATH=$(ethrex_PATH)/target/release/ethrex ethrex_DEV_DOCKER_COMPOSE_PATH=$(ethrex_PATH)/crates/blockchain/dev/docker-compose-dev.yaml ethrex_L2_DOCKER_COMPOSE_PATH=./docker-compose-l2.yaml ethrex_METRICS_DOCKER_COMPOSE_PATH=$(ethrex_PATH)/crates/blockchain/metrics/docker-compose-metrics.yaml +ethrex_METRICS_OVERRIDES_L1_DOCKER_COMPOSE_PATH=$(ethrex_PATH)/crates/blockchain/metrics/docker-compose-metrics-l1-dev.overrides.yaml +ethrex_METRICS_OVERRIDES_L2_DOCKER_COMPOSE_PATH=$(ethrex_PATH)/crates/blockchain/metrics/docker-compose-metrics-l2.overrides.yaml + ethrex_L2_CONTRACTS_PATH=./contracts L1_RPC_URL=http://localhost:8545 @@ -39,12 +42,13 @@ L1_AUTH_PORT=8551 # Used in the .env file. Ensure the same port is used for `ENGINE_API_RPC_URL`. L2_AUTH_PORT=8552 -PROMETHEUS_METRICS_PORT = 3701 +# Matches the ports used by the blockchain/metrics dir +L2_PROMETHEUS_METRICS_PORT = 3702 # Local L1 init-local-l1: ## ๐Ÿš€ Initializes an L1 Lambda ethrex Client with Docker (Used with make init) - docker compose -f ${ethrex_DEV_DOCKER_COMPOSE_PATH} up -d + docker compose -f ${ethrex_DEV_DOCKER_COMPOSE_PATH} -f ${ethrex_METRICS_OVERRIDES_L1_DOCKER_COMPOSE_PATH} up -d init-l1: ## ๐Ÿš€ Initializes an L1 Lambda ethrex Client cargo run --release --manifest-path ../../Cargo.toml --bin ethrex --features "dev" -- \ @@ -55,7 +59,7 @@ init-l1: ## ๐Ÿš€ Initializes an L1 Lambda ethrex Client --datadir ${ethrex_L1_DEV_LIBMDBX} down-local-l1: ## ๐Ÿ›‘ Shuts down the L1 Lambda ethrex Client - docker compose -f ${ethrex_DEV_DOCKER_COMPOSE_PATH} down + docker compose -f ${ethrex_DEV_DOCKER_COMPOSE_PATH} -f ${ethrex_METRICS_OVERRIDES_L1_DOCKER_COMPOSE_PATH} down docker compose -f docker-compose-l2.yaml down restart-local-l1: down-local-l1 init-local-l1 ## ๐Ÿ”„ Restarts the L1 Lambda ethrex Client @@ -82,14 +86,14 @@ init-l2: init-metrics ## ๐Ÿš€ Initializes an L2 Lambda ethrex Client --http.port ${L2_PORT} \ --http.addr 0.0.0.0 \ --authrpc.port ${L2_AUTH_PORT} \ - --metrics.port ${PROMETHEUS_METRICS_PORT} \ + --metrics.port ${L2_PROMETHEUS_METRICS_PORT} \ --datadir ${ethrex_L2_DEV_LIBMDBX} init-metrics: ## ๐Ÿš€ Initializes Grafana and Prometheus with containers - docker compose -f ${ethrex_METRICS_DOCKER_COMPOSE_PATH} up -d + docker compose -f ${ethrex_METRICS_DOCKER_COMPOSE_PATH} -f ${ethrex_METRICS_OVERRIDES_L2_DOCKER_COMPOSE_PATH} up -d down-metrics: ## ๐Ÿ›‘ Shuts down the metrics' containers - docker compose -f ${ethrex_METRICS_DOCKER_COMPOSE_PATH} down + docker compose -f ${ethrex_METRICS_DOCKER_COMPOSE_PATH} -f ${ethrex_METRICS_OVERRIDES_L2_DOCKER_COMPOSE_PATH} down down-l2: ## ๐Ÿ›‘ Shuts down the L2 Lambda ethrex Client pkill -f ethrex || exit 0 From b69f76d081c47c0ecc40303d4d14154becdec0a0 Mon Sep 17 00:00:00 2001 From: fborello-lambda Date: Tue, 17 Dec 2024 10:41:00 -0300 Subject: [PATCH 04/13] docs --- crates/blockchain/metrics/README.md | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 crates/blockchain/metrics/README.md diff --git a/crates/blockchain/metrics/README.md b/crates/blockchain/metrics/README.md new file mode 100644 index 000000000..b58e5f6d5 --- /dev/null +++ b/crates/blockchain/metrics/README.md @@ -0,0 +1,37 @@ +# Metrics + +A `docker-compose` is used to bundle prometheus and grafana services, the `*overrides` files define the ports and mounts the prometheus' configuration file. +If a new dashboard is designed just for the L1 or L2, it can be mounted only in that `*overrides` file. + +To run the node with metrics, the next steps should be followed: +1. Build the `ethrex` binary with the `metrics` feature enabled. +2. Set the `--metrics.port` cli arg of the ethrex binary to match the port defined in `metrics/provisioning/prometheus/prometheus*.yaml` +3. Run the docker containers, example with the L2: + +```sh +docker compose -f docker-compose-metrics.yaml -f docker-compose-metrics-l2.override.yaml up +``` + +>[!NOTE] +> The L2's Makefile automatically starts the prometheus and grafana services with `make init`. For the L1 used in dev mode and the L2. + + +- For the L2 we use the following files in conjunction: + - `docker-compose-metrics.yaml` + - `docker-compose-metrics-l2.overrides.yaml` + - The defaults are: + - PORT `3702` → metrics API (used by prometheus) + - PORT `3802` → Grafana + - usr: `admin` + - pwd: `admin` + - PORT `9092` → Prometheus + +- For the L1 dev we use the following files in conjunction: + - `docker-compose-metrics.yaml` + - `docker-compose-metrics-l1-dev.overrides.yaml` + - The defaults are: + - PORT `3701` → metrics API (used by prometheus) + - PORT `3801` → Grafana + - usr: `admin` + - pwd: `admin` + - PORT `9091` → Prometheus From e59cb38925455f9fa029da19c6c1390a9910a35e Mon Sep 17 00:00:00 2001 From: fborello-lambda Date: Tue, 17 Dec 2024 12:08:30 -0300 Subject: [PATCH 05/13] refactor: add metrics macro and features --- cmd/ethrex/Cargo.toml | 2 +- cmd/ethrex/ethrex.rs | 2 +- crates/blockchain/Cargo.toml | 6 +-- crates/blockchain/metrics/Cargo.toml | 6 ++- .../metrics/{metrics.rs => metrics_api.rs} | 3 +- crates/blockchain/metrics/mod.rs | 49 +++++++++++++++++++ crates/blockchain/payload.rs | 11 +++-- 7 files changed, 68 insertions(+), 11 deletions(-) rename crates/blockchain/metrics/{metrics.rs => metrics_api.rs} (99%) create mode 100644 crates/blockchain/metrics/mod.rs diff --git a/cmd/ethrex/Cargo.toml b/cmd/ethrex/Cargo.toml index e805dae8a..c392c891b 100644 --- a/cmd/ethrex/Cargo.toml +++ b/cmd/ethrex/Cargo.toml @@ -43,7 +43,7 @@ path = "./ethrex.rs" [features] default = ["dep:ethrex-storage", "libmdbx"] dev = ["dep:ethrex-dev"] -metrics = ["dep:ethrex-metrics"] +metrics = ["dep:ethrex-metrics", "ethrex-blockchain/metrics"] libmdbx = ["dep:libmdbx", "ethrex-storage/libmdbx"] redb = ["dep:redb", "ethrex-storage/redb"] l2 = ["ethrex-vm/l2"] diff --git a/cmd/ethrex/ethrex.rs b/cmd/ethrex/ethrex.rs index d58fa8f47..bb8384b77 100644 --- a/cmd/ethrex/ethrex.rs +++ b/cmd/ethrex/ethrex.rs @@ -217,7 +217,7 @@ async fn main() { .get_one::("metrics.port") .map_or("3000".to_string(), |v| v.clone()); - let metrics_api = ethrex_metrics::start_prometheus_metrics_api(metrics_port); + let metrics_api = ethrex_metrics::metrics_api::start_prometheus_metrics_api(metrics_port); tracker.spawn(metrics_api); } } diff --git a/crates/blockchain/Cargo.toml b/crates/blockchain/Cargo.toml index 9747ba211..d5e2a09c3 100644 --- a/crates/blockchain/Cargo.toml +++ b/crates/blockchain/Cargo.toml @@ -19,7 +19,7 @@ ethrex-vm = { path = "../vm", default-features = false } k256 = { version = "0.13.3", features = ["ecdh"] } -ethrex-metrics = { path = "./metrics", optional = true } +ethrex-metrics = { path = "./metrics", default-features = false } [dev-dependencies] serde_json.workspace = true @@ -29,8 +29,8 @@ hex = "0.4.3" path = "./blockchain.rs" [features] -default = ["c-kzg", "metrics"] +default = ["c-kzg"] libmdbx = ["ethrex-core/libmdbx", "ethrex-storage/default", "ethrex-vm/libmdbx"] levm = ["ethrex-vm/levm"] c-kzg = ["ethrex-core/c-kzg"] -metrics = ["dep:ethrex-metrics"] +metrics = ["ethrex-metrics/api"] diff --git a/crates/blockchain/metrics/Cargo.toml b/crates/blockchain/metrics/Cargo.toml index f7d79f6ef..b92a89bd9 100644 --- a/crates/blockchain/metrics/Cargo.toml +++ b/crates/blockchain/metrics/Cargo.toml @@ -19,4 +19,8 @@ axum = "0.7.9" [lib] -path = "./metrics.rs" +path = "./mod.rs" + +[features] +default = ["api"] +api = [] diff --git a/crates/blockchain/metrics/metrics.rs b/crates/blockchain/metrics/metrics_api.rs similarity index 99% rename from crates/blockchain/metrics/metrics.rs rename to crates/blockchain/metrics/metrics_api.rs index 65802bda3..907d07073 100644 --- a/crates/blockchain/metrics/metrics.rs +++ b/crates/blockchain/metrics/metrics_api.rs @@ -1,7 +1,6 @@ -use std::sync::{Arc, LazyLock, Mutex}; - use axum::{routing::get, Router}; use prometheus::{Encoder, IntCounter, Registry, TextEncoder}; +use std::sync::{Arc, LazyLock, Mutex}; pub static TRANSACTION_COUNTER: LazyLock>> = LazyLock::new(|| { Arc::new(Mutex::new( diff --git a/crates/blockchain/metrics/mod.rs b/crates/blockchain/metrics/mod.rs new file mode 100644 index 000000000..65422f1cc --- /dev/null +++ b/crates/blockchain/metrics/mod.rs @@ -0,0 +1,49 @@ +#[cfg(feature = "api")] +pub mod metrics_api; + +/// A macro to conditionally enable metrics-related code. +/// +/// This macro wraps the provided code block with a `#[cfg(feature = "metrics")]` attribute. +/// The enclosed code will only be compiled and executed if the `metrics` feature is enabled in the +/// `Cargo.toml` file. +/// +/// ## Usage +/// +/// If the `metrics` feature is enabled, the code inside the macro will be executed. Otherwise, +/// it will be excluded from the compilation. This is useful for enabling/disabling metrics collection +/// code without cluttering the source with manual feature conditionals. +/// +/// ## In `Cargo.toml` +/// The `metrics` feature has to be set in the Cargo.toml of the crate we desire to take metrics from. +/// +/// To enable the `metrics` feature, add the following to your `Cargo.toml`: +/// ```toml +/// ethrex-metrics = { path = "./metrics", default-features = false } +/// [features] +/// metrics = ["ethrex-metrics/api"] +/// ``` +/// +/// In this way, when the `metrics` feature is enabled for that crate, the macro is triggered and the metrics_api is also used. +/// +/// Example In Code: +/// ```sh +/// use ethrex_metrics::metrics; +// #[cfg(feature = "metrics")] +// use ethrex_metrics::metrics_api::TRANSACTION_COUNTER; +/// +/// metrics!( +/// let tx_counter = TRANSACTION_COUNTER.lock().unwrap(); +/// tx_counter.inc(); +/// ); +/// ``` +/// +/// If you build without the `metrics` feature, the code inside `metrics!` will not be compiled, nor will the Prometheus crate. +#[macro_export] +macro_rules! metrics { + ($($code:tt)*) => { + #[cfg(feature = "metrics")] + { + $($code)* + } + }; +} diff --git a/crates/blockchain/payload.rs b/crates/blockchain/payload.rs index 5d4d60a86..cdb177504 100644 --- a/crates/blockchain/payload.rs +++ b/crates/blockchain/payload.rs @@ -20,7 +20,10 @@ use ethrex_vm::{ }; use sha3::{Digest, Keccak256}; -use ethrex_metrics::TRANSACTION_COUNTER; +use ethrex_metrics::metrics; + +#[cfg(feature = "metrics")] +use ethrex_metrics::metrics_api::TRANSACTION_COUNTER; use crate::{ constants::{ @@ -323,8 +326,10 @@ pub fn fill_transactions(context: &mut PayloadBuildContext) -> Result<(), ChainE } // Increment the transaction counter // TODO: differentiate between total, failed and succedded - let tx_counter = TRANSACTION_COUNTER.lock().unwrap(); - tx_counter.inc(); + metrics!( + let tx_counter = TRANSACTION_COUNTER.lock().unwrap(); + tx_counter.inc(); + ); // Execute tx let receipt = match apply_transaction(&head_tx, context) { From a212dc25b86d7d6a39f4776e54ba64cbaf203a66 Mon Sep 17 00:00:00 2001 From: fborello-lambda Date: Tue, 17 Dec 2024 13:39:30 -0300 Subject: [PATCH 06/13] refactor: rm unwraps --- crates/blockchain/metrics/metrics_api.rs | 42 ++++++++++++++++-------- crates/blockchain/metrics/mod.rs | 6 ++++ 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/crates/blockchain/metrics/metrics_api.rs b/crates/blockchain/metrics/metrics_api.rs index 907d07073..41cb8d68c 100644 --- a/crates/blockchain/metrics/metrics_api.rs +++ b/crates/blockchain/metrics/metrics_api.rs @@ -2,6 +2,8 @@ use axum::{routing::get, Router}; use prometheus::{Encoder, IntCounter, Registry, TextEncoder}; use std::sync::{Arc, LazyLock, Mutex}; +use crate::MetricsApiError; + pub static TRANSACTION_COUNTER: LazyLock>> = LazyLock::new(|| { Arc::new(Mutex::new( IntCounter::new( @@ -12,32 +14,46 @@ pub static TRANSACTION_COUNTER: LazyLock>> = LazyLock::new )) }); -pub async fn start_prometheus_metrics_api(port: String) { +pub async fn start_prometheus_metrics_api(port: String) -> Result<(), MetricsApiError> { let app = Router::new() .route("/metrics", get(get_metrics)) .route("/health", get("Service Up")); // Start the axum app - let listener = tokio::net::TcpListener::bind(&format!("0.0.0.0:{port}")) - .await - .unwrap(); - axum::serve(listener, app).await.unwrap(); + let listener = tokio::net::TcpListener::bind(&format!("0.0.0.0:{port}")).await?; + axum::serve(listener, app).await?; + + Ok(()) } async fn get_metrics() -> String { let r = Registry::new(); let tx_counter = TRANSACTION_COUNTER.clone(); - r.register(Box::new(tx_counter.lock().unwrap().clone())) - .unwrap(); + + let tx_counter_lock = match tx_counter.lock() { + Ok(lock) => lock, + Err(e) => { + tracing::error!("Failed to lock mutex: {e}"); + return String::new(); + } + }; + + if r.register(Box::new(tx_counter_lock.clone())).is_err() { + tracing::error!("Failed to register metric"); + return String::new(); + } let encoder = TextEncoder::new(); let metric_families = r.gather(); let mut buffer = Vec::new(); - encoder.encode(&metric_families, &mut buffer).unwrap(); - - let str = String::from_utf8(buffer).unwrap(); - tracing::info!("{str}"); - - str + if encoder.encode(&metric_families, &mut buffer).is_err() { + tracing::error!("Failed to encode metrics"); + return String::new(); + } + + String::from_utf8(buffer).unwrap_or_else(|e| { + tracing::error!("Failed to convert buffer to String: {e}"); + String::new() + }) } diff --git a/crates/blockchain/metrics/mod.rs b/crates/blockchain/metrics/mod.rs index 65422f1cc..ed85f3f70 100644 --- a/crates/blockchain/metrics/mod.rs +++ b/crates/blockchain/metrics/mod.rs @@ -47,3 +47,9 @@ macro_rules! metrics { } }; } + +#[derive(Debug, thiserror::Error)] +pub enum MetricsApiError { + #[error("{0}")] + TcpError(#[from] std::io::Error), +} From ea8a867a28cfd1b763c9cf517db5dcbf3390930d Mon Sep 17 00:00:00 2001 From: fborello-lambda Date: Tue, 17 Dec 2024 17:45:42 -0300 Subject: [PATCH 07/13] refactor: Metrics struct to have a complex dashboard --- Cargo.lock | 1 + crates/blockchain/metrics/Cargo.toml | 2 + crates/blockchain/metrics/metrics_api.rs | 184 +++++++++++---- .../dashboards/demo_dashboards/dashboard.json | 214 +++++++++++++++++- crates/blockchain/payload.rs | 24 +- 5 files changed, 378 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 991c63279..2a3b0af14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2180,6 +2180,7 @@ name = "ethrex-metrics" version = "0.1.0" dependencies = [ "axum", + "ethrex-core", "prometheus", "serde", "serde_json", diff --git a/crates/blockchain/metrics/Cargo.toml b/crates/blockchain/metrics/Cargo.toml index b92a89bd9..df0267e30 100644 --- a/crates/blockchain/metrics/Cargo.toml +++ b/crates/blockchain/metrics/Cargo.toml @@ -12,6 +12,8 @@ thiserror.workspace = true serde_json.workspace = true serde.workspace = true +ethrex-core = { path = "../../common", default-features = false } + prometheus = "0.13.4" # TODO: remove? diff --git a/crates/blockchain/metrics/metrics_api.rs b/crates/blockchain/metrics/metrics_api.rs index 41cb8d68c..cd28c3149 100644 --- a/crates/blockchain/metrics/metrics_api.rs +++ b/crates/blockchain/metrics/metrics_api.rs @@ -1,18 +1,152 @@ use axum::{routing::get, Router}; -use prometheus::{Encoder, IntCounter, Registry, TextEncoder}; +use ethrex_core::types::TxType; +use prometheus::{Encoder, IntCounter, IntCounterVec, Opts, Registry, TextEncoder}; use std::sync::{Arc, LazyLock, Mutex}; use crate::MetricsApiError; -pub static TRANSACTION_COUNTER: LazyLock>> = LazyLock::new(|| { - Arc::new(Mutex::new( - IntCounter::new( - "transactions_counter", - "keeps track of the executed transactions", - ) - .unwrap(), - )) -}); +pub struct Metrics { + pub transactions_tracker: Arc>, + pub transactions_total: Arc>, +} + +impl Default for Metrics { + fn default() -> Self { + Self::new() + } +} + +impl Metrics { + pub fn new() -> Self { + Metrics { + transactions_tracker: Arc::new(Mutex::new( + IntCounterVec::new( + Opts::new( + "transactions_tracker", + "Keeps track of all transactions depending on status and tx_type", + ), + &["status", "tx_type"], + ) + .unwrap(), + )), + transactions_total: Arc::new(Mutex::new( + IntCounter::new("transactions_total", "Keeps track of all transactions").unwrap(), + )), + } + } + + pub fn inc_tx_with_status_and_type(&self, status: MetricsTxStatus, tx_type: MetricsTxType) { + let txs = self.transactions_tracker.clone(); + + let txs_lock = match txs.lock() { + Ok(lock) => lock, + Err(e) => { + tracing::error!("Failed to lock mutex: {e}"); + return; + } + }; + + let txs_builder = + match txs_lock.get_metric_with_label_values(&[status.to_str(), tx_type.to_str()]) { + Ok(builder) => builder, + Err(e) => { + tracing::error!("Failed to build Metric: {e}"); + return; + } + }; + + txs_builder.inc(); + } + + pub fn inc_tx(&self) { + let txs = self.transactions_total.clone(); + + let txs_lock = match txs.lock() { + Ok(lock) => lock, + Err(e) => { + tracing::error!("Failed to lock mutex: {e}"); + return; + } + }; + + txs_lock.inc(); + } + + pub fn gather_metrics(&self) -> String { + let r = Registry::new(); + + let txs_tracker = self.transactions_tracker.clone(); + let txs_tracker_lock = match txs_tracker.lock() { + Ok(lock) => lock, + Err(e) => { + tracing::error!("Failed to lock transactions_tracker mutex: {e}"); + return String::new(); + } + }; + + let txs_lock = self.transactions_total.clone(); + let txs_lock = match txs_lock.lock() { + Ok(lock) => lock, + Err(e) => { + tracing::error!("Failed to lock transactions_total mutex: {e}"); + return String::new(); + } + }; + + if r.register(Box::new(txs_lock.clone())).is_err() { + tracing::error!("Failed to register metric"); + return String::new(); + } + if r.register(Box::new(txs_tracker_lock.clone())).is_err() { + tracing::error!("Failed to register metric"); + return String::new(); + } + + let encoder = TextEncoder::new(); + let metric_families = r.gather(); + + let mut buffer = Vec::new(); + if encoder.encode(&metric_families, &mut buffer).is_err() { + tracing::error!("Failed to encode metrics"); + return String::new(); + } + + String::from_utf8(buffer).unwrap_or_else(|e| { + tracing::error!("Failed to convert buffer to String: {e}"); + String::new() + }) + } +} + +pub enum MetricsTxStatus { + Failed, + Succeeded, +} + +impl MetricsTxStatus { + pub fn to_str(&self) -> &str { + match self { + MetricsTxStatus::Failed => "failed", + MetricsTxStatus::Succeeded => "succedded", + } + } +} + +pub struct MetricsTxType(pub TxType); + +impl MetricsTxType { + pub fn to_str(&self) -> &str { + match self.0 { + ethrex_core::types::TxType::Legacy => "Legacy", + ethrex_core::types::TxType::EIP2930 => "EIP2930", + ethrex_core::types::TxType::EIP1559 => "EIP1559", + ethrex_core::types::TxType::EIP4844 => "EIP4844", + ethrex_core::types::TxType::Privileged => "Privileged", + } + } +} + +pub static METRICS: LazyLock = LazyLock::new(Metrics::default); pub async fn start_prometheus_metrics_api(port: String) -> Result<(), MetricsApiError> { let app = Router::new() @@ -27,33 +161,5 @@ pub async fn start_prometheus_metrics_api(port: String) -> Result<(), MetricsApi } async fn get_metrics() -> String { - let r = Registry::new(); - let tx_counter = TRANSACTION_COUNTER.clone(); - - let tx_counter_lock = match tx_counter.lock() { - Ok(lock) => lock, - Err(e) => { - tracing::error!("Failed to lock mutex: {e}"); - return String::new(); - } - }; - - if r.register(Box::new(tx_counter_lock.clone())).is_err() { - tracing::error!("Failed to register metric"); - return String::new(); - } - - let encoder = TextEncoder::new(); - let metric_families = r.gather(); - - let mut buffer = Vec::new(); - if encoder.encode(&metric_families, &mut buffer).is_err() { - tracing::error!("Failed to encode metrics"); - return String::new(); - } - - String::from_utf8(buffer).unwrap_or_else(|e| { - tracing::error!("Failed to convert buffer to String: {e}"); - String::new() - }) + METRICS.gather_metrics() } diff --git a/crates/blockchain/metrics/provisioning/grafana_provisioning/dashboards/demo_dashboards/dashboard.json b/crates/blockchain/metrics/provisioning/grafana_provisioning/dashboards/demo_dashboards/dashboard.json index 560e6c171..7289519ff 100644 --- a/crates/blockchain/metrics/provisioning/grafana_provisioning/dashboards/demo_dashboards/dashboard.json +++ b/crates/blockchain/metrics/provisioning/grafana_provisioning/dashboards/demo_dashboards/dashboard.json @@ -77,7 +77,7 @@ }, "disableTextWrap": false, "editorMode": "builder", - "expr": "transactions_counter", + "expr": "transactions_total", "fullMetaSearch": false, "includeNullMetadata": true, "legendFormat": "__auto", @@ -176,7 +176,7 @@ "disableTextWrap": false, "editorMode": "builder", "exemplar": false, - "expr": "rate(transactions_counter[$__rate_interval])", + "expr": "rate(transactions_total[$__rate_interval])", "format": "table", "fullMetaSearch": false, "includeNullMetadata": true, @@ -187,9 +187,217 @@ "useBackend": false } ], - "title": "Transactions Per Second", + "title": "Total Transactions Per Second", "transparent": true, "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prom-001" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [] + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 0, + "y": 9 + }, + "id": 3, + "options": { + "displayLabels": [ + "percent" + ], + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "pieType": "donut", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "transactions_tracker{status=\"succedded\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "{{tx_type}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Succeeded Transactions By Type", + "type": "piechart" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prom-001" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 5, + "y": 9 + }, + "id": 5, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "prom-001" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "transactions_tracker", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{tx_type}} || {{status}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Transactions", + "transparent": true, + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "prom-001" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [] + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 7, + "x": 17, + "y": 9 + }, + "id": 4, + "options": { + "displayLabels": [ + "percent" + ], + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "pieType": "donut", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.4.0", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "transactions_tracker", + "format": "time_series", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{tx_type}} || {{status}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Status of Transactions", + "type": "piechart" } ], "preload": false, diff --git a/crates/blockchain/payload.rs b/crates/blockchain/payload.rs index cdb177504..6dce1a9a5 100644 --- a/crates/blockchain/payload.rs +++ b/crates/blockchain/payload.rs @@ -23,7 +23,7 @@ use sha3::{Digest, Keccak256}; use ethrex_metrics::metrics; #[cfg(feature = "metrics")] -use ethrex_metrics::metrics_api::TRANSACTION_COUNTER; +use ethrex_metrics::metrics_api::{MetricsTxStatus, MetricsTxType, METRICS}; use crate::{ constants::{ @@ -324,11 +324,12 @@ pub fn fill_transactions(context: &mut PayloadBuildContext) -> Result<(), ChainE )?; continue; } - // Increment the transaction counter - // TODO: differentiate between total, failed and succedded + + // Increment the total transaction counter + // CHECK: do we want it here to count every processed transaction + // or we want it before the return? metrics!( - let tx_counter = TRANSACTION_COUNTER.lock().unwrap(); - tx_counter.inc(); + METRICS.inc_tx(); ); // Execute tx @@ -342,11 +343,24 @@ pub fn fill_transactions(context: &mut PayloadBuildContext) -> Result<(), ChainE .store() .ok_or(ChainError::StoreError(StoreError::MissingStore))?, )?; + + metrics!( + METRICS.inc_tx_with_status_and_type( + MetricsTxStatus::Succeeded, + MetricsTxType(head_tx.tx_type()) + ); + ); receipt } // Ignore following txs from sender Err(e) => { debug!("Failed to execute transaction: {}, {e}", tx_hash); + metrics!( + METRICS.inc_tx_with_status_and_type( + MetricsTxStatus::Failed, + MetricsTxType(head_tx.tx_type()) + ); + ); txs.pop(); continue; } From 1d45ecdcd8bbcf8a6226fd5d65a28a52868dcbdc Mon Sep 17 00:00:00 2001 From: fborello-lambda Date: Wed, 18 Dec 2024 10:48:19 -0300 Subject: [PATCH 08/13] pr_comments: remove cfg_if for metrics --- cmd/ethrex/Cargo.toml | 4 ++-- cmd/ethrex/ethrex.rs | 21 ++++++++++----------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/cmd/ethrex/Cargo.toml b/cmd/ethrex/Cargo.toml index c392c891b..90d55fcd7 100644 --- a/cmd/ethrex/Cargo.toml +++ b/cmd/ethrex/Cargo.toml @@ -34,7 +34,7 @@ redb = { workspace = true, optional = true } cfg-if = "1.0.0" ethrex-dev = { path = "../../crates/blockchain/dev", optional = true } -ethrex-metrics = { path = "../../crates/blockchain/metrics", optional = true } +ethrex-metrics = { path = "../../crates/blockchain/metrics" } [[bin]] name = "ethrex" @@ -43,7 +43,7 @@ path = "./ethrex.rs" [features] default = ["dep:ethrex-storage", "libmdbx"] dev = ["dep:ethrex-dev"] -metrics = ["dep:ethrex-metrics", "ethrex-blockchain/metrics"] +metrics = ["ethrex-blockchain/metrics"] libmdbx = ["dep:libmdbx", "ethrex-storage/libmdbx"] redb = ["dep:redb", "ethrex-storage/redb"] l2 = ["ethrex-vm/l2"] diff --git a/cmd/ethrex/ethrex.rs b/cmd/ethrex/ethrex.rs index bb8384b77..a769ac2ce 100644 --- a/cmd/ethrex/ethrex.rs +++ b/cmd/ethrex/ethrex.rs @@ -30,6 +30,8 @@ use tracing_subscriber::{filter::Directive, EnvFilter, FmtSubscriber}; mod cli; mod decode; +use ethrex_metrics; + const DEFAULT_DATADIR: &str = "ethrex"; #[tokio::main] async fn main() { @@ -208,18 +210,15 @@ async fn main() { tracker.spawn(rpc_api); - // Start the prometheus /metrics api - cfg_if::cfg_if! { - if #[cfg(feature = "metrics")] { - use ethrex_metrics; - - let metrics_port = matches - .get_one::("metrics.port") - .map_or("3000".to_string(), |v| v.clone()); + // Check if the metrics.port is present, else set it to 0 + let metrics_port = matches + .get_one::("metrics.port") + .map_or("0".to_string(), |v| v.clone()); - let metrics_api = ethrex_metrics::metrics_api::start_prometheus_metrics_api(metrics_port); - tracker.spawn(metrics_api); - } + // Start the metrics_api with the given metrics.port if it's != 0 + if metrics_port != *"0" { + let metrics_api = ethrex_metrics::metrics_api::start_prometheus_metrics_api(metrics_port); + tracker.spawn(metrics_api); } // We do not want to start the networking module if the l2 feature is enabled. From d1eb417fa5313ea8482e325990249d2f6784976d Mon Sep 17 00:00:00 2001 From: fborello-lambda Date: Wed, 18 Dec 2024 10:53:38 -0300 Subject: [PATCH 09/13] chore: lint --- cmd/ethrex/ethrex.rs | 2 -- cmd/ethrex_l2/src/commands/test.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/cmd/ethrex/ethrex.rs b/cmd/ethrex/ethrex.rs index a769ac2ce..8921c0f3a 100644 --- a/cmd/ethrex/ethrex.rs +++ b/cmd/ethrex/ethrex.rs @@ -30,8 +30,6 @@ use tracing_subscriber::{filter::Directive, EnvFilter, FmtSubscriber}; mod cli; mod decode; -use ethrex_metrics; - const DEFAULT_DATADIR: &str = "ethrex"; #[tokio::main] async fn main() { diff --git a/cmd/ethrex_l2/src/commands/test.rs b/cmd/ethrex_l2/src/commands/test.rs index 059972345..f6ce4b65f 100644 --- a/cmd/ethrex_l2/src/commands/test.rs +++ b/cmd/ethrex_l2/src/commands/test.rs @@ -1,7 +1,7 @@ use crate::config::EthrexL2Config; use bytes::Bytes; use clap::Subcommand; -use ethereum_types::{Address, H160, H256, U256}; +use ethereum_types::{Address, H256, U256}; use ethrex_blockchain::constants::TX_GAS_COST; use ethrex_l2::utils::eth_client::{eth_sender::Overrides, EthClient}; use keccak_hash::keccak; From 6b72066dcc0c58189f94675504cc953cbf353558 Mon Sep 17 00:00:00 2001 From: fborello-lambda Date: Wed, 18 Dec 2024 11:17:44 -0300 Subject: [PATCH 10/13] test: ci_test --- crates/l2/.env.example | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/l2/.env.example b/crates/l2/.env.example index df1d1693d..dfa93718b 100644 --- a/crates/l2/.env.example +++ b/crates/l2/.env.example @@ -17,14 +17,14 @@ L1_WATCHER_L2_PROPOSER_PRIVATE_KEY=0x385c546456b6a603a1cfcaa9ec9494ba4832da08dd6 ENGINE_API_RPC_URL=http://localhost:8552 ENGINE_API_JWT_PATH=./jwt.hex PROVER_SERVER_LISTEN_IP=127.0.0.1 -PROVER_SERVER_LISTEN_PORT=3000 +PROVER_SERVER_LISTEN_PORT=3900 # Not the same account as the COMMITTER_L1 Account # The proposer is in charge of blob commitments. # The prover_server is in charge of verifying the zkProofs. PROVER_SERVER_VERIFIER_ADDRESS=0xE25583099BA105D9ec0A67f5Ae86D90e50036425 PROVER_SERVER_VERIFIER_PRIVATE_KEY=0x39725efee3fb28614de3bacaffe4cc4bd8c436257e2c8bb887c4b5c4be45e76d PROVER_SERVER_DEV_MODE=true -PROVER_CLIENT_PROVER_SERVER_ENDPOINT=localhost:3000 +PROVER_CLIENT_PROVER_SERVER_ENDPOINT=localhost:3900 PROVER_CLIENT_INTERVAL_MS=5000 COMMITTER_ON_CHAIN_PROPOSER_ADDRESS=0xe9927d77c931f8648da4cc6751ef4e5e2ce74608 COMMITTER_L1_ADDRESS=0x3d1e15a1a55578f7c920884a9943b3b35d0d885b From 83d203e726747a2394edafd1e61c6ab833542784 Mon Sep 17 00:00:00 2001 From: fborello-lambda Date: Wed, 18 Dec 2024 11:52:47 -0300 Subject: [PATCH 11/13] test: ci_test change ports --- crates/l2/docker-compose-l2.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/l2/docker-compose-l2.yaml b/crates/l2/docker-compose-l2.yaml index 1f48484ec..14761acfb 100644 --- a/crates/l2/docker-compose-l2.yaml +++ b/crates/l2/docker-compose-l2.yaml @@ -32,8 +32,10 @@ services: context: ../../ dockerfile: ./crates/l2/Dockerfile ports: + # RPC - 127.0.0.1:1729:1729 - - 3000:3000 + # Proposer + - 3900:3900 environment: - ETH_RPC_URL=http://ethrex_l1:8545 - ENV_FILE=/.env From 3603da2c691b3677db315bb68076e5a81e5eeef7 Mon Sep 17 00:00:00 2001 From: fborello-lambda Date: Wed, 18 Dec 2024 16:35:52 -0300 Subject: [PATCH 12/13] fix: block_producer --- crates/blockchain/dev/block_producer.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/blockchain/dev/block_producer.rs b/crates/blockchain/dev/block_producer.rs index 4d0dffbca..7f5620586 100644 --- a/crates/blockchain/dev/block_producer.rs +++ b/crates/blockchain/dev/block_producer.rs @@ -29,9 +29,10 @@ pub async fn start_block_producer( let payload_attributes = PayloadAttributesV3 { timestamp: SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(), + prev_randao: H256::zero(), suggested_fee_recipient: coinbase_address, parent_beacon_block_root: Some(parent_beacon_block_root), - ..Default::default() + withdrawals: Some(Vec::new()), }; let fork_choice_response = match engine_client .engine_forkchoice_updated_v3(fork_choice_state, Some(payload_attributes)) @@ -78,6 +79,7 @@ pub async fn start_block_producer( let mut hasher = Sha256::new(); hasher.update(commitment); let mut hash = hasher.finalize(); + // https://eips.ethereum.org/EIPS/eip-4844 -> kzg_to_versioned_hash hash[0] = 0x01; H256::from_slice(&hash) }) From 4a6650506768bab917b6f4ddf18840c0df2bfaf5 Mon Sep 17 00:00:00 2001 From: fborello-lambda Date: Thu, 19 Dec 2024 10:29:23 -0300 Subject: [PATCH 13/13] refactor: rename files so it's easier to add new metrics --- cmd/ethrex/ethrex.rs | 2 +- crates/blockchain/metrics/api.rs | 19 ++++++++++++ ...metrics_api.rs => metrics_transactions.rs} | 29 ++++--------------- crates/blockchain/metrics/mod.rs | 11 ++++--- crates/blockchain/payload.rs | 26 +++++++---------- 5 files changed, 40 insertions(+), 47 deletions(-) create mode 100644 crates/blockchain/metrics/api.rs rename crates/blockchain/metrics/{metrics_api.rs => metrics_transactions.rs} (86%) diff --git a/cmd/ethrex/ethrex.rs b/cmd/ethrex/ethrex.rs index 8921c0f3a..ffc429f15 100644 --- a/cmd/ethrex/ethrex.rs +++ b/cmd/ethrex/ethrex.rs @@ -215,7 +215,7 @@ async fn main() { // Start the metrics_api with the given metrics.port if it's != 0 if metrics_port != *"0" { - let metrics_api = ethrex_metrics::metrics_api::start_prometheus_metrics_api(metrics_port); + let metrics_api = ethrex_metrics::api::start_prometheus_metrics_api(metrics_port); tracker.spawn(metrics_api); } diff --git a/crates/blockchain/metrics/api.rs b/crates/blockchain/metrics/api.rs new file mode 100644 index 000000000..8f8689827 --- /dev/null +++ b/crates/blockchain/metrics/api.rs @@ -0,0 +1,19 @@ +use axum::{routing::get, Router}; + +use crate::{metrics_transactions::METRICS_TX, MetricsApiError}; + +pub async fn start_prometheus_metrics_api(port: String) -> Result<(), MetricsApiError> { + let app = Router::new() + .route("/metrics", get(get_metrics)) + .route("/health", get("Service Up")); + + // Start the axum app + let listener = tokio::net::TcpListener::bind(&format!("0.0.0.0:{port}")).await?; + axum::serve(listener, app).await?; + + Ok(()) +} + +async fn get_metrics() -> String { + METRICS_TX.gather_metrics() +} diff --git a/crates/blockchain/metrics/metrics_api.rs b/crates/blockchain/metrics/metrics_transactions.rs similarity index 86% rename from crates/blockchain/metrics/metrics_api.rs rename to crates/blockchain/metrics/metrics_transactions.rs index cd28c3149..1d2f97d60 100644 --- a/crates/blockchain/metrics/metrics_api.rs +++ b/crates/blockchain/metrics/metrics_transactions.rs @@ -1,24 +1,23 @@ -use axum::{routing::get, Router}; use ethrex_core::types::TxType; use prometheus::{Encoder, IntCounter, IntCounterVec, Opts, Registry, TextEncoder}; use std::sync::{Arc, LazyLock, Mutex}; -use crate::MetricsApiError; +pub static METRICS_TX: LazyLock = LazyLock::new(MetricsTx::default); -pub struct Metrics { +pub struct MetricsTx { pub transactions_tracker: Arc>, pub transactions_total: Arc>, } -impl Default for Metrics { +impl Default for MetricsTx { fn default() -> Self { Self::new() } } -impl Metrics { +impl MetricsTx { pub fn new() -> Self { - Metrics { + MetricsTx { transactions_tracker: Arc::new(Mutex::new( IntCounterVec::new( Opts::new( @@ -145,21 +144,3 @@ impl MetricsTxType { } } } - -pub static METRICS: LazyLock = LazyLock::new(Metrics::default); - -pub async fn start_prometheus_metrics_api(port: String) -> Result<(), MetricsApiError> { - let app = Router::new() - .route("/metrics", get(get_metrics)) - .route("/health", get("Service Up")); - - // Start the axum app - let listener = tokio::net::TcpListener::bind(&format!("0.0.0.0:{port}")).await?; - axum::serve(listener, app).await?; - - Ok(()) -} - -async fn get_metrics() -> String { - METRICS.gather_metrics() -} diff --git a/crates/blockchain/metrics/mod.rs b/crates/blockchain/metrics/mod.rs index ed85f3f70..ca78d93c7 100644 --- a/crates/blockchain/metrics/mod.rs +++ b/crates/blockchain/metrics/mod.rs @@ -1,5 +1,7 @@ #[cfg(feature = "api")] -pub mod metrics_api; +pub mod api; +#[cfg(feature = "api")] +pub mod metrics_transactions; /// A macro to conditionally enable metrics-related code. /// @@ -29,12 +31,9 @@ pub mod metrics_api; /// ```sh /// use ethrex_metrics::metrics; // #[cfg(feature = "metrics")] -// use ethrex_metrics::metrics_api::TRANSACTION_COUNTER; +// use ethrex_metrics::metrics_transactions::{METRICS_TX}; /// -/// metrics!( -/// let tx_counter = TRANSACTION_COUNTER.lock().unwrap(); -/// tx_counter.inc(); -/// ); +/// metrics!(METRICS_TX.inc()); /// ``` /// /// If you build without the `metrics` feature, the code inside `metrics!` will not be compiled, nor will the Prometheus crate. diff --git a/crates/blockchain/payload.rs b/crates/blockchain/payload.rs index 2a0c00ec6..49ef4f88c 100644 --- a/crates/blockchain/payload.rs +++ b/crates/blockchain/payload.rs @@ -23,7 +23,7 @@ use sha3::{Digest, Keccak256}; use ethrex_metrics::metrics; #[cfg(feature = "metrics")] -use ethrex_metrics::metrics_api::{MetricsTxStatus, MetricsTxType, METRICS}; +use ethrex_metrics::metrics_transactions::{MetricsTxStatus, MetricsTxType, METRICS_TX}; use crate::{ constants::{ @@ -332,9 +332,7 @@ pub fn fill_transactions(context: &mut PayloadBuildContext) -> Result<(), ChainE // Increment the total transaction counter // CHECK: do we want it here to count every processed transaction // or we want it before the return? - metrics!( - METRICS.inc_tx(); - ); + metrics!(METRICS_TX.inc_tx()); // Execute tx let receipt = match apply_transaction(&head_tx, context) { @@ -348,23 +346,19 @@ pub fn fill_transactions(context: &mut PayloadBuildContext) -> Result<(), ChainE .ok_or(ChainError::StoreError(StoreError::MissingStore))?, )?; - metrics!( - METRICS.inc_tx_with_status_and_type( - MetricsTxStatus::Succeeded, - MetricsTxType(head_tx.tx_type()) - ); - ); + metrics!(METRICS_TX.inc_tx_with_status_and_type( + MetricsTxStatus::Succeeded, + MetricsTxType(head_tx.tx_type()) + )); receipt } // Ignore following txs from sender Err(e) => { debug!("Failed to execute transaction: {}, {e}", tx_hash); - metrics!( - METRICS.inc_tx_with_status_and_type( - MetricsTxStatus::Failed, - MetricsTxType(head_tx.tx_type()) - ); - ); + metrics!(METRICS_TX.inc_tx_with_status_and_type( + MetricsTxStatus::Failed, + MetricsTxType(head_tx.tx_type()) + )); txs.pop(); continue; }