feat: improve operation diagnostics and API logging

This commit is contained in:
admin committed 2026-10-08 19:35:31 +02:00
1 parent aaea5b6f58
commit 2d743d2220
9 files changed
+272 -47

No files matched your search

+42 -19
View File
@@ -28,7 +28,7 @@ from aiogram.types import (
import payments
import user_store
from apinex_client import APINEX_STT_URL, POST_HISTORY_CHAR_LIMIT, POST_HISTORY_LIMIT, classify_intent, create_tone_profile, extract_posts_from_images, extract_transcript, generate_ideas, generate_posts, transcribe_audio
from apinex_client import APINEX_STT_URL, POST_HISTORY_CHAR_LIMIT, POST_HISTORY_LIMIT, classify_intent, create_tone_profile, current_operation_id, diagnostic_operation, extract_posts_from_images, extract_transcript, generate_ideas, generate_posts, transcribe_audio
from help_message import WELCOME_RICH_HTML, help_plain_view, help_view, rich_to_plain
from keyboards import MAIN_MENU_VALUE, main_menu_keyboard, mode_keyboard, resolve_button_text
from payments import send_subscription_invoice
@@ -167,18 +167,29 @@ def split_text(text: str, limit: int = 4000) -> list[str]:
async def report_error(message: Message, stage: str, exc: Exception) -> None:
detail = str(exc).strip()
detail = re.sub(r"(?i)(?:sk-[A-Za-z0-9_-]{12,}|[0-9]{8,}:[A-Za-z0-9_-]{20,})", "[секрет скрыт]", detail)
detail = re.sub(r"(?i)bearer\s+[^\s\"']+", "Bearer [секрет скрыт]", detail)
detail = re.sub(r"(?i)(api[_-]?key|access[_-]?token|secret|password)(\s*[=:]\s*)[^\s,;\"']+", r"\1\2[секрет скрыт]", detail)
detail = re.sub(r"https?://\S+", "[внешний сервис]", detail)
detail = detail or "нет подробностей"
operation_id = current_operation_id() or "-"
user_id = message.from_user.id if message.from_user else "-"
try:
await message.answer(f"❌ Не получилось: {stage}. Попробуй ещё раз позже.")
if is_development_environment():
await message.answer(
f"🧪 Dev-диагностика: {stage}. Причина: <code>{html.escape(detail[:240])}</code>",
f"🧪 Dev-диагностика: {stage}. ID: <code>{operation_id}</code>. "
f"Причина: <code>{html.escape(detail[:240])}</code>",
parse_mode="HTML",
)
except Exception as send_exc:
logger.error("Unable to report processing error: %s", type(send_exc).__name__)
logger.error("User operation failed at %s (%s): %s", stage, type(exc).__name__, detail[:240])
safe_exc = RuntimeError(f"{type(exc).__name__}: {detail[:2000]}")
safe_exc.__traceback__ = exc.__traceback__
logger.error(
"User operation failed operation_id=%s user_id=%s stage=%s; traceback follows",
operation_id, user_id, stage,
exc_info=(RuntimeError, safe_exc, safe_exc.__traceback__),
)
async def notify_development_deploy(bot: Bot) -> None:
@@ -517,6 +528,16 @@ async def _classify_and_respond(
text: str,
show_typing: bool = True,
state: FSMContext | None = None,
) -> None:
with diagnostic_operation():
await _classify_and_respond_inner(message, text, show_typing, state)
async def _classify_and_respond_inner(
message: Message,
text: str,
show_typing: bool = True,
state: FSMContext | None = None,
) -> None:
if message.from_user is None:
await message.answer("Не удалось определить пользователя.")
@@ -660,17 +681,18 @@ async def _finish_media_group(key: tuple[int, str]) -> None:
async def _handle_image_message(message: Message, bot: Bot) -> None:
await notify_development_deploy(bot)
try:
if message.media_group_id:
messages = await _collect_media_group(message, bot)
if messages[0].message_id != message.message_id:
return
else:
messages = [message]
async with typing_status(bot, message.chat.id):
await _process_image_group(messages, bot)
except Exception as exc:
await report_error(message, "чтение скриншота или обновление ToV", exc)
with diagnostic_operation():
try:
if message.media_group_id:
messages = await _collect_media_group(message, bot)
if messages[0].message_id != message.message_id:
return
else:
messages = [message]
async with typing_status(bot, message.chat.id):
await _process_image_group(messages, bot)
except Exception as exc:
await report_error(message, "чтение скриншота или обновление ToV", exc)
@router.message(F.photo | (F.document & F.document.mime_type.startswith("image/")))
@@ -712,11 +734,12 @@ async def transcribe_voice(message: Message, bot: Bot, state: FSMContext) -> Non
if not await require_mode(message, state, "generation"):
return
await notify_development_deploy(bot)
try:
async with typing_status(bot, message.chat.id):
await _process_voice(message, bot, state)
except Exception as exc:
await report_error(message, "обработка голосового и генерация", exc)
with diagnostic_operation():
try:
async with typing_status(bot, message.chat.id):
await _process_voice(message, bot, state)
except Exception as exc:
await report_error(message, "обработка голосового и генерация", exc)
@router.message(F.text & F.text.startswith("/"))