Pārlūkot izejas kodu

准备全项目级别改造log服务。

wangxq 1 nedēļu atpakaļ
vecāks
revīzija
eb0f5f1fd4

+ 0 - 287
docs/全局log服务开发方案.md

@@ -1,287 +0,0 @@
-# Vanna.ai 源码日志系统深入分析报告
-
-Vanna.ai 作为一个基于 RAG 的 SQL 生成框架,其日志系统设计相对简化且**主要针对 Jupyter Notebook 环境进行优化**。通过对源码的深入分析,我发现该项目采用了**最小化日志设计**,优先考虑交互式环境的用户体验而非传统的企业级日志管理。
-
-## 核心发现:简化的日志架构
-
-### VannaBase 中的日志方法
-
-在 `src/vanna/base/base.py` 的 VannaBase 抽象基类中,存在一个**可重写的日志方法**(第77-78行):
-
-```python
-def log(self, message: str):
-    """
-    可被子类重写的日志方法
-    默认使用 print 语句输出
-    """
-    print(message)
-```
-
-**设计特点分析:**
-- **方法签名简单**:仅接受 `message` 参数,不支持日志级别区分
-- **默认实现**:直接使用 `print()` 语句,无格式化或时间戳
-- **重写机制**:子类可完全自定义日志行为
-- **Jupyter 优化**:官方明确表示"由于 Jupyter notebooks 的行为特性,日志函数故意使用 print 语句"
-
-### 项目级别日志对象检查结果
-
-经过全面的源码结构分析,**vanna.ai 项目中不存在项目级别的统一日志对象**:
-
-- ❌ **无专门日志模块**:没有 `logging.py`、`logger.py` 等专门文件
-- ❌ **无全局日志配置**:缺乏统一的日志管理和配置机制  
-- ❌ **无标准日志级别**:不支持 `logger.debug()`、`logger.warning()` 等标准方法
-- ❌ **无日志导出接口**:无法获取全局 logger 对象用于统一管理
-
-## 当前日志使用模式分析
-
-### 实际使用场景
-
-通过对各模块的代码分析,发现日志使用主要集中在以下场景:
-
-**1. SQL 提取成功通知**
-```python
-def _extract_sql(self, llm_response: str) -> str:
-    # SQL 提取逻辑
-    if sqls:
-        sql = sqls[-1]
-        self.log(f"Extracted SQL: {sql}")  # 关键操作日志
-        return sql
-```
-
-**2. 模型调用信息**
-```python
-# 在 OpenAI_Chat 模块中
-print(f"Using model {model} for {num_tokens} tokens (approx)")
-```
-
-**3. 错误信息输出**
-```python
-# 嵌入生成错误
-print(f"Error generating embedding: {e}")
-```
-
-### 日志分布特征
-
-- **print 语句为主**:项目广泛使用 `print()` 而非标准 logging 模块
-- **无结构化设计**:缺乏统一的日志格式和级别管理
-- **调试导向**:主要用于开发调试和 Jupyter 环境的状态展示
-- **无持久化**:所有日志输出到标准输出,无文件存储能力
-
-## 日志配置和扩展能力分析
-
-### 当前配置支持
-
-**基础配置结构:**
-```python
-class VannaBase(ABC):
-    def __init__(self, config=None):
-        if config is None:
-            config = {}
-        self.config = config
-        # 无日志相关配置参数
-```
-
-**配置局限性:**
-- ❌ **无日志级别配置**:不支持 `log_level` 参数
-- ❌ **无输出路径配置**:无法配置日志文件输出路径
-- ❌ **无滚动策略配置**:不支持日志轮转和清理策略
-- ❌ **无格式化配置**:无法自定义日志输出格式
-
-### 扩展性设计优势
-
-尽管功能简化,vanna.ai 的架构具备良好的**扩展性基础**:
-
-**1. 策略模式支持**
-```python
-class CustomVanna(VannaBase):
-    def log(self, message: str):
-        # 自定义日志实现
-        import logging
-        logging.info(message)
-```
-
-**2. 多重继承兼容**
-```python
-class MyVanna(ChromaDB_VectorStore, OpenAI_Chat):
-    def log(self, message: str):
-        # 统一的日志策略
-        self._custom_logger.info(message)
-```
-
-## 企业级日志需求的实现方案
-
-### 推荐的自定义实现
-
-基于当前架构,为支持企业级日志管理,建议采用以下实现模式:
-
-**1. 基础日志增强**
-```python
-import logging
-import os
-from typing import Optional
-
-class EnhancedVannaBase(VannaBase):
-    def __init__(self, config=None):
-        super().__init__(config)
-        self._setup_logging()
-    
-    def _setup_logging(self):
-        """设置增强的日志系统"""
-        log_config = self.config.get('logging', {})
-        
-        # 日志级别配置
-        level = log_config.get('level', 'INFO')
-        
-        # 日志格式配置
-        format_str = log_config.get(
-            'format', 
-            '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
-        )
-        
-        # 输出配置
-        log_file = log_config.get('file_path')
-        
-        # 创建logger
-        self.logger = logging.getLogger(f'vanna.{self.__class__.__name__}')
-        self.logger.setLevel(getattr(logging, level.upper()))
-        
-        # 添加处理器
-        if log_file:
-            handler = logging.FileHandler(log_file)
-        else:
-            handler = logging.StreamHandler()
-        
-        formatter = logging.Formatter(format_str)
-        handler.setFormatter(formatter)
-        self.logger.addHandler(handler)
-    
-    def log(self, message: str, level: str = 'info'):
-        """增强的日志方法"""
-        log_method = getattr(self.logger, level.lower(), self.logger.info)
-        log_method(message)
-    
-    # 标准日志方法
-    def debug(self, message: str):
-        self.log(message, 'debug')
-    
-    def info(self, message: str):
-        self.log(message, 'info')
-    
-    def warning(self, message: str):
-        self.log(message, 'warning')
-    
-    def error(self, message: str):
-        self.log(message, 'error')
-```
-
-**2. 配置文件支持**
-```python
-# config.yaml
-logging:
-  level: DEBUG
-  format: "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
-  file_path: "/var/log/vanna/app.log"
-  rotation:
-    max_size: "10MB"
-    backup_count: 5
-```
-
-**3. 日志滚动策略实现**
-```python
-from logging.handlers import RotatingFileHandler
-
-def _setup_file_handler(self, log_config):
-    """设置带滚动的文件处理器"""
-    log_file = log_config.get('file_path')
-    rotation = log_config.get('rotation', {})
-    
-    max_bytes = self._parse_size(rotation.get('max_size', '10MB'))
-    backup_count = rotation.get('backup_count', 5)
-    
-    handler = RotatingFileHandler(
-        log_file, 
-        maxBytes=max_bytes,
-        backupCount=backup_count
-    )
-    return handler
-```
-
-## 社区需求和发展趋势
-
-### GitHub Issue #387 反馈分析
-
-社区对日志系统改进存在**强烈需求**:
-
-- **用户反馈**:"Vanna uses print instead of logging" 不利于生产环境使用
-- **改进建议**:升级到标准 logging 解决方案
-- **向后兼容需求**:需要在改进时保持 Jupyter 环境的友好性
-
-### 推荐的渐进式改进路径
-
-**阶段1:兼容性增强**
-- 在 VannaBase 中添加可选的标准 logging 支持
-- 通过配置参数在 print 和 logging 之间切换
-- 保持现有 API 完全向后兼容
-
-**阶段2:功能完善**
-- 添加日志级别管理和过滤能力
-- 支持结构化日志输出(JSON 格式)
-- 实现日志文件输出和滚动策略
-
-**阶段3:企业级特性**
-- 添加日志聚合和监控集成支持
-- 支持分布式日志追踪
-- 提供日志分析和可视化工具
-
-## 定制开发建议
-
-对于基于 vanna 框架进行定制开发的项目,建议采用以下策略:
-
-### 短期解决方案
-
-**1. 重写基类日志方法**
-```python
-class ProductionVanna(ChromaDB_VectorStore, OpenAI_Chat):
-    def __init__(self, config=None):
-        super().__init__(config)
-        self.setup_production_logging()
-    
-    def log(self, message: str):
-        self.logger.info(message)
-    
-    def setup_production_logging(self):
-        # 实现企业级日志配置
-        pass
-```
-
-**2. 环境变量配置**
-```python
-import os
-
-LOG_LEVEL = os.getenv('VANNA_LOG_LEVEL', 'INFO')
-LOG_FILE = os.getenv('VANNA_LOG_FILE', None)
-```
-
-### 长期架构规划
-
-**1. 独立日志模块设计**
-- 创建专门的 `vanna.logging` 模块
-- 提供统一的日志管理接口
-- 支持插件化的日志处理器
-
-**2. 配置驱动的日志系统**
-- 支持 YAML/JSON 配置文件
-- 动态日志级别调整
-- 运行时日志配置热更新
-
-## 总结与展望
-
-Vanna.ai 当前的日志系统体现了**简化设计**的理念,优先考虑 Jupyter 环境的用户体验。虽然缺乏企业级日志管理功能,但其**良好的架构扩展性**为自定义实现提供了坚实基础。
-
-**关键技术特征:**
-- **当前状态**:基于 print 的简化日志,主要服务于交互式环境
-- **扩展能力**:通过重写 `log()` 方法可实现完全自定义的日志策略
-- **发展趋势**:社区推动向标准 logging 模块迁移,同时保持向后兼容
-
-**定制开发建议:**
-对于需要企业级日志功能的项目,建议通过继承和配置的方式实现增强的日志系统,在保持 vanna.ai 核心功能的同时,添加生产环境所需的日志管理能力。这种方式既能利用现有的架构优势,又能满足复杂的日志需求。

+ 894 - 0
docs/全局log服务改造方案.md

@@ -0,0 +1,894 @@
+# 项目日志系统改造设计方案(精简实用版)
+
+## 1. 整体设计理念
+
+基于您的需求,设计一套类似Log4j的统一日志服务,专注核心功能:
+- 统一的日志级别管理(info/error/debug/warning)
+- 可配置的日志输出路径
+- 支持控制台和文件输出
+- 不同模块独立日志文件(data_pipeline、agent、vanna等)
+- 自动日志轮转和清理
+- 与现有vanna/langchain/langgraph技术栈兼容
+
+## 2. 核心架构设计
+
+### 2.1 精简的日志服务层次结构
+
+```
+项目根目录/
+├── core/
+│   └── logging/
+│       ├── __init__.py           # 日志服务入口
+│       └── log_manager.py        # 核心日志管理器
+├── logs/                         # 日志文件目录
+│   ├── data_pipeline.log        # data_pipeline模块日志
+│   ├── agent.log                # agent模块日志
+│   ├── vanna.log                # vanna模块日志
+│   ├── langchain.log            # langchain模块日志
+│   ├── langgraph.log            # langgraph模块日志
+│   └── app.log                  # 主应用日志
+└── config/
+    └── logging_config.yaml       # 日志配置文件
+```
+
+### 2.2 核心日志管理器设计(增强版)
+
+基于用户反馈,增强版包含以下特性:
+- **异步日志支持**
+- **灵活的上下文管理**(user_id可选)
+- **错误降级策略**
+- **重点支持citu_app.py**
+
+```python
+# core/logging/log_manager.py
+import logging
+import logging.handlers
+import os
+from typing import Dict, Optional
+from pathlib import Path
+import yaml
+import asyncio
+from concurrent.futures import ThreadPoolExecutor
+import contextvars
+
+# 上下文变量,存储可选的上下文信息
+log_context = contextvars.ContextVar('log_context', default={})
+
+class ContextFilter(logging.Filter):
+    """添加上下文信息到日志记录"""
+    def filter(self, record):
+        ctx = log_context.get()
+        # 设置默认值,避免格式化错误
+        record.session_id = ctx.get('session_id', 'N/A')
+        record.user_id = ctx.get('user_id', 'anonymous')
+        record.request_id = ctx.get('request_id', 'N/A')
+        return True
+
+class LogManager:
+    """统一日志管理器 - 类似Log4j的功能"""
+    
+    _instance = None
+    _loggers: Dict[str, logging.Logger] = {}
+    _initialized = False
+    _executor = None
+    _fallback_to_console = False  # 标记是否降级到控制台
+    
+    def __new__(cls):
+        if cls._instance is None:
+            cls._instance = super().__new__(cls)
+        return cls._instance
+    
+    def __init__(self):
+        if not self._initialized:
+            self.config = None
+            self.base_log_dir = Path("logs")
+            self._executor = ThreadPoolExecutor(max_workers=2, thread_name_prefix="log")
+            self._setup_base_directory()
+            LogManager._initialized = True
+    
+    def initialize(self, config_path: str = "config/logging_config.yaml"):
+        """初始化日志系统"""
+        self.config = self._load_config(config_path)
+        self._setup_base_directory()
+        self._configure_root_logger()
+    
+    def get_logger(self, name: str, module: str = "default") -> logging.Logger:
+        """获取指定模块的logger"""
+        logger_key = f"{module}.{name}"
+        
+        if logger_key not in self._loggers:
+            logger = logging.getLogger(logger_key)
+            self._configure_logger(logger, module)
+            self._loggers[logger_key] = logger
+        
+        return self._loggers[logger_key]
+    
+    async def alog(self, logger: logging.Logger, level: str, message: str, **kwargs):
+        """异步日志方法"""
+        loop = asyncio.get_event_loop()
+        await loop.run_in_executor(
+            self._executor,
+            lambda: getattr(logger, level)(message, **kwargs)
+        )
+    
+    def set_context(self, **kwargs):
+        """设置日志上下文(可选)"""
+        ctx = log_context.get()
+        ctx.update(kwargs)
+        log_context.set(ctx)
+    
+    def clear_context(self):
+        """清除日志上下文"""
+        log_context.set({})
+    
+    def _load_config(self, config_path: str) -> dict:
+        """加载配置文件(带错误处理)"""
+        try:
+            with open(config_path, 'r', encoding='utf-8') as f:
+                return yaml.safe_load(f)
+        except FileNotFoundError:
+            print(f"[WARNING] 配置文件 {config_path} 未找到,使用默认配置")
+            return self._get_default_config()
+        except Exception as e:
+            print(f"[ERROR] 加载配置文件失败: {e},使用默认配置")
+            return self._get_default_config()
+    
+    def _setup_base_directory(self):
+        """创建日志目录(带降级策略)"""
+        try:
+            self.base_log_dir.mkdir(parents=True, exist_ok=True)
+            self._fallback_to_console = False
+        except Exception as e:
+            print(f"[WARNING] 无法创建日志目录 {self.base_log_dir},将只使用控制台输出: {e}")
+            self._fallback_to_console = True
+    
+    def _configure_logger(self, logger: logging.Logger, module: str):
+        """配置具体的logger(支持降级)"""
+        module_config = self.config.get('modules', {}).get(module, self.config['default'])
+        
+        # 设置日志级别
+        level = getattr(logging, module_config['level'].upper())
+        logger.setLevel(level)
+        
+        # 清除已有处理器
+        logger.handlers.clear()
+        logger.propagate = False
+        
+        # 添加控制台处理器
+        if module_config.get('console', {}).get('enabled', True):
+            console_handler = self._create_console_handler(module_config['console'])
+            console_handler.addFilter(ContextFilter())
+            logger.addHandler(console_handler)
+        
+        # 添加文件处理器(如果没有降级到控制台)
+        if not self._fallback_to_console and module_config.get('file', {}).get('enabled', True):
+            try:
+                file_handler = self._create_file_handler(module_config['file'], module)
+                file_handler.addFilter(ContextFilter())
+                logger.addHandler(file_handler)
+            except Exception as e:
+                print(f"[WARNING] 无法创建文件处理器: {e}")
+    
+    def _get_default_config(self) -> dict:
+        """获取默认配置"""
+        return {
+            'global': {'base_level': 'INFO'},
+            'default': {
+                'level': 'INFO',
+                'console': {
+                    'enabled': True,
+                    'level': 'INFO',
+                    'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
+                },
+                'file': {
+                    'enabled': True,
+                    'level': 'DEBUG',
+                    'filename': 'app.log',
+                    'format': '%(asctime)s [%(levelname)s] [%(name)s] [user:%(user_id)s] [session:%(session_id)s] %(filename)s:%(lineno)d - %(message)s',
+                    'rotation': {
+                        'enabled': True,
+                        'max_size': '50MB',
+                        'backup_count': 10
+                    }
+                }
+            },
+            'modules': {}
+        }
+    
+    def _create_console_handler(self, console_config: dict) -> logging.StreamHandler:
+        """创建控制台处理器"""
+        handler = logging.StreamHandler()
+        handler.setLevel(getattr(logging, console_config.get('level', 'INFO').upper()))
+        
+        formatter = logging.Formatter(
+            console_config.get('format', '%(asctime)s [%(levelname)s] %(name)s: %(message)s'),
+            datefmt='%Y-%m-%d %H:%M:%S'
+        )
+        handler.setFormatter(formatter)
+        return handler
+    
+    def _create_file_handler(self, file_config: dict, module: str) -> logging.Handler:
+        """创建文件处理器(支持自动轮转)"""
+        log_file = self.base_log_dir / file_config.get('filename', f'{module}.log')
+        
+        # 使用RotatingFileHandler实现自动轮转和清理
+        rotation_config = file_config.get('rotation', {})
+        if rotation_config.get('enabled', False):
+            handler = logging.handlers.RotatingFileHandler(
+                log_file,
+                maxBytes=self._parse_size(rotation_config.get('max_size', '50MB')),
+                backupCount=rotation_config.get('backup_count', 10),
+                encoding='utf-8'
+            )
+        else:
+            handler = logging.FileHandler(log_file, encoding='utf-8')
+        
+        handler.setLevel(getattr(logging, file_config.get('level', 'DEBUG').upper()))
+        
+        formatter = logging.Formatter(
+            file_config.get('format', '%(asctime)s [%(levelname)s] [%(name)s] %(filename)s:%(lineno)d - %(message)s'),
+            datefmt='%Y-%m-%d %H:%M:%S'
+        )
+        handler.setFormatter(formatter)
+        return handler
+    
+    def _parse_size(self, size_str: str) -> int:
+        """解析大小字符串,如 '50MB' -> 字节数"""
+        size_str = size_str.upper()
+        if size_str.endswith('KB'):
+            return int(size_str[:-2]) * 1024
+        elif size_str.endswith('MB'):
+            return int(size_str[:-2]) * 1024 * 1024
+        elif size_str.endswith('GB'):
+            return int(size_str[:-2]) * 1024 * 1024 * 1024
+        else:
+            return int(size_str)
+    
+    def __del__(self):
+        """清理资源"""
+        if self._executor:
+            self._executor.shutdown(wait=False)
+```
+
+### 2.3 统一日志接口(增强版)
+
+```python
+# core/logging/__init__.py
+from .log_manager import LogManager
+import logging
+
+# 全局日志管理器实例
+_log_manager = LogManager()
+
+def initialize_logging(config_path: str = "config/logging_config.yaml"):
+    """初始化项目日志系统"""
+    _log_manager.initialize(config_path)
+
+def get_logger(name: str, module: str = "default") -> logging.Logger:
+    """获取logger实例 - 主要API"""
+    return _log_manager.get_logger(name, module)
+
+# 便捷方法
+def get_data_pipeline_logger(name: str) -> logging.Logger:
+    """获取data_pipeline模块logger"""
+    return get_logger(name, "data_pipeline")
+
+def get_agent_logger(name: str) -> logging.Logger:
+    """获取agent模块logger"""
+    return get_logger(name, "agent")
+
+def get_vanna_logger(name: str) -> logging.Logger:
+    """获取vanna模块logger"""
+    return get_logger(name, "vanna")
+
+# 上下文管理便捷方法
+def set_log_context(**kwargs):
+    """设置日志上下文(可选)
+    示例: set_log_context(user_id='user123', session_id='sess456')
+    """
+    _log_manager.set_context(**kwargs)
+
+def clear_log_context():
+    """清除日志上下文"""
+    _log_manager.clear_context()
+
+# 异步日志便捷方法
+async def alog_info(logger: logging.Logger, message: str, **kwargs):
+    """异步记录INFO日志"""
+    await _log_manager.alog(logger, 'info', message, **kwargs)
+
+async def alog_error(logger: logging.Logger, message: str, **kwargs):
+    """异步记录ERROR日志"""
+    await _log_manager.alog(logger, 'error', message, **kwargs)
+
+async def alog_debug(logger: logging.Logger, message: str, **kwargs):
+    """异步记录DEBUG日志"""
+    await _log_manager.alog(logger, 'debug', message, **kwargs)
+
+async def alog_warning(logger: logging.Logger, message: str, **kwargs):
+    """异步记录WARNING日志"""
+    await _log_manager.alog(logger, 'warning', message, **kwargs)
+```
+
+### 2.4 日志配置文件(支持上下文信息)
+
+```yaml
+# config/logging_config.yaml
+version: 1
+
+# 全局配置
+global:
+  base_level: INFO
+  
+# 默认配置
+default:
+  level: INFO
+  console:
+    enabled: true
+    level: INFO
+    format: "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
+  file:
+    enabled: true
+    level: DEBUG
+    filename: "app.log"
+    # 支持上下文信息,但有默认值避免错误
+    format: "%(asctime)s [%(levelname)s] [%(name)s] [user:%(user_id)s] [session:%(session_id)s] %(filename)s:%(lineno)d - %(message)s"
+    rotation:
+      enabled: true
+      max_size: "50MB"
+      backup_count: 10
+
+# 模块特定配置
+modules:
+  data_pipeline:
+    level: DEBUG
+    console:
+      enabled: true
+      level: INFO
+      format: "🔄 %(asctime)s [%(levelname)s] Pipeline: %(message)s"
+    file:
+      enabled: true
+      level: DEBUG
+      filename: "data_pipeline.log"
+      format: "%(asctime)s [%(levelname)s] [%(name)s] %(filename)s:%(lineno)d - %(message)s"
+      rotation:
+        enabled: true
+        max_size: "30MB"
+        backup_count: 8
+  
+  agent:
+    level: DEBUG
+    console:
+      enabled: true
+      level: INFO
+      format: "🤖 %(asctime)s [%(levelname)s] Agent: %(message)s"
+    file:
+      enabled: true
+      level: DEBUG
+      filename: "agent.log"
+      # Agent模块支持user_id和session_id
+      format: "%(asctime)s [%(levelname)s] [%(name)s] [user:%(user_id)s] [session:%(session_id)s] %(filename)s:%(lineno)d - %(message)s"
+      rotation:
+        enabled: true
+        max_size: "30MB"
+        backup_count: 8
+  
+  vanna:
+    level: INFO
+    console:
+      enabled: true
+      level: INFO
+      format: "🧠 %(asctime)s [%(levelname)s] Vanna: %(message)s"
+    file:
+      enabled: true
+      level: DEBUG
+      filename: "vanna.log"
+      format: "%(asctime)s [%(levelname)s] [%(name)s] %(filename)s:%(lineno)d - %(message)s"
+      rotation:
+        enabled: true
+        max_size: "20MB"
+        backup_count: 5
+```
+
+## 3. 改造实施步骤
+
+### 3.1 第一阶段:基础架构搭建
+
+1. **创建日志服务目录结构**
+   ```bash
+   mkdir -p core/logging
+   mkdir -p config
+   mkdir -p logs
+   ```
+
+2. **实现核心组件**
+   - 创建 `core/logging/log_manager.py`
+   - 创建 `core/logging/__init__.py`
+   - 创建 `config/logging_config.yaml`
+
+3. **集成到citu_app.py(主要应用)**
+   ```python
+   # 在citu_app.py的开头添加
+   from core.logging import initialize_logging, get_logger, set_log_context, clear_log_context
+   import uuid
+   
+   # 初始化日志系统
+   initialize_logging("config/logging_config.yaml")
+   app_logger = get_logger("CituApp", "default")
+   
+   # 在Flask应用配置后集成请求级别的日志上下文
+   @app.flask_app.before_request
+   def before_request():
+       # 为每个请求设置上下文(如果有的话)
+       request_id = str(uuid.uuid4())[:8]
+       user_id = request.headers.get('X-User-ID', 'anonymous')
+       set_log_context(request_id=request_id, user_id=user_id)
+   
+   @app.flask_app.after_request
+   def after_request(response):
+       # 清理上下文
+       clear_log_context()
+       return response
+   ```
+
+### 3.2 第二阶段:模块改造
+
+#### 3.2.1 改造data_pipeline模块
+
+```python
+# 替换 data_pipeline/utils/logger.py 中的使用方式
+from core.logging import get_data_pipeline_logger
+
+def setup_logging(verbose: bool = False, log_file: str = None, log_dir: str = None):
+    """
+    保持原有接口,内部使用新的日志系统
+    """
+    # 不再需要复杂的设置,直接使用统一日志系统
+    pass
+
+# 在各个文件中使用
+# data_pipeline/qa_generation/qs_agent.py
+class QuestionSQLGenerationAgent:
+    def __init__(self, ...):
+        # 替换原有的 logging.getLogger("schema_tools.QSAgent")
+        self.logger = get_data_pipeline_logger("QSAgent")
+        
+    async def generate(self):
+        self.logger.info("🚀 开始生成Question-SQL训练数据")
+        # ... 其他代码
+        
+        # 手动记录关键节点的时间
+        start_time = time.time()
+        self.logger.info("开始初始化LLM组件")
+        
+        self._initialize_llm_components()
+        
+        init_time = time.time() - start_time
+        self.logger.info(f"LLM组件初始化完成,耗时: {init_time:.2f}秒")
+```
+
+#### 3.2.2 改造Agent模块(支持可选的用户上下文)
+
+```python
+# 在ask_agent接口中使用
+@app.flask_app.route('/api/v0/ask_agent', methods=['POST'])
+def ask_agent():
+    logger = get_agent_logger("AskAgent")
+    
+    try:
+        data = request.json
+        question = data.get('question', '')
+        user_id = data.get('user_id')  # 可选
+        session_id = data.get('session_id')  # 可选
+        
+        # 设置上下文(如果有的话)
+        if user_id or session_id:
+            set_log_context(user_id=user_id or 'anonymous', session_id=session_id or 'N/A')
+        
+        logger.info(f"收到问题: {question[:50]}...")
+        
+        # 异步记录示例(在async函数中)
+        # await alog_info(logger, f"开始处理问题: {question}")
+        
+        # ... 其他处理逻辑
+        
+    except Exception as e:
+        logger.error(f"处理失败: {str(e)}", exc_info=True)
+        # ...
+```
+
+#### 3.2.3 改造vanna相关代码
+
+由于vanna使用print方式,创建简单的适配器:
+
+```python
+# core/logging/vanna_adapter.py
+from core.logging import get_vanna_logger
+
+class VannaLogAdapter:
+    """Vanna日志适配器 - 将print转换为logger调用"""
+    
+    def __init__(self, logger_name: str = "VannaBase"):
+        self.logger = get_vanna_logger(logger_name)
+    
+    def log(self, message: str):
+        """替换vanna的log方法"""
+        # 根据内容判断日志级别
+        message_lower = message.lower()
+        if any(keyword in message_lower for keyword in ['error', 'exception', 'fail']):
+            self.logger.error(message)
+        elif any(keyword in message_lower for keyword in ['warning', 'warn']):
+            self.logger.warning(message)
+        else:
+            self.logger.info(message)
+
+# 使用装饰器改造vanna实例
+def enhance_vanna_logging(vanna_instance):
+    """增强vanna实例的日志功能"""
+    adapter = VannaLogAdapter(vanna_instance.__class__.__name__)
+    
+    # 替换log方法
+    vanna_instance.log = adapter.log
+    return vanna_instance
+
+# 在vanna实例创建时使用
+# core/vanna_llm_factory.py
+from core.logging.vanna_adapter import enhance_vanna_logging
+
+def create_vanna_instance():
+    # 原有创建逻辑
+    vn = VannaDefault(...)
+    
+    # 增强日志功能
+    vn = enhance_vanna_logging(vn)
+    
+    return vn
+```
+
+### 3.3 第三阶段:workflow级别的时间统计
+
+对于跨多个函数的执行时间统计,在关键业务节点手动记录:
+
+```python
+# data_pipeline/schema_workflow.py
+import time
+from core.logging import get_data_pipeline_logger
+
+class SchemaWorkflowOrchestrator:
+    def __init__(self, ...):
+        self.logger = get_data_pipeline_logger("SchemaWorkflow")
+    
+    async def run_full_workflow(self):
+        """执行完整工作流"""
+        workflow_start = time.time()
+        self.logger.info("🚀 开始执行完整的Schema工作流")
+        
+        try:
+            # 步骤1:生成DDL和MD文件
+            step1_start = time.time()
+            self.logger.info("📝 步骤1: 开始生成DDL和MD文件")
+            
+            result1 = await self.generate_ddl_md()
+            
+            step1_time = time.time() - step1_start
+            self.logger.info(f"✅ 步骤1完成,生成了{result1['ddl_count']}个DDL文件和{result1['md_count']}个MD文件,耗时: {step1_time:.2f}秒")
+            
+            # 步骤2:生成Question-SQL对
+            step2_start = time.time()
+            self.logger.info("❓ 步骤2: 开始生成Question-SQL对")
+            
+            result2 = await self.generate_qa_pairs()
+            
+            step2_time = time.time() - step2_start
+            self.logger.info(f"✅ 步骤2完成,生成了{result2['qa_count']}个问答对,耗时: {step2_time:.2f}秒")
+            
+            # 步骤3:验证SQL
+            step3_start = time.time()
+            self.logger.info("🔍 步骤3: 开始验证SQL")
+            
+            result3 = await self.validate_sql()
+            
+            step3_time = time.time() - step3_start
+            self.logger.info(f"✅ 步骤3完成,验证了{result3['validated_count']}个SQL,修复了{result3['fixed_count']}个,耗时: {step3_time:.2f}秒")
+            
+            # 步骤4:加载训练数据
+            step4_start = time.time()
+            self.logger.info("📚 步骤4: 开始加载训练数据")
+            
+            result4 = await self.load_training_data()
+            
+            step4_time = time.time() - step4_start
+            self.logger.info(f"✅ 步骤4完成,加载了{result4['loaded_count']}个训练文件,耗时: {step4_time:.2f}秒")
+            
+            # 总结
+            total_time = time.time() - workflow_start
+            self.logger.info(f"🎉 完整工作流执行成功!总耗时: {total_time:.2f}秒")
+            self.logger.info(f"   - DDL/MD生成: {step1_time:.2f}秒")
+            self.logger.info(f"   - QA生成: {step2_time:.2f}秒")  
+            self.logger.info(f"   - SQL验证: {step3_time:.2f}秒")
+            self.logger.info(f"   - 数据加载: {step4_time:.2f}秒")
+            
+            return {
+                "success": True,
+                "total_time": total_time,
+                "steps": {
+                    "ddl_md": {"time": step1_time, "result": result1},
+                    "qa_generation": {"time": step2_time, "result": result2},
+                    "sql_validation": {"time": step3_time, "result": result3},
+                    "data_loading": {"time": step4_time, "result": result4}
+                }
+            }
+            
+        except Exception as e:
+            total_time = time.time() - workflow_start
+            self.logger.error(f"❌ 工作流执行失败,耗时: {total_time:.2f}秒,错误: {str(e)}")
+            raise
+```
+
+## 4. 实际使用示例
+
+### 4.1 在citu_app.py中的使用(主要应用)
+
+```python
+# citu_app.py
+from core.logging import initialize_logging, get_logger, set_log_context, clear_log_context
+import uuid
+
+# 应用启动时初始化
+initialize_logging("config/logging_config.yaml")
+app_logger = get_logger("CituApp", "default")
+
+# API端点示例
+@app.flask_app.route('/api/v0/ask_agent', methods=['POST'])
+def ask_agent():
+    logger = get_agent_logger("AskAgent")
+    request_id = str(uuid.uuid4())[:8]
+    
+    try:
+        data = request.json
+        user_id = data.get('user_id')
+        
+        # 设置上下文(安全的,即使没有user_id)
+        set_log_context(
+            request_id=request_id,
+            user_id=user_id or 'anonymous'
+        )
+        
+        logger.info(f"开始处理请求")
+        # ... 业务逻辑
+        
+        logger.info(f"请求处理成功")
+        return success_response(...)
+        
+    except Exception as e:
+        logger.error(f"请求处理失败: {str(e)}", exc_info=True)
+        return error_response(...)
+    finally:
+        clear_log_context()
+```
+
+### 4.2 在data_pipeline中的使用
+
+```python
+# data_pipeline/ddl_generation/training_data_agent.py
+from core.logging import get_data_pipeline_logger
+import time
+
+class SchemaTrainingDataAgent:
+    def __init__(self, db_config, output_dir):
+        self.logger = get_data_pipeline_logger("TrainingDataAgent")
+        self.db_config = db_config
+        self.output_dir = output_dir
+        
+    async def process_tables(self, table_list):
+        """处理表列表"""
+        start_time = time.time()
+        self.logger.info(f"开始处理{len(table_list)}个表的训练数据生成")
+        
+        success_count = 0
+        failed_tables = []
+        
+        for table in table_list:
+            try:
+                table_start = time.time()
+                self.logger.debug(f"开始处理表: {table}")
+                
+                await self._process_single_table(table)
+                
+                table_time = time.time() - table_start
+                self.logger.info(f"表 {table} 处理完成,耗时: {table_time:.2f}秒")
+                success_count += 1
+                
+            except Exception as e:
+                self.logger.error(f"表 {table} 处理失败: {str(e)}")
+                failed_tables.append(table)
+        
+        total_time = time.time() - start_time
+        self.logger.info(f"批量处理完成,成功: {success_count}个,失败: {len(failed_tables)}个,总耗时: {total_time:.2f}秒")
+        
+        if failed_tables:
+            self.logger.warning(f"处理失败的表: {failed_tables}")
+            
+        return {
+            "success_count": success_count,
+            "failed_count": len(failed_tables),
+            "failed_tables": failed_tables,
+            "total_time": total_time
+        }
+```
+
+### 4.3 在Agent中的使用(支持异步)
+
+```python
+# agent/citu_agent.py
+from core.logging import get_agent_logger, alog_info, alog_error
+
+class CituLangGraphAgent:
+    def __init__(self):
+        self.logger = get_agent_logger("CituAgent")
+    
+    async def process_question(self, question: str, session_id: str = None, user_id: str = None):
+        """异步处理问题"""
+        # 设置上下文(如果有的话)
+        if user_id or session_id:
+            set_log_context(user_id=user_id or 'anonymous', session_id=session_id or 'N/A')
+        
+        # 同步日志
+        self.logger.info(f"开始处理问题: {question[:50]}...")
+        
+        try:
+            # 异步日志
+            await alog_info(self.logger, f"开始分类问题")
+            
+            # 业务逻辑
+            result = await self._classify_question(question)
+            
+            await alog_info(self.logger, f"分类完成: {result.question_type}")
+            
+            return result
+            
+        except Exception as e:
+            await alog_error(self.logger, f"处理失败: {str(e)}")
+            raise
+```
+
+### 4.4 增强vanna日志
+
+```python
+# core/vanna_llm_factory.py
+from core.logging.vanna_adapter import enhance_vanna_logging
+from core.logging import get_vanna_logger
+
+def create_vanna_instance():
+    """创建增强了日志功能的vanna实例"""
+    logger = get_vanna_logger("VannaFactory")
+    logger.info("🧠 开始创建Vanna实例")
+    
+    try:
+        # 原有创建逻辑
+        vn = VannaDefault(
+            config={
+                'api_key': os.getenv('OPENAI_API_KEY'),
+                'model': 'gpt-4'
+            }
+        )
+        
+        # 增强日志功能
+        vn = enhance_vanna_logging(vn)
+        
+        logger.info("✅ Vanna实例创建成功")
+        return vn
+        
+    except Exception as e:
+        logger.error(f"❌ Vanna实例创建失败: {str(e)}")
+        raise
+```
+
+## 5. 配置调优建议
+
+### 5.1 开发环境配置
+
+```yaml
+# config/logging_config_dev.yaml
+version: 1
+
+global:
+  base_level: DEBUG
+
+default:
+  level: DEBUG
+  console:
+    enabled: true
+    level: DEBUG
+  file:
+    enabled: false  # 开发环境可以只用控制台
+
+modules:
+  data_pipeline:
+    level: DEBUG
+    console:
+      enabled: true
+      level: DEBUG
+      format: "🔄 %(asctime)s [%(levelname)s] Pipeline: %(message)s"
+    file:
+      enabled: true
+      level: DEBUG
+      filename: "data_pipeline.log"
+      
+  agent:
+    level: DEBUG
+    console:
+      enabled: true
+      level: DEBUG
+      format: "🤖 %(asctime)s [%(levelname)s] Agent: %(message)s"
+```
+
+### 5.2 生产环境配置
+
+```yaml
+# config/logging_config_prod.yaml
+version: 1
+
+global:
+  base_level: INFO
+
+default:
+  level: INFO
+  console:
+    enabled: false  # 生产环境不输出到控制台
+  file:
+    enabled: true
+    level: INFO
+    rotation:
+      enabled: true
+      max_size: "100MB"
+      backup_count: 20
+
+modules:
+  data_pipeline:
+    level: INFO
+    console:
+      enabled: false
+    file:
+      enabled: true
+      level: INFO
+      filename: "data_pipeline.log"
+      rotation:
+        enabled: true
+        max_size: "50MB"
+        backup_count: 15
+        
+  langchain:
+    level: ERROR  # 生产环境只记录错误
+    console:
+      enabled: false
+    file:
+      enabled: true
+      level: ERROR
+```
+
+## 6. 注意事项
+
+基于用户反馈,特别注意以下几点:
+
+1. **上下文安全性**:即使没有用户信息,日志系统也能正常工作(使用默认值)
+2. **降级策略**:当文件系统不可用时,自动降级到控制台输出
+3. **异步支持**:在async函数中使用异步日志方法,避免阻塞
+4. **主应用聚焦**:重点关注citu_app.py的集成,忽略flask_app.py和chainlit_app.py
+5. **性能考虑**:保持原有的跨函数时间统计方式,不强制使用装饰器
+
+## 7. 总结
+
+这个精简实用的日志改造方案提供了:
+
+1. **统一的日志管理**:类似Log4j的架构,单一配置文件管理所有日志
+2. **模块化日志文件**:每个模块独立的日志文件,便于问题定位
+3. **自动日志轮转**:使用RotatingFileHandler自动管理日志文件大小和数量
+4. **灵活的配置**:支持不同环境的配置,控制台和文件输出可独立配置
+5. **简单易用**:提供便捷的API,一行代码获取对应模块的logger
+6. **性能友好**:手动记录关键节点时间,不影响整体性能
+7. **技术栈兼容**:专门为vanna/langchain/langgraph设计适配器
+8. **异步支持**:适配项目大量使用async/await的特点
+9. **安全容错**:上下文信息可选,文件系统可降级
+
+该方案专注核心功能,去掉了不必要的复杂性,是一个可以直接落地实施的实用设计。

+ 635 - 0
docs/全局log服务改造方案_优化版.md

@@ -0,0 +1,635 @@
+# 项目日志系统改造设计方案(优化版)
+
+## 1. 整体设计理念
+
+基于您的需求和反馈,设计一套统一的日志服务,专注核心功能:
+- 统一的日志级别管理(info/error/debug/warning)
+- 可配置的日志输出路径
+- 支持控制台和文件输出
+- 4个模块独立日志文件(app、agent、vanna、data_pipeline)
+- 自动日志轮转和清理
+- 灵活的上下文管理
+
+## 2. 核心架构设计
+
+### 2.1 精简的日志服务层次结构
+
+```
+项目根目录/
+├── core/
+│   └── logging/
+│       ├── __init__.py           # 日志服务入口
+│       └── log_manager.py        # 核心日志管理器
+├── logs/                         # 日志文件目录
+│   ├── app.log                  # 主应用日志(citu_app.py和通用模块)
+│   ├── agent.log                # agent模块日志
+│   ├── vanna.log                # vanna相关模块日志
+│   └── data_pipeline.log        # data_pipeline模块日志
+└── config/
+    └── logging_config.yaml       # 日志配置文件
+```
+
+### 2.2 核心日志管理器设计
+
+```python
+# core/logging/log_manager.py
+import logging
+import logging.handlers
+import os
+from typing import Dict, Optional
+from pathlib import Path
+import yaml
+import contextvars
+
+# 上下文变量,存储可选的上下文信息
+log_context = contextvars.ContextVar('log_context', default={})
+
+class ContextFilter(logging.Filter):
+    """添加上下文信息到日志记录"""
+    def filter(self, record):
+        ctx = log_context.get()
+        # 设置默认值,避免格式化错误
+        record.session_id = ctx.get('session_id', 'N/A')
+        record.user_id = ctx.get('user_id', 'anonymous')
+        record.request_id = ctx.get('request_id', 'N/A')
+        return True
+
+class LogManager:
+    """统一日志管理器 - 类似Log4j的功能"""
+    
+    _instance = None
+    _loggers: Dict[str, logging.Logger] = {}
+    _initialized = False
+    _fallback_to_console = False  # 标记是否降级到控制台
+    
+    def __new__(cls):
+        if cls._instance is None:
+            cls._instance = super().__new__(cls)
+        return cls._instance
+    
+    def __init__(self):
+        if not self._initialized:
+            self.config = None
+            self.base_log_dir = Path("logs")
+            self._setup_base_directory()
+            LogManager._initialized = True
+    
+    def initialize(self, config_path: str = "config/logging_config.yaml"):
+        """初始化日志系统"""
+        self.config = self._load_config(config_path)
+        self._setup_base_directory()
+        self._configure_root_logger()
+    
+    def get_logger(self, name: str, module: str = "default") -> logging.Logger:
+        """获取指定模块的logger"""
+        logger_key = f"{module}.{name}"
+        
+        if logger_key not in self._loggers:
+            logger = logging.getLogger(logger_key)
+            self._configure_logger(logger, module)
+            self._loggers[logger_key] = logger
+        
+        return self._loggers[logger_key]
+    
+    def set_context(self, **kwargs):
+        """设置日志上下文(可选)"""
+        ctx = log_context.get()
+        ctx.update(kwargs)
+        log_context.set(ctx)
+    
+    def clear_context(self):
+        """清除日志上下文"""
+        log_context.set({})
+    
+    def _load_config(self, config_path: str) -> dict:
+        """加载配置文件"""
+        try:
+            with open(config_path, 'r', encoding='utf-8') as f:
+                return yaml.safe_load(f)
+        except FileNotFoundError:
+            print(f"[WARNING] 配置文件 {config_path} 未找到,使用默认配置")
+            return self._get_default_config()
+        except Exception as e:
+            print(f"[ERROR] 加载配置文件失败: {e},使用默认配置")
+            return self._get_default_config()
+    
+    def _get_default_config(self) -> dict:
+        """获取默认配置"""
+        return {
+            'global': {'base_level': 'INFO'},
+            'default': {
+                'level': 'INFO',
+                'console': {
+                    'enabled': True,
+                    'level': 'INFO',
+                    'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
+                },
+                'file': {
+                    'enabled': True,
+                    'level': 'DEBUG',
+                    'filename': 'app.log',
+                    'format': '%(asctime)s [%(levelname)s] [%(name)s] [user:%(user_id)s] [session:%(session_id)s] %(filename)s:%(lineno)d - %(message)s',
+                    'rotation': {
+                        'enabled': True,
+                        'max_size': '50MB',
+                        'backup_count': 10
+                    }
+                }
+            },
+            'modules': {}
+        }
+    
+    def _setup_base_directory(self):
+        """创建日志目录(带降级策略)"""
+        try:
+            self.base_log_dir.mkdir(parents=True, exist_ok=True)
+            self._fallback_to_console = False
+        except Exception as e:
+            print(f"[WARNING] 无法创建日志目录 {self.base_log_dir},将只使用控制台输出: {e}")
+            self._fallback_to_console = True
+    
+    def _configure_root_logger(self):
+        """配置根日志器"""
+        root_logger = logging.getLogger()
+        root_logger.setLevel(getattr(logging, self.config['global']['base_level'].upper()))
+    
+    def _configure_logger(self, logger: logging.Logger, module: str):
+        """配置具体的logger"""
+        module_config = self.config.get('modules', {}).get(module, self.config['default'])
+        
+        # 设置日志级别
+        level = getattr(logging, module_config['level'].upper())
+        logger.setLevel(level)
+        
+        # 清除已有处理器
+        logger.handlers.clear()
+        logger.propagate = False
+        
+        # 添加控制台处理器
+        if module_config.get('console', {}).get('enabled', True):
+            console_handler = self._create_console_handler(module_config['console'])
+            console_handler.addFilter(ContextFilter())
+            logger.addHandler(console_handler)
+        
+        # 添加文件处理器(如果没有降级到控制台)
+        if not self._fallback_to_console and module_config.get('file', {}).get('enabled', True):
+            try:
+                file_handler = self._create_file_handler(module_config['file'], module)
+                file_handler.addFilter(ContextFilter())
+                logger.addHandler(file_handler)
+            except Exception as e:
+                print(f"[WARNING] 无法创建文件处理器: {e}")
+    
+    def _create_console_handler(self, console_config: dict) -> logging.StreamHandler:
+        """创建控制台处理器"""
+        handler = logging.StreamHandler()
+        handler.setLevel(getattr(logging, console_config.get('level', 'INFO').upper()))
+        
+        formatter = logging.Formatter(
+            console_config.get('format', '%(asctime)s [%(levelname)s] %(name)s: %(message)s'),
+            datefmt='%Y-%m-%d %H:%M:%S'
+        )
+        handler.setFormatter(formatter)
+        return handler
+    
+    def _create_file_handler(self, file_config: dict, module: str) -> logging.Handler:
+        """创建文件处理器(支持自动轮转)"""
+        log_file = self.base_log_dir / file_config.get('filename', f'{module}.log')
+        
+        # 使用RotatingFileHandler实现自动轮转和清理
+        rotation_config = file_config.get('rotation', {})
+        if rotation_config.get('enabled', False):
+            handler = logging.handlers.RotatingFileHandler(
+                log_file,
+                maxBytes=self._parse_size(rotation_config.get('max_size', '50MB')),
+                backupCount=rotation_config.get('backup_count', 10),
+                encoding='utf-8'
+            )
+        else:
+            handler = logging.FileHandler(log_file, encoding='utf-8')
+        
+        handler.setLevel(getattr(logging, file_config.get('level', 'DEBUG').upper()))
+        
+        formatter = logging.Formatter(
+            file_config.get('format', '%(asctime)s [%(levelname)s] [%(name)s] %(filename)s:%(lineno)d - %(message)s'),
+            datefmt='%Y-%m-%d %H:%M:%S'
+        )
+        handler.setFormatter(formatter)
+        return handler
+    
+    def _parse_size(self, size_str: str) -> int:
+        """解析大小字符串,如 '50MB' -> 字节数"""
+        size_str = size_str.upper()
+        if size_str.endswith('KB'):
+            return int(size_str[:-2]) * 1024
+        elif size_str.endswith('MB'):
+            return int(size_str[:-2]) * 1024 * 1024
+        elif size_str.endswith('GB'):
+            return int(size_str[:-2]) * 1024 * 1024 * 1024
+        else:
+            return int(size_str)
+```
+
+### 2.3 统一日志接口
+
+```python
+# core/logging/__init__.py
+from .log_manager import LogManager
+import logging
+
+# 全局日志管理器实例
+_log_manager = LogManager()
+
+def initialize_logging(config_path: str = "config/logging_config.yaml"):
+    """初始化项目日志系统"""
+    _log_manager.initialize(config_path)
+
+def get_logger(name: str, module: str = "default") -> logging.Logger:
+    """获取logger实例 - 主要API"""
+    return _log_manager.get_logger(name, module)
+
+# 便捷方法
+def get_data_pipeline_logger(name: str) -> logging.Logger:
+    """获取data_pipeline模块logger"""
+    return get_logger(name, "data_pipeline")
+
+def get_agent_logger(name: str) -> logging.Logger:
+    """获取agent模块logger"""
+    return get_logger(name, "agent")
+
+def get_vanna_logger(name: str) -> logging.Logger:
+    """获取vanna模块logger"""
+    return get_logger(name, "vanna")
+
+def get_app_logger(name: str) -> logging.Logger:
+    """获取app模块logger"""
+    return get_logger(name, "app")
+
+# 上下文管理便捷方法
+def set_log_context(**kwargs):
+    """设置日志上下文(可选)
+    示例: set_log_context(user_id='user123', session_id='sess456')
+    """
+    _log_manager.set_context(**kwargs)
+
+def clear_log_context():
+    """清除日志上下文"""
+    _log_manager.clear_context()
+```
+
+### 2.4 日志配置文件
+
+```yaml
+# config/logging_config.yaml
+version: 1
+
+# 全局配置
+global:
+  base_level: INFO
+  
+# 默认配置(用于app.log)
+default:
+  level: INFO
+  console:
+    enabled: true
+    level: INFO
+    format: "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
+  file:
+    enabled: true
+    level: DEBUG
+    filename: "app.log"
+    format: "%(asctime)s [%(levelname)s] [%(name)s] [user:%(user_id)s] [session:%(session_id)s] %(filename)s:%(lineno)d - %(message)s"
+    rotation:
+      enabled: true
+      max_size: "50MB"
+      backup_count: 10
+
+# 模块特定配置
+modules:
+  app:
+    level: INFO
+    console:
+      enabled: true
+      level: INFO
+      format: "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
+    file:
+      enabled: true
+      level: DEBUG
+      filename: "app.log"
+      format: "%(asctime)s [%(levelname)s] [%(name)s] [user:%(user_id)s] [session:%(session_id)s] %(filename)s:%(lineno)d - %(message)s"
+      rotation:
+        enabled: true
+        max_size: "50MB"
+        backup_count: 10
+  
+  data_pipeline:
+    level: DEBUG
+    console:
+      enabled: true
+      level: INFO
+      format: "%(asctime)s [%(levelname)s] Pipeline: %(message)s"
+    file:
+      enabled: true
+      level: DEBUG
+      filename: "data_pipeline.log"
+      format: "%(asctime)s [%(levelname)s] [%(name)s] %(filename)s:%(lineno)d - %(message)s"
+      rotation:
+        enabled: true
+        max_size: "30MB"
+        backup_count: 8
+  
+  agent:
+    level: DEBUG
+    console:
+      enabled: true
+      level: INFO
+      format: "%(asctime)s [%(levelname)s] Agent: %(message)s"
+    file:
+      enabled: true
+      level: DEBUG
+      filename: "agent.log"
+      format: "%(asctime)s [%(levelname)s] [%(name)s] [user:%(user_id)s] [session:%(session_id)s] %(filename)s:%(lineno)d - %(message)s"
+      rotation:
+        enabled: true
+        max_size: "30MB"
+        backup_count: 8
+  
+  vanna:
+    level: INFO
+    console:
+      enabled: true
+      level: INFO
+      format: "%(asctime)s [%(levelname)s] Vanna: %(message)s"
+    file:
+      enabled: true
+      level: DEBUG
+      filename: "vanna.log"
+      format: "%(asctime)s [%(levelname)s] [%(name)s] %(filename)s:%(lineno)d - %(message)s"
+      rotation:
+        enabled: true
+        max_size: "20MB"
+        backup_count: 5
+```
+
+## 3. 改造实施步骤
+
+### 3.1 第一阶段:基础架构搭建
+
+1. **创建日志服务目录结构**
+   ```bash
+   mkdir -p core/logging
+   mkdir -p config
+   mkdir -p logs
+   ```
+
+2. **实现核心组件**
+   - 创建 `core/logging/log_manager.py`
+   - 创建 `core/logging/__init__.py`
+   - 创建 `config/logging_config.yaml`
+
+3. **集成到citu_app.py**
+   ```python
+   # 在citu_app.py的开头添加
+   from core.logging import initialize_logging, get_app_logger, set_log_context, clear_log_context
+   
+   # 初始化日志系统
+   initialize_logging("config/logging_config.yaml")
+   app_logger = get_app_logger("CituApp")
+   
+   # 在Flask应用配置后集成
+   @app.flask_app.before_request
+   def before_request():
+       # 为每个请求设置上下文(如果有的话)
+       request_id = str(uuid.uuid4())[:8]
+       user_id = request.headers.get('X-User-ID', 'anonymous')
+       set_log_context(request_id=request_id, user_id=user_id)
+   
+   @app.flask_app.after_request
+   def after_request(response):
+       # 清理上下文
+       clear_log_context()
+       return response
+   ```
+
+### 3.2 第二阶段:模块改造
+
+#### 3.2.1 改造Agent模块
+
+```python
+# 替换所有print语句
+# agent/citu_agent.py
+from core.logging import get_agent_logger
+
+class CituLangGraphAgent:
+    def __init__(self):
+        self.logger = get_agent_logger("CituAgent")
+        self.logger.info("LangGraph Agent初始化")
+        # 直接替换原有的 print 语句
+```
+
+#### 3.2.2 改造data_pipeline模块(直接改造,无需兼容)
+
+```python
+# 方案一:完全删除 data_pipeline/utils/logger.py
+# 直接在所有使用位置导入新的日志系统
+
+# 方案二:保留文件但清空实现
+# data_pipeline/utils/logger.py
+"""
+原有日志系统已被新的统一日志系统替代
+保留此文件仅为避免导入错误
+"""
+from core.logging import get_data_pipeline_logger
+
+def setup_logging(verbose: bool = False, log_file: str = None, log_dir: str = None):
+    """函数保留以避免调用错误,但不做任何事"""
+    pass
+
+def get_logger(name: str = "DataPipeline"):
+    """直接返回新的logger"""
+    return get_data_pipeline_logger(name)
+
+# 在各个文件中直接使用新的logger
+# data_pipeline/schema_workflow.py
+from core.logging import get_data_pipeline_logger
+
+class SchemaWorkflowOrchestrator:
+    def __init__(self, ...):
+        # 原来: self.logger = logging.getLogger("schema_tools.SchemaWorkflowOrchestrator") 
+        # 现在: 直接使用新系统
+        self.logger = get_data_pipeline_logger("SchemaWorkflow")
+
+# data_pipeline/qa_generation/qs_agent.py
+from core.logging import get_data_pipeline_logger
+
+class QuestionSQLGenerationAgent:
+    def __init__(self, ...):
+        # 原来: self.logger = logging.getLogger("schema_tools.QSAgent")
+        # 现在: 直接替换
+        self.logger = get_data_pipeline_logger("QSAgent")
+```
+
+#### 3.2.3 改造Vanna相关代码
+
+```python
+# customllm/base_llm_chat.py
+from core.logging import get_vanna_logger
+
+class BaseLLMChat(VannaBase, ABC):
+    def __init__(self, config=None):
+        VannaBase.__init__(self, config=config)
+        self.logger = get_vanna_logger("BaseLLMChat")
+        
+        # 替换原有的print
+        self.logger.info("传入的 config 参数如下:")
+        for key, value in self.config.items():
+            self.logger.info(f"  {key}: {value}")
+```
+
+### 3.3 第三阶段:通用模块改造
+
+```python
+# common/qa_feedback_manager.py
+from core.logging import get_app_logger
+
+class QAFeedbackManager:
+    def __init__(self, vanna_instance=None):
+        self.logger = get_app_logger("QAFeedbackManager")
+        # 替换原有的print
+        self.logger.info("QAFeedbackManager 初始化")
+```
+
+## 4. 实际使用示例
+
+### 4.1 在citu_app.py中的使用
+
+```python
+# citu_app.py
+from core.logging import initialize_logging, get_app_logger, get_agent_logger, set_log_context
+import uuid
+
+# 应用启动时初始化
+initialize_logging("config/logging_config.yaml")
+app_logger = get_app_logger("CituApp")
+
+# API端点示例
+@app.flask_app.route('/api/v0/ask_agent', methods=['POST'])
+def ask_agent():
+    logger = get_agent_logger("AskAgent")
+    request_id = str(uuid.uuid4())[:8]
+    
+    try:
+        data = request.json
+        user_id = data.get('user_id')
+        
+        # 设置上下文(安全的,即使没有user_id)
+        set_log_context(
+            request_id=request_id,
+            user_id=user_id or 'anonymous'
+        )
+        
+        logger.info("开始处理请求")
+        # ... 业务逻辑
+        
+        logger.info("请求处理成功")
+        return success_response(...)
+        
+    except Exception as e:
+        logger.error(f"请求处理失败: {str(e)}", exc_info=True)
+        return error_response(...)
+    finally:
+        clear_log_context()
+```
+
+### 4.2 在data_pipeline中的使用
+
+```python
+# data_pipeline/schema_workflow.py
+import time
+from core.logging import get_data_pipeline_logger
+
+class SchemaWorkflowOrchestrator:
+    def __init__(self, ...):
+        self.logger = get_data_pipeline_logger("SchemaWorkflow")
+    
+    async def execute_complete_workflow(self):
+        """执行完整工作流"""
+        workflow_start = time.time()
+        self.logger.info("开始执行完整的Schema工作流")
+        
+        # 保持原有的跨函数时间统计逻辑
+        try:
+            # 步骤1
+            step1_start = time.time()
+            self.logger.info("步骤1: 开始生成DDL和MD文件")
+            # ... 执行逻辑
+            step1_time = time.time() - step1_start
+            self.logger.info(f"步骤1完成,耗时: {step1_time:.2f}秒")
+            
+            # 继续其他步骤...
+            
+        except Exception as e:
+            self.logger.error(f"工作流执行失败: {str(e)}", exc_info=True)
+            raise
+```
+
+## 5. 配置调优建议
+
+### 5.1 开发环境配置
+
+```yaml
+# config/logging_config_dev.yaml
+version: 1
+
+global:
+  base_level: DEBUG
+
+default:
+  level: DEBUG
+  console:
+    enabled: true
+    level: DEBUG
+  file:
+    enabled: false  # 开发环境可以只用控制台
+```
+
+### 5.2 生产环境配置
+
+```yaml
+# config/logging_config_prod.yaml
+version: 1
+
+global:
+  base_level: INFO
+
+default:
+  level: INFO
+  console:
+    enabled: false  # 生产环境不输出到控制台
+  file:
+    enabled: true
+    level: INFO
+    rotation:
+      enabled: true
+      max_size: "100MB"
+      backup_count: 20
+```
+
+## 6. 注意事项
+
+1. **上下文安全性**:即使没有用户信息,日志系统也能正常工作(使用默认值)
+2. **降级策略**:当文件系统不可用时,自动降级到控制台输出
+3. **模块分离**:4个模块(app/agent/vanna/data_pipeline)的日志完全分离
+4. **直接改造**:data_pipeline模块直接使用新系统,无需兼容
+5. **性能考虑**:保持原有的跨函数时间统计方式
+
+## 7. 总结
+
+这个优化后的方案特点:
+
+1. **4个模块清晰分离**:app、agent、vanna、data_pipeline各自独立日志文件
+2. **直接改造策略**:所有模块(包括data_pipeline)直接使用新系统,不考虑兼容性
+3. **移除冗余特性**:去掉异步支持、原有标签等,专注核心功能
+4. **简单统一的API**:每个模块都有专门的获取logger方法
+
+该方案已根据您的反馈完全优化,可以直接落地实施。 

+ 180 - 0
docs/日志服务使用清单.md

@@ -0,0 +1,180 @@
+# 项目日志服务使用清单
+
+## 概述
+本文档详细列出了项目中各个模块应该使用的日志服务,作为日志改造的实施指南。项目日志系统分为4个独立模块:app、agent、vanna、data_pipeline。
+
+## 日志文件分配方案
+
+### 1. agent.log - Agent相关模块
+
+#### agent目录
+| 文件 | 当前日志方式 | 目标Logger | 说明 |
+|------|-------------|-----------|------|
+| `agent/citu_agent.py` | print | `get_agent_logger("CituAgent")` | 主Agent类,包含大量调试信息 |
+| `agent/classifier.py` | print | `get_agent_logger("Classifier")` | 问题分类器 |
+| `agent/config.py` | print | `get_agent_logger("Config")` | 配置管理 |
+| `agent/state.py` | 无 | 无需日志 | 仅定义数据结构 |
+
+#### agent/tools目录
+| 文件 | 当前日志方式 | 目标Logger | 说明 |
+|------|-------------|-----------|------|
+| `agent/tools/general_chat.py` | print | `get_agent_logger("GeneralChat")` | 聊天工具 |
+| `agent/tools/sql_execution.py` | print | `get_agent_logger("SQLExecution")` | SQL执行工具 |
+| `agent/tools/sql_generation.py` | print | `get_agent_logger("SQLGeneration")` | SQL生成工具 |
+| `agent/tools/summary_generation.py` | print | `get_agent_logger("SummaryGeneration")` | 摘要生成工具 |
+| `agent/tools/utils.py` | 无 | `get_agent_logger("ToolUtils")` | 工具函数(如需日志) |
+
+### 2. data_pipeline.log - Data Pipeline相关模块
+
+#### data_pipeline根目录
+| 文件 | 当前日志方式 | 目标Logger | 说明 |
+|------|-------------|-----------|------|
+| `data_pipeline/schema_workflow.py` | logging.getLogger("schema_tools.SchemaWorkflowOrchestrator") | `get_data_pipeline_logger("SchemaWorkflow")` | 工作流编排器 |
+| `data_pipeline/metadata_only_generator.py` | print | `get_data_pipeline_logger("MetadataGenerator")` | 元数据生成器 |
+| `data_pipeline/config.py` | 无 | 无需日志 | 配置文件 |
+
+#### data_pipeline/ddl_generation目录
+| 文件 | 当前日志方式 | 目标Logger | 说明 |
+|------|-------------|-----------|------|
+| `data_pipeline/ddl_generation/training_data_agent.py` | logging.getLogger("schema_tools.Agent") | `get_data_pipeline_logger("TrainingDataAgent")` | 训练数据代理 |
+| `data_pipeline/ddl_generation/ddl_md_generator.py` | 无具体日志 | `get_data_pipeline_logger("DDLMDGenerator")` | DDL/MD生成器 |
+
+#### data_pipeline/qa_generation目录
+| 文件 | 当前日志方式 | 目标Logger | 说明 |
+|------|-------------|-----------|------|
+| `data_pipeline/qa_generation/qs_agent.py` | logging.getLogger("schema_tools.QSAgent") | `get_data_pipeline_logger("QSAgent")` | Question-SQL代理 |
+| `data_pipeline/qa_generation/qs_generator.py` | 无具体日志 | `get_data_pipeline_logger("QSGenerator")` | Question-SQL生成器 |
+
+#### data_pipeline/analyzers目录
+| 文件 | 当前日志方式 | 目标Logger | 说明 |
+|------|-------------|-----------|------|
+| `data_pipeline/analyzers/md_analyzer.py` | logging.getLogger("schema_tools.MDFileAnalyzer") | `get_data_pipeline_logger("MDAnalyzer")` | MD文件分析器 |
+| `data_pipeline/analyzers/theme_extractor.py` | logging.getLogger("schema_tools.ThemeExtractor") | `get_data_pipeline_logger("ThemeExtractor")` | 主题提取器 |
+
+#### data_pipeline/validators目录
+| 文件 | 当前日志方式 | 目标Logger | 说明 |
+|------|-------------|-----------|------|
+| `data_pipeline/validators/sql_validator.py` | logging.getLogger("schema_tools.SQLValidator") | `get_data_pipeline_logger("SQLValidator")` | SQL验证器 |
+| `data_pipeline/validators/sql_validation_agent.py` | logging.getLogger("schema_tools.SQLValidationAgent") | `get_data_pipeline_logger("SQLValidationAgent")` | SQL验证代理 |
+| `data_pipeline/validators/file_count_validator.py` | logging.getLogger("schema_tools.FileCountValidator") | `get_data_pipeline_logger("FileCountValidator")` | 文件计数验证器 |
+| `data_pipeline/validators/sql_validate_cli.py` | print | `get_data_pipeline_logger("SQLValidateCLI")` | SQL验证命令行工具 |
+| `data_pipeline/validators/sql_validation_example.py` | print | `get_data_pipeline_logger("SQLValidationExample")` | SQL验证示例 |
+
+#### data_pipeline/trainer目录
+| 文件 | 当前日志方式 | 目标Logger | 说明 |
+|------|-------------|-----------|------|
+| `data_pipeline/trainer/vanna_trainer.py` | print | `get_data_pipeline_logger("VannaTrainer")` | Vanna训练器 |
+| `data_pipeline/trainer/run_training.py` | 依赖其他模块日志 | `get_data_pipeline_logger("RunTraining")` | 训练运行脚本 |
+
+#### data_pipeline/utils目录
+| 文件 | 当前日志方式 | 目标Logger | 说明 |
+|------|-------------|-----------|------|
+| `data_pipeline/utils/logger.py` | 自定义日志系统 | **直接删除或改造为空函数** | 原日志工具,直接使用新系统 |
+| `data_pipeline/utils/table_parser.py` | logging.getLogger("schema_tools.TableListParser") | `get_data_pipeline_logger("TableParser")` | 表解析器 |
+| `data_pipeline/utils/system_filter.py` | logging.getLogger("schema_tools.SystemTableFilter") | `get_data_pipeline_logger("SystemFilter")` | 系统表过滤器 |
+| `data_pipeline/utils/permission_checker.py` | logging.getLogger("schema_tools.DatabasePermissionChecker") | `get_data_pipeline_logger("PermissionChecker")` | 权限检查器 |
+| `data_pipeline/utils/large_table_handler.py` | logging.getLogger("schema_tools.LargeTableHandler") | `get_data_pipeline_logger("LargeTableHandler")` | 大表处理器 |
+| `data_pipeline/utils/file_manager.py` | logging.getLogger("schema_tools.FileNameManager") | `get_data_pipeline_logger("FileManager")` | 文件管理器 |
+
+#### data_pipeline/tools目录
+| 文件 | 当前日志方式 | 目标Logger | 说明 |
+|------|-------------|-----------|------|
+| `data_pipeline/tools/base.py` | logging.getLogger + logging.debug | `get_data_pipeline_logger("ToolBase")` | 工具基类 |
+| `data_pipeline/tools/comment_generator.py` | 依赖base.py | `get_data_pipeline_logger("CommentGenerator")` | 注释生成器 |
+| `data_pipeline/tools/data_sampler.py` | 依赖base.py | `get_data_pipeline_logger("DataSampler")` | 数据采样器 |
+| `data_pipeline/tools/database_inspector.py` | 依赖base.py | `get_data_pipeline_logger("DatabaseInspector")` | 数据库检查器 |
+| `data_pipeline/tools/ddl_generator.py` | 依赖base.py | `get_data_pipeline_logger("DDLGenerator")` | DDL生成器 |
+| `data_pipeline/tools/doc_generator.py` | 依赖base.py | `get_data_pipeline_logger("DocGenerator")` | 文档生成器 |
+
+### 3. vanna.log - Vanna相关模块
+
+#### core目录
+| 文件 | 当前日志方式 | 目标Logger | 说明 |
+|------|-------------|-----------|------|
+| `core/vanna_llm_factory.py` | 无 | `get_vanna_logger("VannaFactory")` | Vanna实例工厂 |
+| `core/embedding_function.py` | 无 | `get_vanna_logger("EmbeddingFunction")` | 嵌入函数 |
+
+#### customllm目录
+| 文件 | 当前日志方式 | 目标Logger | 说明 |
+|------|-------------|-----------|------|
+| `customllm/base_llm_chat.py` | print | `get_vanna_logger("BaseLLMChat")` | LLM聊天基类 |
+| `customllm/deepseek_chat.py` | 继承base_llm_chat | `get_vanna_logger("DeepSeekChat")` | DeepSeek聊天实现 |
+| `customllm/ollama_chat.py` | 继承base_llm_chat | `get_vanna_logger("OllamaChat")` | Ollama聊天实现 |
+| `customllm/qianwen_chat.py` | 继承base_llm_chat | `get_vanna_logger("QianwenChat")` | 千问聊天实现 |
+
+#### customembedding目录
+| 文件 | 当前日志方式 | 目标Logger | 说明 |
+|------|-------------|-----------|------|
+| `customembedding/ollama_embedding.py` | 无 | `get_vanna_logger("OllamaEmbedding")` | Ollama嵌入实现 |
+
+#### custompgvector目录
+| 文件 | 当前日志方式 | 目标Logger | 说明 |
+|------|-------------|-----------|------|
+| `custompgvector/pgvector.py` | logging.info/error | `get_vanna_logger("PGVector")` | PostgreSQL向量存储 |
+
+#### common目录(Vanna相关)
+| 文件 | 当前日志方式 | 目标Logger | 说明 |
+|------|-------------|-----------|------|
+| `common/vanna_instance.py` | 无 | `get_vanna_logger("VannaInstance")` | Vanna实例管理 |
+| `common/vanna_combinations.py` | 无 | `get_vanna_logger("VannaCombinations")` | Vanna组合配置 |
+
+### 4. app.log - 主应用和通用模块
+
+#### 主应用文件
+| 文件 | 当前日志方式 | 目标Logger | 说明 |
+|------|-------------|-----------|------|
+| `citu_app.py` | 无 | `get_app_logger("CituApp")` | 主应用入口 |
+| `flask_app.py` | 无 | `get_app_logger("FlaskApp")` | Flask应用(暂不考虑) |
+| `chainlit_app.py` | 无 | `get_app_logger("ChainlitApp")` | Chainlit应用(暂不考虑) |
+| `app_config.py` | 无 | 无需日志 | 应用配置 |
+
+#### common目录(通用功能)
+| 文件 | 当前日志方式 | 目标Logger | 说明 |
+|------|-------------|-----------|------|
+| `common/qa_feedback_manager.py` | print | `get_app_logger("QAFeedbackManager")` | QA反馈管理 |
+| `common/redis_conversation_manager.py` | 无 | `get_app_logger("RedisConversation")` | Redis对话管理 |
+| `common/embedding_cache_manager.py` | 无 | `get_app_logger("EmbeddingCache")` | 嵌入缓存管理 |
+| `common/session_aware_cache.py` | 无 | `get_app_logger("SessionCache")` | 会话缓存 |
+| `common/utils.py` | 无 | `get_app_logger("CommonUtils")` | 通用工具 |
+| `common/messages.py` | 无 | 无需日志 | 消息定义 |
+| `common/result.py` | 无 | 无需日志 | 结果定义 |
+
+## 特殊处理说明
+
+### 1. data_pipeline/utils/logger.py
+当前是data_pipeline模块的日志系统,改造方案:
+- 直接删除原有日志系统代码
+- 保留函数接口(如setup_logging),但函数体为空或直接pass
+- 所有调用该模块的地方直接使用新日志系统
+
+### 2. print语句替换策略
+- 所有 `print()` 语句根据内容判断日志级别
+- 包含 error/exception/fail 的内容 -> `logger.error()`
+- 包含 warning/warn 的内容 -> `logger.warning()`
+- 包含 debug 的内容 -> `logger.debug()`
+- 其他内容 -> `logger.info()`
+
+### 3. 现有logging.getLogger替换
+- 所有 `logging.getLogger("schema_tools.*")` 替换为 `get_data_pipeline_logger()`
+- 保持原有的日志级别习惯
+
+## 实施优先级
+
+1. **第一优先级**:citu_app.py 和 agent 模块(核心功能)
+2. **第二优先级**:data_pipeline 模块(直接改造)
+3. **第三优先级**:vanna 相关模块
+4. **第四优先级**:common 通用模块
+
+## 注意事项
+
+1. **模块分离**:4个模块的日志完全独立,各自输出到对应的日志文件
+2. **上下文信息**:在 agent 模块和 app 模块中支持 user_id 和 session_id(可选)
+3. **性能统计**:保留原有的时间统计方式,不使用装饰器
+4. **直接改造**:所有模块直接使用新系统,不考虑向后兼容
+
+## 测试建议
+
+1. 先在开发环境测试,确保日志正常输出到对应文件
+2. 检查日志文件轮转功能是否正常
+3. 验证各模块日志是否正确分离
+4. 确认错误降级到控制台的功能正常工作