From 5083423660ce48d857e021289290ed5d342a0719 Mon Sep 17 00:00:00 2001 From: Alexey Barabanov Date: Fri, 24 Oct 2025 10:06:38 +0000 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=BE=D0=B1=D1=83?= =?UTF-8?q?=D1=82=20logger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- get_data.py | 188 +++++++++++++++++++++------------------------------- 1 file changed, 76 insertions(+), 112 deletions(-) diff --git a/get_data.py b/get_data.py index a82adae..8e336ed 100644 --- a/get_data.py +++ b/get_data.py @@ -27,12 +27,13 @@ import os import sys import time import json -import getpass import argparse import logging +import logging.config import threading import concurrent.futures import queue +import platform from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry from datetime import datetime, timedelta, timezone @@ -85,39 +86,55 @@ STATUS_CHECK_INTERVAL_SEC = 60 # проверять "онлайн?" раз ONLINE_FETCH_INTERVAL_SEC = 1 # когда матч онлайн, дергать три запроса каждую секунду POLL_INTERVAL_OFFLINE_SEC = 300 # резервный интервал сна при ошибках/до старта -USERNAME = getpass.getuser() - TELEGRAM_BOT_TOKEN = "7639240596:AAH0YtdQoWZSC-_R_EW4wKAHHNLIA0F_ARY" TELEGRAM_CHAT_ID = 228977654 +myhost = platform.node() -# Настройка логгера -def setup_logger(level: str = "INFO") -> logging.Logger: - user = getpass.getuser() - log_dir = "LOGS" - os.makedirs(log_dir, exist_ok=True) +LOG_CONFIG = { + "version": 1, + "handlers": { + "telegram": { + "class": "telegram_handler.TelegramHandler", + "level": "INFO", + "token": TELEGRAM_BOT_TOKEN, + "chat_id": TELEGRAM_CHAT_ID, + "formatter": "telegram", + }, + "console": { + "class": "logging.StreamHandler", + "level": "DEBUG", + "formatter": "simple", + "stream": "ext://sys.stdout", + }, + "file": { + "class": "logging.FileHandler", + "level": "DEBUG", + "formatter": "simple", + "filename": f"logs/GFX_{myhost}.log", + "encoding": "utf-8", + }, + }, + "loggers": { + __name__: {"handlers": ["console", "file", "telegram"], "level": "DEBUG"}, + }, + "formatters": { + "telegram": { + "class": "telegram_handler.HtmlFormatter", + "format": "%(levelname)s %(message)s", + "use_emoji": "True", + }, + "simple": { + "class": "logging.Formatter", + "format": "%(asctime)s %(levelname)-8s %(funcName)s() - %(message)s", + "datefmt": "%d.%m.%Y %H:%M:%S", + }, + }, +} - log_path = os.path.join(log_dir, f"{user}.log") - - logger = logging.getLogger("game_watcher") - logger.setLevel(getattr(logging, level.upper(), logging.INFO)) - logger.propagate = False - - if not logger.handlers: - fmt = logging.Formatter("%(asctime)s | %(levelname)s | %(message)s") - - # потоковый вывод (консоль) - sh = logging.StreamHandler(sys.stdout) - sh.setFormatter(fmt) - logger.addHandler(sh) - - # запись в файл - fh = logging.FileHandler(log_path, encoding="utf-8") - fh.setFormatter(fmt) - logger.addHandler(fh) - - logger.info("Логи будут писаться в: %s", log_path) - return logger +logging.config.dictConfig(LOG_CONFIG) +logger = logging.getLogger(__name__) +logger.handlers[2].formatter.use_emoji = True # ========================== @@ -125,53 +142,6 @@ def setup_logger(level: str = "INFO") -> logging.Logger: # ========================== -def send_telegram(message: str) -> None: - """ - Отправка уведомления в Telegram. - Можно использовать как переменные окружения, - так и значения, указанные в TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID. - """ - import requests - logger = logging.getLogger("game_watcher") - - # 1️⃣ Сначала пытаемся взять значения из переменных окружения - token = os.getenv("TELEGRAM_BOT_TOKEN", TELEGRAM_BOT_TOKEN) - chat_id = os.getenv("TELEGRAM_CHAT_ID", str(TELEGRAM_CHAT_ID)) - - if not token or not chat_id: - logger.warning( - "TELEGRAM_BOT_TOKEN/CHAT_ID не заданы — сообщение не отправлено: %s", - message, - ) - return - - url = f"https://api.telegram.org/bot{token}/sendMessage" - try: - resp = requests.post( - url, json={"chat_id": chat_id, "text": message}, timeout=10 - ) - resp.raise_for_status() - logger.info("Сообщение успешно отправлено в Telegram: %s", message) - except requests.HTTPError as e: - logger.error("Ошибка HTTP при отправке в Telegram: %s | Ответ: %s", e, resp.text) - except Exception as e: - logger.error("Ошибка отправки в Telegram: %s", e) - - -def notify_error(msg: str) -> None: - user = getpass.getuser() - full_msg = f"[{user}] ❗ Ошибка: {msg}" - logging.getLogger("game_watcher").error(full_msg) - # send_telegram(full_msg) - - -def notify_info(msg: str) -> None: - user = getpass.getuser() - full_msg = f"[{user}] ℹ️ {msg}" - logging.getLogger("game_watcher").info(full_msg) - # send_telegram(full_msg) - - def fetch_json(url: str, params: dict | None = None, session: requests.Session | None = None) -> dict: """ GET JSON с таймаутом и внятными ошибками. @@ -1075,8 +1045,7 @@ def Json_Team_Generation(merged: dict, *, out_dir: str = "static", who: str | No def validate_league_or_die(league: str) -> str: league = (league or DEFAULT_LEAGUE).lower().strip() if league not in ALLOWED_LEAGUES: - msg = f"Неверный тег лиги: '{league}'. Допустимо: {sorted(ALLOWED_LEAGUES)}" - notify_error(msg) + logger.warning(f"Неверный тег лиги: '{league}'. Допустимо: {sorted(ALLOWED_LEAGUES)}") sys.exit(2) return league @@ -1086,12 +1055,12 @@ def get_last_season_or_die(league: str, lang: str) -> str: try: data = fetch_json(url) season = extract_last_season(data) - logging.getLogger("game_watcher").info( - "Последний сезон для %s: %s", league, season + logging.info( + f"Последний сезон для {league}: {season}" ) return season except Exception as e: - notify_error(f"Не получилось получить последний сезон для {league}: {e}") + logger.warning(f"Не получилось получить последний сезон для {league}: {e}") sys.exit(3) @@ -1101,10 +1070,10 @@ def get_team_schedule_or_die(league: str, season: str, team: str, lang: str) -> data = fetch_json(url) team_games = extract_team_schedule_for_season(data, team) if not team_games: - notify_error(f"Для команды {team} не найдено игр в сезоне {season}.") + logger.warning(f"Для команды {team} не найдено игр в сезоне {season}.") return team_games except Exception as e: - notify_error(f"Не получилось получить расписание {league}/{season}: {e}") + logger.warning(f"Не получилось получить расписание {league}/{season}: {e}") return [] @@ -1166,7 +1135,7 @@ class PostProcessor: Json_Team_Generation(merged, out_dir="static", who="team1") Json_Team_Generation(merged, out_dir="static", who="team2") except Exception as e: - logging.getLogger("game_watcher").exception("Postproc failed: %s", e) + logging.exception(f"Postproc failed: {e}") def stop(self): self._stop.set() @@ -1179,7 +1148,7 @@ class OnlinePoller: self.lang = lang self._stop_event = threading.Event() self._thread: threading.Thread | None = None - self._log = logging.getLogger("game_watcher") + self._log = logging.info("start") self._on_update = on_update self._post = PostProcessor() @@ -1204,7 +1173,7 @@ class OnlinePoller: if self._thread and self._thread.is_alive(): self._stop_event.set() self._thread.join(timeout=2) - self._log.info("Онлайн-поллер для игры %s остановлен.", self.game_id) + self._log.info(f"Онлайн-поллер для игры {self.game_id} остановлен.") self._thread = None try: self._session.close() @@ -1249,7 +1218,7 @@ class OnlinePoller: len(ls) if isinstance(ls, dict) else "—", ) except Exception as e: - notify_error(f"Сбой online-поллера для игры {self.game_id}: {e}") + logger.warning(f"Сбой online-поллера для игры {self.game_id}: {e}") # лёгкая задержка после ошибки, но не «наказание» на целую секунду time.sleep(0.2) @@ -1273,8 +1242,7 @@ class OnlinePoller: self._log.info("Онлайн-поллер для игры %s запущен.", self.game_id) def monitor_game_loop(league: str, game_id: str, lang:str, stop_event: threading.Event) -> None: - log = logging.getLogger("game_watcher") - notify_info(f"Старт мониторинга игры {game_id} ({league}).") + logger.info(f"Старт мониторинга игры {game_id} ({league}).") poller = OnlinePoller(league, game_id, lang) was_online = False @@ -1285,15 +1253,14 @@ def monitor_game_loop(league: str, game_id: str, lang:str, stop_event: threading is_finished = status in {"resultconfirmed", "result"} if is_finished: - log.info("Матч %s завершён. Останавливаем мониторинг.", game_id) - notify_info(f"Матч {game_id} завершён.") + logger.info(f"Матч {game_id} завершён. Останавливаем мониторинг.") break if is_online and not was_online: - log.info("Матч %s перешёл в онлайн. Запускаем быстрый опрос (1 сек).", game_id) + logger.info(f"Матч {game_id} перешёл в онлайн. Запускаем быстрый опрос (1 сек).") poller.start() elif not is_online and was_online: - log.info("Матч %s вышел из онлайна (или ещё не стартовал). Останавливаем быстрый опрос.", game_id) + logger.info(f"Матч {game_id} вышел из онлайна (или ещё не стартовал). Останавливаем быстрый опрос.") poller.stop() was_online = is_online @@ -1302,13 +1269,13 @@ def monitor_game_loop(league: str, game_id: str, lang:str, stop_event: threading stop_event.wait(STATUS_CHECK_INTERVAL_SEC) except Exception as e: - notify_error(f"Сбой проверки статуса матча {game_id}: {e}") + logger.warning(f"Сбой проверки статуса матча {game_id}: {e}") # При ошибке — не дергаем быстро, подождём немного и повторим stop_event.wait(POLL_INTERVAL_OFFLINE_SEC) # Гарантированно остановим быстрый опрос при завершении poller.stop() - log.info("Мониторинг матча %s остановлен.", game_id) + logger.info(f"Мониторинг матча {game_id} остановлен.") def next_midnight_local(now: datetime) -> datetime: @@ -1333,15 +1300,12 @@ def daily_rollover_loop( - выбираем сегодняшнюю игру или последний сыгранный - при наличии сегодняшней игры — перезапускаем монитор на неё """ - log = logging.getLogger("game_watcher") while not stop_event.is_set(): now = datetime.now(APP_TZ) wakeup_at = next_midnight_local(now) seconds = (wakeup_at - now).total_seconds() - log.info( - "Ежедневная перекладка: проснусь %s (через ~%d сек).", - wakeup_at.isoformat(), - int(seconds), + logger.info( + f"Ежедневная перекладка: проснусь {wakeup_at.isoformat()} (через {int(seconds)} сек)." ) if stop_event.wait(seconds): break @@ -1351,7 +1315,7 @@ def daily_rollover_loop( season = season_getter(league, lang) games = schedule_getter(league, season, team, lang) if not games: - notify_info( + logger.info( f"Ежедневная проверка: у {team} нет игр в расписании сезона {season}." ) continue @@ -1361,19 +1325,19 @@ def daily_rollover_loop( ) if today_game: gid = today_game["game"]["id"] - notify_info( + logger.info( f"Сегодня у {team} есть игра: gameID={gid}. Перезапуск мониторинга." ) monitor_mgr.restart(gid, lang) elif last_played: gid = last_played["game"]["id"] - notify_info( + logger.info( f"Сегодня у {team} нет игры. Последняя сыгранная: gameID={gid}. Мониторинг НЕ запускаем." ) else: - notify_info(f"Сегодня у {team} нет игры и нет предыдущих сыгранных.") + logger.info(f"Сегодня у {team} нет игры и нет предыдущих сыгранных.") except Exception as e: - notify_error(f"Ошибка ежедневной проверки: {e}") + logger.warning(f"Ошибка ежедневной проверки: {e}") class MonitorManager: @@ -1426,8 +1390,7 @@ def main(): ) args = parser.parse_args() - logger = setup_logger(args.log_level) - logger.info("Запуск программы пользователем: %s", USERNAME) + logger.info("Запуск программы пользователем: %s", myhost) logger.info("Запуск с параметрами: league=%s, team=%s, lang=%s", args.league, args.team, args.lang) league = validate_league_or_die(args.league) @@ -1439,7 +1402,7 @@ def main(): # 2) Получить расписание для команды team_games = get_team_schedule_or_die(league, season, team, args.lang) if not team_games: - notify_error("Расписание пустое — работа завершена.") + logger.warning("Расписание пустое — работа завершена.") sys.exit(4) # 3) Найти сегодняшнюю или последнюю сыгранную игру @@ -1451,7 +1414,7 @@ def main(): if today_game: # В исходном расписании предполагалось наличие game.id game_id = today_game["game"]["id"] - notify_info( + logger.info( f"Сегодня у {team} есть игра: gameID={game_id}. Запускаю мониторинг." ) monitor_mgr.restart(game_id, args.lang) @@ -1470,15 +1433,16 @@ def main(): ) Json_Team_Generation(merged, out_dir="static", who="team1") Json_Team_Generation(merged, out_dir="static", who="team2") - notify_info( + # print(merged) + logger.info( f"Сегодня у {team} нет игры. Последняя сыгранная: gameID={game_id}. Мониторинг не запускаю." ) except Exception as e: - logging.getLogger("game_watcher").exception( + logging.exception( "Оффлайн-сохранение для gameID=%s упало: %s", game_id, e ) else: - notify_info(f"Сегодня у {team} нет игры и нет предыдущих сыгранных.") + logger.info(f"Сегодня у {team} нет игры и нет предыдущих сыгранных.") # 4) Ежедневная перекладка расписания stop_event = threading.Event()