refactor(build): reformat code and add tonic health dependency

- Reformatted build script with proper indentation and line breaks
- Added tonic-health dependency to Cargo.toml and updated lock file
- Improved error handling in disk cache with concurrent deletion checks
- Refactored conditional chains using && and let expressions
- Reformatted struct initialization and function parameter lists
- Added proper spacing and alignment in language stats processing
- Improved assertion formatting in test cases
- Reorganized import statements and code layout in multiple files
- Updated metrics functions with better parameter handling and formatting
This commit is contained in:
zhenyi
2026-06-11 13:56:15 +08:00
parent c32a7cad2f
commit a40da90ef9
31 changed files with 696 additions and 417 deletions
+145 -70
View File
@@ -20,7 +20,7 @@
//! - GET /debug/config — Runtime configuration
use dashmap::DashMap;
use std::sync::atomic::{AtomicU64, AtomicBool, Ordering};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Arc, OnceLock};
use std::time::{Duration, Instant};
@@ -82,7 +82,9 @@ struct MetricsInner {
static METRICS: OnceLock<Arc<MetricsInner>> = OnceLock::new();
/// Handle for dynamic log level reload.
static LOG_RELOAD_HANDLE: OnceLock<Option<tracing_subscriber::reload::Handle<EnvFilter, tracing_subscriber::Registry>>> = OnceLock::new();
static LOG_RELOAD_HANDLE: OnceLock<
Option<tracing_subscriber::reload::Handle<EnvFilter, tracing_subscriber::Registry>>,
> = OnceLock::new();
use tracing_subscriber::EnvFilter;
@@ -141,7 +143,9 @@ fn record_duration_bucket(map: &DashMap<String, AtomicU64>, key_prefix: &str, du
}
pub fn set_slow_request_threshold(ms: u64) {
metrics().slow_request_threshold_ms.store(ms, Ordering::Relaxed);
metrics()
.slow_request_threshold_ms
.store(ms, Ordering::Relaxed);
}
pub fn set_ready(ready: bool) {
@@ -254,12 +258,19 @@ pub fn record_hook_execution(hook_type: &str, result: &str, duration: Duration)
record_duration_bucket(&m.hook_duration_buckets, hook_type, duration_ms);
}
pub fn set_raft_state(term: u64, commit_index: u64, last_applied: u64, is_leader: bool, log_entries: u64) {
pub fn set_raft_state(
term: u64,
commit_index: u64,
last_applied: u64,
is_leader: bool,
log_entries: u64,
) {
let m = metrics();
m.raft_term.store(term, Ordering::Relaxed);
m.raft_commit_index.store(commit_index, Ordering::Relaxed);
m.raft_last_applied.store(last_applied, Ordering::Relaxed);
m.raft_is_leader.store(if is_leader { 1 } else { 0 }, Ordering::Relaxed);
m.raft_is_leader
.store(if is_leader { 1 } else { 0 }, Ordering::Relaxed);
m.raft_log_entries.store(log_entries, Ordering::Relaxed);
}
@@ -268,7 +279,8 @@ pub fn inc_raft_append_entries(success: bool) {
let m = metrics();
m.raft_append_entries_total.fetch_add(1, Ordering::Relaxed);
if success {
m.raft_append_entries_success.fetch_add(1, Ordering::Relaxed);
m.raft_append_entries_success
.fetch_add(1, Ordering::Relaxed);
}
}
@@ -296,14 +308,22 @@ fn prom_escape(value: &str) -> String {
out
}
fn render_counter_map(out: &mut String, name: &str, help: &str, map: &DashMap<String, AtomicU64>, labels: &[&str]) {
fn render_counter_map(
out: &mut String,
name: &str,
help: &str,
map: &DashMap<String, AtomicU64>,
labels: &[&str],
) {
out.push_str(&format!("# HELP {name} {help}\n"));
out.push_str(&format!("# TYPE {name} counter\n"));
for entry in map {
let (key, count) = (entry.key(), entry.value().load(Ordering::Relaxed));
let parts: Vec<&str> = key.split(':').collect();
if parts.len() == labels.len() {
let label_str: String = labels.iter().zip(parts.iter())
let label_str: String = labels
.iter()
.zip(parts.iter())
.map(|(l, v)| format!("{l}=\"{}\"", prom_escape(v)))
.collect::<Vec<_>>()
.join(",");
@@ -324,7 +344,10 @@ fn render_histogram(out: &mut String, name: &str, help: &str, map: &DashMap<Stri
} else {
bound_str.to_string()
};
out.push_str(&format!("{name}_bucket{{method=\"{}\",le=\"{le}\"}} {count}\n", prom_escape(key)));
out.push_str(&format!(
"{name}_bucket{{method=\"{}\",le=\"{le}\"}} {count}\n",
prom_escape(key)
));
}
}
out.push('\n');
@@ -350,16 +373,30 @@ pub fn render_metrics() -> String {
out.push_str(&format!("gitks_repository_count {repos}\n\n"));
// gRPC requests
render_counter_map(&mut out, "gitks_requests_total",
"Total gRPC requests by method and status", &m.request_count, &["method", "status"]);
render_counter_map(
&mut out,
"gitks_requests_total",
"Total gRPC requests by method and status",
&m.request_count,
&["method", "status"],
);
// gRPC duration
render_histogram(&mut out, "gitks_request_duration_milliseconds",
"Request duration histogram in ms", &m.duration_buckets);
render_histogram(
&mut out,
"gitks_request_duration_milliseconds",
"Request duration histogram in ms",
&m.duration_buckets,
);
// Slow requests
render_counter_map(&mut out, "gitks_slow_requests_total",
"Slow gRPC requests by method", &m.slow_request_count, &["method"]);
render_counter_map(
&mut out,
"gitks_slow_requests_total",
"Slow gRPC requests by method",
&m.slow_request_count,
&["method"],
);
// Cache
let hits = m.cache_hits.load(Ordering::Relaxed);
@@ -372,26 +409,58 @@ pub fn render_metrics() -> String {
out.push_str(&format!("gitks_cache_misses_total {misses}\n\n"));
// Errors
render_counter_map(&mut out, "gitks_errors_total",
"Total errors by kind", &m.error_count, &["kind"]);
render_counter_map(
&mut out,
"gitks_errors_total",
"Total errors by kind",
&m.error_count,
&["kind"],
);
// Git subprocess
render_counter_map(&mut out, "gitks_git_cmd_total",
"Git subprocess calls by command", &m.git_cmd_count, &["command"]);
render_histogram(&mut out, "gitks_git_cmd_duration_milliseconds",
"Git subprocess duration in ms", &m.git_cmd_duration_buckets);
render_counter_map(
&mut out,
"gitks_git_cmd_total",
"Git subprocess calls by command",
&m.git_cmd_count,
&["command"],
);
render_histogram(
&mut out,
"gitks_git_cmd_duration_milliseconds",
"Git subprocess duration in ms",
&m.git_cmd_duration_buckets,
);
// Cache operations
render_counter_map(&mut out, "gitks_cache_ops_total",
"Cache operations by cache and result", &m.cache_op_count, &["cache", "result"]);
render_histogram(&mut out, "gitks_cache_op_duration_milliseconds",
"Cache operation duration in ms", &m.cache_op_duration_buckets);
render_counter_map(
&mut out,
"gitks_cache_ops_total",
"Cache operations by cache and result",
&m.cache_op_count,
&["cache", "result"],
);
render_histogram(
&mut out,
"gitks_cache_op_duration_milliseconds",
"Cache operation duration in ms",
&m.cache_op_duration_buckets,
);
// Hook execution
render_counter_map(&mut out, "gitks_hook_executions_total",
"Hook executions by type and result", &m.hook_count, &["hook_type", "result"]);
render_histogram(&mut out, "gitks_hook_duration_milliseconds",
"Hook execution duration in ms", &m.hook_duration_buckets);
render_counter_map(
&mut out,
"gitks_hook_executions_total",
"Hook executions by type and result",
&m.hook_count,
&["hook_type", "result"],
);
render_histogram(
&mut out,
"gitks_hook_duration_milliseconds",
"Hook execution duration in ms",
&m.hook_duration_buckets,
);
// Raft consensus metrics
let raft_term = m.raft_term.load(Ordering::Relaxed);
@@ -426,11 +495,15 @@ pub fn render_metrics() -> String {
out.push_str("# HELP gitks_raft_append_entries_total Total AppendEntries RPCs sent\n");
out.push_str("# TYPE gitks_raft_append_entries_total counter\n");
out.push_str(&format!("gitks_raft_append_entries_total {raft_ae_total}\n\n"));
out.push_str(&format!(
"gitks_raft_append_entries_total {raft_ae_total}\n\n"
));
out.push_str("# HELP gitks_raft_append_entries_success Successful AppendEntries RPCs\n");
out.push_str("# TYPE gitks_raft_append_entries_success counter\n");
out.push_str(&format!("gitks_raft_append_entries_success {raft_ae_success}\n\n"));
out.push_str(&format!(
"gitks_raft_append_entries_success {raft_ae_success}\n\n"
));
out.push_str("# HELP gitks_raft_elections_total Total elections triggered\n");
out.push_str("# TYPE gitks_raft_elections_total counter\n");
@@ -438,7 +511,9 @@ pub fn render_metrics() -> String {
out.push_str("# HELP gitks_raft_elections_won Elections won by this node\n");
out.push_str("# TYPE gitks_raft_elections_won counter\n");
out.push_str(&format!("gitks_raft_elections_won {raft_elections_won}\n\n"));
out.push_str(&format!(
"gitks_raft_elections_won {raft_elections_won}\n\n"
));
out
}
@@ -446,12 +521,12 @@ pub fn render_metrics() -> String {
use bytes::Bytes;
use http_body_util::Full;
use hyper::body::Incoming;
use hyper::{Request, Response, Method};
use hyper::service::Service;
use hyper::{Method, Request, Response};
use std::convert::Infallible;
use std::future::Future;
use std::pin::Pin;
use std::net::SocketAddr;
use std::pin::Pin;
/// Global cancel token for the HTTP server, set from main.
static HTTP_CANCEL: OnceLock<tokio_util::sync::CancellationToken> = OnceLock::new();
@@ -501,9 +576,7 @@ async fn handle_request(req: Request<Incoming>) -> Result<Response<Full<Bytes>>,
let body = render_metrics();
text_response(200, "text/plain; version=0.0.4; charset=utf-8", body)
}
(Method::GET, "/health") => {
json_response(200, r#"{"status":"healthy"}"#)
}
(Method::GET, "/health") => json_response(200, r#"{"status":"healthy"}"#),
(Method::GET, "/ready") => {
if metrics().ready.load(Ordering::Relaxed) {
json_response(200, r#"{"status":"ready"}"#)
@@ -519,30 +592,28 @@ async fn handle_request(req: Request<Incoming>) -> Result<Response<Full<Bytes>>,
};
json_response(200, &format!(r#"{{"log_level":"{msg}"}}"#))
}
(Method::PUT, "/debug/log-level") => {
match handle_log_level_update(req).await {
Ok(resp) => resp,
Err(e) => json_response(400, &format!(r#"{{"error":"{e}"}}"#)),
}
}
(Method::PUT, "/debug/log-level") => match handle_log_level_update(req).await {
Ok(resp) => resp,
Err(e) => json_response(400, &format!(r#"{{"error":"{e}"}}"#)),
},
(Method::GET, "/debug/config") => {
let threshold = metrics().slow_request_threshold_ms.load(Ordering::Relaxed);
let ready = metrics().ready.load(Ordering::Relaxed);
json_response(200, &format!(
r#"{{"slow_request_threshold_ms":{},"ready":{}}}"#, threshold, ready
))
}
_ => {
json_response(404, r#"{"error":"not found"}"#)
json_response(
200,
&format!(
r#"{{"slow_request_threshold_ms":{},"ready":{}}}"#,
threshold, ready
),
)
}
_ => json_response(404, r#"{"error":"not found"}"#),
};
Ok(response)
}
async fn handle_log_level_update(
req: Request<Incoming>,
) -> Result<Response<Full<Bytes>>, String> {
async fn handle_log_level_update(req: Request<Incoming>) -> Result<Response<Full<Bytes>>, String> {
use http_body_util::BodyExt;
let body_bytes = req
@@ -551,8 +622,8 @@ async fn handle_log_level_update(
.map_err(|e| format!("failed to read body: {e}"))?
.to_bytes();
let new_filter = String::from_utf8(body_bytes.to_vec())
.map_err(|e| format!("invalid UTF-8: {e}"))?;
let new_filter =
String::from_utf8(body_bytes.to_vec()).map_err(|e| format!("invalid UTF-8: {e}"))?;
let new_filter = new_filter.trim().to_string();
if new_filter.is_empty() {
@@ -566,31 +637,35 @@ async fn handle_log_level_update(
Ok(json_response(500, &format!(r#"{{"error":"{e}"}}"#)))
} else {
tracing::info!(new_filter = %new_filter, "log level updated via HTTP");
Ok(json_response(200, &format!(
r#"{{"status":"ok","filter":"{}"}}"#, new_filter
)))
Ok(json_response(
200,
&format!(r#"{{"status":"ok","filter":"{}"}}"#, new_filter),
))
}
}
Err(e) => Ok(json_response(400, &format!(
r#"{{"error":"invalid filter: {e}"}}"#
))),
Err(e) => Ok(json_response(
400,
&format!(r#"{{"error":"invalid filter: {e}"}}"#),
)),
},
_ => Ok(json_response(501, r#"{"error":"dynamic log level not configured"}"#)),
_ => Ok(json_response(
501,
r#"{"error":"dynamic log level not configured"}"#,
)),
}
}
/// Start the HTTP server (metrics + health + debug) using hyper 1.x.
pub fn start_metrics_server(port: u16) -> tokio::task::JoinHandle<()> {
tokio::spawn(async move {
let listener = match tokio::net::TcpListener::bind(SocketAddr::from(([0, 0, 0, 0], port)))
.await
{
Ok(l) => l,
Err(e) => {
tracing::error!(port, error = %e, "failed to bind HTTP server");
return;
}
};
let listener =
match tokio::net::TcpListener::bind(SocketAddr::from(([0, 0, 0, 0], port))).await {
Ok(l) => l,
Err(e) => {
tracing::error!(port, error = %e, "failed to bind HTTP server");
return;
}
};
tracing::info!(port, "HTTP server started (metrics + health + debug)");
let cancel = HTTP_CANCEL