git_cache_proxy/
metrics.rs1use prometheus::{Encoder, IntCounter, IntCounterVec, IntGauge, Opts, Registry, TextEncoder};
15
16pub struct Metrics {
17 pub registry: Registry,
18 requests: IntCounterVec,
22 upstream: IntCounterVec,
25 cache_bytes: IntGauge,
29 cache_mirrors: IntGauge,
31 evictions: IntCounter,
33}
34
35impl Metrics {
36 pub fn new() -> Self {
37 let registry = Registry::new();
38 let requests = IntCounterVec::new(
39 Opts::new("gitcacheproxy_requests_total", "Git requests served"),
40 &["kind", "result", "repo"],
41 )
42 .expect("valid metric");
43 let upstream = IntCounterVec::new(
44 Opts::new(
45 "gitcacheproxy_upstream_ops_total",
46 "Upstream clone/fetch operations",
47 ),
48 &["op", "result", "repo"],
49 )
50 .expect("valid metric");
51 let cache_bytes = IntGauge::new(
52 "gitcacheproxy_cache_bytes",
53 "Total size of the on-disk mirror cache in bytes",
54 )
55 .expect("valid metric");
56 let cache_mirrors = IntGauge::new(
57 "gitcacheproxy_cache_mirrors",
58 "Number of cached mirrors on disk",
59 )
60 .expect("valid metric");
61 let evictions = IntCounter::new(
62 "gitcacheproxy_evictions_total",
63 "Idle mirrors evicted to keep the cache under the configured cap",
64 )
65 .expect("valid metric");
66 registry
67 .register(Box::new(requests.clone()))
68 .expect("register requests");
69 registry
70 .register(Box::new(upstream.clone()))
71 .expect("register upstream");
72 registry
73 .register(Box::new(cache_bytes.clone()))
74 .expect("register cache_bytes");
75 registry
76 .register(Box::new(cache_mirrors.clone()))
77 .expect("register cache_mirrors");
78 registry
79 .register(Box::new(evictions.clone()))
80 .expect("register evictions");
81 Self {
82 registry,
83 requests,
84 upstream,
85 cache_bytes,
86 cache_mirrors,
87 evictions,
88 }
89 }
90
91 pub fn record_request(&self, kind: &str, result: &str, repo: &str) {
97 self.requests.with_label_values(&[kind, result, repo]).inc();
98 }
99
100 pub fn record_upstream(&self, op: &str, result: &str, repo: &str) {
104 self.upstream.with_label_values(&[op, result, repo]).inc();
105 }
106
107 pub fn set_cache_size(&self, bytes: u64, mirrors: usize) {
109 self.cache_bytes.set(bytes as i64);
110 self.cache_mirrors.set(mirrors as i64);
111 }
112
113 pub fn record_eviction(&self) {
115 self.evictions.inc();
116 }
117
118 pub fn gather(&self) -> String {
119 let mut buf = Vec::new();
120 let enc = TextEncoder::new();
121 let _ = enc.encode(&self.registry.gather(), &mut buf);
123 String::from_utf8_lossy(&buf).into_owned()
124 }
125}
126
127impl Default for Metrics {
128 fn default() -> Self {
129 Self::new()
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn gather_renders_recorded_series() {
139 let m = Metrics::new();
140 m.record_request("info_refs", "ok", "group/foo.git");
141 m.record_request("upload_pack", "error", "group/bar.git");
142 m.record_upstream("fetch", "ok", "group/foo.git");
143 m.record_upstream("clone", "error", "group/bar.git");
144 m.set_cache_size(2048, 3);
145 m.record_eviction();
146 m.record_eviction();
147
148 let out = m.gather();
149 assert!(out.contains("gitcacheproxy_cache_bytes 2048"));
150 assert!(out.contains("gitcacheproxy_cache_mirrors 3"));
151 assert!(out.contains("gitcacheproxy_evictions_total 2"));
152 assert!(out.contains(
153 r#"gitcacheproxy_requests_total{kind="info_refs",repo="group/foo.git",result="ok"} 1"#
154 ));
155 assert!(out.contains(
156 r#"gitcacheproxy_requests_total{kind="upload_pack",repo="group/bar.git",result="error"} 1"#
157 ));
158 assert!(out.contains(r#"op="fetch",repo="group/foo.git",result="ok"#));
159 assert!(out.contains(r#"op="clone",repo="group/bar.git",result="error"#));
160 }
161}