feat(logger, telegram_bot): Add real-time Telegram logging capability
- Add TelegramLogHandler class to stream logs to Telegram in real-time - Implement enable_telegram_logging() method to activate Telegram log streaming - Implement disable_telegram_logging() method to deactivate Telegram log streaming - Implement is_telegram_logging_enabled() method to check logging status - Add /logs_live command to enable real-time log push notifications - Add /logs_stop command to disable real-time log push notifications - Update help text to document new real-time logging commands - Remove obsolete team.json.example file - Enables administrators to monitor system logs in real-time through Telegram bot
This commit is contained in:
54
logger.py
54
logger.py
@@ -72,6 +72,38 @@ class FileFormatter(logging.Formatter):
|
|||||||
return f"[{timestamp}] [{level}] {icon}{record.getMessage()}"
|
return f"[{timestamp}] [{level}] {icon}{record.getMessage()}"
|
||||||
|
|
||||||
|
|
||||||
|
class TelegramLogHandler(logging.Handler):
|
||||||
|
"""Telegram 实时日志处理器"""
|
||||||
|
|
||||||
|
def __init__(self, callback, level=logging.INFO):
|
||||||
|
"""初始化 Telegram 日志处理器
|
||||||
|
|
||||||
|
Args:
|
||||||
|
callback: 回调函数,接收 (message: str, level: str) 参数
|
||||||
|
level: 日志级别
|
||||||
|
"""
|
||||||
|
super().__init__(level)
|
||||||
|
self.callback = callback
|
||||||
|
self.setFormatter(FileFormatter())
|
||||||
|
|
||||||
|
def emit(self, record):
|
||||||
|
"""发送日志记录到 Telegram"""
|
||||||
|
try:
|
||||||
|
msg = self.format(record)
|
||||||
|
# 映射日志级别到 BotNotifier 的级别
|
||||||
|
level_map = {
|
||||||
|
logging.DEBUG: "debug",
|
||||||
|
logging.INFO: "info",
|
||||||
|
logging.WARNING: "warning",
|
||||||
|
logging.ERROR: "error",
|
||||||
|
logging.CRITICAL: "error"
|
||||||
|
}
|
||||||
|
level = level_map.get(record.levelno, "info")
|
||||||
|
self.callback(msg, level)
|
||||||
|
except Exception:
|
||||||
|
self.handleError(record)
|
||||||
|
|
||||||
|
|
||||||
class Logger:
|
class Logger:
|
||||||
"""统一日志输出 (基于 Python logging 模块)"""
|
"""统一日志输出 (基于 Python logging 模块)"""
|
||||||
|
|
||||||
@@ -113,6 +145,7 @@ class Logger:
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.use_color = use_color
|
self.use_color = use_color
|
||||||
self.enable_file_log = enable_file_log
|
self.enable_file_log = enable_file_log
|
||||||
|
self._telegram_handler = None # Telegram 日志处理器
|
||||||
|
|
||||||
# 从环境变量读取日志级别,默认 INFO
|
# 从环境变量读取日志级别,默认 INFO
|
||||||
if level is None:
|
if level is None:
|
||||||
@@ -162,6 +195,27 @@ class Logger:
|
|||||||
return self.ICONS.get(icon, icon)
|
return self.ICONS.get(icon, icon)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
def enable_telegram_logging(self, callback, level: int = logging.INFO):
|
||||||
|
"""启用 Telegram 实时日志
|
||||||
|
|
||||||
|
Args:
|
||||||
|
callback: 回调函数,接收 (message: str, level: str) 参数
|
||||||
|
level: 日志级别,默认 INFO
|
||||||
|
"""
|
||||||
|
if self._telegram_handler is None:
|
||||||
|
self._telegram_handler = TelegramLogHandler(callback, level)
|
||||||
|
self._logger.addHandler(self._telegram_handler)
|
||||||
|
|
||||||
|
def disable_telegram_logging(self):
|
||||||
|
"""禁用 Telegram 实时日志"""
|
||||||
|
if self._telegram_handler is not None:
|
||||||
|
self._logger.removeHandler(self._telegram_handler)
|
||||||
|
self._telegram_handler = None
|
||||||
|
|
||||||
|
def is_telegram_logging_enabled(self) -> bool:
|
||||||
|
"""检查 Telegram 实时日志是否启用"""
|
||||||
|
return self._telegram_handler is not None
|
||||||
|
|
||||||
def info(self, msg: str, icon: str = None, indent: int = 0):
|
def info(self, msg: str, icon: str = None, indent: int = 0):
|
||||||
"""信息日志"""
|
"""信息日志"""
|
||||||
prefix = " " * indent
|
prefix = " " * indent
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"_comment": "格式1 (旧格式): 通过 https://chatgpt.com/api/auth/session 获取授权信息",
|
|
||||||
"_comment2": "登录 ChatGPT Team 账号后访问此链接获取完整的 session 信息",
|
|
||||||
"user": {
|
|
||||||
"id": "user-xxxxxxxxxxxxxxxxxxxxxxxx",
|
|
||||||
"email": "your-email@example.com",
|
|
||||||
"idp": "auth0",
|
|
||||||
"iat": 0,
|
|
||||||
"mfa": false
|
|
||||||
},
|
|
||||||
"expires": "2026-01-01T00:00:00.000Z",
|
|
||||||
"account": {
|
|
||||||
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
||||||
"planType": "team",
|
|
||||||
"structure": "workspace",
|
|
||||||
"workspaceType": null,
|
|
||||||
"organizationId": "org-xxxxxxxxxxxxxxxxxxxxxxxx",
|
|
||||||
"isDelinquent": false,
|
|
||||||
"gracePeriodId": null
|
|
||||||
},
|
|
||||||
"accessToken": "eyJhbGciOiJSUzI1NiIs...(your access token)",
|
|
||||||
"authProvider": "openai"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_comment": "格式2 (新格式-有Token): 简化配置,只需邮箱、密码和Token",
|
|
||||||
"_comment2": "适用于已有 accessToken 的情况",
|
|
||||||
"account": "team-owner@example.com",
|
|
||||||
"password": "YourPassword@2025",
|
|
||||||
"token": "eyJhbGciOiJSUzI1NiIs...(your access token)",
|
|
||||||
"authorized": false,
|
|
||||||
"account_id": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_comment": "格式3 (新格式-无Token): 只需邮箱和密码,程序会自动登录获取Token",
|
|
||||||
"_comment2": "适用于没有 accessToken 的情况,程序启动时会自动登录",
|
|
||||||
"account": "team-owner2@example.com",
|
|
||||||
"password": "YourPassword@2025"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -102,6 +102,8 @@ class ProvisionerBot:
|
|||||||
("run_all", self.cmd_run_all),
|
("run_all", self.cmd_run_all),
|
||||||
("stop", self.cmd_stop),
|
("stop", self.cmd_stop),
|
||||||
("logs", self.cmd_logs),
|
("logs", self.cmd_logs),
|
||||||
|
("logs_live", self.cmd_logs_live),
|
||||||
|
("logs_stop", self.cmd_logs_stop),
|
||||||
("dashboard", self.cmd_dashboard),
|
("dashboard", self.cmd_dashboard),
|
||||||
("import", self.cmd_import),
|
("import", self.cmd_import),
|
||||||
("stock", self.cmd_stock),
|
("stock", self.cmd_stock),
|
||||||
@@ -204,6 +206,8 @@ class ProvisionerBot:
|
|||||||
/team <n> - 查看第 n 个 Team 处理详情
|
/team <n> - 查看第 n 个 Team 处理详情
|
||||||
/config - 查看系统配置
|
/config - 查看系统配置
|
||||||
/logs [n] - 查看最近 n 条日志
|
/logs [n] - 查看最近 n 条日志
|
||||||
|
/logs_live - 启用实时日志推送
|
||||||
|
/logs_stop - 停止实时日志推送
|
||||||
|
|
||||||
<b>🚀 任务控制:</b>
|
<b>🚀 任务控制:</b>
|
||||||
/run <n> - 开始处理第 n 个 Team
|
/run <n> - 开始处理第 n 个 Team
|
||||||
@@ -849,6 +853,40 @@ class ProvisionerBot:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
await update.message.reply_text(f"❌ 读取日志失败: {e}")
|
await update.message.reply_text(f"❌ 读取日志失败: {e}")
|
||||||
|
|
||||||
|
@admin_only
|
||||||
|
async def cmd_logs_live(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
|
"""启用实时日志推送"""
|
||||||
|
from logger import log
|
||||||
|
|
||||||
|
if log.is_telegram_logging_enabled():
|
||||||
|
await update.message.reply_text("📡 实时日志已经在运行中")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 启用 Telegram 日志推送
|
||||||
|
def log_callback(message: str, level: str):
|
||||||
|
"""日志回调函数"""
|
||||||
|
if self.notifier:
|
||||||
|
self.notifier.queue_message(message, level)
|
||||||
|
|
||||||
|
log.enable_telegram_logging(log_callback)
|
||||||
|
await update.message.reply_text(
|
||||||
|
"✅ 实时日志已启用\n"
|
||||||
|
"所有日志将实时推送到此聊天\n"
|
||||||
|
"使用 /logs_stop 停止推送"
|
||||||
|
)
|
||||||
|
|
||||||
|
@admin_only
|
||||||
|
async def cmd_logs_stop(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
|
"""停止实时日志推送"""
|
||||||
|
from logger import log
|
||||||
|
|
||||||
|
if not log.is_telegram_logging_enabled():
|
||||||
|
await update.message.reply_text("📭 实时日志未启用")
|
||||||
|
return
|
||||||
|
|
||||||
|
log.disable_telegram_logging()
|
||||||
|
await update.message.reply_text("✅ 实时日志已停止")
|
||||||
|
|
||||||
@admin_only
|
@admin_only
|
||||||
async def cmd_dashboard(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
async def cmd_dashboard(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||||
"""查看 S2A 仪表盘统计"""
|
"""查看 S2A 仪表盘统计"""
|
||||||
|
|||||||
Reference in New Issue
Block a user