新增自定义日志模块,替换全局logging实现;优化日志输出格式与颜色支持

This commit is contained in:
yinpeng
2024-12-16 16:20:53 +08:00
parent 829c1b02ec
commit 56a069dbac
10 changed files with 131 additions and 24 deletions

3
.vscode/launch.json vendored
View File

@@ -15,7 +15,8 @@
"--host",
"127.0.0.1",
"--port",
"8000"
"8000",
"--no-access-log"
],
"jinja": true
}

View File

@@ -17,4 +17,4 @@ ENV MODEL_SEARCH='["gemini-2.0-flash-exp"]'
EXPOSE 8000
# Run the application
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--no-access-log"]

View File

@@ -1,6 +1,5 @@
from http.client import HTTPException
from fastapi import APIRouter, Depends, Header
import logging
from fastapi.responses import StreamingResponse
from app.core.security import SecurityService
@@ -10,9 +9,10 @@ from app.services.chat_service import ChatService
from app.services.embedding_service import EmbeddingService
from app.schemas.request_model import ChatRequest, EmbeddingRequest
from app.core.config import settings
from app.core.logger import get_api_logger
router = APIRouter()
logger = logging.getLogger(__name__)
logger = get_api_logger()
# 初始化服务
security_service = SecurityService(settings.ALLOWED_TOKENS)
@@ -59,7 +59,6 @@ async def chat_completion(
tools=request.tools,
tool_choice=request.tool_choice,
)
# 处理流式响应
if request.stream:

105
app/core/logger.py Normal file
View File

@@ -0,0 +1,105 @@
import logging
import sys
from typing import Dict, Optional
import platform
# ANSI转义序列颜色代码
COLORS = {
'DEBUG': '\033[34m', # 蓝色
'INFO': '\033[32m', # 绿色
'WARNING': '\033[33m', # 黄色
'ERROR': '\033[31m', # 红色
'CRITICAL': '\033[1;31m' # 红色加粗
}
# Windows系统启用ANSI支持
if platform.system() == 'Windows':
import ctypes
kernel32 = ctypes.windll.kernel32
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
class ColoredFormatter(logging.Formatter):
"""
自定义的日志格式化器,添加颜色支持
"""
def format(self, record):
# 获取对应级别的颜色代码
color = COLORS.get(record.levelname, '')
# 添加颜色代码和重置代码
record.levelname = f"{color}{record.levelname}\033[0m"
return super().format(record)
# 日志格式
FORMATTER = ColoredFormatter(
"%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s"
)
# 日志级别映射
LOG_LEVELS = {
"debug": logging.DEBUG,
"info": logging.INFO,
"warning": logging.WARNING,
"error": logging.ERROR,
"critical": logging.CRITICAL,
}
class Logger:
_loggers: Dict[str, logging.Logger] = {}
@staticmethod
def setup_logger(
name: str,
level: str = "info",
) -> logging.Logger:
"""
设置并获取logger
:param name: logger名称
:param level: 日志级别
:return: logger实例
"""
if name in Logger._loggers:
return Logger._loggers[name]
logger = logging.getLogger(name)
logger.setLevel(LOG_LEVELS.get(level.lower(), logging.INFO))
logger.propagate = False
# 添加控制台输出
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(FORMATTER)
logger.addHandler(console_handler)
Logger._loggers[name] = logger
return logger
@staticmethod
def get_logger(name: str) -> Optional[logging.Logger]:
"""
获取已存在的logger
:param name: logger名称
:return: logger实例或None
"""
return Logger._loggers.get(name)
# 预定义的loggers
def get_api_logger():
return Logger.setup_logger("api")
def get_chat_logger():
return Logger.setup_logger("chat")
def get_model_logger():
return Logger.setup_logger("model")
def get_security_logger():
return Logger.setup_logger("security")
def get_key_manager_logger():
return Logger.setup_logger("key_manager")
def get_main_logger():
return Logger.setup_logger("main")
def get_embeddings_logger():
return Logger.setup_logger("embeddings")

View File

@@ -1,8 +1,8 @@
from fastapi import HTTPException, Header
import logging
from typing import Optional
from app.core.logger import get_security_logger
logger = logging.getLogger(__name__)
logger = get_security_logger()
class SecurityService:

View File

@@ -1,15 +1,12 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import logging
from app.core.logger import get_main_logger
from app.api.routes import router
import uvicorn
# 配置日志
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
logger = get_main_logger()
app = FastAPI()

View File

@@ -2,12 +2,12 @@ import httpx
import json
import time
import uuid
import logging
from typing import Dict, Any, Optional, AsyncGenerator, Union
import openai
from app.core.config import settings
from app.core.logger import get_chat_logger
logger = logging.getLogger(__name__)
logger = get_chat_logger()
class ChatService:
@@ -170,6 +170,9 @@ class ChatService:
gemini_model = model
gemini_messages = self.convert_messages_to_gemini_format(messages)
if not stream:
# 非流式模式下,移除代码执行工具
tools.remove({"code_execution": {}})
payload = {
"contents": gemini_messages,
"generationConfig": {"temperature": temperature},
@@ -241,7 +244,9 @@ class ChatService:
url = f"https://generativelanguage.googleapis.com/v1beta/models/{gemini_model}:generateContent?key={api_key}"
response = await client.post(url, json=payload)
if response.status_code != 200:
raise Exception(f"API error: {response.status_code}")
error_text = response.text
error_code = response.status_code
raise Exception(f"API调用错误 - 状态码: {error_code}, 响应内容: {error_text}")
gemini_response = response.json()
return self.convert_gemini_response_to_openai(gemini_response, model, finish_reason="stop")
except Exception as e:
@@ -287,10 +292,10 @@ class ChatService:
language = code_data.get("language", "").lower()
code = code_data.get("code", "").strip()
return f"""\n```{language}\n{code}\n```\n"""
return f"""\n【代码执行】\n```{language}\n{code}\n```\n"""
def format_execution_result(self, result_data: dict) -> str:
"""格式化执行结果输出"""
outcome = result_data.get("outcome", "")
output = result_data.get("output", "").strip()
return f"""\n【执行结果】\noutcome: {outcome}\noutput: {output}\n"""
return f"""\n【执行结果】\noutcome: {outcome}\noutput:\n```{output}```\n"""

View File

@@ -1,8 +1,8 @@
import logging
import openai
from typing import Union, List, Dict, Any
from app.core.logger import get_embeddings_logger
logger = logging.getLogger(__name__)
logger = get_embeddings_logger()
class EmbeddingService:

View File

@@ -1,9 +1,9 @@
import asyncio
from itertools import cycle
import logging
from typing import Dict
from app.core.logger import get_key_manager_logger
logger = logging.getLogger(__name__)
logger = get_key_manager_logger()
class KeyManager:
@@ -42,7 +42,7 @@ class KeyManager:
current_key = await self.get_next_key()
if current_key == initial_key:
await self.reset_failure_counts()
# await self.reset_failure_counts() 取消重置
return current_key
async def handle_api_failure(self, api_key: str) -> str:

View File

@@ -1,9 +1,9 @@
import requests
from datetime import datetime, timezone
from typing import Optional, Dict, Any
import logging
from app.core.logger import get_model_logger
logger = logging.getLogger(__name__)
logger = get_model_logger()
class ModelService: