mirror of https://github.com/Mai-with-u/MaiBot.git
refactor: 优化日志系统关闭流程,使用 print 替代 logger 输出,确保在关闭后仍能记录信息
feat: 添加服务器关闭时的超时处理,避免 shutdown 持续挂起 fix: 更新生产模式设置,使用 Starlette 的 FileResponse 处理静态文件pull/1364/head
parent
e57a996626
commit
060ce5d55b
13
bot.py
13
bot.py
|
|
@ -107,9 +107,6 @@ async def graceful_shutdown(): # sourcery skip: use-named-expression
|
|||
|
||||
logger.info("麦麦优雅关闭完成")
|
||||
|
||||
# 关闭日志系统,释放文件句柄
|
||||
shutdown_logging()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"麦麦关闭失败: {e}", exc_info=True)
|
||||
|
||||
|
|
@ -241,7 +238,7 @@ if __name__ == "__main__":
|
|||
# 确保 loop 在任何情况下都尝试关闭(如果存在且未关闭)
|
||||
if "loop" in locals() and loop and not loop.is_closed():
|
||||
loop.close()
|
||||
logger.info("事件循环已关闭")
|
||||
print("[主程序] 事件循环已关闭")
|
||||
|
||||
# 关闭日志系统,释放文件句柄
|
||||
try:
|
||||
|
|
@ -249,6 +246,8 @@ if __name__ == "__main__":
|
|||
except Exception as e:
|
||||
print(f"关闭日志系统时出错: {e}")
|
||||
|
||||
# 在程序退出前暂停,让你有机会看到输出
|
||||
# input("按 Enter 键退出...") # <--- 添加这行
|
||||
sys.exit(exit_code) # <--- 使用记录的退出码
|
||||
print("[主程序] 准备退出...")
|
||||
|
||||
# 使用 os._exit() 强制退出,避免被阻塞
|
||||
# 由于已经在 graceful_shutdown() 中完成了所有清理工作,这是安全的
|
||||
os._exit(exit_code)
|
||||
|
|
|
|||
|
|
@ -843,8 +843,8 @@ def start_log_cleanup_task():
|
|||
|
||||
def shutdown_logging():
|
||||
"""优雅关闭日志系统,释放所有文件句柄"""
|
||||
logger = get_logger("logger")
|
||||
logger.info("正在关闭日志系统...")
|
||||
# 先输出到控制台,避免日志系统关闭后无法输出
|
||||
print("[logger] 正在关闭日志系统...")
|
||||
|
||||
# 关闭所有handler
|
||||
root_logger = logging.getLogger()
|
||||
|
|
@ -865,4 +865,5 @@ def shutdown_logging():
|
|||
handler.close()
|
||||
logger_obj.removeHandler(handler)
|
||||
|
||||
logger.info("日志系统已关闭")
|
||||
# 使用 print 而不是 logger,因为 logger 已经关闭
|
||||
print("[logger] 日志系统已关闭")
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from fastapi import FastAPI, APIRouter
|
|||
from fastapi.middleware.cors import CORSMiddleware # 新增导入
|
||||
from typing import Optional
|
||||
from uvicorn import Config, Server as UvicornServer
|
||||
import asyncio
|
||||
import os
|
||||
from rich.traceback import install
|
||||
|
||||
|
|
@ -82,8 +83,17 @@ class Server:
|
|||
"""安全关闭服务器"""
|
||||
if self._server:
|
||||
self._server.should_exit = True
|
||||
await self._server.shutdown()
|
||||
self._server = None
|
||||
try:
|
||||
# 添加 3 秒超时,避免 shutdown 永久挂起
|
||||
await asyncio.wait_for(self._server.shutdown(), timeout=3.0)
|
||||
except asyncio.TimeoutError:
|
||||
# 超时就强制标记为 None,让垃圾回收处理
|
||||
pass
|
||||
except Exception:
|
||||
# 忽略其他异常
|
||||
pass
|
||||
finally:
|
||||
self._server = None
|
||||
|
||||
def get_app(self) -> FastAPI:
|
||||
"""获取 FastAPI 实例"""
|
||||
|
|
|
|||
|
|
@ -41,8 +41,7 @@ def setup_production_mode() -> bool:
|
|||
"""设置生产模式 - 挂载静态文件"""
|
||||
try:
|
||||
from src.common.server import get_global_server
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.responses import FileResponse
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
server = get_global_server()
|
||||
base_dir = Path(__file__).parent.parent.parent
|
||||
|
|
@ -58,14 +57,6 @@ def setup_production_mode() -> bool:
|
|||
logger.warning("💡 请确认前端已正确构建")
|
||||
return False
|
||||
|
||||
# 挂载静态资源
|
||||
if (static_path / "assets").exists():
|
||||
server.app.mount(
|
||||
"/assets",
|
||||
StaticFiles(directory=str(static_path / "assets")),
|
||||
name="assets"
|
||||
)
|
||||
|
||||
# 处理 SPA 路由
|
||||
@server.app.get("/{full_path:path}")
|
||||
async def serve_spa(full_path: str):
|
||||
|
|
@ -77,6 +68,7 @@ def setup_production_mode() -> bool:
|
|||
# 检查文件是否存在
|
||||
file_path = static_path / full_path
|
||||
if file_path.is_file():
|
||||
# 直接返回文件,Starlette 会自动管理文件句柄
|
||||
return FileResponse(file_path)
|
||||
|
||||
# 返回 index.html(SPA 路由)
|
||||
|
|
|
|||
Loading…
Reference in New Issue