|
@@ -0,0 +1,287 @@
|
|
|
|
+# 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 核心功能的同时,添加生产环境所需的日志管理能力。这种方式既能利用现有的架构优势,又能满足复杂的日志需求。
|