mirror of
https://github.com/dnslin/aria2bot.git
synced 2026-01-11 04:02:20 +08:00
feat(logging): 为 aria2 安装器、服务和 Telegram 处理器添加详细日志记录
This commit is contained in:
@@ -11,6 +11,8 @@ from concurrent.futures import ThreadPoolExecutor
|
||||
from pathlib import Path
|
||||
from urllib import error, request
|
||||
|
||||
from src.utils.logger import get_logger
|
||||
|
||||
from src.core import (
|
||||
ARIA2_BIN,
|
||||
ARIA2_CONFIG_DIR,
|
||||
@@ -28,6 +30,9 @@ from src.core import (
|
||||
)
|
||||
|
||||
|
||||
logger = get_logger("installer")
|
||||
|
||||
|
||||
class Aria2Installer:
|
||||
GITHUB_API = "https://api.github.com/repos/P3TERX/Aria2-Pro-Core/releases/latest"
|
||||
GITHUB_MIRROR = "https://gh-api.p3terx.com/repos/P3TERX/Aria2-Pro-Core/releases/latest"
|
||||
@@ -45,11 +50,13 @@ class Aria2Installer:
|
||||
|
||||
async def get_latest_version(self) -> str:
|
||||
"""从 GitHub API 获取最新版本号"""
|
||||
logger.info("正在获取 aria2 最新版本...")
|
||||
loop = asyncio.get_running_loop()
|
||||
last_error: Exception | None = None
|
||||
|
||||
for url in (self.GITHUB_API, self.GITHUB_MIRROR):
|
||||
try:
|
||||
logger.info(f"尝试从 {url} 获取版本信息")
|
||||
data = await loop.run_in_executor(
|
||||
self._executor, functools.partial(self._fetch_url, url)
|
||||
)
|
||||
@@ -57,8 +64,10 @@ class Aria2Installer:
|
||||
tag_name = payload.get("tag_name")
|
||||
if not tag_name:
|
||||
raise DownloadError("tag_name missing in GitHub API response")
|
||||
logger.info(f"获取到最新版本: {tag_name}")
|
||||
return tag_name
|
||||
except Exception as exc: # noqa: PERF203
|
||||
logger.error(f"从 {url} 获取版本失败: {exc}")
|
||||
last_error = exc
|
||||
continue
|
||||
|
||||
@@ -74,6 +83,8 @@ class Aria2Installer:
|
||||
f"{resolved_version}/{archive_name}"
|
||||
)
|
||||
|
||||
logger.info(f"正在下载 aria2 二进制文件: {archive_name}")
|
||||
logger.info(f"下载地址: {download_url}")
|
||||
loop = asyncio.get_running_loop()
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
@@ -89,14 +100,18 @@ class Aria2Installer:
|
||||
await loop.run_in_executor(
|
||||
self._executor, functools.partial(self._write_file, archive_path, data)
|
||||
)
|
||||
logger.info("二进制文件下载完成")
|
||||
except Exception as exc: # noqa: PERF203
|
||||
logger.error(f"下载二进制文件失败: {exc}")
|
||||
raise DownloadError(f"Failed to download aria2 binary: {exc}") from exc
|
||||
|
||||
try:
|
||||
logger.info("正在解压二进制文件...")
|
||||
binary_path = await loop.run_in_executor(
|
||||
self._executor, functools.partial(self._extract_binary, archive_path, extract_dir)
|
||||
)
|
||||
except Exception as exc: # noqa: PERF203
|
||||
logger.error(f"解压二进制文件失败: {exc}")
|
||||
raise DownloadError(f"Failed to extract aria2 binary: {exc}") from exc
|
||||
|
||||
try:
|
||||
@@ -105,13 +120,16 @@ class Aria2Installer:
|
||||
ARIA2_BIN.unlink()
|
||||
shutil.move(str(binary_path), ARIA2_BIN)
|
||||
ARIA2_BIN.chmod(0o755)
|
||||
logger.info(f"aria2 二进制文件已安装到: {ARIA2_BIN}")
|
||||
except Exception as exc: # noqa: PERF203
|
||||
logger.error(f"安装二进制文件失败: {exc}")
|
||||
raise DownloadError(f"Failed to install aria2 binary: {exc}") from exc
|
||||
|
||||
return ARIA2_BIN
|
||||
|
||||
async def download_config(self) -> None:
|
||||
"""下载配置模板文件"""
|
||||
logger.info("正在下载配置文件...")
|
||||
ARIA2_CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
||||
loop = asyncio.get_running_loop()
|
||||
|
||||
@@ -127,16 +145,19 @@ class Aria2Installer:
|
||||
await loop.run_in_executor(
|
||||
self._executor, functools.partial(self._write_file, target, data)
|
||||
)
|
||||
logger.info(f"配置文件已下载: {filename}")
|
||||
last_error = None
|
||||
break
|
||||
except Exception as exc: # noqa: PERF203
|
||||
last_error = exc
|
||||
continue
|
||||
if last_error is not None:
|
||||
logger.error(f"下载配置文件失败: {filename} - {last_error}")
|
||||
raise DownloadError(f"Failed to download {filename}: {last_error}") from last_error
|
||||
|
||||
def render_config(self) -> None:
|
||||
"""渲染配置文件,注入用户参数"""
|
||||
logger.info("正在渲染配置文件...")
|
||||
if not ARIA2_CONF.exists():
|
||||
raise ConfigError("Config template not found. Run download_config first.")
|
||||
|
||||
@@ -156,6 +177,8 @@ class Aria2Installer:
|
||||
"max-connection-per-server=": str(self.config.max_connection_per_server),
|
||||
}
|
||||
|
||||
logger.info(f"配置参数: RPC端口={self.config.rpc_port}, 下载目录={self.config.download_dir}")
|
||||
|
||||
new_lines: list[str] = []
|
||||
for line in content.splitlines():
|
||||
stripped = line.lstrip()
|
||||
@@ -174,16 +197,20 @@ class Aria2Installer:
|
||||
ARIA2_SESSION.touch(exist_ok=True)
|
||||
self.config.download_dir.mkdir(parents=True, exist_ok=True)
|
||||
ARIA2_LOG.touch(exist_ok=True)
|
||||
logger.info(f"配置文件已保存: {ARIA2_CONF}")
|
||||
except OSError as exc:
|
||||
logger.error(f"保存配置文件失败: {exc}")
|
||||
raise ConfigError(f"Failed to render config: {exc}") from exc
|
||||
|
||||
async def install(self, version: str | None = None) -> dict:
|
||||
"""完整安装流程"""
|
||||
logger.info("开始安装 aria2...")
|
||||
resolved_version = version or await self.get_latest_version()
|
||||
await self.download_binary(resolved_version)
|
||||
await self.download_config()
|
||||
self.render_config()
|
||||
|
||||
logger.info(f"aria2 安装完成! 版本: {resolved_version}, 路径: {ARIA2_BIN}")
|
||||
return {
|
||||
"version": resolved_version,
|
||||
"binary": str(ARIA2_BIN),
|
||||
@@ -195,31 +222,40 @@ class Aria2Installer:
|
||||
|
||||
def uninstall(self) -> None:
|
||||
"""卸载 aria2"""
|
||||
logger.info("开始卸载 aria2...")
|
||||
errors: list[Exception] = []
|
||||
|
||||
try:
|
||||
if ARIA2_BIN.exists():
|
||||
ARIA2_BIN.unlink()
|
||||
logger.info(f"已删除二进制文件: {ARIA2_BIN}")
|
||||
except Exception as exc: # noqa: PERF203
|
||||
logger.error(f"删除二进制文件失败: {exc}")
|
||||
errors.append(exc)
|
||||
|
||||
try:
|
||||
if ARIA2_CONFIG_DIR.exists():
|
||||
shutil.rmtree(ARIA2_CONFIG_DIR)
|
||||
logger.info(f"已删除配置目录: {ARIA2_CONFIG_DIR}")
|
||||
except Exception as exc: # noqa: PERF203
|
||||
logger.error(f"删除配置目录失败: {exc}")
|
||||
errors.append(exc)
|
||||
|
||||
try:
|
||||
service_path = Path.home() / ".config" / "systemd" / "user" / "aria2.service"
|
||||
if service_path.exists():
|
||||
service_path.unlink()
|
||||
logger.info(f"已删除服务文件: {service_path}")
|
||||
except Exception as exc: # noqa: PERF203
|
||||
logger.error(f"删除服务文件失败: {exc}")
|
||||
errors.append(exc)
|
||||
|
||||
if errors:
|
||||
messages = "; ".join(str(err) for err in errors)
|
||||
raise Aria2Error(f"Failed to uninstall aria2: {messages}")
|
||||
|
||||
logger.info("aria2 卸载完成")
|
||||
|
||||
def _fetch_url(self, url: str) -> bytes:
|
||||
"""阻塞式 URL 获取,放在线程池中运行"""
|
||||
req = request.Request(url, headers={"User-Agent": "aria2-installer"})
|
||||
|
||||
@@ -4,6 +4,8 @@ from __future__ import annotations
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from src.utils.logger import get_logger
|
||||
|
||||
from src.core import (
|
||||
ARIA2_BIN,
|
||||
ARIA2_CONF,
|
||||
@@ -31,6 +33,8 @@ RestartSec=5
|
||||
WantedBy=default.target
|
||||
"""
|
||||
|
||||
logger = get_logger("service")
|
||||
|
||||
|
||||
class Aria2ServiceManager:
|
||||
def __init__(self) -> None:
|
||||
@@ -63,16 +67,22 @@ class Aria2ServiceManager:
|
||||
raise ServiceError(f"Failed to write service file: {exc}") from exc
|
||||
|
||||
def start(self) -> None:
|
||||
logger.info("正在启动 aria2 服务...")
|
||||
if not is_aria2_installed():
|
||||
raise NotInstalledError("aria2 is not installed")
|
||||
self._ensure_service_file()
|
||||
self._run_systemctl("start", "aria2")
|
||||
logger.info("aria2 服务已启动")
|
||||
|
||||
def stop(self) -> None:
|
||||
logger.info("正在停止 aria2 服务...")
|
||||
self._run_systemctl("stop", "aria2")
|
||||
logger.info("aria2 服务已停止")
|
||||
|
||||
def restart(self) -> None:
|
||||
logger.info("正在重启 aria2 服务...")
|
||||
self._run_systemctl("restart", "aria2")
|
||||
logger.info("aria2 服务已重启")
|
||||
|
||||
def enable(self) -> None:
|
||||
self._run_systemctl("enable", "aria2")
|
||||
@@ -81,6 +91,7 @@ class Aria2ServiceManager:
|
||||
self._run_systemctl("disable", "aria2")
|
||||
|
||||
def status(self) -> dict:
|
||||
logger.info("正在获取 aria2 服务状态...")
|
||||
installed = is_aria2_installed()
|
||||
pid = self.get_pid() if installed else None
|
||||
|
||||
@@ -103,6 +114,7 @@ class Aria2ServiceManager:
|
||||
running = active_proc.returncode == 0
|
||||
enabled = enabled_proc.returncode == 0
|
||||
|
||||
logger.info(f"aria2 状态: 已安装={installed}, 运行中={running}, PID={pid}")
|
||||
return {
|
||||
"installed": installed,
|
||||
"running": running,
|
||||
|
||||
@@ -4,6 +4,8 @@ from __future__ import annotations
|
||||
from telegram import Update
|
||||
from telegram.ext import ContextTypes, CommandHandler
|
||||
|
||||
from src.utils.logger import get_logger
|
||||
|
||||
from src.core import (
|
||||
Aria2Config,
|
||||
Aria2Error,
|
||||
@@ -17,6 +19,16 @@ from src.core import (
|
||||
)
|
||||
from src.aria2 import Aria2Installer, Aria2ServiceManager
|
||||
|
||||
logger = get_logger("handlers")
|
||||
|
||||
|
||||
def _get_user_info(update: Update) -> str:
|
||||
"""获取用户信息用于日志"""
|
||||
user = update.effective_user
|
||||
if user:
|
||||
return f"用户ID={user.id}, 用户名={user.username or 'N/A'}"
|
||||
return "未知用户"
|
||||
|
||||
|
||||
class Aria2BotAPI:
|
||||
def __init__(self, config: Aria2Config | None = None):
|
||||
@@ -61,6 +73,7 @@ class Aria2BotAPI:
|
||||
return self.config.rpc_port
|
||||
|
||||
async def install(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
logger.info(f"收到 /install 命令 - {_get_user_info(update)}")
|
||||
await self._reply(update, context, "正在安装 aria2,处理中,请稍候...")
|
||||
try:
|
||||
result = await self.installer.install()
|
||||
@@ -82,12 +95,16 @@ class Aria2BotAPI:
|
||||
]
|
||||
),
|
||||
)
|
||||
logger.info(f"/install 命令执行成功 - {_get_user_info(update)}")
|
||||
except (DownloadError, ConfigError, Aria2Error) as exc:
|
||||
logger.error(f"/install 命令执行失败: {exc} - {_get_user_info(update)}")
|
||||
await self._reply(update, context, f"安装失败:{exc}")
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.error(f"/install 命令执行失败(未知错误): {exc} - {_get_user_info(update)}")
|
||||
await self._reply(update, context, f"安装失败,发生未知错误:{exc}")
|
||||
|
||||
async def uninstall(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
logger.info(f"收到 /uninstall 命令 - {_get_user_info(update)}")
|
||||
await self._reply(update, context, "正在卸载 aria2,处理中,请稍候...")
|
||||
try:
|
||||
try:
|
||||
@@ -96,53 +113,73 @@ class Aria2BotAPI:
|
||||
pass
|
||||
self.installer.uninstall()
|
||||
await self._reply(update, context, "卸载完成 ✅")
|
||||
logger.info(f"/uninstall 命令执行成功 - {_get_user_info(update)}")
|
||||
except Aria2Error as exc:
|
||||
logger.error(f"/uninstall 命令执行失败: {exc} - {_get_user_info(update)}")
|
||||
await self._reply(update, context, f"卸载失败:{exc}")
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.error(f"/uninstall 命令执行失败(未知错误): {exc} - {_get_user_info(update)}")
|
||||
await self._reply(update, context, f"卸载失败,发生未知错误:{exc}")
|
||||
|
||||
async def start_service(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
logger.info(f"收到 /start 命令 - {_get_user_info(update)}")
|
||||
try:
|
||||
if not is_aria2_installed():
|
||||
logger.info(f"/start 命令: aria2 未安装 - {_get_user_info(update)}")
|
||||
await self._reply(update, context, "aria2 未安装,请先运行 /install")
|
||||
return
|
||||
self.service.start()
|
||||
await self._reply(update, context, "aria2 服务已启动 ✅")
|
||||
logger.info(f"/start 命令执行成功 - {_get_user_info(update)}")
|
||||
except NotInstalledError:
|
||||
logger.info(f"/start 命令: aria2 未安装 - {_get_user_info(update)}")
|
||||
await self._reply(update, context, "aria2 未安装,请先运行 /install")
|
||||
except ServiceError as exc:
|
||||
logger.error(f"/start 命令执行失败: {exc} - {_get_user_info(update)}")
|
||||
await self._reply(update, context, f"启动失败:{exc}")
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.error(f"/start 命令执行失败(未知错误): {exc} - {_get_user_info(update)}")
|
||||
await self._reply(update, context, f"启动失败,发生未知错误:{exc}")
|
||||
|
||||
async def stop_service(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
logger.info(f"收到 /stop 命令 - {_get_user_info(update)}")
|
||||
try:
|
||||
self.service.stop()
|
||||
await self._reply(update, context, "aria2 服务已停止 ✅")
|
||||
logger.info(f"/stop 命令执行成功 - {_get_user_info(update)}")
|
||||
except ServiceError as exc:
|
||||
logger.error(f"/stop 命令执行失败: {exc} - {_get_user_info(update)}")
|
||||
await self._reply(update, context, f"停止失败:{exc}")
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.error(f"/stop 命令执行失败(未知错误): {exc} - {_get_user_info(update)}")
|
||||
await self._reply(update, context, f"停止失败,发生未知错误:{exc}")
|
||||
|
||||
async def restart_service(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
logger.info(f"收到 /restart 命令 - {_get_user_info(update)}")
|
||||
try:
|
||||
self.service.restart()
|
||||
await self._reply(update, context, "aria2 服务已重启 ✅")
|
||||
logger.info(f"/restart 命令执行成功 - {_get_user_info(update)}")
|
||||
except ServiceError as exc:
|
||||
logger.error(f"/restart 命令执行失败: {exc} - {_get_user_info(update)}")
|
||||
await self._reply(update, context, f"重启失败:{exc}")
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.error(f"/restart 命令执行失败(未知错误): {exc} - {_get_user_info(update)}")
|
||||
await self._reply(update, context, f"重启失败,发生未知错误:{exc}")
|
||||
|
||||
async def status(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
logger.info(f"收到 /status 命令 - {_get_user_info(update)}")
|
||||
try:
|
||||
info = self.service.status()
|
||||
version = get_aria2_version() or "未知"
|
||||
rpc_secret = self._get_rpc_secret() or "未设置"
|
||||
rpc_port = self._get_rpc_port() or self.config.rpc_port or "未知"
|
||||
except ServiceError as exc:
|
||||
logger.error(f"/status 命令执行失败: {exc} - {_get_user_info(update)}")
|
||||
await self._reply(update, context, f"获取状态失败:{exc}")
|
||||
return
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.error(f"/status 命令执行失败(未知错误): {exc} - {_get_user_info(update)}")
|
||||
await self._reply(update, context, f"获取状态失败,发生未知错误:{exc}")
|
||||
return
|
||||
|
||||
@@ -156,33 +193,44 @@ class Aria2BotAPI:
|
||||
f"- RPC 密钥:`{rpc_secret}`"
|
||||
)
|
||||
await self._reply(update, context, text, parse_mode="Markdown")
|
||||
logger.info(f"/status 命令执行成功 - {_get_user_info(update)}")
|
||||
|
||||
async def view_logs(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
logger.info(f"收到 /logs 命令 - {_get_user_info(update)}")
|
||||
try:
|
||||
logs = self.service.view_log(lines=30)
|
||||
except ServiceError as exc:
|
||||
logger.error(f"/logs 命令执行失败: {exc} - {_get_user_info(update)}")
|
||||
await self._reply(update, context, f"读取日志失败:{exc}")
|
||||
return
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.error(f"/logs 命令执行失败(未知错误): {exc} - {_get_user_info(update)}")
|
||||
await self._reply(update, context, f"读取日志失败,发生未知错误:{exc}")
|
||||
return
|
||||
|
||||
if not logs.strip():
|
||||
await self._reply(update, context, "暂无日志内容。")
|
||||
logger.info(f"/logs 命令执行成功(无日志) - {_get_user_info(update)}")
|
||||
return
|
||||
|
||||
await self._reply(update, context, f"最近 30 行日志:\n{logs}")
|
||||
logger.info(f"/logs 命令执行成功 - {_get_user_info(update)}")
|
||||
|
||||
async def clear_logs(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
logger.info(f"收到 /clear_logs 命令 - {_get_user_info(update)}")
|
||||
try:
|
||||
self.service.clear_log()
|
||||
await self._reply(update, context, "日志已清空 ✅")
|
||||
logger.info(f"/clear_logs 命令执行成功 - {_get_user_info(update)}")
|
||||
except ServiceError as exc:
|
||||
logger.error(f"/clear_logs 命令执行失败: {exc} - {_get_user_info(update)}")
|
||||
await self._reply(update, context, f"清空日志失败:{exc}")
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.error(f"/clear_logs 命令执行失败(未知错误): {exc} - {_get_user_info(update)}")
|
||||
await self._reply(update, context, f"清空日志失败,发生未知错误:{exc}")
|
||||
|
||||
async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||
logger.info(f"收到 /help 命令 - {_get_user_info(update)}")
|
||||
commands = [
|
||||
"/install - 安装 aria2",
|
||||
"/uninstall - 卸载 aria2",
|
||||
|
||||
Reference in New Issue
Block a user