mirror of
https://github.com/snailyp/gemini-balance.git
synced 2026-09-09 01:46:38 +08:00
refactor: 更新日志格式,增强可读性;移除初始化模块,整合初始化逻辑
This commit is contained in:
@@ -199,7 +199,7 @@ async def sync_initial_settings():
|
|||||||
|
|
||||||
if type_match:
|
if type_match:
|
||||||
setattr(settings, key, parsed_db_value)
|
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
|
updated_in_memory = True
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Parsed DB value type mismatch for key '{key}'. Expected {target_type}, got {type(parsed_db_value)}. Skipping update.")
|
logger.warning(f"Parsed DB value type mismatch for key '{key}'. Expected {target_type}, got {type(parsed_db_value)}. Skipping update.")
|
||||||
|
|||||||
+92
-60
@@ -1,4 +1,5 @@
|
|||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
from pathlib import Path # Add pathlib import
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
from fastapi.templating import Jinja2Templates
|
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.exception.exceptions import setup_exception_handlers
|
||||||
from app.router.routes import setup_routers
|
from app.router.routes import setup_routers
|
||||||
from app.service.key.key_manager import get_key_manager_instance
|
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.connection import connect_to_db, disconnect_from_db
|
||||||
from app.database.initialization import initialize_database
|
from app.database.initialization import initialize_database
|
||||||
from app.scheduler.key_checker import start_scheduler, stop_scheduler
|
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()
|
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:
|
def _get_current_version(default_version: str = "0.0.0") -> str:
|
||||||
"""Reads the current version from the VERSION file."""
|
"""Reads the current version from the VERSION file."""
|
||||||
|
version_file = VERSION_FILE_PATH # Use Path object
|
||||||
try:
|
try:
|
||||||
# Assuming execution from project root d:/develop/pythonProjects/gemini-balance
|
# Use Path object's open method
|
||||||
with open(VERSION_FILE_PATH, 'r', encoding='utf-8') as f:
|
with version_file.open('r', encoding='utf-8') as f:
|
||||||
version = f.read().strip()
|
version = f.read().strip()
|
||||||
if not version:
|
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 default_version
|
||||||
return version
|
return version
|
||||||
except FileNotFoundError:
|
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
|
return default_version
|
||||||
except IOError as e:
|
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
|
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}")
|
logger.info(f"Update info stored in app.state: {update_info}")
|
||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
# --- Helper functions for lifespan ---
|
||||||
async def lifespan(app: FastAPI):
|
|
||||||
"""
|
|
||||||
应用程序生命周期管理器
|
|
||||||
|
|
||||||
Args:
|
async def _setup_database_and_config(app_settings):
|
||||||
app: FastAPI应用实例
|
"""Initializes database, syncs settings, and initializes KeyManager."""
|
||||||
"""
|
|
||||||
# 启动事件
|
|
||||||
logger.info("Application starting up...")
|
|
||||||
try:
|
|
||||||
# 初始化数据库
|
|
||||||
initialize_database()
|
initialize_database()
|
||||||
logger.info("Database initialized successfully")
|
logger.info("Database initialized successfully")
|
||||||
|
|
||||||
# 连接到数据库
|
|
||||||
await connect_to_db()
|
await connect_to_db()
|
||||||
|
|
||||||
# 同步初始配置(DB优先,然后同步回DB)
|
|
||||||
await sync_initial_settings()
|
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")
|
||||||
|
|
||||||
# 初始化KeyManager (使用可能已从DB更新的settings)
|
async def _shutdown_database():
|
||||||
await get_key_manager_instance(settings.API_KEYS)
|
"""Disconnects from the database."""
|
||||||
logger.info("KeyManager initialized successfully")
|
await disconnect_from_db()
|
||||||
except Exception as e:
|
logger.info("Disconnected from database.")
|
||||||
logger.error(f"Failed to initialize application: {str(e)}")
|
|
||||||
# 不重新抛出,允许应用继续运行,但记录错误
|
|
||||||
# raise # 取消注释以在初始化失败时停止应用
|
|
||||||
|
|
||||||
# 检查更新 (在核心初始化之后)
|
def _start_scheduler():
|
||||||
update_available, latest_version, error_message = await check_for_updates()
|
"""Starts the background scheduler."""
|
||||||
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}")
|
|
||||||
|
|
||||||
|
|
||||||
# 启动调度器 (如果初始化成功)
|
|
||||||
try:
|
try:
|
||||||
start_scheduler()
|
start_scheduler()
|
||||||
logger.info("Scheduler started successfully.")
|
logger.info("Scheduler started successfully.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to start scheduler: {e}")
|
logger.error(f"Failed to start scheduler: {e}")
|
||||||
|
|
||||||
|
def _stop_scheduler():
|
||||||
yield # 应用程序运行期间
|
"""Stops the background scheduler."""
|
||||||
|
|
||||||
# 关闭事件
|
|
||||||
logger.info("Application shutting down...")
|
|
||||||
|
|
||||||
# 停止调度器
|
|
||||||
stop_scheduler()
|
stop_scheduler()
|
||||||
logger.info("Scheduler stopped.")
|
logger.info("Scheduler stopped.")
|
||||||
|
|
||||||
# 断开数据库连接
|
async def _perform_update_check(app: FastAPI):
|
||||||
await disconnect_from_db()
|
"""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:
|
def create_app() -> FastAPI:
|
||||||
"""
|
"""
|
||||||
@@ -117,27 +144,32 @@ def create_app() -> FastAPI:
|
|||||||
Returns:
|
Returns:
|
||||||
FastAPI: 配置好的FastAPI应用程序实例
|
FastAPI: 配置好的FastAPI应用程序实例
|
||||||
"""
|
"""
|
||||||
# 初始化应用程序
|
# Removed: initialize_app() call
|
||||||
initialize_app()
|
|
||||||
|
|
||||||
# 创建FastAPI应用
|
# 创建FastAPI应用
|
||||||
|
# Read version from file for consistency
|
||||||
|
current_version = _get_current_version()
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title="Gemini Balance API",
|
title="Gemini Balance API",
|
||||||
description="Gemini API代理服务,支持负载均衡和密钥管理",
|
description="Gemini API代理服务,支持负载均衡和密钥管理",
|
||||||
version="1.0.0",
|
version=current_version,
|
||||||
lifespan=lifespan
|
lifespan=lifespan
|
||||||
)
|
)
|
||||||
|
|
||||||
# 初始化 app.state (如果尚未存在)
|
# Initialize app.state early to ensure it exists before lifespan potentially uses it
|
||||||
if not hasattr(app, "state"):
|
if not hasattr(app, "state"):
|
||||||
from starlette.datastructures import State
|
from starlette.datastructures import State
|
||||||
app.state = State()
|
app.state = State()
|
||||||
# 确保 update_info 即使在 lifespan 之前访问也不会出错
|
# Set a default/initial state for update_info
|
||||||
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
|
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)
|
setup_middlewares(app)
|
||||||
|
|||||||
@@ -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")
|
|
||||||
+1
-1
@@ -35,7 +35,7 @@ class ColoredFormatter(logging.Formatter):
|
|||||||
|
|
||||||
# 日志格式
|
# 日志格式
|
||||||
FORMATTER = ColoredFormatter(
|
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 日志级别映射
|
# 日志级别映射
|
||||||
|
|||||||
Reference in New Issue
Block a user