feat: standardize main.py with health checks, structured logging, and API docs

- Add module-level docstring with endpoint documentation
- Add /health endpoint with database connectivity check
- Add root endpoint redirecting to API docs
- Add FastAPI metadata (title, description, version)
- Reorganize imports and code structure with section separators
- Add shutdown event handler
- Change failed DB connection log level to warning
This commit is contained in:
2026-02-27 18:40:52 -05:00
parent 4fe97e0753
commit b73be057f3

121
main.py
View File

@@ -1,19 +1,48 @@
"""
eamco_money_api - FastAPI Delivery Pricing Microservice.
This microservice provides endpoints for managing delivery pricing,
cost calculations, and financial data for oil deliveries.
Endpoints:
GET /health - Health check with database connectivity status
GET /delivery/... - Delivery pricing and cost endpoints
Usage:
# Development
uvicorn main:app --reload --host 0.0.0.0 --port 8000
# Production (Docker)
docker run -p 8000:8000 eamco_money_api
"""
import logging import logging
import sys import sys
import uuid import uuid
from fastapi import FastAPI, Request from fastapi import FastAPI, Request
from app.routers import delivery
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from starlette.middleware.base import BaseHTTPMiddleware from starlette.middleware.base import BaseHTTPMiddleware
from config import load_config
from sqlalchemy import create_engine, text from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from config import load_config
from app.routers import delivery
# =============================================================================
# CONFIGURATION
# =============================================================================
ApplicationConfig = load_config() ApplicationConfig = load_config()
# Configure logging - DEBUG in development, INFO in production # =============================================================================
# LOGGING CONFIGURATION
# =============================================================================
def setup_logging(): def setup_logging():
"""Configure structured logging for the application."""
log_level = logging.DEBUG if ApplicationConfig.CURRENT_SETTINGS != 'PRODUCTION' else logging.INFO log_level = logging.DEBUG if ApplicationConfig.CURRENT_SETTINGS != 'PRODUCTION' else logging.INFO
formatter = logging.Formatter( formatter = logging.Formatter(
@@ -34,9 +63,13 @@ def setup_logging():
return logging.getLogger('eamco_money_api') return logging.getLogger('eamco_money_api')
logger = setup_logging() logger = setup_logging()
# Database setup with connection pooling # =============================================================================
# DATABASE SETUP
# =============================================================================
engine = create_engine( engine = create_engine(
ApplicationConfig.SQLALCHEMY_DATABASE_URI, ApplicationConfig.SQLALCHEMY_DATABASE_URI,
pool_pre_ping=True, pool_pre_ping=True,
@@ -46,6 +79,7 @@ engine = create_engine(
) )
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
def check_db_connection(): def check_db_connection():
""" """
Test database connectivity. Test database connectivity.
@@ -58,11 +92,27 @@ def check_db_connection():
except Exception: except Exception:
return False return False
app = FastAPI()
# =============================================================================
# FASTAPI APPLICATION
# =============================================================================
app = FastAPI(
title="eamco_money_api",
description="Delivery pricing and cost calculation microservice",
version="1.0.0",
docs_url="/docs",
redoc_url="/redoc",
)
# =============================================================================
# MIDDLEWARE
# =============================================================================
# Request ID middleware for request tracking/correlation
class RequestIDMiddleware(BaseHTTPMiddleware): class RequestIDMiddleware(BaseHTTPMiddleware):
"""Request ID middleware for request tracking/correlation."""
async def dispatch(self, request: Request, call_next): async def dispatch(self, request: Request, call_next):
request_id = request.headers.get("X-Request-ID") or str(uuid.uuid4())[:8] request_id = request.headers.get("X-Request-ID") or str(uuid.uuid4())[:8]
request.state.request_id = request_id request.state.request_id = request_id
@@ -70,11 +120,9 @@ class RequestIDMiddleware(BaseHTTPMiddleware):
response.headers["X-Request-ID"] = request_id response.headers["X-Request-ID"] = request_id
return response return response
app.add_middleware(RequestIDMiddleware) app.add_middleware(RequestIDMiddleware)
app.include_router(delivery.router)
app.add_middleware( app.add_middleware(
CORSMiddleware, CORSMiddleware,
allow_origins=ApplicationConfig.origins, allow_origins=ApplicationConfig.origins,
@@ -83,9 +131,50 @@ app.add_middleware(
allow_headers=["*"], allow_headers=["*"],
) )
@app.get("/") # =============================================================================
def read_root(): # ROUTERS
return {"Status": "Money Service is online"} # =============================================================================
app.include_router(delivery.router)
# =============================================================================
# ENDPOINTS
# =============================================================================
@app.get("/", include_in_schema=False)
async def root():
"""Root endpoint - redirect to docs."""
return {
"service": "eamco_money_api",
"version": "1.0.0",
"docs": "/docs",
}
@app.get("/health", tags=["Health"])
async def health_check():
"""
Health check endpoint.
Returns service status and database connectivity.
Use this endpoint for container health checks and monitoring.
Returns:
JSON with status and db_connected flag
"""
db_connected = check_db_connection()
return {
"status": "healthy" if db_connected else "degraded",
"db_connected": db_connected,
}
# =============================================================================
# STARTUP/SHUTDOWN EVENTS
# =============================================================================
@app.on_event("startup") @app.on_event("startup")
async def startup_event(): async def startup_event():
@@ -107,4 +196,10 @@ async def startup_event():
if check_db_connection(): if check_db_connection():
logger.info("DB Connection: ✅ OK") logger.info("DB Connection: ✅ OK")
else: else:
logger.info("DB Connection: ❌ FAILED") logger.warning("DB Connection: ❌ FAILED")
@app.on_event("shutdown")
async def shutdown_event():
"""Application shutdown - cleanup."""
logger.info("🛑 eamco_money_api SHUTTING DOWN")