mirror of
https://github.com/fastapi-practices/fastapi-best-architecture.git
synced 2026-09-21 21:15:13 +00:00
Update redis and server monitor implementations (#1000)
* Update redis and server monitor implementations * Fix server disk information
This commit is contained in:
@@ -1,16 +1,40 @@
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from backend.common.response.response_schema import ResponseModel, response_base
|
from backend.app.admin.schema.monitor import RedisCommandStat, RedisMonitorInfo, RedisServerInfo
|
||||||
|
from backend.common.response.response_schema import ResponseSchemaModel, response_base
|
||||||
from backend.common.security.jwt import DependsJwtAuth
|
from backend.common.security.jwt import DependsJwtAuth
|
||||||
from backend.utils.redis_info import redis_info
|
from backend.database.redis import redis_client
|
||||||
|
from backend.utils.format import fmt_seconds
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.get('', summary='redis 监控', dependencies=[DependsJwtAuth])
|
@router.get('', summary='redis 监控', dependencies=[DependsJwtAuth])
|
||||||
async def get_redis_info() -> ResponseModel:
|
async def get_redis_info() -> ResponseSchemaModel[RedisMonitorInfo]:
|
||||||
data = {
|
info = await redis_client.info()
|
||||||
'info': await redis_info.get_info(),
|
db_size = await redis_client.dbsize()
|
||||||
'stats': await redis_info.get_stats(),
|
|
||||||
}
|
uptime_formatted = fmt_seconds(int(info.get('uptime_in_seconds', 0)))
|
||||||
|
|
||||||
|
server_info = RedisServerInfo(
|
||||||
|
redis_version=str(info.get('redis_version', '')),
|
||||||
|
redis_mode=str(info.get('redis_mode', '')),
|
||||||
|
os=str(info.get('os', '')),
|
||||||
|
arch_bits=str(info.get('arch_bits', '')),
|
||||||
|
tcp_port=str(info.get('tcp_port', '')),
|
||||||
|
uptime_in_seconds=uptime_formatted,
|
||||||
|
connected_clients=str(info.get('connected_clients', '')),
|
||||||
|
used_memory_human=str(info.get('used_memory_human', '')),
|
||||||
|
used_memory_peak_human=str(info.get('used_memory_peak_human', '')),
|
||||||
|
maxmemory_human=str(info.get('maxmemory_human', '0B')),
|
||||||
|
keys_num=str(db_size),
|
||||||
|
)
|
||||||
|
|
||||||
|
command_stats = await redis_client.info('commandstats')
|
||||||
|
stats_list = []
|
||||||
|
for key, value in command_stats.items():
|
||||||
|
if isinstance(value, dict):
|
||||||
|
stats_list.append(RedisCommandStat(name=key.split('_')[-1], value=str(value.get('calls', '0'))))
|
||||||
|
|
||||||
|
data = RedisMonitorInfo(info=server_info, stats=stats_list)
|
||||||
return response_base.success(data=data)
|
return response_base.success(data=data)
|
||||||
|
|||||||
@@ -1,21 +1,134 @@
|
|||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from datetime import timezone as tz
|
||||||
|
|
||||||
|
import psutil
|
||||||
|
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
from starlette.concurrency import run_in_threadpool
|
from starlette.concurrency import run_in_threadpool
|
||||||
|
|
||||||
from backend.common.response.response_schema import ResponseModel, response_base
|
from backend.app.admin.schema.monitor import (
|
||||||
|
CpuInfo,
|
||||||
|
DiskInfo,
|
||||||
|
MemInfo,
|
||||||
|
ServerMonitorInfo,
|
||||||
|
ServiceInfo,
|
||||||
|
SysInfo,
|
||||||
|
)
|
||||||
|
from backend.common.response.response_schema import ResponseSchemaModel, response_base
|
||||||
from backend.common.security.jwt import DependsJwtAuth
|
from backend.common.security.jwt import DependsJwtAuth
|
||||||
from backend.utils.server_info import server_info
|
from backend.utils.format import fmt_bytes, fmt_seconds
|
||||||
|
from backend.utils.timezone import timezone
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.get('', summary='server 监控', dependencies=[DependsJwtAuth])
|
@router.get('', summary='server 监控', dependencies=[DependsJwtAuth])
|
||||||
async def get_server_info() -> ResponseModel:
|
async def get_server_info() -> ResponseSchemaModel[ServerMonitorInfo]: # noqa: C901
|
||||||
data = {
|
def get_all_info() -> ServerMonitorInfo: # noqa: C901
|
||||||
# 扔到线程池,避免阻塞
|
# CPU 信息
|
||||||
'cpu': await run_in_threadpool(server_info.get_cpu_info),
|
cpu_data = {
|
||||||
'mem': await run_in_threadpool(server_info.get_mem_info),
|
'usage': round(psutil.cpu_percent(interval=0.1), 2),
|
||||||
'sys': await run_in_threadpool(server_info.get_sys_info),
|
'logical_num': psutil.cpu_count(logical=True) or 0,
|
||||||
'disk': await run_in_threadpool(server_info.get_disk_info),
|
'physical_num': psutil.cpu_count(logical=False) or 0,
|
||||||
'service': await run_in_threadpool(server_info.get_service_info),
|
'max_freq': 0.0,
|
||||||
}
|
'min_freq': 0.0,
|
||||||
|
'current_freq': 0.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
if hasattr(psutil, 'cpu_freq'):
|
||||||
|
cpu_freq = psutil.cpu_freq()
|
||||||
|
if cpu_freq:
|
||||||
|
cpu_data.update({
|
||||||
|
'max_freq': round(cpu_freq.max, 2),
|
||||||
|
'min_freq': round(cpu_freq.min, 2),
|
||||||
|
'current_freq': round(cpu_freq.current, 2),
|
||||||
|
})
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
cpu = CpuInfo(**cpu_data)
|
||||||
|
|
||||||
|
# 内存信息
|
||||||
|
mem = psutil.virtual_memory()
|
||||||
|
gb_factor = 1024**3
|
||||||
|
mem_info = MemInfo(
|
||||||
|
total=round(mem.total / gb_factor, 2),
|
||||||
|
used=round(mem.used / gb_factor, 2),
|
||||||
|
free=round(mem.available / gb_factor, 2),
|
||||||
|
usage=round(mem.percent, 2),
|
||||||
|
)
|
||||||
|
|
||||||
|
# 系统信息
|
||||||
|
hostname = socket.gethostname()
|
||||||
|
ip = '127.0.0.1'
|
||||||
|
try:
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
||||||
|
s.settimeout(0.5)
|
||||||
|
s.connect(('8.8.8.8', 80))
|
||||||
|
ip = s.getsockname()[0]
|
||||||
|
except (TimeoutError, socket.gaierror, OSError):
|
||||||
|
pass
|
||||||
|
sys_info = SysInfo(name=hostname, ip=ip, os=platform.system(), arch=platform.machine())
|
||||||
|
|
||||||
|
# 磁盘信息
|
||||||
|
disk_list = []
|
||||||
|
exclude_fstypes = {'overlay', 'overlay2', 'tmpfs', 'devtmpfs', 'shm', 'proc', 'sysfs', 'cgroup', 'cgroup2'}
|
||||||
|
seen_devices = set()
|
||||||
|
for partition in psutil.disk_partitions(all=False):
|
||||||
|
# 跳过虚拟文件系统
|
||||||
|
if partition.fstype.lower() in exclude_fstypes:
|
||||||
|
continue
|
||||||
|
# 跳过重复设备(同一设备的不同挂载点)
|
||||||
|
if partition.device in seen_devices:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
usage = psutil.disk_usage(partition.mountpoint)
|
||||||
|
if usage:
|
||||||
|
seen_devices.add(partition.device)
|
||||||
|
disk_list.append(
|
||||||
|
DiskInfo(
|
||||||
|
dir=partition.mountpoint,
|
||||||
|
type=partition.fstype,
|
||||||
|
device=partition.device,
|
||||||
|
total=fmt_bytes(usage.total),
|
||||||
|
free=fmt_bytes(usage.free),
|
||||||
|
used=fmt_bytes(usage.used),
|
||||||
|
usage=f'{usage.percent:.2f}%',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except (PermissionError, OSError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 服务信息
|
||||||
|
process = psutil.Process(os.getpid())
|
||||||
|
proc_mem = process.memory_info()
|
||||||
|
try:
|
||||||
|
create_time = datetime.fromtimestamp(process.create_time(), tz=tz.utc)
|
||||||
|
start_time = timezone.from_datetime(create_time)
|
||||||
|
except (psutil.NoSuchProcess, OSError):
|
||||||
|
start_time = timezone.now()
|
||||||
|
|
||||||
|
elapsed = fmt_seconds(round((timezone.now() - start_time).total_seconds()))
|
||||||
|
|
||||||
|
service = ServiceInfo(
|
||||||
|
name='Python3',
|
||||||
|
version=platform.python_version(),
|
||||||
|
home=sys.executable,
|
||||||
|
cpu_usage=f'{process.cpu_percent(interval=0.1):.2f}%',
|
||||||
|
mem_vms=fmt_bytes(proc_mem.vms),
|
||||||
|
mem_rss=fmt_bytes(proc_mem.rss),
|
||||||
|
mem_free=fmt_bytes(proc_mem.vms - proc_mem.rss),
|
||||||
|
startup=timezone.to_str(start_time),
|
||||||
|
elapsed=elapsed,
|
||||||
|
)
|
||||||
|
|
||||||
|
return ServerMonitorInfo(cpu=cpu, mem=mem_info, sys=sys_info, disk=disk_list, service=service)
|
||||||
|
|
||||||
|
data = await run_in_threadpool(get_all_info)
|
||||||
return response_base.success(data=data)
|
return response_base.success(data=data)
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
from pydantic import Field
|
||||||
|
|
||||||
|
from backend.common.schema import SchemaBase
|
||||||
|
|
||||||
|
|
||||||
|
class CpuInfo(SchemaBase):
|
||||||
|
"""CPU 信息"""
|
||||||
|
|
||||||
|
usage: float = Field(description='CPU 使用率 (%)')
|
||||||
|
logical_num: int = Field(description='逻辑核心数')
|
||||||
|
physical_num: int = Field(description='物理核心数')
|
||||||
|
max_freq: float = Field(description='最大频率 (MHz)')
|
||||||
|
min_freq: float = Field(description='最小频率 (MHz)')
|
||||||
|
current_freq: float = Field(description='当前频率 (MHz)')
|
||||||
|
|
||||||
|
|
||||||
|
class MemInfo(SchemaBase):
|
||||||
|
"""内存信息"""
|
||||||
|
|
||||||
|
total: float = Field(description='总内存 (GB)')
|
||||||
|
used: float = Field(description='已使用内存 (GB)')
|
||||||
|
free: float = Field(description='可用内存 (GB)')
|
||||||
|
usage: float = Field(description='内存使用率 (%)')
|
||||||
|
|
||||||
|
|
||||||
|
class SysInfo(SchemaBase):
|
||||||
|
"""系统信息"""
|
||||||
|
|
||||||
|
name: str = Field(description='主机名')
|
||||||
|
ip: str = Field(description='IP 地址')
|
||||||
|
os: str = Field(description='操作系统')
|
||||||
|
arch: str = Field(description='系统架构')
|
||||||
|
|
||||||
|
|
||||||
|
class DiskInfo(SchemaBase):
|
||||||
|
"""磁盘信息"""
|
||||||
|
|
||||||
|
dir: str = Field(description='挂载点')
|
||||||
|
type: str = Field(description='文件系统类型')
|
||||||
|
device: str = Field(description='设备名称')
|
||||||
|
total: str = Field(description='总容量')
|
||||||
|
free: str = Field(description='可用容量')
|
||||||
|
used: str = Field(description='已使用容量')
|
||||||
|
usage: str = Field(description='使用率')
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceInfo(SchemaBase):
|
||||||
|
"""服务信息"""
|
||||||
|
|
||||||
|
name: str = Field(description='服务名称')
|
||||||
|
version: str = Field(description='版本')
|
||||||
|
home: str = Field(description='安装路径')
|
||||||
|
cpu_usage: str = Field(description='CPU 使用率')
|
||||||
|
mem_vms: str = Field(description='虚拟内存')
|
||||||
|
mem_rss: str = Field(description='物理内存')
|
||||||
|
mem_free: str = Field(description='可用内存')
|
||||||
|
startup: str = Field(description='启动时间')
|
||||||
|
elapsed: str = Field(description='运行时长')
|
||||||
|
|
||||||
|
|
||||||
|
class ServerMonitorInfo(SchemaBase):
|
||||||
|
"""服务器监控信息"""
|
||||||
|
|
||||||
|
cpu: CpuInfo = Field(description='CPU 信息')
|
||||||
|
mem: MemInfo = Field(description='内存信息')
|
||||||
|
sys: SysInfo = Field(description='系统信息')
|
||||||
|
disk: list[DiskInfo] = Field(description='磁盘信息列表')
|
||||||
|
service: ServiceInfo = Field(description='服务信息')
|
||||||
|
|
||||||
|
|
||||||
|
class RedisServerInfo(SchemaBase):
|
||||||
|
"""Redis 服务器信息"""
|
||||||
|
|
||||||
|
redis_version: str = Field(description='Redis 版本')
|
||||||
|
redis_mode: str = Field(description='运行模式')
|
||||||
|
os: str = Field(description='操作系统')
|
||||||
|
arch_bits: str = Field(description='架构位数')
|
||||||
|
tcp_port: str = Field(description='TCP 端口')
|
||||||
|
uptime_in_seconds: str = Field(description='运行时长')
|
||||||
|
connected_clients: str = Field(description='已连接客户端数')
|
||||||
|
used_memory_human: str = Field(description='已使用内存')
|
||||||
|
used_memory_peak_human: str = Field(description='内存使用峰值')
|
||||||
|
maxmemory_human: str = Field(description='最大内存限制')
|
||||||
|
keys_num: str = Field(description='键总数')
|
||||||
|
|
||||||
|
|
||||||
|
class RedisCommandStat(SchemaBase):
|
||||||
|
"""Redis 命令统计"""
|
||||||
|
|
||||||
|
name: str = Field(description='命令名称')
|
||||||
|
value: str = Field(description='调用次数')
|
||||||
|
|
||||||
|
|
||||||
|
class RedisMonitorInfo(SchemaBase):
|
||||||
|
"""Redis 监控信息"""
|
||||||
|
|
||||||
|
info: RedisServerInfo = Field(description='服务器信息')
|
||||||
|
stats: list[RedisCommandStat] = Field(description='命令统计列表')
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
def fmt_seconds(seconds: int) -> str:
|
||||||
|
"""格式化秒数为可读的时间字符串"""
|
||||||
|
days, rem = divmod(int(seconds), 86400)
|
||||||
|
hours, rem = divmod(rem, 3600)
|
||||||
|
minutes, secs = divmod(rem, 60)
|
||||||
|
parts = []
|
||||||
|
if days:
|
||||||
|
parts.append(f'{days} 天')
|
||||||
|
if hours:
|
||||||
|
parts.append(f'{hours} 小时')
|
||||||
|
if minutes:
|
||||||
|
parts.append(f'{minutes} 分钟')
|
||||||
|
if secs:
|
||||||
|
parts.append(f'{secs} 秒')
|
||||||
|
return ' '.join(parts) if parts else '0 秒'
|
||||||
|
|
||||||
|
|
||||||
|
def fmt_bytes(size: float) -> str:
|
||||||
|
s, factor = size, 1024
|
||||||
|
for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']:
|
||||||
|
if abs(s) < factor:
|
||||||
|
return f'{s:.2f} {unit}B'
|
||||||
|
s /= factor
|
||||||
|
return f'{s:.2f} YB'
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
from backend.database.redis import redis_client
|
|
||||||
from backend.utils.server_info import server_info
|
|
||||||
|
|
||||||
|
|
||||||
class RedisInfo:
|
|
||||||
@staticmethod
|
|
||||||
async def get_info() -> dict[str, str]:
|
|
||||||
"""获取 Redis 服务器信息"""
|
|
||||||
|
|
||||||
# 获取原始信息
|
|
||||||
info = await redis_client.info()
|
|
||||||
|
|
||||||
# 格式化信息
|
|
||||||
fmt_info: dict[str, str] = {}
|
|
||||||
for key, value in info.items():
|
|
||||||
if isinstance(value, dict):
|
|
||||||
# 将字典格式化为字符串
|
|
||||||
fmt_info[key] = ','.join(f'{k}={v}' for k, v in value.items())
|
|
||||||
else:
|
|
||||||
fmt_info[key] = str(value)
|
|
||||||
|
|
||||||
# 添加数据库大小信息
|
|
||||||
db_size = await redis_client.dbsize()
|
|
||||||
fmt_info['keys_num'] = str(db_size)
|
|
||||||
|
|
||||||
# 格式化运行时间
|
|
||||||
uptime = int(fmt_info.get('uptime_in_seconds', '0'))
|
|
||||||
fmt_info['uptime_in_seconds'] = server_info.fmt_seconds(uptime)
|
|
||||||
|
|
||||||
return fmt_info
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
async def get_stats() -> list[dict[str, str]]:
|
|
||||||
"""获取 Redis 命令统计信息"""
|
|
||||||
|
|
||||||
# 获取命令统计信息
|
|
||||||
command_stats = await redis_client.info('commandstats')
|
|
||||||
|
|
||||||
# 格式化统计信息
|
|
||||||
stats_list: list[dict[str, str]] = []
|
|
||||||
for key, value in command_stats.items():
|
|
||||||
if not isinstance(value, dict):
|
|
||||||
continue
|
|
||||||
|
|
||||||
command_name = key.split('_')[-1]
|
|
||||||
call_count = str(value.get('calls', '0'))
|
|
||||||
stats_list.append({'name': command_name, 'value': call_count})
|
|
||||||
|
|
||||||
return stats_list
|
|
||||||
|
|
||||||
|
|
||||||
redis_info: RedisInfo = RedisInfo()
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
import os
|
|
||||||
import platform
|
|
||||||
import socket
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
from datetime import timezone as tz
|
|
||||||
|
|
||||||
import psutil
|
|
||||||
|
|
||||||
from backend.utils.timezone import timezone
|
|
||||||
|
|
||||||
|
|
||||||
class ServerInfo:
|
|
||||||
@staticmethod
|
|
||||||
def format_bytes(size: float) -> str:
|
|
||||||
"""
|
|
||||||
格式化字节大小
|
|
||||||
|
|
||||||
:param size: 字节大小
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
factor = 1024
|
|
||||||
for unit in ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']:
|
|
||||||
if abs(size) < factor:
|
|
||||||
return f'{size:.2f} {unit}B'
|
|
||||||
size /= factor
|
|
||||||
return f'{size:.2f} YB'
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def fmt_seconds(seconds: int) -> str:
|
|
||||||
"""
|
|
||||||
格式化秒数为可读的时间字符串
|
|
||||||
|
|
||||||
:param seconds: 秒数
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
days, rem = divmod(int(seconds), 86400)
|
|
||||||
hours, rem = divmod(rem, 3600)
|
|
||||||
minutes, seconds = divmod(rem, 60)
|
|
||||||
|
|
||||||
parts = []
|
|
||||||
if days:
|
|
||||||
parts.append(f'{days} 天')
|
|
||||||
if hours:
|
|
||||||
parts.append(f'{hours} 小时')
|
|
||||||
if minutes:
|
|
||||||
parts.append(f'{minutes} 分钟')
|
|
||||||
if seconds:
|
|
||||||
parts.append(f'{seconds} 秒')
|
|
||||||
|
|
||||||
return ' '.join(parts) if parts else '0 秒'
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def fmt_timedelta(td: timedelta) -> str:
|
|
||||||
"""
|
|
||||||
格式化时间差
|
|
||||||
|
|
||||||
:param td: 时间差对象
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
return ServerInfo.fmt_seconds(round(td.total_seconds()))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_cpu_info() -> dict[str, float | int]:
|
|
||||||
"""获取 CPU 信息"""
|
|
||||||
cpu_info = {
|
|
||||||
'usage': round(psutil.cpu_percent(interval=0.1), 2), # %
|
|
||||||
'logical_num': psutil.cpu_count(logical=True) or 0,
|
|
||||||
'physical_num': psutil.cpu_count(logical=False) or 0,
|
|
||||||
'max_freq': 0.0,
|
|
||||||
'min_freq': 0.0,
|
|
||||||
'current_freq': 0.0,
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
if hasattr(psutil, 'cpu_freq'):
|
|
||||||
cpu_freq = psutil.cpu_freq()
|
|
||||||
if cpu_freq: # Some systems return None
|
|
||||||
cpu_info.update({
|
|
||||||
'max_freq': round(cpu_freq.max, 2),
|
|
||||||
'min_freq': round(cpu_freq.min, 2),
|
|
||||||
'current_freq': round(cpu_freq.current, 2),
|
|
||||||
})
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return cpu_info
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_mem_info() -> dict[str, float]:
|
|
||||||
"""获取内存信息"""
|
|
||||||
mem = psutil.virtual_memory()
|
|
||||||
gb_factor = 1024**3
|
|
||||||
return {
|
|
||||||
'total': round(mem.total / gb_factor, 2),
|
|
||||||
'used': round(mem.used / gb_factor, 2),
|
|
||||||
'free': round(mem.available / gb_factor, 2),
|
|
||||||
'usage': round(mem.percent, 2),
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_sys_info() -> dict[str, str]:
|
|
||||||
"""获取服务器信息"""
|
|
||||||
hostname = socket.gethostname()
|
|
||||||
ip = '127.0.0.1'
|
|
||||||
|
|
||||||
try:
|
|
||||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
|
||||||
s.settimeout(0.5)
|
|
||||||
s.connect(('8.8.8.8', 80))
|
|
||||||
ip = s.getsockname()[0]
|
|
||||||
except (TimeoutError, socket.gaierror, OSError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
return {
|
|
||||||
'name': hostname,
|
|
||||||
'ip': ip,
|
|
||||||
'os': platform.system(),
|
|
||||||
'arch': platform.machine(),
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_disk_info() -> list[dict[str, str]]:
|
|
||||||
"""获取磁盘信息"""
|
|
||||||
disk_info = []
|
|
||||||
for partition in psutil.disk_partitions(all=False):
|
|
||||||
usage = psutil.disk_usage(partition.mountpoint)
|
|
||||||
if usage:
|
|
||||||
disk_info.append({
|
|
||||||
'dir': partition.mountpoint,
|
|
||||||
'type': partition.fstype,
|
|
||||||
'device': partition.device,
|
|
||||||
'total': ServerInfo.format_bytes(usage.total),
|
|
||||||
'free': ServerInfo.format_bytes(usage.free),
|
|
||||||
'used': ServerInfo.format_bytes(usage.used),
|
|
||||||
'usage': f'{usage.percent:.2f}%',
|
|
||||||
})
|
|
||||||
return disk_info
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_service_info() -> dict[str, str | datetime]:
|
|
||||||
"""获取服务信息"""
|
|
||||||
process = psutil.Process(os.getpid())
|
|
||||||
mem_info = process.memory_info()
|
|
||||||
|
|
||||||
try:
|
|
||||||
create_time = datetime.fromtimestamp(process.create_time(), tz=tz.utc)
|
|
||||||
start_time = timezone.from_datetime(create_time)
|
|
||||||
except (psutil.NoSuchProcess, OSError):
|
|
||||||
start_time = timezone.now()
|
|
||||||
|
|
||||||
elapsed = ServerInfo.fmt_timedelta(timezone.now() - start_time)
|
|
||||||
|
|
||||||
return {
|
|
||||||
'name': 'Python3',
|
|
||||||
'version': platform.python_version(),
|
|
||||||
'home': sys.executable,
|
|
||||||
'cpu_usage': f'{process.cpu_percent(interval=0.1):.2f}%',
|
|
||||||
'mem_vms': ServerInfo.format_bytes(mem_info.vms),
|
|
||||||
'mem_rss': ServerInfo.format_bytes(mem_info.rss),
|
|
||||||
'mem_free': ServerInfo.format_bytes(mem_info.vms - mem_info.rss),
|
|
||||||
'startup': timezone.to_str(start_time),
|
|
||||||
'elapsed': elapsed,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
server_info: ServerInfo = ServerInfo()
|
|
||||||
Reference in New Issue
Block a user