feat(server): add tracing spans and caching to archive and blame services
- Add tracing spans with repo labels for archive and blame operations - Implement caching for archive list entries when using OID selectors - Implement caching for blame operations when using OID selectors - Add detailed
This commit is contained in:
+61
-17
@@ -1,6 +1,7 @@
|
||||
mod archive;
|
||||
mod blame;
|
||||
mod branch;
|
||||
mod cache;
|
||||
mod commit;
|
||||
mod diff;
|
||||
mod merge;
|
||||
@@ -23,11 +24,23 @@ use crate::pb::{
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GitksService {
|
||||
/// 所有仓库的根路径前缀
|
||||
pub(crate) repo_prefix: PathBuf,
|
||||
/// Root prefix path for all repositories
|
||||
pub repo_prefix: PathBuf,
|
||||
}
|
||||
|
||||
impl GitksService {
|
||||
fn repo_label(&self, header: Option<&crate::pb::RepositoryHeader>) -> String {
|
||||
header
|
||||
.and_then(|h| {
|
||||
if h.relative_path.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(h.relative_path.clone())
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| "unknown".into())
|
||||
}
|
||||
|
||||
pub(crate) fn resolve(
|
||||
&self,
|
||||
header: Option<&crate::pb::RepositoryHeader>,
|
||||
@@ -35,7 +48,12 @@ impl GitksService {
|
||||
let header =
|
||||
header.ok_or_else(|| tonic::Status::invalid_argument("repository is required"))?;
|
||||
let header = self.prefixed_header(header);
|
||||
GitBare::from_repository_header(&header).map_err(into_status)
|
||||
let gb = GitBare::from_repository_header(&header).map_err(into_status)?;
|
||||
tracing::debug!(
|
||||
repo = %gb.bare_dir.display(),
|
||||
"resolved repository"
|
||||
);
|
||||
Ok(gb)
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_for_init(
|
||||
@@ -49,7 +67,7 @@ impl GitksService {
|
||||
return Err(tonic::Status::invalid_argument("relative_path is required"));
|
||||
}
|
||||
let candidate = self.repo_prefix.join(relative_path);
|
||||
// 路径穿越检查
|
||||
// Path traversal check
|
||||
let canonical = candidate
|
||||
.canonicalize()
|
||||
.unwrap_or_else(|_| candidate.clone());
|
||||
@@ -65,11 +83,8 @@ impl GitksService {
|
||||
Ok(canonical)
|
||||
}
|
||||
|
||||
/// 将客户端传入的 header 注入 repo_prefix 作为 storage_path
|
||||
fn prefixed_header(
|
||||
&self,
|
||||
header: &crate::pb::RepositoryHeader,
|
||||
) -> crate::pb::RepositoryHeader {
|
||||
/// Inject repo_prefix as storage_path into the client-provided header
|
||||
fn prefixed_header(&self, header: &crate::pb::RepositoryHeader) -> crate::pb::RepositoryHeader {
|
||||
crate::pb::RepositoryHeader {
|
||||
storage_path: self.repo_prefix.to_string_lossy().into_owned(),
|
||||
relative_path: header.relative_path.clone(),
|
||||
@@ -115,20 +130,49 @@ pub(crate) fn git_cmd(gb: &GitBare, args: &[&str]) -> Result<std::process::Outpu
|
||||
gb.bare_dir.to_string_lossy().into_owned(),
|
||||
];
|
||||
full_args.extend(args.iter().map(|s| s.to_string()));
|
||||
std::process::Command::new("git")
|
||||
tracing::debug!(
|
||||
repo = %gb.bare_dir.display(),
|
||||
args = %full_args.iter().skip(2).cloned().collect::<Vec<_>>().join(" "),
|
||||
"spawning git subprocess"
|
||||
);
|
||||
let result = std::process::Command::new("git")
|
||||
.args(&full_args)
|
||||
.output()
|
||||
.map_err(|e| tonic::Status::internal(e.to_string()))
|
||||
.map_err(|e| {
|
||||
tracing::error!(
|
||||
repo = %gb.bare_dir.display(),
|
||||
error = %e,
|
||||
"failed to spawn git subprocess"
|
||||
);
|
||||
tonic::Status::internal(e.to_string())
|
||||
})?;
|
||||
if !result.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&result.stderr);
|
||||
tracing::warn!(
|
||||
repo = %gb.bare_dir.display(),
|
||||
status = ?result.status.code(),
|
||||
stderr = %stderr.trim(),
|
||||
"git subprocess exited with non-zero status"
|
||||
);
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn serve(
|
||||
addr: std::net::SocketAddr,
|
||||
repo_prefix: PathBuf,
|
||||
) -> Result<(), tonic::transport::Error> {
|
||||
let span = tracing::info_span!("gitks.server", %addr);
|
||||
let _enter = span.enter();
|
||||
let svc = GitksService { repo_prefix };
|
||||
tonic::transport::Server::builder()
|
||||
.add_service(repository_service_server::RepositoryServiceServer::new(svc.clone()))
|
||||
.add_service(archive_service_server::ArchiveServiceServer::new(svc.clone()))
|
||||
tracing::info!("registering gRPC services");
|
||||
let server = tonic::transport::Server::builder()
|
||||
.add_service(repository_service_server::RepositoryServiceServer::new(
|
||||
svc.clone(),
|
||||
))
|
||||
.add_service(archive_service_server::ArchiveServiceServer::new(
|
||||
svc.clone(),
|
||||
))
|
||||
.add_service(blame_service_server::BlameServiceServer::new(svc.clone()))
|
||||
.add_service(branch_service_server::BranchServiceServer::new(svc.clone()))
|
||||
.add_service(commit_service_server::CommitServiceServer::new(svc.clone()))
|
||||
@@ -136,7 +180,7 @@ pub async fn serve(
|
||||
.add_service(merge_service_server::MergeServiceServer::new(svc.clone()))
|
||||
.add_service(pack_service_server::PackServiceServer::new(svc.clone()))
|
||||
.add_service(tag_service_server::TagServiceServer::new(svc.clone()))
|
||||
.add_service(tree_service_server::TreeServiceServer::new(svc))
|
||||
.serve(addr)
|
||||
.await
|
||||
.add_service(tree_service_server::TreeServiceServer::new(svc));
|
||||
tracing::info!("server ready, starting to accept connections");
|
||||
server.serve(addr).await
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user