Add --debug flag for verbose logging

- Add LAST30DAYS_DEBUG env var / --debug flag
- Log HTTP requests, responses, and errors
- Show API error details when debug enabled
- Helps diagnose API failures

Usage: python3 last30days.py "topic" --debug

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Matt Van Horn
2026-01-23 16:54:31 -08:00
parent 9604d0ba46
commit 7b3150a69d
4 changed files with 54 additions and 2 deletions
+14
View File
@@ -11,10 +11,12 @@ Options:
--sources=MODE Source selection: auto|reddit|x|both (default: auto) --sources=MODE Source selection: auto|reddit|x|both (default: auto)
--quick Faster research with fewer sources (8-12 each) --quick Faster research with fewer sources (8-12 each)
--deep Comprehensive research with more sources (50-70 Reddit, 40-60 X) --deep Comprehensive research with more sources (50-70 Reddit, 40-60 X)
--debug Enable verbose debug logging
""" """
import argparse import argparse
import json import json
import os
import sys import sys
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
@@ -190,9 +192,21 @@ def main():
action="store_true", action="store_true",
help="Comprehensive research with more sources (50-70 Reddit, 40-60 X)", help="Comprehensive research with more sources (50-70 Reddit, 40-60 X)",
) )
parser.add_argument(
"--debug",
action="store_true",
help="Enable verbose debug logging",
)
args = parser.parse_args() args = parser.parse_args()
# Enable debug logging if requested
if args.debug:
os.environ["LAST30DAYS_DEBUG"] = "1"
# Re-import http to pick up debug flag
from lib import http as http_module
http_module.DEBUG = True
# Determine depth # Determine depth
if args.quick and args.deep: if args.quick and args.deep:
print("Error: Cannot use both --quick and --deep", file=sys.stderr) print("Error: Cannot use both --quick and --deep", file=sys.stderr)
+20
View File
@@ -1,6 +1,8 @@
"""HTTP utilities for last30days skill (stdlib only).""" """HTTP utilities for last30days skill (stdlib only)."""
import json import json
import os
import sys
import time import time
import urllib.error import urllib.error
import urllib.request import urllib.request
@@ -8,6 +10,14 @@ from typing import Any, Dict, Optional
from urllib.parse import urlencode from urllib.parse import urlencode
DEFAULT_TIMEOUT = 30 DEFAULT_TIMEOUT = 30
DEBUG = os.environ.get("LAST30DAYS_DEBUG", "").lower() in ("1", "true", "yes")
def log(msg: str):
"""Log debug message to stderr."""
if DEBUG:
sys.stderr.write(f"[DEBUG] {msg}\n")
sys.stderr.flush()
MAX_RETRIES = 3 MAX_RETRIES = 3
RETRY_DELAY = 1.0 RETRY_DELAY = 1.0
USER_AGENT = "last30days-skill/1.0 (Claude Code Skill)" USER_AGENT = "last30days-skill/1.0 (Claude Code Skill)"
@@ -55,11 +65,16 @@ def request(
req = urllib.request.Request(url, data=data, headers=headers, method=method) req = urllib.request.Request(url, data=data, headers=headers, method=method)
log(f"{method} {url}")
if json_data:
log(f"Payload keys: {list(json_data.keys())}")
last_error = None last_error = None
for attempt in range(retries): for attempt in range(retries):
try: try:
with urllib.request.urlopen(req, timeout=timeout) as response: with urllib.request.urlopen(req, timeout=timeout) as response:
body = response.read().decode('utf-8') body = response.read().decode('utf-8')
log(f"Response: {response.status} ({len(body)} bytes)")
return json.loads(body) if body else {} return json.loads(body) if body else {}
except urllib.error.HTTPError as e: except urllib.error.HTTPError as e:
body = None body = None
@@ -67,6 +82,9 @@ def request(
body = e.read().decode('utf-8') body = e.read().decode('utf-8')
except: except:
pass pass
log(f"HTTP Error {e.code}: {e.reason}")
if body:
log(f"Error body: {body[:500]}")
last_error = HTTPError(f"HTTP {e.code}: {e.reason}", e.code, body) last_error = HTTPError(f"HTTP {e.code}: {e.reason}", e.code, body)
# Don't retry client errors (4xx) except rate limits # Don't retry client errors (4xx) except rate limits
@@ -76,10 +94,12 @@ def request(
if attempt < retries - 1: if attempt < retries - 1:
time.sleep(RETRY_DELAY * (attempt + 1)) time.sleep(RETRY_DELAY * (attempt + 1))
except urllib.error.URLError as e: except urllib.error.URLError as e:
log(f"URL Error: {e.reason}")
last_error = HTTPError(f"URL Error: {e.reason}") last_error = HTTPError(f"URL Error: {e.reason}")
if attempt < retries - 1: if attempt < retries - 1:
time.sleep(RETRY_DELAY * (attempt + 1)) time.sleep(RETRY_DELAY * (attempt + 1))
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
log(f"JSON decode error: {e}")
last_error = HTTPError(f"Invalid JSON response: {e}") last_error = HTTPError(f"Invalid JSON response: {e}")
raise last_error raise last_error
+10 -1
View File
@@ -2,10 +2,17 @@
import json import json
import re import re
import sys
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
from . import http from . import http
def _log_error(msg: str):
"""Log error to stderr."""
sys.stderr.write(f"[REDDIT ERROR] {msg}\n")
sys.stderr.flush()
OPENAI_RESPONSES_URL = "https://api.openai.com/v1/responses" OPENAI_RESPONSES_URL = "https://api.openai.com/v1/responses"
# Depth configurations: (min, max) threads to request # Depth configurations: (min, max) threads to request
@@ -120,7 +127,9 @@ def parse_reddit_response(response: Dict[str, Any]) -> List[Dict[str, Any]]:
if "error" in response and response["error"]: if "error" in response and response["error"]:
error = response["error"] error = response["error"]
err_msg = error.get("message", str(error)) if isinstance(error, dict) else str(error) err_msg = error.get("message", str(error)) if isinstance(error, dict) else str(error)
print(f"[REDDIT ERROR] OpenAI API error: {err_msg}", flush=True) _log_error(f"OpenAI API error: {err_msg}")
if http.DEBUG:
_log_error(f"Full error response: {json.dumps(response, indent=2)[:1000]}")
return items return items
# Try to find the output text # Try to find the output text
+10 -1
View File
@@ -2,10 +2,17 @@
import json import json
import re import re
import sys
from typing import Any, Dict, List, Optional from typing import Any, Dict, List, Optional
from . import http from . import http
def _log_error(msg: str):
"""Log error to stderr."""
sys.stderr.write(f"[X ERROR] {msg}\n")
sys.stderr.flush()
# xAI uses chat completions endpoint # xAI uses chat completions endpoint
XAI_CHAT_URL = "https://api.x.ai/v1/chat/completions" XAI_CHAT_URL = "https://api.x.ai/v1/chat/completions"
@@ -129,7 +136,9 @@ def parse_x_response(response: Dict[str, Any]) -> List[Dict[str, Any]]:
if "error" in response and response["error"]: if "error" in response and response["error"]:
error = response["error"] error = response["error"]
err_msg = error.get("message", str(error)) if isinstance(error, dict) else str(error) err_msg = error.get("message", str(error)) if isinstance(error, dict) else str(error)
print(f"[X ERROR] xAI API error: {err_msg}", flush=True) _log_error(f"xAI API error: {err_msg}")
if http.DEBUG:
_log_error(f"Full error response: {json.dumps(response, indent=2)[:1000]}")
return items return items
# Try to find the output text # Try to find the output text