Skip to content

Commit 2a8e638

Browse files
committed
Add Prometheus metrics
1 parent a699d9e commit 2a8e638

File tree

10 files changed

+158
-6
lines changed

10 files changed

+158
-6
lines changed

.cargo/config.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[env]
2+
AXUM_HTTP_REQUESTS_TOTAL = "notifico_http_requests_total"
3+
AXUM_HTTP_REQUESTS_DURATION_SECONDS = "notifico_http_requests_duration_seconds"
4+
AXUM_HTTP_REQUESTS_PENDING = "notifico_http_requests_pending"
5+
AXUM_HTTP_RESPONSE_BODY_SIZE = "notifico_http_response_body_size"

Cargo.lock

Lines changed: 110 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

notifico-app/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ anyhow = "1.0.95"
1616
async-trait = "0.1.84"
1717
axum = { workspace = true }
1818
axum-extra = { workspace = true }
19+
axum-prometheus = "0.8.0"
1920
backoff = { version = "0.4.0", features = ["tokio"] }
2021
chrono = "0.4.39"
2122
clap = { workspace = true }

notifico-app/src/controllers/api_key.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ pub struct ApiKeyController {
3636
impl ApiKeyController {
3737
pub fn new(db: DatabaseConnection) -> Self {
3838
let authorization_cache_capacity = 100;
39-
gauge!("ingest_api_key_cache_capacity").set(authorization_cache_capacity as f64);
39+
gauge!("notifico_ingest_api_key_cache_capacity").set(authorization_cache_capacity as f64);
4040

41-
let authorization_cache_gauge = gauge!("ingest_api_key_cache_total");
41+
let authorization_cache_gauge = gauge!("notifico_ingest_api_key_cache_total");
4242
let authorization_cache_gauge_for_fut = authorization_cache_gauge.clone();
4343

4444
let authorization_cache = Cache::builder()
@@ -54,9 +54,9 @@ impl ApiKeyController {
5454
db,
5555
authorization_cache,
5656
authorization_cache_gauge,
57-
authorization_cache_hit: counter!("ingest_api_key_cache_hit"),
58-
authorization_cache_miss: counter!("ingest_api_key_cache_miss"),
59-
authorization_invalid_key: counter!("ingest_api_key_invalid"),
57+
authorization_cache_hit: counter!("notifico_ingest_api_key_cache_hit"),
58+
authorization_cache_miss: counter!("notifico_ingest_api_key_cache_miss"),
59+
authorization_invalid_key: counter!("notifico_ingest_api_key_invalid"),
6060
}
6161
}
6262
}

notifico-app/src/http/ingest.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use axum::{Extension, Json};
55
use axum_extra::headers::authorization::Bearer;
66
use axum_extra::headers::Authorization;
77
use axum_extra::TypedHeader;
8+
use axum_prometheus::PrometheusMetricLayer;
89
use notifico_core::pipeline::context::EventContext;
910
use notifico_core::pipeline::event::{ProcessEventRequest, RecipientSelector};
1011
use notifico_core::queue::SenderChannel;
@@ -40,6 +41,7 @@ pub async fn start(serviceapi_bind: SocketAddr, ext: HttpIngestExtensions) {
4041
let app = OpenApiRouter::with_openapi(ApiDoc::openapi())
4142
.routes(routes!(trigger))
4243
.routes(routes!(trigger_webhook))
44+
.layer(PrometheusMetricLayer::new())
4345
.layer(Extension(ext));
4446

4547
let (mut app, api) = app.split_for_parts();

notifico-app/src/http/metrics.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use axum::routing::get;
2+
use axum::Router;
3+
use axum_prometheus::metrics_exporter_prometheus::PrometheusHandle;
4+
use axum_prometheus::PrometheusMetricLayer;
5+
use std::net::SocketAddr;
6+
use tokio::net::TcpListener;
7+
8+
pub async fn start(bind: SocketAddr, handle: PrometheusHandle) {
9+
// Bind everything now to catch any errors before spinning up the coroutines
10+
let listener = TcpListener::bind(bind).await.unwrap();
11+
12+
// API
13+
let app = Router::new()
14+
.route("/metrics", get(|| async move { handle.render() }))
15+
.layer(PrometheusMetricLayer::new());
16+
17+
tokio::spawn(async { axum::serve(listener, app).await.unwrap() });
18+
}

notifico-app/src/http/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod auth;
22
pub mod ingest;
3+
pub mod metrics;
34
pub mod public;
45
pub mod ui;

notifico-app/src/http/public.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use axum::{Extension, Json};
77
use axum_extra::headers::authorization::Bearer;
88
use axum_extra::headers::Authorization;
99
use axum_extra::TypedHeader;
10+
use axum_prometheus::PrometheusMetricLayer;
1011
use jsonwebtoken::{DecodingKey, Validation};
1112
use notifico_core::http::SecretKey;
1213
use serde::Deserialize;
@@ -48,6 +49,7 @@ pub(crate) async fn start(bind: SocketAddr, ext: HttpPublicExtensions) {
4849
let app = OpenApiRouter::with_openapi(openapi)
4950
.routes(routes!(list_unsubscribe))
5051
.routes(routes!(subscription_parameters))
52+
.layer(PrometheusMetricLayer::new())
5153
.layer(Extension(ext.secret_key.clone()))
5254
.layer(Extension(ext.subscription_controller.clone()));
5355

notifico-app/src/http/ui/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use axum::http::header::CONTENT_TYPE;
1212
use axum::http::{StatusCode, Uri};
1313
use axum::response::{Html, IntoResponse, Response};
1414
use axum::Router;
15+
use axum_prometheus::PrometheusMetricLayer;
1516
use notifico_core::credentials::env::EnvCredentialStorage;
1617
use notifico_core::http::SecretKey;
1718
use notifico_core::transport::TransportRegistry;
@@ -44,7 +45,9 @@ pub(crate) async fn start(bind: SocketAddr, ext: HttpUiExtensions) {
4445
let service_listener = TcpListener::bind(bind).await.unwrap();
4546

4647
// Service API
47-
let app = Router::new().nest("/api", api::get_router(ext.clone()));
48+
let app = Router::new()
49+
.nest("/api", api::get_router(ext.clone()))
50+
.layer(PrometheusMetricLayer::new());
4851
let app = app.fallback(static_handler);
4952

5053
tokio::spawn(async { axum::serve(service_listener, app).await.unwrap() });

notifico-app/src/main.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crate::http::ingest::HttpIngestExtensions;
1919
use crate::http::public::HttpPublicExtensions;
2020
use crate::http::ui::HttpUiExtensions;
2121
use crate::plugin::SubscriptionPlugin;
22+
use axum_prometheus::{Handle, MakeDefaultHandle};
2223
use clap::{Parser, Subcommand};
2324
use migration::{Migrator, MigratorTrait};
2425
use notifico_attachment::AttachmentPlugin;
@@ -70,6 +71,9 @@ struct Args {
7071
#[clap(long, env = "NOTIFICO_PUBLIC_BIND", default_value = "[::]:8002")]
7172
public: SocketAddr,
7273

74+
#[clap(long, env = "NOTIFICO_METRICS_BIND")]
75+
metrics: Option<SocketAddr>,
76+
7377
#[command(subcommand)]
7478
command: Commands,
7579
}
@@ -94,6 +98,8 @@ async fn main() {
9498
.with(EnvFilter::from_default_env())
9599
.init();
96100

101+
let prometheus_handle = Handle::make_default_handle(Handle::default()); // Registers Prometheus as default metrics recorder
102+
97103
debug!("Config: {:#?}", args);
98104

99105
match args.command {
@@ -200,6 +206,10 @@ async fn main() {
200206
let transport_registry = Arc::new(transport_registry);
201207

202208
// Spawn HTTP servers
209+
if let Some(metrics_bind) = args.metrics {
210+
info!("Starting HTTP metrics server on {}", metrics_bind);
211+
http::metrics::start(metrics_bind, prometheus_handle).await;
212+
}
203213
if components.is_empty() || components.contains(COMPONENT_INGEST) {
204214
info!("Starting HTTP ingest server on {}", args.ingest);
205215
let ext = HttpIngestExtensions {

0 commit comments

Comments
 (0)