refactor(logging): introduce centralized logging and FastAPI lifespan
Some checks failed
Build and Push Docker / build-and-push (push) Failing after 2m38s
Some checks failed
Build and Push Docker / build-and-push (push) Failing after 2m38s
This commit is contained in:
@ -4,6 +4,7 @@ import threading
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import logging
|
||||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
from config import (
|
from config import (
|
||||||
@ -13,6 +14,8 @@ from config import (
|
|||||||
from api.doubao_tts import text_to_speech
|
from api.doubao_tts import text_to_speech
|
||||||
from memory_module.memory_integration import Mem0Integration
|
from memory_module.memory_integration import Mem0Integration
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ChatService:
|
class ChatService:
|
||||||
def __init__(self, user_id: str = None):
|
def __init__(self, user_id: str = None):
|
||||||
@ -25,7 +28,7 @@ class ChatService:
|
|||||||
if self._initialized:
|
if self._initialized:
|
||||||
return
|
return
|
||||||
|
|
||||||
print(f"[INFO] Initializing Mem0 integration for user: {self.user_id}")
|
logger.info(f"Initializing Mem0 integration for user: {self.user_id}")
|
||||||
self._initialized = True
|
self._initialized = True
|
||||||
|
|
||||||
def chat(self, user_input: str, include_audio: bool = True) -> Dict[str, Any]:
|
def chat(self, user_input: str, include_audio: bool = True) -> Dict[str, Any]:
|
||||||
|
47
api/main.py
47
api/main.py
@ -1,9 +1,25 @@
|
|||||||
|
from contextlib import asynccontextmanager
|
||||||
from fastapi import FastAPI, HTTPException
|
from fastapi import FastAPI, HTTPException
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# 添加项目根目录到Python路径
|
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
from chat_service import ChatService
|
from chat_service import ChatService
|
||||||
|
from logging_config import setup_logging, get_logger
|
||||||
|
|
||||||
|
# 设置日志
|
||||||
|
setup_logging(
|
||||||
|
level=os.getenv('LOG_LEVEL', 'INFO'),
|
||||||
|
enable_file_logging=os.getenv('ENABLE_FILE_LOGGING', 'false').lower() == 'true'
|
||||||
|
)
|
||||||
|
|
||||||
|
# 获取logger
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# 请求和响应模型
|
# 请求和响应模型
|
||||||
@ -23,21 +39,30 @@ class ChatResponse(BaseModel):
|
|||||||
audio_error: Optional[str] = None
|
audio_error: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
# 创建 FastAPI 应用
|
|
||||||
app = FastAPI(
|
|
||||||
title="Mem0 Memory API",
|
|
||||||
description="基于 Mem0 的记忆增强聊天服务 API",
|
|
||||||
version="1.0.0"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 全局聊天服务实例
|
# 全局聊天服务实例
|
||||||
chat_service = ChatService()
|
chat_service = ChatService()
|
||||||
|
|
||||||
|
|
||||||
@app.on_event("startup")
|
@asynccontextmanager
|
||||||
async def startup_event():
|
async def lifespan(app: FastAPI):
|
||||||
"""应用启动时初始化聊天服务"""
|
"""应用生命周期管理器"""
|
||||||
|
logger.info("Starting Mem0 Memory API...")
|
||||||
|
# 应用启动时初始化聊天服务
|
||||||
chat_service.initialize()
|
chat_service.initialize()
|
||||||
|
logger.info("Mem0 Memory API started successfully")
|
||||||
|
yield
|
||||||
|
# 应用关闭时可以在这里执行清理操作
|
||||||
|
# 例如关闭数据库连接、释放资源等
|
||||||
|
logger.info("Shutting down Mem0 Memory API...")
|
||||||
|
|
||||||
|
|
||||||
|
# 创建 FastAPI 应用
|
||||||
|
app = FastAPI(
|
||||||
|
title="Mem0 Memory API",
|
||||||
|
description="基于 Mem0 的记忆增强聊天服务 API",
|
||||||
|
version="1.0.0",
|
||||||
|
lifespan=lifespan
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
@ -80,4 +105,4 @@ if __name__ == "__main__":
|
|||||||
host="0.0.0.0",
|
host="0.0.0.0",
|
||||||
port=8000,
|
port=8000,
|
||||||
reload=True
|
reload=True
|
||||||
)
|
)
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
# main.py
|
# main.py
|
||||||
import sys
|
import sys
|
||||||
|
import logging
|
||||||
from haystack import Document
|
from haystack import Document
|
||||||
|
|
||||||
# 需要 OpenAIDocumentEmbedder 来嵌入要写入的文档
|
# 需要 OpenAIDocumentEmbedder 来嵌入要写入的文档
|
||||||
from haystack.components.embedders import OpenAIDocumentEmbedder
|
from haystack.components.embedders import OpenAIDocumentEmbedder
|
||||||
from haystack.utils import Secret
|
from haystack.utils import Secret
|
||||||
|
|
||||||
|
# 设置logger
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# 导入所需的配置和构建函数
|
# 导入所需的配置和构建函数
|
||||||
from config import (
|
from config import (
|
||||||
DEFAULT_USER_ID,
|
DEFAULT_USER_ID,
|
||||||
@ -130,7 +134,7 @@ def run_chat_session(user_id: str):
|
|||||||
print(f"Chatbot: {answer} (Tokens: {total_tokens})")
|
print(f"Chatbot: {answer} (Tokens: {total_tokens})")
|
||||||
else:
|
else:
|
||||||
print("Chatbot: Sorry, I couldn't generate an answer for that.")
|
print("Chatbot: Sorry, I couldn't generate an answer for that.")
|
||||||
print("Debug Info (Pipeline Results):", results) # 打印完整结果以供调试
|
logger.debug(f"Pipeline Results: {results}") # 记录调试信息
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\nExiting chat session. Goodbye!")
|
print("\nExiting chat session. Goodbye!")
|
||||||
|
117
logging_config.py
Normal file
117
logging_config.py
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import logging
|
||||||
|
import logging.config
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
def setup_logging(
|
||||||
|
level: str = None,
|
||||||
|
log_file: str = None,
|
||||||
|
enable_file_logging: bool = False
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
设置日志配置
|
||||||
|
|
||||||
|
Args:
|
||||||
|
level: 日志级别 (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
||||||
|
log_file: 日志文件名
|
||||||
|
enable_file_logging: 是否启用文件日志
|
||||||
|
"""
|
||||||
|
# 如果没有指定级别,从环境变量获取,默认INFO
|
||||||
|
if level is None:
|
||||||
|
level = os.getenv('LOG_LEVEL', 'INFO').upper()
|
||||||
|
|
||||||
|
# 默认日志格式
|
||||||
|
log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||||
|
date_format = '%Y-%m-%d %H:%M:%S'
|
||||||
|
|
||||||
|
# 基本配置
|
||||||
|
config = {
|
||||||
|
'version': 1,
|
||||||
|
'disable_existing_loggers': False,
|
||||||
|
'formatters': {
|
||||||
|
'standard': {
|
||||||
|
'format': log_format,
|
||||||
|
'datefmt': date_format
|
||||||
|
},
|
||||||
|
'detailed': {
|
||||||
|
'format': '%(asctime)s - %(name)s - %(levelname)s - %(module)s - %(funcName)s - %(lineno)d - %(message)s',
|
||||||
|
'datefmt': date_format
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'handlers': {
|
||||||
|
'console': {
|
||||||
|
'level': level,
|
||||||
|
'class': 'logging.StreamHandler',
|
||||||
|
'formatter': 'standard',
|
||||||
|
'stream': 'ext://sys.stdout'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'root': {
|
||||||
|
'level': level,
|
||||||
|
'handlers': ['console']
|
||||||
|
},
|
||||||
|
'loggers': {
|
||||||
|
# 针对特定模块的日志配置
|
||||||
|
'memory_integration': {
|
||||||
|
'level': level,
|
||||||
|
'handlers': ['console'],
|
||||||
|
'propagate': False
|
||||||
|
},
|
||||||
|
'chat_service': {
|
||||||
|
'level': level,
|
||||||
|
'handlers': ['console'],
|
||||||
|
'propagate': False
|
||||||
|
},
|
||||||
|
'rag_pipeline': {
|
||||||
|
'level': level,
|
||||||
|
'handlers': ['console'],
|
||||||
|
'propagate': False
|
||||||
|
},
|
||||||
|
'uvicorn': {
|
||||||
|
'level': 'INFO',
|
||||||
|
'handlers': ['console'],
|
||||||
|
'propagate': False
|
||||||
|
},
|
||||||
|
'fastapi': {
|
||||||
|
'level': 'INFO',
|
||||||
|
'handlers': ['console'],
|
||||||
|
'propagate': False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 如果启用文件日志
|
||||||
|
if enable_file_logging:
|
||||||
|
if log_file is None:
|
||||||
|
# 默认日志文件名
|
||||||
|
log_dir = 'logs'
|
||||||
|
os.makedirs(log_dir, exist_ok=True)
|
||||||
|
log_file = os.path.join(
|
||||||
|
log_dir,
|
||||||
|
f"app_{datetime.now().strftime('%Y%m%d')}.log"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 添加文件处理器
|
||||||
|
config['handlers']['file'] = {
|
||||||
|
'level': 'DEBUG', # 文件日志记录所有级别
|
||||||
|
'class': 'logging.handlers.RotatingFileHandler',
|
||||||
|
'filename': log_file,
|
||||||
|
'maxBytes': 10485760, # 10MB
|
||||||
|
'backupCount': 5,
|
||||||
|
'formatter': 'detailed',
|
||||||
|
'encoding': 'utf-8'
|
||||||
|
}
|
||||||
|
|
||||||
|
# 为所有logger添加文件处理器
|
||||||
|
for logger_name in config['loggers']:
|
||||||
|
config['loggers'][logger_name]['handlers'].append('file')
|
||||||
|
config['root']['handlers'].append('file')
|
||||||
|
|
||||||
|
# 应用配置
|
||||||
|
logging.config.dictConfig(config)
|
||||||
|
|
||||||
|
|
||||||
|
def get_logger(name: str) -> logging.Logger:
|
||||||
|
"""获取指定名称的logger"""
|
||||||
|
return logging.getLogger(name)
|
@ -4,6 +4,9 @@ from datetime import datetime
|
|||||||
import openai
|
import openai
|
||||||
import threading
|
import threading
|
||||||
from mem0 import Memory
|
from mem0 import Memory
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Mem0Integration:
|
class Mem0Integration:
|
||||||
@ -42,19 +45,19 @@ In your response, consider the memories above to provide a personalized answer."
|
|||||||
memories = results.get("memories", results.get("results", []))
|
memories = results.get("memories", results.get("results", []))
|
||||||
return memories
|
return memories
|
||||||
else:
|
else:
|
||||||
print(f"[ERROR] Unexpected search results format: {type(results)}")
|
logger.error(f"Unexpected search results format: {type(results)}")
|
||||||
return []
|
return []
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[ERROR] Failed to search memories: {e}")
|
logger.error(f"Failed to search memories: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def add_memory(self, messages: List[Dict[str, str]], user_id: str, metadata: Optional[Dict] = None) -> Dict[str, Any]:
|
def add_memory(self, messages: List[Dict[str, str]], user_id: str, metadata: Optional[Dict] = None) -> Dict[str, Any]:
|
||||||
"""Add a memory for the user."""
|
"""Add a memory for the user."""
|
||||||
try:
|
try:
|
||||||
# Debug: Print what we're trying to add
|
# Debug: Log what we're trying to add
|
||||||
print(f"[DEBUG] Adding memory for user {user_id}")
|
logger.debug(f"Adding memory for user {user_id}")
|
||||||
print(f"[DEBUG] Messages: {messages}")
|
logger.debug(f"Messages: {messages}")
|
||||||
print(f"[DEBUG] Metadata: {metadata}")
|
logger.debug(f"Metadata: {metadata}")
|
||||||
|
|
||||||
result = self.memory.add(
|
result = self.memory.add(
|
||||||
messages=messages,
|
messages=messages,
|
||||||
@ -63,13 +66,12 @@ In your response, consider the memories above to provide a personalized answer."
|
|||||||
infer= False
|
infer= False
|
||||||
)
|
)
|
||||||
|
|
||||||
# Debug: Print the result
|
# Debug: Log the result
|
||||||
print(f"[DEBUG] Add memory result: {result}")
|
logger.debug(f"Add memory result: {result}")
|
||||||
return result
|
return result
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[ERROR] Failed to add memory: {e}")
|
logger.error(f"Failed to add memory: {e}")
|
||||||
import traceback
|
logger.exception("Exception details:")
|
||||||
traceback.print_exc()
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def format_memories_for_prompt(self, memories: List[Any]) -> str:
|
def format_memories_for_prompt(self, memories: List[Any]) -> str:
|
||||||
|
Reference in New Issue
Block a user