Files
DevServer/app/worker.py
Hardik Sankhla 2304c3ed12 Initial commit: Complete DevServer monitoring framework
- FastAPI application with OpenTelemetry instrumentation
- Redis caching and Celery task processing
- Docker Compose setup with monitoring stack
- Grafana dashboards and Prometheus metrics
- Jaeger distributed tracing
- Comprehensive documentation in docs/ folder
- Monitoring configuration and troubleshooting guides
2025-10-13 09:55:17 +05:30

204 lines
7.8 KiB
Python

import os
import time
import json
import redis
from celery import Celery
from celery.signals import task_prerun, task_postrun, task_failure
from app.logging import setup_logging, get_logger
from opentelemetry import trace
# Setup logging for Celery worker
logger = setup_logging(service_name="devserver.celery.worker", log_level="DEBUG")
logger.info("=" * 60)
logger.info("DevServer Celery Worker Initializing...")
logger.info("=" * 60)
logger.info("Service: devserver.celery.worker")
# Get tracer for worker
tracer = trace.get_tracer("devserver.celery.tracer")
app = Celery(
"DevServerTasks",
broker=os.getenv("CELERY_BROKER_URL"),
backend=os.getenv("CELERY_RESULT_BACKEND"),
)
logger.info(f"Celery broker: {os.getenv('CELERY_BROKER_URL')}")
logger.info(f"Celery backend: {os.getenv('CELERY_RESULT_BACKEND')}")
# Initialize Redis client for worker
logger.info("Initializing Redis connection for worker...")
try:
redis_client = redis.Redis(host='redis', port=6379, db=0, decode_responses=True)
redis_client.ping()
logger.info("Redis connection established successfully")
except Exception as e:
logger.error(f"Failed to connect to Redis: {e}", exc_info=True)
# Celery signal handlers
@task_prerun.connect
def task_prerun_handler(sender=None, task_id=None, task=None, args=None, kwargs=None, **extra):
"""Log before task execution"""
task_name = task.name if task else sender.name if sender else "unknown" # type: ignore
logger.info(f"⏳ Task starting: {task_name}")
logger.info(f" Service: devserver.celery.worker")
logger.info(f" Task ID: {task_id}")
logger.debug(f" Args: {args}")
logger.debug(f" Kwargs: {kwargs}")
@task_postrun.connect
def task_postrun_handler(sender=None, task_id=None, task=None, args=None, kwargs=None, retval=None, **extra):
"""Log after task execution"""
task_name = task.name if task else sender.name if sender else "unknown" # type: ignore
logger.info(f"Task completed: {task_name}")
logger.info(f" Service: devserver.celery.worker")
logger.info(f" Task ID: {task_id}")
logger.debug(f" Result: {retval}")
@task_failure.connect
def task_failure_handler(sender=None, task_id=None, exception=None, args=None, kwargs=None, traceback=None, **extra):
"""Log task failures"""
task_name = sender.name if sender else "unknown" # type: ignore
logger.error(f"Task failed: {task_name}")
logger.error(f" Service: devserver.celery.worker")
logger.error(f" Task ID: {task_id}")
logger.error(f" Exception: {exception}")
logger.error(f" Args: {args}")
logger.error(f" Traceback: {traceback}")
@app.task
def long_running_task(duration):
"""Simulates a long-running task by sleeping for the specified duration."""
logger.info(f"Executing long_running_task: duration={duration}s")
start_time = time.time()
time.sleep(duration)
elapsed = time.time() - start_time
result = f"Task completed after {duration} seconds (actual: {elapsed:.2f}s)"
logger.info(f"{result}")
return result
@app.task(name="devserver.celery.tasks.fibonacci")
def calculate_fibonacci(n):
"""Calculate the nth Fibonacci number (with intentional delay for demo) and cache the result."""
with tracer.start_as_current_span("celery.task.fibonacci") as span:
span.set_attribute("service.name", "devserver.celery.worker")
span.set_attribute("task.name", "fibonacci")
span.set_attribute("fibonacci.n", n)
cache_key = f"fibonacci:result:{n}"
logger.info(f"Calculating Fibonacci number: n={n}, cache_key={cache_key}")
start_time = time.time()
# Check cache first
try:
cached_result = redis_client.get(cache_key)
if cached_result:
span.set_attribute("cache.hit", True)
result_value = int(str(cached_result))
logger.info(f"Cache hit in worker! Fibonacci({n}) = {result_value}")
elapsed = time.time() - start_time
return {
"service": "devserver.celery.worker",
"task": "fibonacci",
"n": n,
"result": result_value,
"computation_time_seconds": elapsed,
"source": "cache"
}
except (ValueError, TypeError) as e:
logger.warning(f"Cache value conversion failed in worker: {e}")
except Exception as e:
logger.warning(f"Cache check failed in worker: {e}")
span.set_attribute("cache.hit", False)
def fib(num):
if num <= 1:
return num
return fib(num - 1) + fib(num - 2)
# Add a small delay to make the task more visible in traces
logger.debug("Adding artificial delay for visibility (0.5s)")
time.sleep(0.5)
logger.debug(f"Starting Fibonacci calculation for n={n}")
result = fib(n)
elapsed = time.time() - start_time
span.set_attribute("fibonacci.result", result)
span.set_attribute("task.duration_seconds", elapsed)
logger.info(f"Fibonacci calculated: n={n}, result={result}, time={elapsed:.2f}s")
# Store result in cache (TTL: 1 hour for Fibonacci results)
try:
redis_client.setex(cache_key, 3600, str(result))
span.set_attribute("cache.stored", True)
logger.info(f"Stored Fibonacci result in cache: {cache_key} = {result} (TTL: 3600s)")
except Exception as e:
logger.error(f"Failed to store Fibonacci result in cache: {e}")
return {
"service": "devserver.celery.worker",
"task": "fibonacci",
"n": n,
"result": result,
"message": f"The {n}th Fibonacci number is {result}",
"computation_time_seconds": elapsed,
"source": "computation"
}
@app.task(name="devserver.celery.tasks.process_data")
def process_data_with_cache(data, cache_key):
"""Process data and cache the result in Redis."""
with tracer.start_as_current_span("celery.task.process_data") as span:
span.set_attribute("service.name", "devserver.celery.worker")
span.set_attribute("task.name", "process_data")
span.set_attribute("cache.key", cache_key)
span.set_attribute("data.keys_count", len(data))
logger.info(f"Processing data with caching")
logger.debug(f"Cache key: {cache_key}")
logger.debug(f"Data keys: {list(data.keys())}")
start_time = time.time()
# Simulate processing
logger.debug("Simulating data processing (1s delay)")
time.sleep(1)
# Process the data (example: count keys and values)
processed = {
"service": "devserver.celery.worker",
"task": "process_data",
"input": data,
"num_keys": len(data),
"total_length": sum(len(str(v)) for v in data.values()),
"processed_at": time.time()
}
logger.debug(f"Data processed: num_keys={processed['num_keys']}, total_length={processed['total_length']}")
# Cache the result for 5 minutes
try:
redis_client.setex(cache_key, 300, json.dumps(processed))
span.set_attribute("cache.operation", "success")
logger.info(f"Result cached successfully: cache_key={cache_key}, ttl=300s")
except Exception as e:
span.set_attribute("cache.operation", "failed")
logger.error(f"Failed to cache result: {e}", exc_info=True)
elapsed = time.time() - start_time
span.set_attribute("task.duration_seconds", elapsed)
logger.info(f"Data processing complete: time={elapsed:.2f}s")
return processed