feat: round-5 abstractions — CircuitBreakerHealthCheck, TypedKeyRegistry, MetricsHttpHandler
This commit is contained in:
1 parent
0a978c063e
commit
510aadc066
10 files changed
+263
-1
No files matched your search
@@ -21,6 +21,8 @@ client = ["dep:reqwest", "dep:tokio"]
|
||||
server-hyper = ["dep:hyper", "dep:tokio"]
|
||||
# Server backed by axum.
|
||||
server-axum = ["dep:axum", "dep:hyper", "dep:tokio"]
|
||||
# Metrics HTTP endpoint serving Prometheus text format from a MetricsCollector.
|
||||
metrics-http = ["dep:axum", "dep:tower", "dep:tokio", "dep:mytheclipse"]
|
||||
|
||||
[dependencies]
|
||||
tracing = "0.1"
|
||||
@@ -29,8 +31,10 @@ tokio = { version = "1.53", features = ["sync", "time", "rt", "macros"], optiona
|
||||
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"], optional = true }
|
||||
hyper = { version = "1", features = ["full"], optional = true }
|
||||
axum = { version = "0.8", optional = true }
|
||||
tower = { version = "0.5", optional = true, default-features = false, features = ["util"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
mytheclipse = { version = "1.5", path = "../mytheclipse", optional = true, default-features = false, features = ["observability"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.53", features = ["full"] }
|
||||
@@ -17,3 +17,9 @@ pub use client::HttpClient;
|
||||
|
||||
#[cfg(feature = "server-axum")]
|
||||
pub mod server;
|
||||
|
||||
#[cfg(feature = "metrics-http")]
|
||||
pub mod metrics_http;
|
||||
|
||||
#[cfg(feature = "metrics-http")]
|
||||
pub use metrics_http::metrics_routes;
|
||||
@@ -0,0 +1,56 @@
|
||||
//! Prometheus metrics HTTP endpoint (feature `metrics-http`).
|
||||
//!
|
||||
//! [`metrics_routes`] returns an [`axum::Router`] that serves the
|
||||
//! [`MetricsCollector`]'s Prometheus text exposition format at `/metrics`.
|
||||
|
||||
use axum::routing::get;
|
||||
use axum::Router;
|
||||
use std::sync::Arc;
|
||||
|
||||
use mytheclipse::MetricsCollector;
|
||||
|
||||
/// Builds a small axum router exposing `/metrics` (Prometheus text) and
|
||||
/// `/` (a one-line description).
|
||||
pub fn metrics_routes(collector: MetricsCollector) -> Router {
|
||||
let collector = Arc::new(collector);
|
||||
Router::new()
|
||||
.route("/", get(|| async { "mytheclipse metrics" }))
|
||||
.route("/metrics", get(metrics_handler))
|
||||
.with_state(collector)
|
||||
}
|
||||
|
||||
/// Axum handler serving the Prometheus text format.
|
||||
async fn metrics_handler(
|
||||
axum::extract::State(collector): axum::extract::State<Arc<MetricsCollector>>,
|
||||
) -> axum::response::Response {
|
||||
let body = collector.export_prometheus();
|
||||
axum::response::Response::builder()
|
||||
.status(200)
|
||||
.header("content-type", "text/plain; version=0.0.4")
|
||||
.body(axum::body::Body::from(body))
|
||||
.unwrap_or_else(|_| {
|
||||
axum::response::Response::new(axum::body::Body::from(
|
||||
"internal error",
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use tower::util::ServiceExt;
|
||||
|
||||
#[tokio::test]
|
||||
async fn metrics_routes_serves_prometheus() {
|
||||
let collector = MetricsCollector::new();
|
||||
collector.inc_counter("test_reqs", 42);
|
||||
let app = metrics_routes(collector);
|
||||
|
||||
let request = axum::extract::Request::get("/metrics")
|
||||
.body(axum::body::Body::empty())
|
||||
.unwrap();
|
||||
let response = app.oneshot(request).await.unwrap();
|
||||
assert_eq!(response.status(), 200);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user