refactor: 更新日志格式,增强可读性;移除初始化模块,整合初始化逻辑

This commit is contained in:
snaily
2025-04-21 20:54:34 +08:00
parent f83f0fa768
commit 01765b1731
4 changed files with 100 additions and 108 deletions

View File

@@ -199,7 +199,7 @@ async def sync_initial_settings():
if type_match:
setattr(settings, key, parsed_db_value)
logger.info(f"Updated setting '{key}' in memory from database value ({target_type}).")
logger.debug(f"Updated setting '{key}' in memory from database value ({target_type}).")
updated_in_memory = True
else:
logger.warning(f"Parsed DB value type mismatch for key '{key}'. Expected {target_type}, got {type(parsed_db_value)}. Skipping update.")

View File

@@ -1,4 +1,5 @@
from contextlib import asynccontextmanager
from pathlib import Path # Add pathlib import
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
@@ -9,7 +10,6 @@ from app.middleware.middleware import setup_middlewares
from app.exception.exceptions import setup_exception_handlers
from app.router.routes import setup_routers
from app.service.key.key_manager import get_key_manager_instance
from app.core.initialization import initialize_app
from app.database.connection import connect_to_db, disconnect_from_db
from app.database.initialization import initialize_database
from app.scheduler.key_checker import start_scheduler, stop_scheduler
@@ -17,23 +17,30 @@ from app.service.update.update_service import check_for_updates
logger = get_application_logger()
VERSION_FILE_PATH = "VERSION" # Path relative to project root
# Define project paths using pathlib
# Assuming this file is at app/core/application.py
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
VERSION_FILE_PATH = PROJECT_ROOT / "VERSION"
STATIC_DIR = PROJECT_ROOT / "app" / "static"
TEMPLATES_DIR = PROJECT_ROOT / "app" / "templates"
def _get_current_version(default_version: str = "0.0.0") -> str:
"""Reads the current version from the VERSION file."""
version_file = VERSION_FILE_PATH # Use Path object
try:
# Assuming execution from project root d:/develop/pythonProjects/gemini-balance
with open(VERSION_FILE_PATH, 'r', encoding='utf-8') as f:
# Use Path object's open method
with version_file.open('r', encoding='utf-8') as f:
version = f.read().strip()
if not version:
logger.warning(f"VERSION file ('{VERSION_FILE_PATH}') is empty. Using default version '{default_version}'.")
logger.warning(f"VERSION file ('{version_file}') is empty. Using default version '{default_version}'.")
return default_version
return version
except FileNotFoundError:
logger.warning(f"VERSION file not found at '{VERSION_FILE_PATH}'. Using default version '{default_version}'.")
logger.warning(f"VERSION file not found at '{version_file}'. Using default version '{default_version}'.")
return default_version
except IOError as e:
logger.error(f"Error reading VERSION file ('{VERSION_FILE_PATH}'): {e}. Using default version '{default_version}'.")
logger.error(f"Error reading VERSION file ('{version_file}'): {e}. Using default version '{default_version}'.")
return default_version
# 初始化模板引擎,并添加全局变量
@@ -48,67 +55,87 @@ def update_template_globals(app: FastAPI, update_info: dict):
logger.info(f"Update info stored in app.state: {update_info}")
@asynccontextmanager
async def lifespan(app: FastAPI):
"""
应用程序生命周期管理器
Args:
app: FastAPI应用实例
"""
# 启动事件
logger.info("Application starting up...")
try:
# 初始化数据库
initialize_database()
logger.info("Database initialized successfully")
# 连接到数据库
await connect_to_db()
# 同步初始配置DB优先然后同步回DB
await sync_initial_settings()
# --- Helper functions for lifespan ---
# 初始化KeyManager (使用可能已从DB更新的settings)
await get_key_manager_instance(settings.API_KEYS)
logger.info("KeyManager initialized successfully")
except Exception as e:
logger.error(f"Failed to initialize application: {str(e)}")
# 不重新抛出,允许应用继续运行,但记录错误
# raise # 取消注释以在初始化失败时停止应用
async def _setup_database_and_config(app_settings):
"""Initializes database, syncs settings, and initializes KeyManager."""
initialize_database()
logger.info("Database initialized successfully")
await connect_to_db()
await sync_initial_settings()
# Initialize KeyManager using potentially updated settings
await get_key_manager_instance(app_settings.API_KEYS)
logger.info("Database, config sync, and KeyManager initialized successfully")
# 检查更新 (在核心初始化之后)
update_available, latest_version, error_message = await check_for_updates()
update_info = {
"update_available": update_available,
"latest_version": latest_version,
"error_message": error_message,
"current_version": _get_current_version() # Read from VERSION file
}
# 将更新信息存储在 app.state 中
app.state.update_info = update_info
logger.info(f"Update check completed. Info: {update_info}")
async def _shutdown_database():
"""Disconnects from the database."""
await disconnect_from_db()
logger.info("Disconnected from database.")
# 启动调度器 (如果初始化成功)
def _start_scheduler():
"""Starts the background scheduler."""
try:
start_scheduler()
logger.info("Scheduler started successfully.")
except Exception as e:
logger.error(f"Failed to start scheduler: {e}")
yield # 应用程序运行期间
# 关闭事件
logger.info("Application shutting down...")
# 停止调度器
def _stop_scheduler():
"""Stops the background scheduler."""
stop_scheduler()
logger.info("Scheduler stopped.")
# 断开数据库连接
await disconnect_from_db()
async def _perform_update_check(app: FastAPI):
"""Checks for updates and stores the info in app.state."""
update_available, latest_version, error_message = await check_for_updates()
current_version = _get_current_version() # Read from VERSION file
update_info = {
"update_available": update_available,
"latest_version": latest_version,
"error_message": error_message,
"current_version": current_version
}
# Ensure app.state exists and store update info
if not hasattr(app, "state"):
from starlette.datastructures import State
app.state = State()
app.state.update_info = update_info
logger.info(f"Update check completed. Info: {update_info}")
# --- Application Lifespan ---
@asynccontextmanager
async def lifespan(app: FastAPI):
"""
Manages the application startup and shutdown events.
Args:
app: FastAPI应用实例
"""
# Startup events
logger.info("Application starting up...")
try:
# Setup database, config, and KeyManager
await _setup_database_and_config(settings) # Pass settings object
# Perform update check after core components are ready
await _perform_update_check(app)
# Start the scheduler
_start_scheduler()
except Exception as e:
logger.critical(f"Critical error during application startup: {str(e)}", exc_info=True)
# Depending on the severity, you might want to prevent the app from fully starting
# For now, we log critically and let it yield, potentially in a broken state.
# Consider adding more robust error handling here if startup failures should halt the app.
yield # Application runs
# Shutdown events
logger.info("Application shutting down...")
_stop_scheduler()
await _shutdown_database()
def create_app() -> FastAPI:
"""
@@ -117,28 +144,33 @@ def create_app() -> FastAPI:
Returns:
FastAPI: 配置好的FastAPI应用程序实例
"""
# 初始化应用程序
initialize_app()
# Removed: initialize_app() call
# 创建FastAPI应用
# Read version from file for consistency
current_version = _get_current_version()
app = FastAPI(
title="Gemini Balance API",
description="Gemini API代理服务支持负载均衡和密钥管理",
version="1.0.0",
version=current_version,
lifespan=lifespan
)
# 初始化 app.state (如果尚未存在)
# Initialize app.state early to ensure it exists before lifespan potentially uses it
if not hasattr(app, "state"):
from starlette.datastructures import State
app.state = State()
# 确保 update_info 即使在 lifespan 之前访问也不会出错
app.state.update_info = {"update_available": False, "latest_version": None, "error_message": "Checking...", "current_version": _get_current_version()} # Read from VERSION file for initial state
# Set a default/initial state for update_info
app.state.update_info = {
"update_available": False,
"latest_version": None,
"error_message": "Initializing...",
"current_version": current_version # Use version read earlier
}
# 配置静态文件
app.mount("/static", StaticFiles(directory="app/static"), name="static")
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
# 配置中间件
setup_middlewares(app)

View File

@@ -1,40 +0,0 @@
"""
应用程序初始化模块
"""
from pathlib import Path
from typing import List
from app.log.logger import get_initialization_logger
logger = get_initialization_logger()
def ensure_directories_exist(directories: List[str]) -> None:
"""
确保指定的目录存在,如果不存在则创建
Args:
directories: 要确保存在的目录列表
"""
for directory in directories:
try:
Path(directory).mkdir(parents=True, exist_ok=True)
logger.info(f"Ensured directory exists: {directory}")
except Exception as e:
logger.error(f"Failed to create directory {directory}: {str(e)}")
def initialize_app() -> None:
"""
初始化应用程序,确保所需的目录和文件都存在
"""
# 确保必要的目录存在
required_directories = [
"app/static/css",
"app/static/js",
"app/static/icons",
"app/templates",
]
ensure_directories_exist(required_directories)
logger.info("core initialization completed")

View File

@@ -35,7 +35,7 @@ class ColoredFormatter(logging.Formatter):
# 日志格式
FORMATTER = ColoredFormatter(
"%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s"
"%(asctime)s | %(name)-15s | %(levelname)-8s | [%(filename)-20s:%(lineno)-4d] | %(message)s"
)
# 日志级别映射