Skip to content

Commit

Permalink
Merge pull request #13 from sd2k/add-with_default_registry-fn
Browse files Browse the repository at this point in the history
Add 'PrometheusMetrics::with_default_registry' associated function
  • Loading branch information
sd2k authored Jul 10, 2021
2 parents e6e63c4 + ae25cdf commit 218fd14
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Use `Duration::as_secs_f64` instead of manually calculating nanoseconds when calculating request durations. This bumps the minimum supported Rust version to 1.38.0, which is unlikely to be a problem in practice, since Rocket still requires a nightly version of Rust.
- Impl `From<PrometheusMetrics> for Vec<Route>` instead of `Into<Vec<Route>> for PrometheusMetrics`, since the former gives us the latter for free.
- `PrometheusMetrics::registry` is now a `const fn`.
- Add `PrometheusMetrics::with_default_registry` associated function, which creates a new `PrometheusMetrics` using the default global `prometheus::Registry` and will therefore expose metrics created by the various macros in the `prometheus` crate.

## [0.7.0] - 2020-06-19
### Changed
Expand Down
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,14 @@ impl PrometheusMetrics {
Self::with_registry(Registry::new())
}

/// Create a new `PrometheusMetrics` using the default Prometheus `Registry`.
///
/// This will cause the fairing to include metrics created by the various
/// `prometheus` macros, e.g. `register_int_counter`.
pub fn with_default_registry() -> Self {
Self::with_registry(prometheus::default_registry().clone())
}

/// Create a new `PrometheusMetrics` with a custom `Registry`.
pub fn with_registry(registry: Registry) -> Self {
let namespace = env::var(NAMESPACE_ENV_VAR).unwrap_or_else(|_| "rocket".into());
Expand Down
121 changes: 121 additions & 0 deletions tests/default_registry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#![feature(proc_macro_hygiene, decl_macro)]

#[macro_use]
extern crate rocket;

use once_cell::sync::Lazy;
use prometheus::{register_int_counter_vec, IntCounterVec};
use rocket::{http::ContentType, local::Client};
use rocket_prometheus::PrometheusMetrics;
use serde_json::json;

static NAME_COUNTER: Lazy<IntCounterVec> = Lazy::new(|| {
register_int_counter_vec!("name_counter", "Count of names", &["name"])
.expect("Could not create name_counter")
});

mod routes {
use rocket::http::RawStr;
use rocket_contrib::json::Json;
use serde::Deserialize;

use super::NAME_COUNTER;

#[get("/hello/<name>?<caps>")]
pub fn hello(name: &RawStr, caps: Option<bool>) -> String {
NAME_COUNTER.with_label_values(&[name]).inc();
let name = caps
.unwrap_or_default()
.then(|| name.to_uppercase())
.unwrap_or_else(|| name.to_string());
format!("Hello, {}!", name)
}

#[derive(Deserialize)]
pub struct Person {
age: u8,
}

#[post("/hello/<name>?<caps>", format = "json", data = "<person>")]
pub fn hello_post(name: String, person: Json<Person>, caps: Option<bool>) -> String {
let name = caps
.unwrap_or_default()
.then(|| name.to_uppercase())
.unwrap_or_else(|| name.to_string());
format!("Hello, {} year old named {}!", person.age, name)
}
}

#[test]
fn main() {
let prometheus = PrometheusMetrics::new();
prometheus
.registry()
.register(Box::new(NAME_COUNTER.clone()))
.unwrap();
let rocket = rocket::ignite()
.attach(prometheus.clone())
.mount("/", routes![routes::hello, routes::hello_post])
.mount("/metrics", prometheus);
let client = Client::new(rocket).expect("valid rocket instance");
client.get("/hello/foo").dispatch();
client.get("/hello/foo").dispatch();
client.get("/hello/bar").dispatch();
client
.post("/hello/bar")
.header(ContentType::JSON)
.body(serde_json::to_string(&json!({"age": 50})).unwrap())
.dispatch();
let mut metrics = client.get("/metrics").dispatch();
let response = metrics.body_string().unwrap();
assert_eq!(
response
.lines()
.enumerate()
.filter_map(|(i, line)|
// Skip out the 'sum' lines since they depend on request duration.
if i != 18 && i != 32 {
Some(line)
} else {
None
})
.collect::<Vec<&str>>()
.join("\n"),
r#"# HELP name_counter Count of names
# TYPE name_counter counter
name_counter{name="bar"} 1
name_counter{name="foo"} 2
# HELP rocket_http_requests_duration_seconds HTTP request duration in seconds for all requests
# TYPE rocket_http_requests_duration_seconds histogram
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="GET",status="200",le="0.005"} 3
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="GET",status="200",le="0.01"} 3
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="GET",status="200",le="0.025"} 3
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="GET",status="200",le="0.05"} 3
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="GET",status="200",le="0.1"} 3
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="GET",status="200",le="0.25"} 3
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="GET",status="200",le="0.5"} 3
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="GET",status="200",le="1"} 3
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="GET",status="200",le="2.5"} 3
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="GET",status="200",le="5"} 3
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="GET",status="200",le="10"} 3
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="GET",status="200",le="+Inf"} 3
rocket_http_requests_duration_seconds_count{endpoint="/hello/<name>?<caps>",method="GET",status="200"} 3
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="POST",status="200",le="0.005"} 1
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="POST",status="200",le="0.01"} 1
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="POST",status="200",le="0.025"} 1
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="POST",status="200",le="0.05"} 1
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="POST",status="200",le="0.1"} 1
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="POST",status="200",le="0.25"} 1
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="POST",status="200",le="0.5"} 1
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="POST",status="200",le="1"} 1
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="POST",status="200",le="2.5"} 1
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="POST",status="200",le="5"} 1
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="POST",status="200",le="10"} 1
rocket_http_requests_duration_seconds_bucket{endpoint="/hello/<name>?<caps>",method="POST",status="200",le="+Inf"} 1
rocket_http_requests_duration_seconds_count{endpoint="/hello/<name>?<caps>",method="POST",status="200"} 1
# HELP rocket_http_requests_total Total number of HTTP requests
# TYPE rocket_http_requests_total counter
rocket_http_requests_total{endpoint="/hello/<name>?<caps>",method="GET",status="200"} 3
rocket_http_requests_total{endpoint="/hello/<name>?<caps>",method="POST",status="200"} 1"#
);
}

0 comments on commit 218fd14

Please sign in to comment.