Remote debugging initial commit.

This commit is contained in:
SergeantPanda 2025-03-27 09:49:51 -05:00
parent 9ab76c3129
commit 1fcedab1ab
8 changed files with 254 additions and 8 deletions

View file

@ -1,5 +1,6 @@
**/__pycache__
**/.venv
**/venv
**/.classpath
**/.dockerignore
**/.env

9
.gitignore vendored
View file

@ -1,6 +1,7 @@
.DS_Store
**/__pycache__/
**/.vscode/
**/venv
*.pyc
node_modules/
.history/
@ -10,4 +11,10 @@ docker/Dockerfile DEV
static/
data/
.next
next-env.d.ts
next-env.d.ts
media/
celerybeat-schedule*
dump.rdb
debugpy*
uwsgi.sock
package-lock.json

View file

@ -0,0 +1,19 @@
services:
dispatcharr:
# build:
# context: ..
# dockerfile: docker/Dockerfile.dev
image: dispatcharr/dispatcharr
container_name: dispatcharr_debug
ports:
- 5656:5656 # API port
- 9193:9191 # Web UI port
- 8001:8001 # Socket port
- 5678:5678 # Debugging port
volumes:
- ../:/app
environment:
- DISPATCHARR_ENV=dev
- DISPATCHARR_DEBUG=true
- REDIS_HOST=localhost
- CELERY_BROKER_URL=redis://localhost:6379/0

View file

@ -49,6 +49,7 @@ if [[ ! -f /etc/profile.d/dispatcharr.sh ]]; then
echo "export POSTGRES_HOST=$POSTGRES_HOST" >> /etc/profile.d/dispatcharr.sh
echo "export POSTGRES_PORT=$POSTGRES_PORT" >> /etc/profile.d/dispatcharr.sh
echo "export DISPATCHARR_ENV=$DISPATCHARR_ENV" >> /etc/profile.d/dispatcharr.sh
echo "export DISPATCHARR_DEBUG=$DISPATCHARR_DEBUG" >> /etc/profile.d/dispatcharr.sh
echo "export REDIS_HOST=$REDIS_HOST" >> /etc/profile.d/dispatcharr.sh
echo "export REDIS_DB=$REDIS_DB" >> /etc/profile.d/dispatcharr.sh
fi
@ -75,8 +76,18 @@ postgres_pid=$(su - postgres -c "/usr/lib/postgresql/14/bin/pg_ctl -D /data stat
echo "✅ Postgres started with PID $postgres_pid"
pids+=("$postgres_pid")
if [ "$DISPATCHARR_ENV" = "dev" ]; then
uwsgi_file="/app/docker/uwsgi.ini"
if [ "$DISPATCHARR_ENV" = "dev" ] && [ "$DISPATCHARR_DEBUG" != "true" ]; then
uwsgi_file="/app/docker/uwsgi.dev.ini"
elif [ "$DISPATCHARR_DEBUG" = "true" ]; then
uwsgi_file="/app/docker/uwsgi.debug.ini"
fi
if [[ "$DISPATCHARR_ENV" = "dev" ]]; then
. /app/docker/init/99-init-dev.sh
else
echo "🚀 Starting nginx..."
nginx
@ -85,10 +96,6 @@ else
pids+=("$nginx_pid")
fi
uwsgi_file="/app/docker/uwsgi.ini"
if [ "$DISPATCHARR_ENV" = "dev" ]; then
uwsgi_file="/app/docker/uwsgi.dev.ini"
fi
echo "🚀 Starting uwsgi..."
su - $POSTGRES_USER -c "cd /app && uwsgi --ini $uwsgi_file &"
@ -97,7 +104,6 @@ echo "✅ uwsgi started with PID $uwsgi_pid"
pids+=("$uwsgi_pid")
cd /app
python manage.py migrate --noinput
python manage.py collectstatic --noinput

View file

@ -15,5 +15,11 @@ fi
# Install frontend dependencies
cd /app/frontend && npm install
# Install pip dependencies
cd /app && pip install -r requirements.txt
# Install debugpy for remote debugging
if [ "$DISPATCHARR_DEBUG" = "true" ]; then
echo "=== setting up debugpy ==="
pip install debugpy
fi

81
docker/uwsgi.debug.ini Normal file
View file

@ -0,0 +1,81 @@
[uwsgi]
; exec-before = python manage.py collectstatic --noinput
; exec-before = python manage.py migrate --noinput
; First run Redis availability check script once
exec-before = python /app/scripts/wait_for_redis.py
; Start Redis first
attach-daemon = redis-server
; Then start other services
attach-daemon = celery -A dispatcharr worker -l info
attach-daemon = celery -A dispatcharr beat -l info
attach-daemon = daphne -b 0.0.0.0 -p 8001 dispatcharr.asgi:application
attach-daemon = cd /app/frontend && npm run dev
# Core settings
chdir = /app
module = scripts.debug_wrapper:application
virtualenv = /dispatcharrpy
master = true
env = DJANGO_SETTINGS_MODULE=dispatcharr.settings
socket = /app/uwsgi.sock
chmod-socket = 777
vacuum = true
die-on-term = true
static-map = /static=/app/static
# Worker configuration
workers = 1
threads = 4
enable-threads = true
lazy-apps = true
# HTTP server
http = 0.0.0.0:5656
http-keepalive = 1
buffer-size = 65536
http-timeout = 600
# Async mode (use gevent for high concurrency)
gevent = 100
async = 100
# Performance tuning
thunder-lock = true
log-4xx = true
log-5xx = true
disable-logging = false
; Longer timeouts for debugging sessions
harakiri = 3600
socket-timeout = 3600
http-timeout = 3600
# Ignore unknown options
ignore-sigpipe = true
ignore-write-errors = true
disable-write-exception = true
# Explicitly disable for-server option that confuses debugpy
for-server = false
# Debugging settings
py-autoreload = 1
honour-stdin = true
# Environment variables
env = PYTHONPATH=/app
env = PYTHONUNBUFFERED=1
env = PYDEVD_DISABLE_FILE_VALIDATION=1
env = PYTHONUTF8=1
env = PYTHONXOPT=-Xfrozen_modules=off
env = PYDEVD_DEBUG=1
env = DEBUGPY_LOG_DIR=/app/debugpy_logs
# Debugging control variables
env = WAIT_FOR_DEBUGGER=false
env = DEBUG_TIMEOUT=30

90
scripts/debug_wrapper.py Normal file
View file

@ -0,0 +1,90 @@
"""
Debug wrapper for the WSGI application.
This module initializes debugpy and then imports the actual application.
"""
import sys
import os
import time
import logging
import inspect
# Configure logging to output to both console and file
os.makedirs('/app/debugpy_logs', exist_ok=True)
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s [%(levelname)s] %(name)s - %(message)s',
handlers=[
logging.FileHandler('/app/debugpy_logs/debug_wrapper.log'),
logging.StreamHandler(sys.stdout)
]
)
logger = logging.getLogger('debug_wrapper')
# Log system info
logger.info(f"Python version: {sys.version}")
logger.info(f"Current directory: {os.getcwd()}")
logger.info(f"Files in current directory: {os.listdir()}")
logger.info(f"Python path: {sys.path}")
# Default timeout in seconds
DEBUG_TIMEOUT = int(os.environ.get('DEBUG_TIMEOUT', '30'))
# Whether to wait for debugger to attach
WAIT_FOR_DEBUGGER = os.environ.get('WAIT_FOR_DEBUGGER', 'false').lower() == 'true'
logger.info(f"DEBUG_TIMEOUT: {DEBUG_TIMEOUT}")
logger.info(f"WAIT_FOR_DEBUGGER: {WAIT_FOR_DEBUGGER}")
try:
import debugpy
from debugpy import configure
logger.info("Successfully imported debugpy")
# Critical: Configure debugpy to use regular Python for the adapter, not uwsgi
python_path = '/usr/local/bin/python3'
if os.path.exists(python_path):
logger.info(f"Setting debugpy adapter to use Python interpreter: {python_path}")
debugpy.configure(python=python_path)
else:
logger.warning(f"Python path {python_path} not found. Using system default.")
# Don't wait for connection, just set up the debugging session
logger.info("Initializing debugpy on 0.0.0.0:5678...")
try:
# Use connect instead of listen to avoid the adapter process
debugpy.listen(("0.0.0.0", 5678))
logger.info("debugpy now listening on 0.0.0.0:5678")
if WAIT_FOR_DEBUGGER:
logger.info(f"Waiting for debugger to attach (timeout: {DEBUG_TIMEOUT}s)...")
start_time = time.time()
while not debugpy.is_client_connected() and (time.time() - start_time < DEBUG_TIMEOUT):
time.sleep(1)
logger.info("Waiting for debugger connection...")
if debugpy.is_client_connected():
logger.info("Debugger attached!")
else:
logger.info(f"Debugger not attached after {DEBUG_TIMEOUT}s, continuing anyway...")
except Exception as e:
logger.error(f"Error with debugpy.listen: {e}", exc_info=True)
logger.info("Continuing without debugging...")
except ImportError:
logger.error("debugpy not installed, continuing without debugging support")
except Exception as e:
logger.error(f"Failed to initialize debugpy: {e}", exc_info=True)
logger.info("Continuing without debugging support")
# Now import the actual WSGI application
logger.info("Loading WSGI application...")
try:
from dispatcharr.wsgi import application
logger.info("WSGI application loaded successfully")
# Log the application details
logger.info(f"Application type: {type(application)}")
logger.info(f"Application callable: {inspect.isfunction(application) or inspect.ismethod(application)}")
except Exception as e:
logger.error(f"Error loading WSGI application: {e}", exc_info=True)
raise

View file

@ -0,0 +1,36 @@
"""
Standalone debug entry point for the Django application.
This provides a cleaner way to debug without uWSGI complications.
Run this directly with Python to debug:
python standalone_debug.py
"""
import os
import sys
import debugpy
import logging
# Configure basic logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s [%(levelname)s] %(name)s - %(message)s'
)
logger = logging.getLogger('standalone_debug')
# Setup Django environment
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dispatcharr.settings')
# Setup debugpy and wait for connection
logger.info("Setting up debugpy...")
debugpy.listen(("0.0.0.0", 5678))
logger.info("Waiting for debugger to attach... Connect to 0.0.0.0:5678")
debugpy.wait_for_client()
logger.info("Debugger attached!")
# Import Django and run the development server
logger.info("Starting Django development server...")
import django
django.setup()
from django.core.management import execute_from_command_line
execute_from_command_line(['manage.py', 'runserver', '0.0.0.0:8000'])