Как мы вывели в админку ошибки yt-dlp, которые жили только в логах. Bridge на 200 строк и борьба с alert-fatigue
Ruslan_Muratov1999 6 минут назад Как мы вывели в админку ошибки yt-dlp, которые жили только в логах. Bridge на 200 строк и борьба с alert-fatigue Средний 7 мин 288 DevOps * Системное администрирование * Python * Кейс Из...
Anthropic — What company has the best second artificial intelligence model at the end of June?
В сфере искусственного интеллекта произошло заметное событие. Ruslan_Muratov1999 6 минут назад Как мы вывели в админку ошибки yt-dlp, которые жили только в логах. Bridge на 200 строк и борьба с alert-fatigue Средний 7 мин 288 DevOps * Системное администрирование * Python * Кейс Из песочницы Привет, Хабр. Я делаю онлайн-сервис для скачивания видео, бэкенд на Python (FastAPI + yt-dlp).
За месяц набрали ~1500 DAU и упёрлись в проблему: пользователи жалуются на «не работает», а в админке зелёные графики. История о том, как сделать видимыми ошибки, которые молча умирали в логах воркера, и почему первый же релиз пришлось переделывать из-за alert-fatigue. TL;DRУ нас 3 ноды: master (FastAPI на :443) и 2 worker’а (Docker, yt-dlp).
Технические детали
Воркеры падали в unavailable / private / age-restricted, но эти ошибки никогда не доходили до админки — они умирали в docker logs, где их никто не читал. Сделали bridge: воркер POST’ит ошибку в master по /admin/api/incidents/external с X-Internal-Secret, master пишет в SQLite (AdminEvent) и шлёт push админам, если ≥5 ошибок за 60 секунд. Сразу же словили alert-fatigue: 90% событий — это «юзер скинул удалённое/приватное видео», то есть это не наша проблема.
Доделали классификатор content-vs-infra: «контентные» ошибки в журнал пишем, но push не дёргаем. Через 24 часа админка показала реальную картину: 7 событий вместо ожидаемых 200. Из них 2 — реально наш баг (определённый ID видео ронял preflight на обоих воркерах).
Ниже — как это устроено, почему именно так, и что я бы сделал иначе. Контекст: почему ошибки умиралиАрхитектура примерно такая: │ ▼ ──┬──> /api/info (preflight, yt-dlp --skip-download) │ └──> /api/prepare → выбор воркера → POST к worker'у │ ▼ (docker compose, yt-dlp скачивает в /tmp) │ ▼ результат → клиент скачивает с master по /file/{task_id} /api/info бьётся в YouTube-API через yt-dlp прямо с master’а. Если видео приватное / удалено / age-gated — yt-dlp бросает исключение, master ловит его в стандартном try/except и возвращает 400 пользователю.
Отраслевые последствия
Беда вот в чём: исключение уходило в access. log с status=400 и всё. Я знал, что у нас «4xx около 5%», но не знал, почему именно 5%.
В системе аналитики стояла цель download_error, но она триггерилась только на /api/prepare и дальше — то есть когда юзер уже нажал «скачать». А половина ошибок жила на стадии preflight (/api/info), куда аналитика не доставала вообще. Шаг 1: bridge worker → masterСамое простое, что можно сделать — добавить fire-and-forget POST из обработчика исключения.
py — внутри /api/info exception handler except Exception as e: msg = str(e) asyncio. create_task(_report_info_error(req. url, msg)) if "unavailable" in msg.
Этот прогресс даёт важные сигналы о будущем отрасли, и технологический мир внимательно наблюдает.





