feat: 添加超详细的HTTP请求日志系统
详细日志增强: - HTTP请求URL和超时配置 - 完整的请求头信息 (Authorization脱敏显示) - 请求体大小和内容预览 - HTTP响应状态码和原因短语 - 完整的响应头信息 - 响应体大小和内容预览 重试机制日志: - 每次尝试的详细时间统计 - 失败原因的具体记录 - 重试等待过程的状态显示 - 成功/失败的明确标识 错误分析支持: - JSON解析错误的详细信息 - 错误响应的完整内容 - 候选结果数量统计 - 内容预览和截断显示 调试友好: - 分步骤的详细日志 - 时间戳和耗时统计 - 敏感信息的安全处理 - 结构化的日志输出 现在可以完整分析API调用的每个环节!
This commit is contained in:
parent
ea2c72ea05
commit
e626521ac2
|
|
@ -337,21 +337,31 @@ impl GeminiService {
|
||||||
|
|
||||||
// 重试机制
|
// 重试机制
|
||||||
let mut last_error = None;
|
let mut last_error = None;
|
||||||
|
println!("🔄 开始重试机制,最大重试次数: {}", self.config.max_retries);
|
||||||
|
|
||||||
for attempt in 0..self.config.max_retries {
|
for attempt in 0..self.config.max_retries {
|
||||||
println!("🔄 尝试 {}/{}", attempt + 1, self.config.max_retries);
|
println!("🎯 === 第 {}/{} 次尝试 ===", attempt + 1, self.config.max_retries);
|
||||||
|
let attempt_start = std::time::Instant::now();
|
||||||
|
|
||||||
match self.send_generate_request(&generate_url, &client_config, &request_data).await {
|
match self.send_generate_request(&generate_url, &client_config, &request_data).await {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
println!("✅ 成功获取Gemini分析结果");
|
let attempt_duration = attempt_start.elapsed();
|
||||||
|
println!("✅ 第 {} 次尝试成功!耗时: {:?}", attempt + 1, attempt_duration);
|
||||||
|
println!("🎉 成功获取Gemini分析结果");
|
||||||
return self.parse_gemini_response_content(&result);
|
return self.parse_gemini_response_content(&result);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
let attempt_duration = attempt_start.elapsed();
|
||||||
last_error = Some(e);
|
last_error = Some(e);
|
||||||
println!("⚠️ 尝试 {}/{} 失败: {}", attempt + 1, self.config.max_retries, last_error.as_ref().unwrap());
|
println!("❌ 第 {} 次尝试失败,耗时: {:?}", attempt + 1, attempt_duration);
|
||||||
|
println!("📝 失败原因: {}", last_error.as_ref().unwrap());
|
||||||
|
|
||||||
if attempt < self.config.max_retries - 1 {
|
if attempt < self.config.max_retries - 1 {
|
||||||
println!("⏳ 等待 {} 秒后重试...", self.config.retry_delay);
|
println!("⏳ 等待 {} 秒后进行第 {} 次重试...", self.config.retry_delay, attempt + 2);
|
||||||
tokio::time::sleep(tokio::time::Duration::from_secs(self.config.retry_delay)).await;
|
tokio::time::sleep(tokio::time::Duration::from_secs(self.config.retry_delay)).await;
|
||||||
|
println!("🔄 重试等待结束,准备下一次尝试");
|
||||||
|
} else {
|
||||||
|
println!("💔 已达到最大重试次数,放弃重试");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -367,39 +377,114 @@ impl GeminiService {
|
||||||
client_config: &ClientConfig,
|
client_config: &ClientConfig,
|
||||||
request_data: &GenerateContentRequest,
|
request_data: &GenerateContentRequest,
|
||||||
) -> Result<GeminiResponse> {
|
) -> Result<GeminiResponse> {
|
||||||
|
println!("🌐 准备发送HTTP请求...");
|
||||||
|
println!("📍 请求URL: {}", url);
|
||||||
|
println!("⏱️ 超时时间: {} 秒", self.config.timeout);
|
||||||
|
|
||||||
let mut request_builder = self.client
|
let mut request_builder = self.client
|
||||||
.post(url)
|
.post(url)
|
||||||
.timeout(tokio::time::Duration::from_secs(self.config.timeout))
|
.timeout(tokio::time::Duration::from_secs(self.config.timeout))
|
||||||
.json(request_data);
|
.json(request_data);
|
||||||
|
|
||||||
// 添加请求头
|
// 添加请求头并记录日志
|
||||||
|
println!("📋 添加请求头:");
|
||||||
for (key, value) in &client_config.headers {
|
for (key, value) in &client_config.headers {
|
||||||
|
println!(" {}: {}", key, if key == "Authorization" {
|
||||||
|
format!("Bearer {}...", &value[7..std::cmp::min(27, value.len())])
|
||||||
|
} else {
|
||||||
|
value.clone()
|
||||||
|
});
|
||||||
request_builder = request_builder.header(key, value);
|
request_builder = request_builder.header(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 记录请求体信息
|
||||||
|
println!("📦 请求体信息:");
|
||||||
|
println!(" Content-Type: application/json");
|
||||||
|
println!(" Body size: {} bytes", serde_json::to_string(request_data).unwrap_or_default().len());
|
||||||
|
|
||||||
|
// 记录请求体内容(截断显示)
|
||||||
|
let request_json = serde_json::to_string_pretty(request_data).unwrap_or_default();
|
||||||
|
let preview_len = std::cmp::min(1000, request_json.len());
|
||||||
|
println!("📄 请求体预览 (前{}字符):", preview_len);
|
||||||
|
println!("{}", &request_json[..preview_len]);
|
||||||
|
if request_json.len() > 1000 {
|
||||||
|
println!(" ... (请求体已截断,总长度: {} 字符)", request_json.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("🚀 发送HTTP请求...");
|
||||||
let response = request_builder.send().await?;
|
let response = request_builder.send().await?;
|
||||||
let status = response.status();
|
let status = response.status();
|
||||||
let headers = response.headers().clone();
|
let headers = response.headers().clone();
|
||||||
|
|
||||||
println!("📥 生成响应状态: {}", status);
|
println!("📥 HTTP响应接收完成");
|
||||||
println!("📋 生成响应头: {:?}", headers);
|
println!("📊 响应状态: {} {}", status.as_u16(), status.canonical_reason().unwrap_or("Unknown"));
|
||||||
|
println!("📋 响应头详情:");
|
||||||
|
for (name, value) in headers.iter() {
|
||||||
|
println!(" {}: {}", name, value.to_str().unwrap_or("<binary>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录响应大小
|
||||||
|
if let Some(content_length) = headers.get("content-length") {
|
||||||
|
println!("📏 响应大小: {} bytes", content_length.to_str().unwrap_or("unknown"));
|
||||||
|
}
|
||||||
|
|
||||||
if !status.is_success() {
|
if !status.is_success() {
|
||||||
|
println!("❌ HTTP请求失败,状态码: {}", status);
|
||||||
let error_text = response.text().await.unwrap_or_default();
|
let error_text = response.text().await.unwrap_or_default();
|
||||||
println!("❌ 生成失败响应体: {}", error_text);
|
println!("📄 错误响应体长度: {} 字符", error_text.len());
|
||||||
|
println!("📄 错误响应体内容:");
|
||||||
|
println!("{}", error_text);
|
||||||
|
|
||||||
|
// 尝试解析错误响应为JSON
|
||||||
|
if let Ok(error_json) = serde_json::from_str::<serde_json::Value>(&error_text) {
|
||||||
|
println!("🔍 解析后的错误JSON:");
|
||||||
|
println!("{}", serde_json::to_string_pretty(&error_json).unwrap_or_default());
|
||||||
|
}
|
||||||
|
|
||||||
return Err(anyhow!("API请求失败: {} - {}", status, error_text));
|
return Err(anyhow!("API请求失败: {} - {}", status, error_text));
|
||||||
}
|
}
|
||||||
|
|
||||||
let response_text = response.text().await?;
|
let response_text = response.text().await?;
|
||||||
println!("📄 生成成功响应: {}", response_text);
|
println!("✅ HTTP请求成功");
|
||||||
|
println!("📄 响应体长度: {} 字符", response_text.len());
|
||||||
|
|
||||||
|
// 显示响应体预览
|
||||||
|
let preview_len = std::cmp::min(2000, response_text.len());
|
||||||
|
println!("📄 响应体预览 (前{}字符):", preview_len);
|
||||||
|
println!("{}", &response_text[..preview_len]);
|
||||||
|
if response_text.len() > 2000 {
|
||||||
|
println!(" ... (响应体已截断,总长度: {} 字符)", response_text.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("🔍 开始解析JSON响应...");
|
||||||
let gemini_response: GeminiResponse = serde_json::from_str(&response_text)
|
let gemini_response: GeminiResponse = serde_json::from_str(&response_text)
|
||||||
.map_err(|e| anyhow!("解析生成响应失败: {} - 响应内容: {}", e, response_text))?;
|
.map_err(|e| {
|
||||||
|
println!("❌ JSON解析失败: {}", e);
|
||||||
|
println!("📄 完整响应内容: {}", response_text);
|
||||||
|
anyhow!("解析生成响应失败: {} - 响应内容: {}", e, response_text)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
println!("✅ JSON解析成功");
|
||||||
|
println!("📊 候选结果数量: {}", gemini_response.candidates.len());
|
||||||
|
|
||||||
if gemini_response.candidates.is_empty() {
|
if gemini_response.candidates.is_empty() {
|
||||||
|
println!("❌ API返回的候选结果为空");
|
||||||
return Err(anyhow!("API返回结果为空"));
|
return Err(anyhow!("API返回结果为空"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 记录第一个候选结果的信息
|
||||||
|
if let Some(first_candidate) = gemini_response.candidates.first() {
|
||||||
|
println!("📝 第一个候选结果的部分数量: {}", first_candidate.content.parts.len());
|
||||||
|
if let Some(first_part) = first_candidate.content.parts.first() {
|
||||||
|
let text_preview = if first_part.text.len() > 200 {
|
||||||
|
format!("{}...", &first_part.text[..200])
|
||||||
|
} else {
|
||||||
|
first_part.text.clone()
|
||||||
|
};
|
||||||
|
println!("📝 第一个部分内容预览: {}", text_preview);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(gemini_response)
|
Ok(gemini_response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue