diff --git a/check_errors.py b/check_errors.py new file mode 100755 index 0000000..a7928c8 --- /dev/null +++ b/check_errors.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +"""Check API errors in the last hour and send Slack alert if any found.""" + +import os +import sys +from datetime import datetime, timedelta + +sys.path.insert(0, os.path.dirname(__file__)) + +import slack_notifier + + +LOG_FILES = [ + "trading.log", + "sync_cost_basis.log", + "cron.log", +] + +LOOKBACK_MINUTES = 60 + + +def collect_recent_errors() -> list[str]: + since = datetime.now() - timedelta(minutes=LOOKBACK_MINUTES) + since_str = since.strftime("%Y-%m-%d %H:%M") + errors = [] + + for logfile in LOG_FILES: + path = os.path.join(os.path.dirname(__file__), logfile) + if not os.path.exists(path): + continue + with open(path) as f: + for line in f: + if "[ERROR]" not in line: + continue + # Only compare lines that start with a timestamp + if not line[:4].isdigit(): + continue + if line[:16] >= since_str: + errors.append(line.rstrip()) + + return errors + + +def main(): + errors = collect_recent_errors() + if not errors: + return + + # Deduplicate and limit + unique = list(dict.fromkeys(errors)) + display = unique[:20] + remaining = len(unique) - len(display) + + lines = [ + "", + f"⚠️ *API 錯誤警報* (最近 {LOOKBACK_MINUTES} 分鐘,共 {len(unique)} 筆)", + "", + ] + for e in display: + lines.append(f"• `{e[:200]}`") + if remaining > 0: + lines.append(f"\n...另有 {remaining} 筆錯誤") + + slack_notifier._send({"text": "\n".join(lines)}) + + +if __name__ == "__main__": + main() diff --git a/setup.sh b/setup.sh index 5c87f2e..22ab54e 100755 --- a/setup.sh +++ b/setup.sh @@ -118,6 +118,7 @@ echo "[6/6] Setting up crontab..." CRON_MAIN="*/5 * * * * sleep 30 && cd $PROJECT_DIR && /usr/bin/python3 main.py >> cron.log 2>&1" CRON_SYNC="2,32 * * * * cd $PROJECT_DIR && /usr/bin/python3 sync_cost_basis.py >> sync_cost_basis_cron.log 2>&1" +CRON_CHECK="*/5 * * * * cd $PROJECT_DIR && /usr/bin/python3 check_errors.py 2>&1" CRON_ENV="PATH=$HOME/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" CRON_PYPATH="PYTHONPATH=$HOME/.local/lib/python3.12/site-packages" @@ -136,6 +137,7 @@ $CRON_PYPATH $CRON_MAIN $CRON_SYNC +$CRON_CHECK CRONTAB ) | crontab - echo " crontab installed ✓" @@ -157,5 +159,6 @@ else fi echo "" echo "Crontab 排程:" -echo " main.py: 每 5 分鐘(延遲 30 秒)" -echo " sync_cost_basis.py: 每小時 :02 和 :32" +echo " main.py: 每 5 分鐘(延遲 30 秒)" +echo " sync_cost_basis.py: 每小時 :02 和 :32" +echo " check_errors.py: 每 5 分鐘(有錯才 Slack @channel)"