my-homelab-configs/infra/blockchain-devnet/metrics/exporter.py

98 lines
3.0 KiB
Python

#!/usr/bin/env python3
import json
import os
import time
import urllib.error
import urllib.request
from http.server import BaseHTTPRequestHandler, HTTPServer
RPC_URL = os.environ.get("BLOCKCHAIN_METRICS_RPC_URL", "http://anvil:8545")
LISTEN_ADDR = os.environ.get("BLOCKCHAIN_METRICS_ADDR", "0.0.0.0")
LISTEN_PORT = int(os.environ.get("BLOCKCHAIN_METRICS_PORT", "9109"))
def rpc(method):
started = time.monotonic()
body = json.dumps(
{"jsonrpc": "2.0", "method": method, "params": [], "id": 1}
).encode("utf-8")
request = urllib.request.Request(
RPC_URL,
data=body,
headers={"content-type": "application/json"},
method="POST",
)
with urllib.request.urlopen(request, timeout=3) as response:
payload = json.loads(response.read().decode("utf-8"))
if "error" in payload:
raise RuntimeError(payload["error"])
return payload.get("result"), time.monotonic() - started
def hex_to_int(value):
if isinstance(value, str) and value.startswith("0x"):
return int(value, 16)
return int(value)
def collect_metrics():
metrics = []
latencies = {}
values = {}
up = 1
for method in ["eth_chainId", "eth_blockNumber", "net_peerCount", "eth_gasPrice"]:
try:
result, latency = rpc(method)
latencies[method] = latency
values[method] = hex_to_int(result)
except (OSError, RuntimeError, urllib.error.URLError, ValueError):
up = 0
latencies[method] = 0
metrics.append("# HELP homelab_blockchain_rpc_up Blockchain RPC health.")
metrics.append("# TYPE homelab_blockchain_rpc_up gauge")
metrics.append(f"homelab_blockchain_rpc_up {up}")
gauge_map = {
"eth_chainId": "homelab_blockchain_chain_id",
"eth_blockNumber": "homelab_blockchain_latest_block",
"net_peerCount": "homelab_blockchain_peer_count",
"eth_gasPrice": "homelab_blockchain_gas_price_wei",
}
for method, metric_name in gauge_map.items():
if method in values:
metrics.append(f"# TYPE {metric_name} gauge")
metrics.append(f"{metric_name} {values[method]}")
metrics.append("# TYPE homelab_blockchain_rpc_latency_seconds gauge")
for method, latency in latencies.items():
metrics.append(
f'homelab_blockchain_rpc_latency_seconds{{method="{method}"}} {latency:.6f}'
)
return "\n".join(metrics) + "\n"
class MetricsHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path not in ["/metrics", "/"]:
self.send_response(404)
self.end_headers()
return
body = collect_metrics().encode("utf-8")
self.send_response(200)
self.send_header("content-type", "text/plain; version=0.0.4")
self.send_header("content-length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def log_message(self, fmt, *args):
return
if __name__ == "__main__":
server = HTTPServer((LISTEN_ADDR, LISTEN_PORT), MetricsHandler)
server.serve_forever()