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

Add prometheus support #314

Merged
merged 2 commits into from
Dec 26, 2024
Merged
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
356 changes: 346 additions & 10 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ resolver = "2"
members = [
"florestad",
"fuzz",
"metrics",
"crates/floresta",
"crates/floresta-chain",
"crates/floresta-cli",
Expand Down
10 changes: 9 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
FROM debian:11.6-slim@sha256:171530d298096f0697da36b3324182e872db77c66452b85783ea893680cc1b62 AS builder

ARG BUILD_FEATURES=""

RUN apt-get update && apt-get install -y \
build-essential \
cmake \
Expand All @@ -18,8 +20,13 @@ COPY Cargo.* ./
COPY florestad/ florestad/
COPY crates/ crates/
COPY fuzz/ fuzz/
COPY metrics/ metrics/
RUN --mount=type=cache,target=/usr/local/cargo/registry \
cargo build --release
if [ -n "$BUILD_FEATURES" ]; then \
cargo build --release --features "$BUILD_FEATURES"; \
else \
cargo build --release; \
fi

FROM debian:11.6-slim@sha256:171530d298096f0697da36b3324182e872db77c66452b85783ea893680cc1b62

Expand All @@ -29,5 +36,6 @@ RUN chmod +x /usr/local/bin/florestad

EXPOSE 50001
EXPOSE 8332
EXPOSE 3333

CMD [ "florestad" ]
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,24 @@ cargo +nightly fuzz run local_address_str

You can replace `local_address_str` with the name of any other target you want to run.

## Metrics

This project uses [`Prometheus`](https://prometheus.io/) as a monitoring system. To enable it you must build the project with the `metrics` feature enabled:

```sh
cargo build --release --features metrics
```

The easiest way to visualize those metrics is by using some observability graphic tool like [Grafana](https://grafana.com/). To make it easier, you can also straight away use the `docker-compose.yml` file to spin up an infrastructure that will run the project with Prometheus and Grafana.

To run it, first make sure you have [Docker Compose](https://docs.docker.com/compose/) installed and then:

```sh
docker-compose up -d --build
```

Grafana should now be available to you at http://localhost:3000. To log in, please check the credentials defined in the `docker-compose.yml` file.

## Contributing
Contributions are welcome, feel free to open an issue or a pull request.

Expand Down
2 changes: 2 additions & 0 deletions crates/floresta-chain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ hashbrown = { version = "0.14.0", optional = true }
secp256k1 = { version = "*", features = ["alloc"], optional = true }
floresta-common = { path = "../floresta-common", default-features = false, features = ["std"] }
bitcoinconsensus = { version = "0.106.0", optional = true, default-features = false }
metrics = { path = "../../metrics", optional = true }

[dev-dependencies]
criterion = "0.5.1"
Expand All @@ -43,6 +44,7 @@ hex = "0.4.3"
[features]
default = []
bitcoinconsensus = ["bitcoin/bitcoinconsensus", "dep:bitcoinconsensus"]
metrics = ["dep:metrics"]

[[bench]]
name = "chain_state_bench"
Expand Down
5 changes: 5 additions & 0 deletions crates/floresta-chain/src/pruned_utreexo/chain_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ use floresta_common::Channel;
use log::info;
use log::trace;
use log::warn;
#[cfg(feature = "metrics")]
use metrics;
use rustreexo::accumulator::node_hash::NodeHash;
use rustreexo::accumulator::proof::Proof;
use rustreexo::accumulator::stump::Stump;
Expand Down Expand Up @@ -1102,6 +1104,9 @@ impl<PersistedState: ChainStore> UpdatableChainstate for ChainState<PersistedSta
block.txdata.len()
);

#[cfg(feature = "metrics")]
metrics::get_metrics().block_height.set(height.into());

if !self.is_in_idb() || height % 10_000 == 0 {
self.flush()?;
}
Expand Down
33 changes: 33 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
services:
floresta:
container_name: floresta
build:
context: .
args:
BUILD_FEATURES: "metrics"
ports:
- 50001:50001
- 8332:8332
- 3333:3333
restart: unless-stopped
prometheus:
image: prom/prometheus
container_name: prometheus
command:
- "--config.file=/etc/prometheus/prometheus.yml"
ports:
- 9090:9090
restart: unless-stopped
volumes:
- ./metrics/prometheus:/etc/prometheus
grafana:
image: grafana/grafana
container_name: grafana
ports:
- 3000:3000
restart: unless-stopped
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=grafana
volumes:
- ./metrics/grafana:/etc/grafana/provisioning/datasources
2 changes: 2 additions & 0 deletions florestad/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ floresta-electrum = { path = "../crates/floresta-electrum" }
floresta-watch-only = { path = "../crates/floresta-watch-only" }
floresta-wire = { path = "../crates/floresta-wire" }
floresta-compact-filters = { path = "../crates/floresta-compact-filters", optional = true }
metrics = { path = "../metrics", optional = true }

anyhow = "1.0.40"
jsonrpc-http-server = { version = "18.0.0", optional = true }
Expand Down Expand Up @@ -65,6 +66,7 @@ json-rpc = [
"compact-filters"
]
default = ["experimental-p2p", "json-rpc"]
metrics = ["dep:metrics"]

[build-dependencies]
toml = "0.5.10"
31 changes: 31 additions & 0 deletions florestad/src/florestad.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use std::fmt::Arguments;
use std::fs::File;
use std::io;
use std::io::BufReader;
#[cfg(feature = "metrics")]
use std::net::Ipv4Addr;
use std::net::SocketAddr;
use std::path::PathBuf;
use std::process::exit;
Expand Down Expand Up @@ -41,9 +43,15 @@ use log::error;
use log::info;
use log::warn;
use log::Record;
#[cfg(feature = "metrics")]
use metrics;
use tokio::net::TcpListener;
use tokio::sync::RwLock;
use tokio::task;
#[cfg(feature = "metrics")]
use tokio::time::Duration;
#[cfg(feature = "metrics")]
use tokio::time::{self};
use tokio_rustls::rustls::internal::pemfile::certs;
use tokio_rustls::rustls::internal::pemfile::pkcs8_private_keys;
use tokio_rustls::rustls::NoClientAuth;
Expand Down Expand Up @@ -525,6 +533,29 @@ impl Florestad {
*recv = Some(receiver);

task::spawn(chain_provider.run(kill_signal, sender));

// Metrics
#[cfg(feature = "metrics")]
{
let metrics_server_address =
SocketAddr::new(std::net::IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 3333);
task::spawn(metrics::metrics_server(metrics_server_address));
info!(
"Started metrics server on: {}",
metrics_server_address.to_string()
);

// Periodically update memory usage
tokio::spawn(async {
let interval = Duration::from_secs(5);
let mut ticker = time::interval(interval);

loop {
ticker.tick().await;
metrics::get_metrics().update_memory_usage();
}
});
}
}

fn setup_logger(
Expand Down
13 changes: 13 additions & 0 deletions metrics/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "metrics"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
sysinfo = "0.33"
axum = "0.7"
prometheus-client = "0.22.3"
tokio = { version = "1", features = ["full"] }

9 changes: 9 additions & 0 deletions metrics/grafana/datasource.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: 1

datasources:
- name: Prometheus
type: prometheus
url: http://prometheus:9090
isDefault: true
access: proxy
editable: true
14 changes: 14 additions & 0 deletions metrics/prometheus/prometheus.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
global:
scrape_interval: 15s
scrape_timeout: 10s
evaluation_interval: 15s
scrape_configs:
- job_name: prometheus
honor_timestamps: true
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /
scheme: http
static_configs:
- targets:
- floresta:3333
75 changes: 75 additions & 0 deletions metrics/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use std::net::SocketAddr;
use std::sync::atomic::AtomicU64;
use std::sync::OnceLock;

use axum::routing::get;
use axum::Router;
use prometheus_client::encoding::text::encode;
use prometheus_client::metrics::gauge::Gauge;
use prometheus_client::registry::Registry;
use sysinfo::System;

pub struct AppMetrics {
registry: Registry,
pub memory_usage: Gauge<f64, AtomicU64>,
pub block_height: Gauge,
}

impl AppMetrics {
pub fn new() -> Self {
let mut registry = <Registry>::default();
let memory_usage = Gauge::<f64, AtomicU64>::default();
let block_height = Gauge::default();

registry.register("block_height", "Current block height", block_height.clone());
registry.register(
"memory_usage_gigabytes",
"System memory usage in GB",
memory_usage.clone(),
);

Self {
registry,
block_height,
memory_usage,
}
}

/// Gets how much memory is being used by the system in which Floresta is
/// running on, not how much memory Floresta itself it's using.
pub fn update_memory_usage(&self) {
let mut system = System::new_all();
system.refresh_memory();

// get used memory in gigabytes / KB / MB / GB
let used_memory = system.used_memory() as f64 / 1024. / 1024. / 1024.;
self.memory_usage.set(used_memory);
}
}

impl Default for AppMetrics {
fn default() -> Self {
Self::new()
}
}

// Singleton to share metrics across crates
static METRICS: OnceLock<AppMetrics> = OnceLock::new();
pub fn get_metrics() -> &'static AppMetrics {
METRICS.get_or_init(AppMetrics::new)
}

async fn metrics_handler() -> String {
let mut buffer = String::new();
encode(&mut buffer, &get_metrics().registry).unwrap();

buffer
}

pub async fn metrics_server(metrics_server_address: SocketAddr) {
let app = Router::new().route("/", get(metrics_handler));
let listener = tokio::net::TcpListener::bind(metrics_server_address)
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}
Loading