refactor(logger): 日志体系优化,分四类文件存储
- 认证日志 (auth.log): 登录/token/监听器注册,target=ias::auth - 消息队列日志 (queue.log): 入队/出队内容、消费路由,target=ias::queue - 工具日志 (tool.log): 调用参数/输出/耗时/工具错误,target=ias::tool - 错误日志 (error.log): 所有非工具调用的 ERROR/WARN 自动捕获 使用 tracing-subscriber 多层过滤架构,终端输出保持不变
This commit is contained in:
@@ -297,10 +297,10 @@ async fn generate_summary(messages: &[Message], summarizer: Option<&Summarizer>)
|
||||
let prompt = build_summary_prompt(messages);
|
||||
match summarizer(prompt).await {
|
||||
Ok(text) if !text.is_empty() => {
|
||||
tracing::info!("LLM 摘要: {:.100}...", text);
|
||||
tracing::info!(target: "ias::tool", "LLM 摘要: {:.100}...", text);
|
||||
return text;
|
||||
}
|
||||
_ => tracing::warn!("LLM 摘要失败,回退到简单截断"),
|
||||
_ => tracing::warn!(target: "ias::tool", "LLM 摘要失败,回退到简单截断"),
|
||||
}
|
||||
}
|
||||
summarize_messages(messages)
|
||||
|
||||
@@ -201,7 +201,7 @@ impl ChatSession {
|
||||
&self.current_user_id
|
||||
};
|
||||
if let Err(e) = crate::db::models::save_summary(pool, uid, text, reason, message_count).await {
|
||||
tracing::warn!("保存摘要到数据库失败: {}", e);
|
||||
tracing::warn!(target: "ias::tool", "保存摘要到数据库失败: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+42
-42
@@ -90,7 +90,7 @@ pub async fn run(database: &Arc<Database>, memory_store: &Arc<MemoryStore>) {
|
||||
let auth = match load_auth(database).await {
|
||||
Some(a) => a,
|
||||
None => {
|
||||
error!("未登录");
|
||||
error!(target: "ias::auth", "未登录");
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -102,11 +102,11 @@ pub async fn run(database: &Arc<Database>, memory_store: &Arc<MemoryStore>) {
|
||||
client.set_updates_buf(&buf).await;
|
||||
}
|
||||
if let Err(e) = client.notify_start().await {
|
||||
error!("注册监听器失败: {}", e);
|
||||
error!(target: "ias::auth", "注册监听器失败: {}", e);
|
||||
return;
|
||||
}
|
||||
let account_id = auth.account_id.clone();
|
||||
info!("Daemon 已启动");
|
||||
info!(target: "ias::auth", "Daemon 已启动");
|
||||
|
||||
// 4-6. 审批管理器、工具列表、模型配置
|
||||
let approval_manager = Arc::new(ApprovalManager::new(Some(Arc::new(
|
||||
@@ -179,7 +179,7 @@ pub async fn run(database: &Arc<Database>, memory_store: &Arc<MemoryStore>) {
|
||||
})
|
||||
.await;
|
||||
});
|
||||
info!("定时任务调度器已启动");
|
||||
info!(target: "ias::queue", "定时任务调度器已启动");
|
||||
|
||||
// 12. QueueRunner
|
||||
let runner_handle = tokio::spawn(async move {
|
||||
@@ -199,7 +199,7 @@ pub async fn run(database: &Arc<Database>, memory_store: &Arc<MemoryStore>) {
|
||||
}
|
||||
});
|
||||
|
||||
info!("开始监听消息...");
|
||||
info!(target: "ias::auth", "开始监听消息...");
|
||||
|
||||
// 13. 主循环 — 带 Ctrl+C 优雅退出
|
||||
let mut running = true;
|
||||
@@ -219,7 +219,7 @@ pub async fn run(database: &Arc<Database>, memory_store: &Arc<MemoryStore>) {
|
||||
let text = msg.text_content().unwrap_or("(非文本消息)");
|
||||
let ctx_token = msg.context_token.as_deref();
|
||||
let msg_id = msg.message_id.map(|id| id.to_string()).unwrap_or_default();
|
||||
info!("收到消息 from={}: {}", from, text);
|
||||
info!(target: "ias::queue", "收到消息 from={}: {}", from, text);
|
||||
|
||||
let _ = crate::db::models::insert_chat_record(
|
||||
ctx.db.pool(), "inbound", from, &ctx.account_id, text, "wechat", ctx_token, &msg_id,
|
||||
@@ -229,7 +229,7 @@ pub async fn run(database: &Arc<Database>, memory_store: &Arc<MemoryStore>) {
|
||||
if let Some((skill_name, decision)) = ctx.approval.handle_reply(from, text).await {
|
||||
match decision {
|
||||
ApprovalDecision::Approved => {
|
||||
info!("审批通过: {}", skill_name);
|
||||
info!(target: "ias::auth", "审批通过: {}", skill_name);
|
||||
// 5 元组: (orig_text, context_token, tool_name, tool_args, timestamp)
|
||||
if let Some((orig_text, orig_ctx, _tn, _ta, _ts)) = ctx.approval_ctx.lock().await.remove(from) {
|
||||
enqueue_handle.enqueue(PipelineMessage {
|
||||
@@ -261,27 +261,27 @@ pub async fn run(database: &Arc<Database>, memory_store: &Arc<MemoryStore>) {
|
||||
}
|
||||
Err(e) => {
|
||||
if !e.contains("超时") && !e.contains("timeout") {
|
||||
error!("接收消息失败: {}", e);
|
||||
error!(target: "ias::auth", "接收消息失败: {}", e);
|
||||
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = tokio::signal::ctrl_c() => {
|
||||
info!("收到 Ctrl+C,优雅关闭中...");
|
||||
info!(target: "ias::auth", "收到 Ctrl+C,优雅关闭中...");
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shutdown_handle.shutdown();
|
||||
info!("等待消费者处理剩余消息...");
|
||||
info!(target: "ias::queue", "等待消费者处理剩余消息...");
|
||||
tokio::time::sleep(std::time::Duration::from_secs(3)).await;
|
||||
for h in consumer_handles {
|
||||
h.abort();
|
||||
}
|
||||
runner_handle.abort();
|
||||
info!("Daemon 已停止");
|
||||
info!(target: "ias::auth", "Daemon 已停止");
|
||||
}
|
||||
|
||||
// ─── 消费者 ───
|
||||
@@ -336,7 +336,7 @@ async fn llm_consumer_loop(
|
||||
enqueue: &EnqueueHandle,
|
||||
sessions: &Arc<Mutex<HashMap<Uuid, (Conversation, Instant)>>>,
|
||||
) {
|
||||
info!("LLM Consumer 已启动");
|
||||
info!(target: "ias::queue", "LLM Consumer 已启动");
|
||||
|
||||
while let Some(msg) = rx.recv().await {
|
||||
let user_id = msg.channel.user_id.clone();
|
||||
@@ -476,7 +476,7 @@ async fn llm_consumer_loop(
|
||||
*last_access = Instant::now(); // 更新访问时间,防止 TTL 误清理
|
||||
conv.notify_tool_result()
|
||||
} else {
|
||||
warn!("会话已过期: corr={}", correlation_id);
|
||||
warn!(target: "ias::queue", "会话已过期: corr={}", correlation_id);
|
||||
false
|
||||
}
|
||||
};
|
||||
@@ -488,7 +488,7 @@ async fn llm_consumer_loop(
|
||||
}
|
||||
|
||||
MessageKind::ScheduledTask { text, task_name } => {
|
||||
info!("LLM Consumer: 定时任务 user={} task={}", user_id, task_name);
|
||||
info!(target: "ias::queue", "LLM Consumer: 定时任务 user={} task={}", user_id, task_name);
|
||||
let prompt = format!("[定时任务: {}]\n{}", task_name, text);
|
||||
|
||||
// 为保持简洁,定时任务直接作为用户消息处理(使用全新 correlation_id)
|
||||
@@ -508,13 +508,13 @@ async fn llm_consumer_loop(
|
||||
eq.enqueue(msg).await;
|
||||
}
|
||||
|
||||
other => warn!(
|
||||
other => warn!(target: "ias::queue",
|
||||
"LLM Consumer 收到不期望的消息: {:?}",
|
||||
std::mem::discriminant(&other)
|
||||
),
|
||||
}
|
||||
}
|
||||
info!("LLM Consumer 已停止");
|
||||
info!(target: "ias::queue", "LLM Consumer 已停止");
|
||||
}
|
||||
|
||||
/// 执行一轮 LLM 对话,仅在无异步工具等待时发送回复并清理会话。
|
||||
@@ -526,8 +526,8 @@ async fn run_llm_round(
|
||||
sessions: &Arc<Mutex<HashMap<Uuid, (Conversation, Instant)>>>,
|
||||
enqueue: &EnqueueHandle,
|
||||
) {
|
||||
info!("══════════════════════════════════════════════════");
|
||||
info!("🔄 LLM 回合开始 user={} text={}", user_id, text);
|
||||
info!(target: "ias::queue", "══════════════════════════════════════════════════");
|
||||
info!(target: "ias::queue", "🔄 LLM 回合开始 user={} text={}", user_id, text);
|
||||
let result = {
|
||||
let map = sessions.lock().await;
|
||||
let conv = match map.get(&correlation_id) {
|
||||
@@ -571,8 +571,8 @@ async fn resume_llm_round(
|
||||
sessions: &Arc<Mutex<HashMap<Uuid, (Conversation, Instant)>>>,
|
||||
enqueue: &EnqueueHandle,
|
||||
) {
|
||||
info!("══════════════════════════════════════════════════");
|
||||
info!("🔄 LLM 恢复 user={}", user_id);
|
||||
info!(target: "ias::queue", "══════════════════════════════════════════════════");
|
||||
info!(target: "ias::queue", "🔄 LLM 恢复 user={}", user_id);
|
||||
let result = {
|
||||
let map = sessions.lock().await;
|
||||
let conv = match map.get(&correlation_id) {
|
||||
@@ -634,7 +634,7 @@ async fn handle_chat_result(
|
||||
enqueue.enqueue(msg).await;
|
||||
}
|
||||
sessions.lock().await.remove(&correlation_id);
|
||||
info!("✅ LLM 回合结束 user={}", user_id);
|
||||
info!(target: "ias::queue", "✅ LLM 回合结束 user={}", user_id);
|
||||
}
|
||||
|
||||
// ─── Tool Consumer ───
|
||||
@@ -644,7 +644,7 @@ async fn tool_consumer_loop(
|
||||
ctx: &DaemonCtx,
|
||||
enqueue: &EnqueueHandle,
|
||||
) {
|
||||
info!("Tool Consumer 已启动");
|
||||
info!(target: "ias::queue", "Tool Consumer 已启动");
|
||||
|
||||
while let Some(msg) = rx.recv().await {
|
||||
let user_id = msg.channel.user_id.clone();
|
||||
@@ -659,14 +659,14 @@ async fn tool_consumer_loop(
|
||||
context_token,
|
||||
approved,
|
||||
} => {
|
||||
info!(
|
||||
info!(target: "ias::tool",
|
||||
"Tool Consumer: user={} tool={} args={}",
|
||||
user_id, tool_name, arguments
|
||||
);
|
||||
|
||||
// 高风险工具 → 审批(除非已被用户批准)
|
||||
if !approved && crate::tools::is_high_risk(&tool_name) {
|
||||
info!("高风险工具 {} 需要审批", tool_name);
|
||||
info!(target: "ias::tool", "高风险工具 {} 需要审批", tool_name);
|
||||
ctx.approval_ctx.lock().await.insert(
|
||||
user_id.clone(),
|
||||
(
|
||||
@@ -689,7 +689,7 @@ async fn tool_consumer_loop(
|
||||
.enqueue(PipelineMessage::llm_reply(ch, correlation_id, &m, None))
|
||||
.await;
|
||||
}
|
||||
Err(e) => error!("创建审批失败: {}", e),
|
||||
Err(e) => error!(target: "ias::tool", "创建审批失败: {}", e),
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -710,15 +710,15 @@ async fn tool_consumer_loop(
|
||||
.await;
|
||||
}
|
||||
MessageKind::ApprovalRequest { .. } => {
|
||||
warn!("Tool Consumer 收到 ApprovalRequest");
|
||||
warn!(target: "ias::queue", "Tool Consumer 收到 ApprovalRequest");
|
||||
}
|
||||
other => warn!(
|
||||
other => warn!(target: "ias::queue",
|
||||
"Tool Consumer 收到不期望: {:?}",
|
||||
std::mem::discriminant(&other)
|
||||
),
|
||||
}
|
||||
}
|
||||
info!("Tool Consumer 已停止");
|
||||
info!(target: "ias::queue", "Tool Consumer 已停止");
|
||||
}
|
||||
|
||||
// ─── 统一工具执行 ───
|
||||
@@ -737,7 +737,7 @@ fn execute_tool<'a>(
|
||||
match name {
|
||||
"read_memories" => {
|
||||
let result = ctx.memory_store.read_for(user_id, None).await;
|
||||
tracing::info!("🔧 execute_tool: read_memories → {}", result);
|
||||
tracing::info!(target: "ias::tool", "🔧 execute_tool: read_memories → {}", result);
|
||||
return result;
|
||||
}
|
||||
"write_memory" => {
|
||||
@@ -748,7 +748,7 @@ fn execute_tool<'a>(
|
||||
}
|
||||
ctx.memory_store.write_for(user_id, content).await;
|
||||
let result = format!("已添加长期记忆: {}", content);
|
||||
tracing::info!("🔧 execute_tool: write_memory → {}", result);
|
||||
tracing::info!(target: "ias::tool", "🔧 execute_tool: write_memory → {}", result);
|
||||
return result;
|
||||
}
|
||||
"delete_memory" => {
|
||||
@@ -761,7 +761,7 @@ fn execute_tool<'a>(
|
||||
return "请提供有效的 memory_id 参数".to_string();
|
||||
}
|
||||
let result = ctx.memory_store.delete_for(user_id, memory_id).await;
|
||||
tracing::info!("🔧 execute_tool: delete_memory({}) → {}", memory_id, result);
|
||||
tracing::info!(target: "ias::tool", "🔧 execute_tool: delete_memory({}) → {}", memory_id, result);
|
||||
return result;
|
||||
}
|
||||
"update_memory" => {
|
||||
@@ -781,7 +781,7 @@ fn execute_tool<'a>(
|
||||
.memory_store
|
||||
.update_for(user_id, memory_id, content)
|
||||
.await;
|
||||
tracing::info!("🔧 execute_tool: update_memory({}) → {}", memory_id, result);
|
||||
tracing::info!(target: "ias::tool", "🔧 execute_tool: update_memory({}) → {}", memory_id, result);
|
||||
return result;
|
||||
}
|
||||
"read_summaries" => {
|
||||
@@ -795,13 +795,13 @@ fn execute_tool<'a>(
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n---\n")
|
||||
};
|
||||
tracing::info!("🔧 execute_tool: read_summaries → {}条", sums.len());
|
||||
tracing::info!(target: "ias::tool", "🔧 execute_tool: read_summaries → {}条", sums.len());
|
||||
return result;
|
||||
}
|
||||
"query_capabilities" => {
|
||||
let specs = crate::tools::specs();
|
||||
let result = crate::tools::build_capability_guide(&specs);
|
||||
tracing::info!(
|
||||
tracing::info!(target: "ias::tool",
|
||||
"🔧 execute_tool: query_capabilities → {}个工具",
|
||||
specs.len()
|
||||
);
|
||||
@@ -824,7 +824,7 @@ fn execute_tool<'a>(
|
||||
}
|
||||
let target_args = serde_json::to_string(&crate::tools::unpack_call_params(&cp))
|
||||
.unwrap_or_default();
|
||||
tracing::info!(
|
||||
tracing::info!(target: "ias::tool",
|
||||
"🔧 execute_tool: call_capability → {} args={}",
|
||||
target_name,
|
||||
target_args
|
||||
@@ -838,7 +838,7 @@ fn execute_tool<'a>(
|
||||
// 内置工具注册表
|
||||
match crate::tools::execute(name, arguments).await {
|
||||
Some(r) => {
|
||||
tracing::info!(
|
||||
tracing::info!(target: "ias::tool",
|
||||
"🔧 execute_tool: {} → success={} output={}",
|
||||
name,
|
||||
r.success,
|
||||
@@ -847,7 +847,7 @@ fn execute_tool<'a>(
|
||||
r.output
|
||||
}
|
||||
None => {
|
||||
tracing::warn!("🔧 execute_tool: 未知工具 {}", name);
|
||||
tracing::warn!(target: "ias::tool", "🔧 execute_tool: 未知工具 {}", name);
|
||||
format!("未知工具: {}", name)
|
||||
}
|
||||
}
|
||||
@@ -860,7 +860,7 @@ async fn send_consumer_loop(
|
||||
rx: &mut tokio::sync::mpsc::Receiver<PipelineMessage>,
|
||||
ctx: &DaemonCtx,
|
||||
) {
|
||||
info!("Send Consumer 已启动");
|
||||
info!(target: "ias::queue", "Send Consumer 已启动");
|
||||
|
||||
while let Some(msg) = rx.recv().await {
|
||||
match msg.kind {
|
||||
@@ -869,7 +869,7 @@ async fn send_consumer_loop(
|
||||
context_token,
|
||||
} => {
|
||||
let user_id = &msg.channel.user_id;
|
||||
info!(
|
||||
info!(target: "ias::queue",
|
||||
"Send Consumer: user={} text_len={} text={}",
|
||||
user_id,
|
||||
text.len(),
|
||||
@@ -882,7 +882,7 @@ async fn send_consumer_loop(
|
||||
.await
|
||||
{
|
||||
Ok(sent_id) => {
|
||||
info!(
|
||||
info!(target: "ias::queue",
|
||||
"回复成功 user={} msg_id={}",
|
||||
user_id.chars().take(5).collect::<String>(),
|
||||
sent_id
|
||||
@@ -902,13 +902,13 @@ async fn send_consumer_loop(
|
||||
Err(e) => error!("发送回复失败: {}", e),
|
||||
}
|
||||
}
|
||||
other => warn!(
|
||||
other => warn!(target: "ias::queue",
|
||||
"Send Consumer 收到不期望: {:?}",
|
||||
std::mem::discriminant(&other)
|
||||
),
|
||||
}
|
||||
}
|
||||
info!("Send Consumer 已停止");
|
||||
info!(target: "ias::queue", "Send Consumer 已停止");
|
||||
}
|
||||
|
||||
// ─── 辅助函数 ───
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ impl Database {
|
||||
.await
|
||||
.map_err(|e| format!("数据库迁移失败: {}", e))?;
|
||||
|
||||
tracing::info!("数据库连接成功,迁移已完成");
|
||||
tracing::info!(target: "ias::auth", "数据库连接成功,迁移已完成");
|
||||
|
||||
Ok(Self { pool })
|
||||
}
|
||||
|
||||
@@ -246,7 +246,7 @@ impl Conversation {
|
||||
if s.is_idle_timeout() {
|
||||
drop(s);
|
||||
builder::trigger_idle_summary(&self.session, Some(&self.summarizer())).await;
|
||||
tracing::info!("⏰ 检测到 12h 空闲,已生成摘要");
|
||||
tracing::info!(target: "ias::tool", "⏰ 检测到 12h 空闲,已生成摘要");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,7 +308,7 @@ impl Conversation {
|
||||
if let Some(calls) = tool_calls
|
||||
&& !calls.is_empty() {
|
||||
used_tools = true;
|
||||
tracing::info!(
|
||||
tracing::info!(target: "ias::tool",
|
||||
"🔧 LLM 请求 {} 个工具: {}",
|
||||
calls.len(),
|
||||
calls
|
||||
@@ -339,12 +339,12 @@ impl Conversation {
|
||||
if result.starts_with(ASYNC_MARKER) {
|
||||
has_async = true;
|
||||
async_count += 1;
|
||||
tracing::info!("📦 {} → 异步入队", tc.function.name);
|
||||
tracing::info!(target: "ias::tool", "📦 {} → 异步入队", tc.function.name);
|
||||
// 不添加到 session,等待 ToolResult 异步回喂
|
||||
continue;
|
||||
}
|
||||
|
||||
tracing::info!("📦 {} → {}", tc.function.name, result);
|
||||
tracing::info!(target: "ias::tool", "📦 {} → {}", tc.function.name, result);
|
||||
self.session.lock().await.add_tool_result(
|
||||
&tc.id,
|
||||
&tc.function.name,
|
||||
@@ -379,7 +379,7 @@ impl Conversation {
|
||||
drop(s);
|
||||
builder::trigger_overflow_summary(&self.session, Some(&self.summarizer()))
|
||||
.await;
|
||||
tracing::info!("📦 上下文超预算,已触发溢出摘要");
|
||||
tracing::info!(target: "ias::tool", "📦 上下文超预算,已触发溢出摘要");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+57
-11
@@ -1,23 +1,37 @@
|
||||
//! ## 日志初始化
|
||||
//! ## 日志初始化 —— 四类日志分文件存储
|
||||
//!
|
||||
//! ### 日志分类
|
||||
//! | 类别 | target | 文件 | 内容 |
|
||||
//! |------|--------|------|------|
|
||||
//! | 认证日志 | `ias::auth` | `auth.log` | 登录、token、监听器注册 |
|
||||
//! | 消息队列日志 | `ias::queue` | `queue.log` | 消息入队/出队内容、队列路由 |
|
||||
//! | 工具日志 | `ias::tool` | `tool.log` | 工具调用参数、输出、耗时、工具错误 |
|
||||
//! | 错误日志 | (无特殊target) | `error.log` | 所有非工具调用的 ERROR/WARN |
|
||||
//!
|
||||
//! ### 两种模式
|
||||
//! - **终端模式** — 彩色输出到 stderr,适合开发调试
|
||||
//! - **文件模式** (`with_file=true`) — 同时输出到终端和日滚文件
|
||||
//! `~/.ias/logs/ias.log.YYYY-MM-DD`
|
||||
//! - **文件模式** (`with_file=true`) — 终端 + 四类日滚文件
|
||||
//!
|
||||
//! 日志级别通过 `RUST_LOG` 环境变量控制(默认 `info`)。
|
||||
|
||||
use std::path::PathBuf;
|
||||
use tracing::Level;
|
||||
use tracing_subscriber::filter::{filter_fn, Targets};
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use tracing_subscriber::{EnvFilter, Layer};
|
||||
|
||||
/// ## 日志初始化
|
||||
/// ## 日志初始化 —— 四类日志分文件存储
|
||||
///
|
||||
/// ### 日志分类
|
||||
/// * **认证日志** (`auth.log`) — 登录、token、监听器注册等认证相关
|
||||
/// * **消息队列日志** (`queue.log`) — 消息入队/出队内容、队列路由
|
||||
/// * **工具日志** (`tool.log`) — 工具调用参数、输出、耗时、工具错误
|
||||
/// * **错误日志** (`error.log`) — 所有非工具调用的 ERROR/WARN 级别日志
|
||||
///
|
||||
/// ### 两种模式
|
||||
/// * **终端模式** — 彩色输出到 stderr,适合开发调试
|
||||
/// * **文件模式** (`with_file=true`) — 同时输出到终端和日滚文件
|
||||
/// `~/.ias/logs/ias.log.YYYY-MM-DD`
|
||||
/// * **文件模式** (`with_file=true`) — 终端 + 四类日滚文件
|
||||
///
|
||||
/// 日志级别通过 `RUST_LOG` 环境变量控制(默认 `info`)。
|
||||
pub fn init_logger(log_dir: Option<&str>, with_file: bool) {
|
||||
@@ -33,16 +47,48 @@ pub fn init_logger(log_dir: Option<&str>, with_file: bool) {
|
||||
let dir_path = PathBuf::from(dir);
|
||||
std::fs::create_dir_all(&dir_path).ok();
|
||||
|
||||
let file_appender = tracing_appender::rolling::daily(&dir_path, "ias.log");
|
||||
let file_layer = tracing_subscriber::fmt::layer()
|
||||
// ── 认证日志 ──
|
||||
let auth_appender = tracing_appender::rolling::daily(&dir_path, "auth.log");
|
||||
let auth_layer = tracing_subscriber::fmt::layer()
|
||||
.with_target(false)
|
||||
.with_ansi(false)
|
||||
.with_writer(file_appender);
|
||||
.with_writer(auth_appender)
|
||||
.with_filter(Targets::new().with_target("ias::auth", Level::INFO));
|
||||
|
||||
// ── 消息队列日志 ──
|
||||
let queue_appender = tracing_appender::rolling::daily(&dir_path, "queue.log");
|
||||
let queue_layer = tracing_subscriber::fmt::layer()
|
||||
.with_target(false)
|
||||
.with_ansi(false)
|
||||
.with_writer(queue_appender)
|
||||
.with_filter(Targets::new().with_target("ias::queue", Level::INFO));
|
||||
|
||||
// ── 工具日志 ──
|
||||
let tool_appender = tracing_appender::rolling::daily(&dir_path, "tool.log");
|
||||
let tool_layer = tracing_subscriber::fmt::layer()
|
||||
.with_target(false)
|
||||
.with_ansi(false)
|
||||
.with_writer(tool_appender)
|
||||
.with_filter(Targets::new().with_target("ias::tool", Level::INFO));
|
||||
|
||||
// ── 错误日志(所有非工具调用的 WARN/ERROR 级别日志) ──
|
||||
let error_appender = tracing_appender::rolling::daily(&dir_path, "error.log");
|
||||
let error_layer = tracing_subscriber::fmt::layer()
|
||||
.with_target(false)
|
||||
.with_ansi(false)
|
||||
.with_writer(error_appender)
|
||||
.with_filter(filter_fn(|metadata| {
|
||||
metadata.level() <= &Level::WARN
|
||||
&& !metadata.target().starts_with("ias::tool")
|
||||
}));
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(env_filter)
|
||||
.with(terminal)
|
||||
.with(file_layer)
|
||||
.with(auth_layer)
|
||||
.with(queue_layer)
|
||||
.with(tool_layer)
|
||||
.with(error_layer)
|
||||
.init();
|
||||
return;
|
||||
}
|
||||
|
||||
+6
-6
@@ -59,7 +59,7 @@ async fn main() {
|
||||
let fallback = std::path::PathBuf::from(&home).join(".ias").join(".env");
|
||||
if fallback.exists() {
|
||||
dotenvy::from_path(&fallback).ok();
|
||||
tracing::info!("使用全局环境配置: {}", fallback.display());
|
||||
tracing::info!(target: "ias::auth", "使用全局环境配置: {}", fallback.display());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ async fn main() {
|
||||
|
||||
let database = match Database::connect().await {
|
||||
Ok(db) => {
|
||||
info!("✅ 数据库连接成功");
|
||||
info!(target: "ias::auth", "✅ 数据库连接成功");
|
||||
Arc::new(db)
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -159,9 +159,9 @@ async fn cmd_login(timeout_secs: u64, database: &Arc<Database>) {
|
||||
|
||||
// 存数据库
|
||||
if let Err(e) = db::models::save_auth(database.pool(), &auth).await {
|
||||
error!("保存 auth 到数据库失败: {}", e);
|
||||
error!(target: "ias::auth", "保存 auth 到数据库失败: {}", e);
|
||||
} else {
|
||||
info!("认证信息已保存到数据库");
|
||||
info!(target: "ias::auth", "认证信息已保存到数据库");
|
||||
}
|
||||
|
||||
println!("\n✅ 登录成功!");
|
||||
@@ -172,7 +172,7 @@ async fn cmd_login(timeout_secs: u64, database: &Arc<Database>) {
|
||||
);
|
||||
println!(" API: {}", result.base_url);
|
||||
}
|
||||
Err(e) => error!("登录失败: {}", e),
|
||||
Err(e) => error!(target: "ias::auth", "登录失败: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ async fn cmd_send(
|
||||
let auth = match auth {
|
||||
Some(a) => a,
|
||||
None => {
|
||||
error!("未登录,请先执行 login 命令");
|
||||
error!(target: "ias::auth", "未登录,请先执行 login 命令");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
+8
-6
@@ -110,11 +110,11 @@ impl QueueRunner {
|
||||
|
||||
/// 启动路由主循环(阻塞,应在 tokio::spawn 中运行)
|
||||
pub async fn run(&self) {
|
||||
info!("QueueRunner 已启动");
|
||||
info!(target: "ias::queue", "QueueRunner 已启动");
|
||||
loop {
|
||||
// 检查关闭信号
|
||||
if self.shutdown.load(Ordering::Relaxed) {
|
||||
info!("QueueRunner 收到关闭信号,停止路由");
|
||||
info!(target: "ias::queue", "QueueRunner 收到关闭信号,停止路由");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -138,6 +138,7 @@ impl QueueRunner {
|
||||
|
||||
if let Err(_e) = result {
|
||||
warn!(
|
||||
target: "ias::queue",
|
||||
"路由消息失败 (target={:?}, user={}, corr={}): 通道已关闭,跳过",
|
||||
target, user_id, correlation_id
|
||||
);
|
||||
@@ -155,7 +156,7 @@ impl QueueRunner {
|
||||
}
|
||||
}
|
||||
}
|
||||
info!("QueueRunner 已停止");
|
||||
info!(target: "ias::queue", "QueueRunner 已停止");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,6 +172,7 @@ impl EnqueueHandle {
|
||||
pub async fn enqueue(&self, msg: PipelineMessage) {
|
||||
if self.shutdown.load(Ordering::Relaxed) {
|
||||
warn!(
|
||||
target: "ias::queue",
|
||||
"队列已关闭,丢弃消息: user={}",
|
||||
msg.user_id()
|
||||
);
|
||||
@@ -200,7 +202,7 @@ pub async fn drain_and_shutdown(
|
||||
runner_shutdown: &ShutdownHandle,
|
||||
consumer_handles: Vec<tokio::task::JoinHandle<()>>,
|
||||
) {
|
||||
info!("开始关闭队列系统...");
|
||||
info!(target: "ias::queue", "开始关闭队列系统...");
|
||||
|
||||
// 1. 阻止新消息入队(设置关闭标志)
|
||||
runner_shutdown.shutdown();
|
||||
@@ -211,9 +213,9 @@ pub async fn drain_and_shutdown(
|
||||
// 3. 等待消费者 task 完成
|
||||
for handle in consumer_handles {
|
||||
if let Err(e) = handle.await {
|
||||
error!("消费者 task 结束异常: {:?}", e);
|
||||
error!(target: "ias::queue", "消费者 task 结束异常: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
info!("队列系统已完全关闭");
|
||||
info!(target: "ias::queue", "队列系统已完全关闭");
|
||||
}
|
||||
|
||||
+4
-4
@@ -49,13 +49,13 @@ impl Scheduler {
|
||||
let pool = match self.pool.as_ref() {
|
||||
Some(p) => p.clone(),
|
||||
None => {
|
||||
tracing::info!("调度器:数据库未配置,跳过");
|
||||
tracing::info!(target: "ias::queue", "调度器:数据库未配置,跳过");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut tick = interval(Duration::from_secs(5));
|
||||
tracing::info!("调度器已启动(每 5s 检查)");
|
||||
tracing::info!(target: "ias::queue", "调度器已启动(每 5s 检查)");
|
||||
|
||||
loop {
|
||||
tick.tick().await;
|
||||
@@ -69,7 +69,7 @@ impl Scheduler {
|
||||
};
|
||||
|
||||
for task in tasks {
|
||||
tracing::info!("⏰ 执行定时任务: {}", task.name);
|
||||
tracing::info!(target: "ias::queue", "⏰ 执行定时任务: {}", task.name);
|
||||
|
||||
// 执行任务
|
||||
let result = execute_task(&task).await;
|
||||
@@ -83,7 +83,7 @@ impl Scheduler {
|
||||
tracing::error!("发送定时任务通知失败: {}", e);
|
||||
}
|
||||
|
||||
tracing::info!("✅ 定时任务完成: {} → {}", task.name, result);
|
||||
tracing::info!(target: "ias::queue", "✅ 定时任务完成: {} → {}", task.name, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-15
@@ -41,8 +41,6 @@
|
||||
|
||||
use reqwest::Client;
|
||||
use std::time::Duration;
|
||||
use tracing::info;
|
||||
|
||||
use crate::tools::types::{RiskLevel, SkillResult, SkillSpec};
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
@@ -273,8 +271,8 @@ impl AmapClient {
|
||||
params.push(("waypoints", wp));
|
||||
}
|
||||
|
||||
info!(
|
||||
"parms: {:?}",
|
||||
tracing::info!(target: "ias::tool",
|
||||
"驾车路线请求 parms: {:?}",
|
||||
serde_json::to_string(¶ms).unwrap_or_default()
|
||||
);
|
||||
|
||||
@@ -290,8 +288,8 @@ impl AmapClient {
|
||||
.map_err(|e| format!("解析驾车路线响应失败: {}", e))?;
|
||||
|
||||
if resp["status"].as_str() != Some("1") {
|
||||
info!(
|
||||
"parms: {:?}",
|
||||
tracing::info!(target: "ias::tool",
|
||||
"驾车路线失败 parms: {:?}",
|
||||
serde_json::to_string(&resp).unwrap_or_default()
|
||||
);
|
||||
return Err(format!(
|
||||
@@ -456,7 +454,7 @@ pub async fn poi_search_execute(params: serde_json::Value) -> SkillResult {
|
||||
};
|
||||
|
||||
let result = if let Some(loc) = location {
|
||||
tracing::info!(
|
||||
tracing::info!(target: "ias::tool",
|
||||
"📍 周边搜索: {} @ {} (radius={}m, sort=distance)",
|
||||
keywords,
|
||||
loc,
|
||||
@@ -466,7 +464,7 @@ pub async fn poi_search_execute(params: serde_json::Value) -> SkillResult {
|
||||
.poi_around(keywords, loc, radius, page_num, page_size, "distance")
|
||||
.await
|
||||
} else {
|
||||
tracing::info!("🔍 POI 搜索: {} city={:?}", keywords, city);
|
||||
tracing::info!(target: "ias::tool", "🔍 POI 搜索: {} city={:?}", keywords, city);
|
||||
client
|
||||
.poi_text_search(keywords, city, page_num, page_size)
|
||||
.await
|
||||
@@ -513,7 +511,7 @@ pub async fn geocode_execute(params: serde_json::Value) -> SkillResult {
|
||||
Err(e) => return SkillResult::error(e),
|
||||
};
|
||||
|
||||
tracing::info!("📍 地理编码: {} city={:?}", address, city);
|
||||
tracing::info!(target: "ias::tool", "📍 地理编码: {} city={:?}", address, city);
|
||||
|
||||
match client.geocode(address, city).await {
|
||||
Ok(data) => SkillResult::ok_with_data("✅ 地理编码完成".to_string(), data),
|
||||
@@ -554,7 +552,7 @@ pub async fn reverse_geocode_execute(params: serde_json::Value) -> SkillResult {
|
||||
Err(e) => return SkillResult::error(e),
|
||||
};
|
||||
|
||||
tracing::info!("📍 逆地理编码: {}", location);
|
||||
tracing::info!(target: "ias::tool", "📍 逆地理编码: {}", location);
|
||||
|
||||
match client.regeocode(location).await {
|
||||
Ok(data) => SkillResult::ok_with_data("✅ 逆地理编码完成".to_string(), data),
|
||||
@@ -604,7 +602,7 @@ pub async fn route_plan_execute(params: serde_json::Value) -> SkillResult {
|
||||
Err(e) => return SkillResult::error(e),
|
||||
};
|
||||
|
||||
tracing::info!(
|
||||
tracing::info!(target: "ias::tool",
|
||||
"🛣️ 路径规划: type={}, {} → {}",
|
||||
route_type,
|
||||
origin,
|
||||
@@ -706,7 +704,7 @@ pub async fn travel_plan_execute(params: serde_json::Value) -> SkillResult {
|
||||
Err(e) => return SkillResult::error(e),
|
||||
};
|
||||
|
||||
tracing::info!(
|
||||
tracing::info!(target: "ias::tool",
|
||||
"🗺️ 旅游规划: city={}, interests={:?}, route={}",
|
||||
city,
|
||||
interests,
|
||||
@@ -718,7 +716,7 @@ pub async fn travel_plan_execute(params: serde_json::Value) -> SkillResult {
|
||||
|
||||
// 第一步:搜索各类兴趣点
|
||||
for interest in &interests {
|
||||
tracing::info!("📍 搜索 {}...", interest);
|
||||
tracing::info!(target: "ias::tool", "📍 搜索 {}...", interest);
|
||||
match client.poi_text_search(interest, Some(city), 1, 5).await {
|
||||
Ok(data) => {
|
||||
if let Some(pois) = data["pois"].as_array() {
|
||||
@@ -740,10 +738,10 @@ pub async fn travel_plan_execute(params: serde_json::Value) -> SkillResult {
|
||||
}));
|
||||
}
|
||||
all_pois.extend(pois.clone());
|
||||
tracing::info!(" 找到 {} 个 {}", pois.len(), interest);
|
||||
tracing::info!(target: "ias::tool", " 找到 {} 个 {}", pois.len(), interest);
|
||||
}
|
||||
}
|
||||
Err(e) => tracing::warn!(" 搜索 {} 失败: {}", interest, e),
|
||||
Err(e) => tracing::warn!(target: "ias::tool", " 搜索 {} 失败: {}", interest, e),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -258,7 +258,7 @@ impl QWeatherClient {
|
||||
async fn lookup_city(&self, location: &str) -> Result<String, String> {
|
||||
// 检查缓存
|
||||
if let Some(id) = get_cached_city(location) {
|
||||
tracing::debug!("🏙️ 城市缓存命中: {} → {}", location, id);
|
||||
tracing::debug!(target: "ias::tool", "🏙️ 城市缓存命中: {} → {}", location, id);
|
||||
return Ok(id);
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ impl QWeatherClient {
|
||||
|
||||
let id = city.id.clone();
|
||||
cache_city(location.to_string(), id.clone());
|
||||
tracing::debug!("🏙️ 城市查询: {} → {} ({})", location, city.name, id);
|
||||
tracing::debug!(target: "ias::tool", "🏙️ 城市查询: {} → {} ({})", location, city.name, id);
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ impl WeChatClient {
|
||||
})
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("轮询二维码状态网络错误: {},继续等待", e);
|
||||
warn!(target: "ias::auth", "轮询二维码状态网络错误: {},继续等待", e);
|
||||
Ok(StatusResponse {
|
||||
status: "wait".to_string(),
|
||||
bot_token: None,
|
||||
@@ -175,7 +175,7 @@ impl WeChatClient {
|
||||
on_qrcode: impl Fn(&str),
|
||||
timeout_secs: u64,
|
||||
) -> Result<LoginResult, String> {
|
||||
info!("开始微信扫码登录...");
|
||||
info!(target: "ias::auth", "开始微信扫码登录...");
|
||||
|
||||
let (qrcode, qrcode_img_content) = self.get_qrcode().await?;
|
||||
|
||||
@@ -217,7 +217,7 @@ impl WeChatClient {
|
||||
"scaned_but_redirect" => {
|
||||
if let Some(host) = &status.redirect_host {
|
||||
current_base = format!("https://{}", host);
|
||||
info!("IDC 重定向到: {}", current_base);
|
||||
info!(target: "ias::auth", "IDC 重定向到: {}", current_base);
|
||||
}
|
||||
}
|
||||
"confirmed" => {
|
||||
@@ -236,7 +236,7 @@ impl WeChatClient {
|
||||
*self.token.lock().await = token.clone();
|
||||
*self.account_id.lock().await = account_id.clone();
|
||||
|
||||
info!("✅ 登录成功! bot_id={}", account_id);
|
||||
info!(target: "ias::auth", "✅ 登录成功! bot_id={}", account_id);
|
||||
return Ok(LoginResult {
|
||||
token,
|
||||
account_id,
|
||||
@@ -248,7 +248,7 @@ impl WeChatClient {
|
||||
return Err("二维码已过期".to_string());
|
||||
}
|
||||
_ => {
|
||||
warn!("未知状态: {}", status.status);
|
||||
warn!(target: "ias::auth", "未知状态: {}", status.status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +279,7 @@ impl WeChatClient {
|
||||
));
|
||||
}
|
||||
|
||||
info!("监听器已注册");
|
||||
info!(target: "ias::auth", "监听器已注册");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -298,7 +298,7 @@ impl WeChatClient {
|
||||
.await
|
||||
.map_err(|e| format!("解析 notifyStop 响应失败: {}", e))?;
|
||||
|
||||
info!("监听器已注销");
|
||||
info!(target: "ias::auth", "监听器已注销");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -322,6 +322,7 @@ impl WeChatClient {
|
||||
.map_err(|e| format!("解析 getUpdates 响应失败: {}", e))?;
|
||||
if resp.ret != 0 || resp.errcode.unwrap_or(0) != 0 {
|
||||
tracing::warn!(
|
||||
target: "ias::auth",
|
||||
"getUpdates 返回错误: ret={} errcode={:?} errmsg={:?}",
|
||||
resp.ret,
|
||||
resp.errcode,
|
||||
|
||||
Reference in New Issue
Block a user