# ================================================================ # services/forecasting/app/main.py # ================================================================ """ Forecasting Service Main Application Demand prediction and forecasting service for bakery operations """ import structlog from contextlib import asynccontextmanager from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from app.core.config import settings from app.core.database import database_manager, get_db_health from app.api import forecasts, predictions from app.services.messaging import setup_messaging, cleanup_messaging from shared.monitoring.logging import setup_logging from shared.monitoring.metrics import MetricsCollector # Setup structured logging setup_logging("forecasting-service", settings.LOG_LEVEL) logger = structlog.get_logger() # Initialize metrics collector metrics_collector = MetricsCollector("forecasting-service") @asynccontextmanager async def lifespan(app: FastAPI): """Application lifespan manager for startup and shutdown events""" # Startup logger.info("Starting Forecasting Service", version="1.0.0") try: # Initialize database logger.info("Initializing database connection") await database_manager.create_tables() logger.info("Database initialized successfully") # Initialize messaging logger.info("Setting up messaging") await setup_messaging() logger.info("Messaging initialized") # Register custom metrics metrics_collector.register_counter("forecasts_generated_total", "Total forecasts generated") metrics_collector.register_counter("predictions_served_total", "Total predictions served") metrics_collector.register_counter("prediction_errors_total", "Total prediction errors") metrics_collector.register_histogram("forecast_processing_time_seconds", "Time to process forecast request") metrics_collector.register_gauge("active_models_count", "Number of active models") # Start metrics server metrics_collector.start_metrics_server(8080) logger.info("Forecasting Service started successfully") yield except Exception as e: logger.error("Failed to start Forecasting Service", error=str(e)) raise finally: # Shutdown logger.info("Shutting down Forecasting Service") try: await cleanup_messaging() logger.info("Messaging cleanup completed") except Exception as e: logger.error("Error during messaging cleanup", error=str(e)) # Create FastAPI app with lifespan app = FastAPI( title="Bakery Forecasting Service", description="AI-powered demand prediction and forecasting service for bakery operations", version="1.0.0", docs_url="/docs", redoc_url="/redoc", lifespan=lifespan ) # CORS middleware app.add_middleware( CORSMiddleware, allow_origins=settings.CORS_ORIGINS_LIST, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Include API routers app.include_router(forecasts.router, prefix="/api/v1", tags=["forecasts"]) app.include_router(predictions.router, prefix="/api/v1", tags=["predictions"]) @app.get("/health") async def health_check(): """Health check endpoint""" db_health = await get_db_health() return { "status": "healthy" if db_health else "unhealthy", "service": "forecasting-service", "version": "1.0.0", "database": "connected" if db_health else "disconnected", "timestamp": structlog.get_logger().info("Health check requested") } @app.get("/metrics") async def get_metrics(): """Metrics endpoint for Prometheus""" return metrics_collector.get_metrics() if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)