From 67aca64420a5a3ff831a1f0d5a995e8494242bb7 Mon Sep 17 00:00:00 2001 From: SergeantPanda Date: Sat, 10 May 2025 13:25:03 -0500 Subject: [PATCH] Properly set EV for all profiles so uWSGI daemons can see it. --- dispatcharr/celery.py | 68 +++++++++++++++++++++++++++++++++++------ dispatcharr/settings.py | 12 +++++++- docker/entrypoint.sh | 24 ++++++++++++++- 3 files changed, 92 insertions(+), 12 deletions(-) diff --git a/dispatcharr/celery.py b/dispatcharr/celery.py index 28b94263..2fe9e6ec 100644 --- a/dispatcharr/celery.py +++ b/dispatcharr/celery.py @@ -2,35 +2,74 @@ import os from celery import Celery import logging -from django.conf import settings # Import Django settings + +# Initialize with defaults before Django settings are loaded +DEFAULT_LOG_LEVEL = 'DEBUG' + +# Try multiple sources for log level in order of preference +def get_effective_log_level(): + # 1. Direct environment variable + env_level = os.environ.get('DISPATCHARR_LOG_LEVEL', '').upper() + if env_level and not env_level.startswith('$(') and not env_level.startswith('%('): + return env_level + + # 2. Check temp file that may have been created by settings.py + try: + if os.path.exists('/tmp/dispatcharr_log_level'): + with open('/tmp/dispatcharr_log_level', 'r') as f: + file_level = f.read().strip().upper() + if file_level: + return file_level + except: + pass + + # 3. Fallback to default + return DEFAULT_LOG_LEVEL + +# Get effective log level before Django loads +effective_log_level = get_effective_log_level() +print(f"Celery using effective log level: {effective_log_level}") os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dispatcharr.settings') app = Celery("dispatcharr") app.config_from_object("django.conf:settings", namespace="CELERY") app.autodiscover_tasks() +# Use environment variable for log level with fallback to INFO +CELERY_LOG_LEVEL = os.environ.get('DISPATCHARR_LOG_LEVEL', 'INFO').upper() +print(f"Celery using log level from environment: {CELERY_LOG_LEVEL}") + # Configure Celery logging app.conf.update( - worker_log_level=settings.LOG_LEVEL_NAME, # Use same log level from environment + worker_log_level=effective_log_level, worker_log_format='%(asctime)s %(levelname)s %(name)s: %(message)s', - beat_log_level=settings.LOG_LEVEL_NAME, # Use same log level from environment + beat_log_level=effective_log_level, worker_hijack_root_logger=False, worker_task_log_format='%(asctime)s %(levelname)s %(task_name)s: %(message)s', ) @app.on_after_configure.connect def setup_celery_logging(**kwargs): - # Check if user has set logging to INFO level - if settings.LOG_LEVEL_NAME.upper() == 'INFO': - # Get the specific loggers that output the noisy INFO messages - for logger_name in ['celery.app.trace', 'celery.beat', 'celery.worker.strategy', 'celery.beat.Scheduler']: - # Create a custom filter to suppress specific messages - logger = logging.getLogger(logger_name) + # Use our directly determined log level + log_level = effective_log_level + print(f"Celery configuring loggers with level: {log_level}") + # Get the specific loggers that output potentially noisy messages + for logger_name in ['celery.app.trace', 'celery.beat', 'celery.worker.strategy', 'celery.beat.Scheduler']: + logger = logging.getLogger(logger_name) + + # Remove any existing filters first (in case this runs multiple times) + for filter in logger.filters[:]: + if hasattr(filter, '__class__') and filter.__class__.__name__ == 'SuppressFilter': + logger.removeFilter(filter) + + # For INFO level - add a filter to hide repetitive messages + # For DEBUG/TRACE level - don't filter (show everything) + if log_level == 'INFO': # Add a custom filter to completely filter out the repetitive messages class SuppressFilter(logging.Filter): def filter(self, record): - # Return False to completely suppress these specific patterns when at INFO level + # Return False to completely suppress these specific patterns if ( "succeeded in" in getattr(record, 'msg', '') or "Scheduler: Sending due task" in getattr(record, 'msg', '') or @@ -41,3 +80,12 @@ def setup_celery_logging(**kwargs): # Add the filter to each logger logger.addFilter(SuppressFilter()) + + # Set all Celery loggers to the configured level + # This ensures they respect TRACE/DEBUG when set + try: + numeric_level = getattr(logging, log_level) + logger.setLevel(numeric_level) + except (AttributeError, TypeError): + # If the log level string is invalid, default to DEBUG + logger.setLevel(logging.DEBUG) diff --git a/dispatcharr/settings.py b/dispatcharr/settings.py index 1b4337ec..8f3921c0 100644 --- a/dispatcharr/settings.py +++ b/dispatcharr/settings.py @@ -244,7 +244,17 @@ LOG_LEVEL_MAP = { } # Get log level from environment variable, default to INFO if not set -LOG_LEVEL_NAME = os.environ.get('DISPATCHARR_LOG_LEVEL', 'INFO').upper() +# Add debugging output to see exactly what's being detected +env_log_level = os.environ.get('DISPATCHARR_LOG_LEVEL', '') +print(f"Environment DISPATCHARR_LOG_LEVEL detected as: '{env_log_level}'") + +if not env_log_level: + print("No DISPATCHARR_LOG_LEVEL found in environment, using default INFO") + LOG_LEVEL_NAME = 'INFO' +else: + LOG_LEVEL_NAME = env_log_level.upper() + print(f"Setting log level to: {LOG_LEVEL_NAME}") + LOG_LEVEL = LOG_LEVEL_MAP.get(LOG_LEVEL_NAME, 20) # Default to INFO (20) if invalid # Add this to your existing LOGGING configuration or create one if it doesn't exist diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index bbd2b9ba..3d2161d5 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -49,6 +49,14 @@ else echo "📦 Dispatcharr version: ${DISPATCHARR_VERSION}" fi +# Set log level with default if not provided +export DISPATCHARR_LOG_LEVEL=${DISPATCHARR_LOG_LEVEL:-info} +echo "Environment DISPATCHARR_LOG_LEVEL detected as: '${DISPATCHARR_LOG_LEVEL}'" +echo "Setting log level to: ${DISPATCHARR_LOG_LEVEL}" + +# Also make the log level available in /etc/environment for all login shells +#grep -q "DISPATCHARR_LOG_LEVEL" /etc/environment || echo "DISPATCHARR_LOG_LEVEL=${DISPATCHARR_LOG_LEVEL}" >> /etc/environment + # READ-ONLY - don't let users change these export POSTGRES_DIR=/data/db @@ -65,12 +73,24 @@ if [[ ! -f /etc/profile.d/dispatcharr.sh ]]; then 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 DISPATCHARR_LOG_LEVEL=$DISPATCHARR_LOG_LEVEL" >> /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 echo "export POSTGRES_DIR=$POSTGRES_DIR" >> /etc/profile.d/dispatcharr.sh echo "export DISPATCHARR_PORT=$DISPATCHARR_PORT" >> /etc/profile.d/dispatcharr.sh echo "export DISPATCHARR_VERSION=$DISPATCHARR_VERSION" >> /etc/profile.d/dispatcharr.sh echo "export DISPATCHARR_TIMESTAMP=$DISPATCHARR_TIMESTAMP" >> /etc/profile.d/dispatcharr.sh + + # Make sure we also set these variables in /etc/environment + # which is sourced even before /etc/profile.d scripts + for var in PATH VIRTUAL_ENV DJANGO_SETTINGS_MODULE PYTHONUNBUFFERED POSTGRES_DB POSTGRES_USER \ + POSTGRES_PASSWORD POSTGRES_HOST POSTGRES_PORT DISPATCHARR_ENV DISPATCHARR_DEBUG \ + DISPATCHARR_LOG_LEVEL REDIS_HOST REDIS_DB POSTGRES_DIR DISPATCHARR_PORT \ + DISPATCHARR_VERSION DISPATCHARR_TIMESTAMP; do + if [[ -n "${!var}" ]]; then + grep -q "^${var}=" /etc/environment || echo "${var}=${!var}" >> /etc/environment + fi + done fi chmod +x /etc/profile.d/dispatcharr.sh @@ -127,7 +147,9 @@ else uwsgi_file="/app/docker/uwsgi.ini" fi -su - $POSTGRES_USER -c "cd /app && uwsgi --ini $uwsgi_file &" +# Pass all environment variables to the uwsgi process +# The -p/--preserve-environment flag ensures all environment variables are passed through +su -p - $POSTGRES_USER -c "cd /app && uwsgi --ini $uwsgi_file &" uwsgi_pid=$(pgrep uwsgi | sort | head -n1) echo "✅ uwsgi started with PID $uwsgi_pid" pids+=("$uwsgi_pid")