git_cache_proxy/
metrics.rs1use prometheus::{Encoder, IntCounterVec, Opts, Registry, TextEncoder};
15
16pub struct Metrics {
17 pub registry: Registry,
18 requests: IntCounterVec,
22 upstream: IntCounterVec,
25}
26
27impl Metrics {
28 pub fn new() -> Self {
29 let registry = Registry::new();
30 let requests = IntCounterVec::new(
31 Opts::new("gitcacheproxy_requests_total", "Git requests served"),
32 &["kind", "result", "repo"],
33 )
34 .expect("valid metric");
35 let upstream = IntCounterVec::new(
36 Opts::new(
37 "gitcacheproxy_upstream_ops_total",
38 "Upstream clone/fetch operations",
39 ),
40 &["op", "result", "repo"],
41 )
42 .expect("valid metric");
43 registry
44 .register(Box::new(requests.clone()))
45 .expect("register requests");
46 registry
47 .register(Box::new(upstream.clone()))
48 .expect("register upstream");
49 Self {
50 registry,
51 requests,
52 upstream,
53 }
54 }
55
56 pub fn record_request(&self, kind: &str, result: &str, repo: &str) {
62 self.requests.with_label_values(&[kind, result, repo]).inc();
63 }
64
65 pub fn record_upstream(&self, op: &str, result: &str, repo: &str) {
69 self.upstream.with_label_values(&[op, result, repo]).inc();
70 }
71
72 pub fn gather(&self) -> String {
73 let mut buf = Vec::new();
74 let enc = TextEncoder::new();
75 let _ = enc.encode(&self.registry.gather(), &mut buf);
77 String::from_utf8_lossy(&buf).into_owned()
78 }
79}
80
81impl Default for Metrics {
82 fn default() -> Self {
83 Self::new()
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn gather_renders_recorded_series() {
93 let m = Metrics::new();
94 m.record_request("info_refs", "ok", "group/foo.git");
95 m.record_request("upload_pack", "error", "group/bar.git");
96 m.record_upstream("fetch", "ok", "group/foo.git");
97 m.record_upstream("clone", "error", "group/bar.git");
98
99 let out = m.gather();
100 assert!(out.contains(
101 r#"gitcacheproxy_requests_total{kind="info_refs",repo="group/foo.git",result="ok"} 1"#
102 ));
103 assert!(out.contains(
104 r#"gitcacheproxy_requests_total{kind="upload_pack",repo="group/bar.git",result="error"} 1"#
105 ));
106 assert!(out.contains(r#"op="fetch",repo="group/foo.git",result="ok"#));
107 assert!(out.contains(r#"op="clone",repo="group/bar.git",result="error"#));
108 }
109}