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:
+145
-70
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user