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;
|
||||
println!("🔄 开始重试机制,最大重试次数: {}", 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 {
|
||||
Ok(result) => {
|
||||
println!("✅ 成功获取Gemini分析结果");
|
||||
let attempt_duration = attempt_start.elapsed();
|
||||
println!("✅ 第 {} 次尝试成功!耗时: {:?}", attempt + 1, attempt_duration);
|
||||
println!("🎉 成功获取Gemini分析结果");
|
||||
return self.parse_gemini_response_content(&result);
|
||||
}
|
||||
Err(e) => {
|
||||
let attempt_duration = attempt_start.elapsed();
|
||||
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 {
|
||||
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;
|
||||
println!("🔄 重试等待结束,准备下一次尝试");
|
||||
} else {
|
||||
println!("💔 已达到最大重试次数,放弃重试");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -367,39 +377,114 @@ impl GeminiService {
|
|||
client_config: &ClientConfig,
|
||||
request_data: &GenerateContentRequest,
|
||||
) -> Result<GeminiResponse> {
|
||||
println!("🌐 准备发送HTTP请求...");
|
||||
println!("📍 请求URL: {}", url);
|
||||
println!("⏱️ 超时时间: {} 秒", self.config.timeout);
|
||||
|
||||
let mut request_builder = self.client
|
||||
.post(url)
|
||||
.timeout(tokio::time::Duration::from_secs(self.config.timeout))
|
||||
.json(request_data);
|
||||
|
||||
// 添加请求头
|
||||
// 添加请求头并记录日志
|
||||
println!("📋 添加请求头:");
|
||||
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);
|
||||
}
|
||||
|
||||
// 记录请求体信息
|
||||
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 status = response.status();
|
||||
let headers = response.headers().clone();
|
||||
|
||||
println!("📥 生成响应状态: {}", status);
|
||||
println!("📋 生成响应头: {:?}", headers);
|
||||
println!("📥 HTTP响应接收完成");
|
||||
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() {
|
||||
println!("❌ HTTP请求失败,状态码: {}", status);
|
||||
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));
|
||||
}
|
||||
|
||||
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)
|
||||
.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() {
|
||||
println!("❌ 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)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue