• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

payjoin / rust-payjoin / 21284432896

23 Jan 2026 11:22AM UTC coverage: 82.83% (-0.4%) from 83.203%
21284432896

Pull #1296

github

web-flow
Merge f9fc9008c into 45689ba2c
Pull Request #1296: WIP:Add standalone metrics service to Payjoin-service

0 of 55 new or added lines in 1 file covered. (0.0%)

11 existing lines in 1 file now uncovered.

10140 of 12242 relevant lines covered (82.83%)

430.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/payjoin-service/src/metrics.rs
1
use std::pin::Pin;
2
use std::sync::Arc;
3
use std::task::{Context, Poll};
4

5
use axum::body::Body;
6
use axum::extract::Request;
7
use axum::http::{Method, Response, StatusCode};
8
//use axum::response::IntoResponse;
9
//use http_body_util::{BodyExt, Full};
10
use prometheus::{
11
    Encoder, IntCounter, IntCounterVec, Opts, Registry,
12
    TextEncoder,
13
};
14
use tower::Service as TowerService;
15

16
#[derive(Clone)]
17
pub struct MetricsService {
18
    registry: Arc<Registry>,
19

20
    pub http_requests_total: IntCounterVec,
21
    pub total_connections: IntCounter,
22
}
23

24
impl MetricsService {
NEW
25
    pub fn new() -> anyhow::Result<Self> {
×
NEW
26
        let registry = Registry::new();
×
27
        // Total number of HTTP requests by endpoint type, method, and status code
NEW
28
        let http_requests_total = IntCounterVec::new(
×
NEW
29
            Opts::new("http_requests_total", "Total number of HTTP requests"),
×
NEW
30
            &["endpoint_type", "method", "status_code"],
×
NEW
31
        )?;
×
NEW
32
        registry.register(Box::new(http_requests_total.clone()))?;
×
NEW
33
        let total_connections =
×
NEW
34
            IntCounter::new("Total_connections", "Total number of connections")?;
×
NEW
35
        registry.register(Box::new(total_connections.clone()))?;
×
36

NEW
37
        Ok(Self { registry: Arc::new(registry), http_requests_total, total_connections })
×
NEW
38
    }
×
39

NEW
40
    pub fn record_http_request(&self, endpoint_type: &str, method: &str, status_code: u16) {
×
NEW
41
        let status_type = match status_code {
×
NEW
42
            200..=299 => "success",
×
NEW
43
            300..=399 => "redirect",
×
NEW
44
            400..=499 => "client_error",
×
NEW
45
            500..=599 => "server_error",
×
NEW
46
            _ => "unknown",
×
47
        };
NEW
48
        self.http_requests_total.with_label_values(&[endpoint_type, method, status_type]).inc();
×
NEW
49
    }
×
50

NEW
51
    pub fn record_total_connections(&self) { self.total_connections.inc(); }
×
52

NEW
53
    fn encode_metrics(&self) -> Result<Vec<u8>, anyhow::Error> {
×
NEW
54
        let encode = TextEncoder::new();
×
NEW
55
        let all_metrics = self.registry.gather();
×
NEW
56
        let mut buffer = Vec::new();
×
NEW
57
        encode.encode(&all_metrics, &mut buffer)?;
×
NEW
58
        Ok(buffer)
×
NEW
59
    }
×
60
}
61

62
impl Default for MetricsService {
NEW
63
    fn default() -> Self { Self::new().expect("Failed to create metrics service") }
×
64
}
65

66
impl TowerService<Request> for MetricsService {
67
    type Response = Response<Body>;
68
    type Error = anyhow::Error;
69
    type Future =
70
        Pin<Box<dyn std::future::Future<Output = Result<Self::Response, Self::Error>> + Send>>;
71

NEW
72
    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
×
NEW
73
        Poll::Ready(Ok(()))
×
NEW
74
    }
×
75

NEW
76
    fn call(&mut self, req: Request) -> Self::Future {
×
NEW
77
        let this = self.clone();
×
NEW
78
        Box::pin(async move {
×
NEW
79
            if req.uri().path() == "/metrics" && req.method() == Method::GET {
×
NEW
80
                match this.encode_metrics() {
×
NEW
81
                    Ok(body) => {
×
NEW
82
                        let response = Response::builder()
×
NEW
83
                            .status(StatusCode::OK)
×
NEW
84
                            .header("Content-Type", "text/plain; version=0.0.4; charset=utf-8")
×
NEW
85
                            .body(Body::from(body))?;
×
NEW
86
                        Ok(response)
×
87
                    }
NEW
88
                    Err(e) => {
×
NEW
89
                        let response = Response::builder()
×
NEW
90
                            .status(StatusCode::INTERNAL_SERVER_ERROR)
×
NEW
91
                            .body(Body::from(format!("Failed to encode metrics: {}", e)))?;
×
92

NEW
93
                        Ok(response)
×
94
                    }
95
                }
96
            } else {
NEW
97
                let response = Response::builder()
×
NEW
98
                    .status(StatusCode::NOT_FOUND)
×
NEW
99
                    .body(Body::from("Not found"))?;
×
NEW
100
                Ok(response)
×
101
            }
NEW
102
        })
×
NEW
103
    }
×
104
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc