Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): add metrics/observability with prometheus #1518

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions cmd/ethrex/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }

[[bin]]
name = "ethrex"
Expand All @@ -42,6 +43,7 @@ path = "./ethrex.rs"
[features]
default = ["dep:ethrex-storage", "libmdbx"]
dev = ["dep:ethrex-dev"]
metrics = ["ethrex-blockchain/metrics"]
libmdbx = ["dep:libmdbx", "ethrex-storage/libmdbx"]
redb = ["dep:redb", "ethrex-storage/redb"]
l2 = ["dep:ethrex-l2", "ethrex-vm/l2"]
Expand Down
6 changes: 6 additions & 0 deletions cmd/ethrex/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
11 changes: 11 additions & 0 deletions cmd/ethrex/ethrex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,17 @@ async fn main() {

tracker.spawn(rpc_api);

// Check if the metrics.port is present, else set it to 0
let metrics_port = matches
.get_one::<String>("metrics.port")
.map_or("0".to_string(), |v| v.clone());

// Start the metrics_api with the given metrics.port if it's != 0
if metrics_port != *"0" {
let metrics_api = ethrex_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.
cfg_if::cfg_if! {
if #[cfg(feature = "l2")] {
Expand Down
14 changes: 9 additions & 5 deletions cmd/ethrex_l2/src/commands/test.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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::<H256>().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;
Expand Down
11 changes: 5 additions & 6 deletions crates/blockchain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ ethrex-vm = { path = "../vm", default-features = false }

k256 = { version = "0.13.3", features = ["ecdh"] }

ethrex-metrics = { path = "./metrics", default-features = false }

[dev-dependencies]
serde_json.workspace = true
hex = "0.4.3"
Expand All @@ -28,10 +30,7 @@ path = "./blockchain.rs"

[features]
default = ["c-kzg"]
libmdbx = [
"ethrex-core/libmdbx",
"ethrex-storage/default",
"ethrex-vm/libmdbx",
]
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 = ["ethrex-metrics/api"]
2 changes: 1 addition & 1 deletion crates/blockchain/dev/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion crates/blockchain/dev/docker-compose-dev.yaml
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
28 changes: 28 additions & 0 deletions crates/blockchain/metrics/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[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

ethrex-core = { path = "../../common", default-features = false }

prometheus = "0.13.4"

# TODO: remove?
axum = "0.7.9"


[lib]
path = "./mod.rs"

[features]
default = ["api"]
api = []
37 changes: 37 additions & 0 deletions crates/blockchain/metrics/README.md
Original file line number Diff line number Diff line change
@@ -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` &rarr; metrics API (used by prometheus)
- PORT `3802` &rarr; Grafana
- usr: `admin`
- pwd: `admin`
- PORT `9092` &rarr; 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` &rarr; metrics API (used by prometheus)
- PORT `3801` &rarr; Grafana
- usr: `admin`
- pwd: `admin`
- PORT `9091` &rarr; Prometheus
19 changes: 19 additions & 0 deletions crates/blockchain/metrics/api.rs
Original file line number Diff line number Diff line change
@@ -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()
}
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
services:
prometheus:
volumes:
- ../metrics/provisioning/prometheus/prometheus_l2.yaml:/etc/prometheus/prometheus.yaml
ports:
- "9092:9090"
grafana:
ports:
- "3802:3000"
15 changes: 15 additions & 0 deletions crates/blockchain/metrics/docker-compose-metrics.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +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: 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: defined in the .overrides file
depends_on:
- prometheus
Loading
Loading