Files
FastapiAdmin/backend/app/modules/monitor/server/service.py
T
zhangtao f15fdb86c0 feat(ticket,monitor): add ticket stats api and optimize server ip fetch
1. add TicketStatsSchema for work status aggregate
2. add stats method to TicketService to count pending/processing/done tickets
3. add ticket stats api endpoint
4. optimize server local ip detection with fallback logic
2026-09-12 12:33:00 +08:00

144 lines
5.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import asyncio
import platform
import socket
import time
from pathlib import Path
import psutil
from app.utils.common_util import bytes2human
from .schema import (
CpuInfoSchema,
DiskInfoSchema,
MemoryInfoSchema,
PyInfoSchema,
ServerMonitorSchema,
SysInfoSchema,
)
class ServerService:
"""服务监控模块服务层"""
@staticmethod
async def get_server_monitor_info() -> ServerMonitorSchema:
# 采样全部为同步阻塞调用(psutil.disk_usage 在网络盘/卸载中的挂载点上
# 可达秒级,socket.gethostbyname 依赖 DNS),放到工作线程执行避免拖慢事件循环
return await asyncio.to_thread(ServerService._collect_monitor_info)
@staticmethod
def _collect_monitor_info() -> ServerMonitorSchema:
return ServerMonitorSchema(
cpu=ServerService._get_cpu_info(),
mem=ServerService._get_memory_info(),
sys=ServerService._get_system_info(),
py=ServerService._get_python_info(),
disks=ServerService._get_disk_info(),
)
@staticmethod
def _get_cpu_info() -> CpuInfoSchema:
cpu_times = psutil.cpu_times_percent()
cpu_num = psutil.cpu_count(logical=True)
if not cpu_num:
cpu_num = 1
return CpuInfoSchema(
cpu_num=cpu_num,
used=cpu_times.user,
sys=cpu_times.system,
free=cpu_times.idle,
)
@staticmethod
def _get_memory_info() -> MemoryInfoSchema:
memory = psutil.virtual_memory()
return MemoryInfoSchema(
total=bytes2human(memory.total),
used=bytes2human(memory.used),
free=bytes2human(memory.free),
usage=memory.percent,
)
@staticmethod
def _get_local_ip() -> str:
"""获取本机对外通信 IP,逐级降级,任何环境都不抛异常。
1. UDP「连接」路由探测:connect 只让内核查路由表选出站源地址,
不实际发包、不依赖 DNS,容器内能拿到容器网段 IP,macOS 上能拿到
真实局域网 IP(gethostbyname(主机名) 在 macOS 常返回 127.0.0.1);
2. 主机名解析(无默认路由等场景的兜底);
3. 回环地址。
"""
try:
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
sock.connect(("8.8.8.8", 80))
return sock.getsockname()[0]
except OSError:
pass
try:
return socket.gethostbyname(socket.gethostname())
except OSError:
return "127.0.0.1"
@staticmethod
def _get_system_info() -> SysInfoSchema:
return SysInfoSchema(
computer_ip=ServerService._get_local_ip(),
computer_name=platform.node(),
os_arch=platform.machine(),
os_name=platform.platform(),
user_dir=str(Path.cwd()),
)
@staticmethod
def _get_python_info() -> PyInfoSchema:
current_process = psutil.Process()
memory = psutil.virtual_memory()
process_memory = current_process.memory_info()
start_time = current_process.create_time()
run_time = ServerService._calculate_run_time(start_time)
return PyInfoSchema(
name=current_process.name(),
version=platform.python_version(),
start_time=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(start_time)),
run_time=run_time,
home=str(Path(current_process.exe())),
memory_total=bytes2human(memory.available),
memory_used=bytes2human(process_memory.rss),
memory_free=bytes2human(memory.available - process_memory.rss),
memory_usage=round((process_memory.rss / memory.available) * 100, 2),
)
@staticmethod
def _get_disk_info() -> list[DiskInfoSchema]:
disk_info = []
for partition in psutil.disk_partitions():
try:
usage = psutil.disk_usage(partition.mountpoint)
mount_point = str(Path(partition.mountpoint))
disk_info.append(
DiskInfoSchema(
dir_name=mount_point,
sys_type_name=partition.fstype,
type_name=f"本地固定磁盘({mount_point})",
total=bytes2human(usage.total),
used=bytes2human(usage.used),
free=bytes2human(usage.free),
usage=usage.percent,
),
)
except (PermissionError, FileNotFoundError):
continue
return disk_info
@staticmethod
def _calculate_run_time(start_time: float) -> str:
difference = time.time() - start_time
days = int(difference // (24 * 60 * 60))
hours = int((difference % (24 * 60 * 60)) // (60 * 60))
minutes = int((difference % (60 * 60)) // 60)
return f"{days}天{hours}小时{minutes}分钟"