From 23fa70f15cd89378cb8c8b71c05fb57b528fd235 Mon Sep 17 00:00:00 2001 From: benya Date: Sat, 7 Feb 2026 22:43:35 +0300 Subject: [PATCH] Add log time ranges and filter --- handlers/callbacks.py | 77 ++++++++++++++++++++++++++++++++++++++++--- handlers/docker.py | 39 +++++++++++++++++++++- state.py | 1 + 3 files changed, 111 insertions(+), 6 deletions(-) diff --git a/handlers/callbacks.py b/handlers/callbacks.py index f6b571e..c078ab3 100644 --- a/handlers/callbacks.py +++ b/handlers/callbacks.py @@ -1,9 +1,10 @@ import json +import time from aiogram import F from aiogram.types import CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton from app import dp, ADMIN_ID from services.docker import docker_cmd -from state import DOCKER_MAP +from state import DOCKER_MAP, LOG_FILTER_PENDING from handlers.backup import cmd_backup_status @@ -25,11 +26,22 @@ async def docker_callback(cb: CallbackQuery): ) elif action == "logs": - await cb.answer("Loading logs…") - rc, out = await docker_cmd(["logs", "--tail", "80", real]) - + await cb.answer() + opts = InlineKeyboardMarkup( + inline_keyboard=[ + [ + InlineKeyboardButton(text="Tail 80", callback_data=f"logsopt:tail:{alias}"), + InlineKeyboardButton(text="10m", callback_data=f"logsopt:since:{alias}:600"), + InlineKeyboardButton(text="1h", callback_data=f"logsopt:since:{alias}:3600"), + ], + [ + InlineKeyboardButton(text="Filter", callback_data=f"logsopt:filter:{alias}") + ], + ] + ) await cb.message.answer( - f"📜 **Logs: {alias}**\n```{out}```", + f"📜 **Logs options: {alias}**", + reply_markup=opts, parse_mode="Markdown" ) @@ -83,3 +95,58 @@ async def snapshot_back(cb: CallbackQuery): # просто вызываем статус снова fake_msg = cb.message await cmd_backup_status(fake_msg) + + +@dp.callback_query(F.data.startswith("logsopt:")) +async def logs_options(cb: CallbackQuery): + if cb.from_user.id != ADMIN_ID: + return + + parts = cb.data.split(":") + if len(parts) < 3: + await cb.answer("Bad request") + return + + action = parts[1] + alias = parts[2] + real = DOCKER_MAP.get(alias) + if not real: + await cb.answer("Container not found") + return + + if action == "tail": + await cb.answer("Loading logs…") + rc, out = await docker_cmd(["logs", "--tail", "80", real]) + await cb.message.answer( + f"📜 **Logs: {alias}**\n```{out}```", + parse_mode="Markdown" + ) + return + + if action == "since" and len(parts) == 4: + try: + seconds = int(parts[3]) + except ValueError: + await cb.answer("Bad request") + return + since_ts = str(int(time.time() - seconds)) + await cb.answer("Loading logs…") + rc, out = await docker_cmd(["logs", "--since", since_ts, "--tail", "200", real]) + await cb.message.answer( + f"📜 **Logs: {alias}**\n```{out}```", + parse_mode="Markdown" + ) + return + + if action == "filter": + LOG_FILTER_PENDING[cb.from_user.id] = { + "alias": alias, + "since_sec": 1800, + } + await cb.message.answer( + f"🔎 Send filter text for `{alias}` (logs last 30m).", + parse_mode="Markdown", + ) + return + + await cb.answer("Bad request") diff --git a/handlers/docker.py b/handlers/docker.py index ececcf1..0d24e93 100644 --- a/handlers/docker.py +++ b/handlers/docker.py @@ -4,7 +4,8 @@ from app import dp from auth import is_admin_msg from keyboards import docker_kb, docker_inline_kb from services.docker import container_uptime, docker_cmd -from state import DOCKER_MAP +from state import DOCKER_MAP, LOG_FILTER_PENDING +import time async def cmd_docker_status(msg: Message): @@ -74,3 +75,39 @@ async def dl(msg: Message): async def ds(msg: Message): if is_admin_msg(msg): await cmd_docker_status(msg) + + +@dp.message(F.text) +async def log_filter_input(msg: Message): + if not is_admin_msg(msg): + return + + pending = LOG_FILTER_PENDING.pop(msg.from_user.id, None) + if not pending: + return + + alias = pending["alias"] + real = DOCKER_MAP.get(alias) + if not real: + await msg.answer("⚠️ Container not found", reply_markup=docker_kb) + return + + needle = (msg.text or "").strip() + if not needle: + await msg.answer("⚠️ Empty filter text", reply_markup=docker_kb) + return + + since_ts = str(int(time.time() - int(pending.get("since_sec", 1800)))) + rc, out = await docker_cmd(["logs", "--since", since_ts, "--tail", "400", real]) + if rc != 0: + await msg.answer(out, reply_markup=docker_kb) + return + + lines = [line for line in out.splitlines() if needle.lower() in line.lower()] + filtered = "\n".join(lines) if lines else "(no matches)" + + await msg.answer( + f"📜 **Logs filter: {alias}**\n```{filtered}```", + parse_mode="Markdown", + reply_markup=docker_kb, + ) diff --git a/state.py b/state.py index 690e695..5704467 100644 --- a/state.py +++ b/state.py @@ -1,3 +1,4 @@ from typing import Dict DOCKER_MAP: Dict[str, str] = {} +LOG_FILTER_PENDING: Dict[int, dict] = {}