streamlit_healthcheck.server
1from fastapi import FastAPI, HTTPException 2from fastapi.responses import JSONResponse 3from typing import Dict, Any, Optional 4import uvicorn 5from .healthcheck import HealthCheckService 6import logging 7import argparse 8from contextlib import asynccontextmanager 9""" 10This module implements a FastAPI server for monitoring the health of a Streamlit application. 11Features: 12- Provides REST API endpoints to report health status of system resources, dependencies, and Streamlit pages. 13- Uses a configurable health check service to gather health data. 14- Supports startup and shutdown events for proper initialization and cleanup of the health check service. 15- Includes endpoints: 16 - `/health`: Returns complete health status. 17 - `/health/system`: Returns system resource health (CPU, Memory, Disk). 18 - `/health/dependencies`: Returns dependencies health status. 19 - `/health/pages`: Returns Streamlit pages health status. 20- Configurable via command-line arguments for host, port, and health check config file. 21- Uses logging for operational visibility. 22Global Variables: 23- `health_service`: Instance of HealthCheckService, manages health checks. 24- `config_file`: Path to health check configuration file. 25Usage: 26Run as a standalone server or import as a module. 27""" 28 29 30# Set up logging 31logging.basicConfig( 32 level=logging.INFO, 33 format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', 34 handlers=[ 35 logging.StreamHandler() # Prints to console 36 ] 37) 38logger = logging.getLogger(__name__) 39 40# Initialize health check service as global variable 41health_service: Optional[HealthCheckService] = None 42config_file: Optional[str] = None 43 44@asynccontextmanager 45async def lifespan(app: FastAPI): 46 """Lifespan context manager for FastAPI application""" 47 # Startup 48 global health_service 49 logger.info("Initializing health check service") 50 config_path = config_file if config_file is not None else "health_check_config.json" 51 health_service = HealthCheckService(config_path=config_path) 52 health_service.start() 53 54 yield 55 56 # Shutdown 57 if health_service: 58 logger.info("Stopping health check service") 59 health_service.stop() 60 61app = FastAPI( 62 title="Streamlit Health Check API", 63 description="API endpoints for monitoring Streamlit application health", 64 version="1.0.0", 65 lifespan=lifespan 66) 67 68# Initialize health check service and config file path as global variables 69# health_service and config_file are already defined above 70 71 72@app.get("/health", response_model=Dict[str, Any]) 73async def get_health_status(): 74 """ 75 Get complete health check status including system resources, 76 dependencies, custom checks, and Streamlit pages 77 """ 78 global health_service 79 if not health_service: 80 raise HTTPException(status_code=503, detail="Health check service not initialized") 81 82 try: 83 health_data = health_service.get_health_data() 84 return JSONResponse(content=health_data) 85 except Exception as e: 86 logger.error(f"Error getting health data: {str(e)}") 87 raise HTTPException(status_code=500, detail=str(e)) 88 89@app.get("/health/system", response_model=Dict[str, Any]) 90async def get_system_health(): 91 """Get system resource health status (CPU, Memory, Disk)""" 92 global health_service 93 if not health_service: 94 raise HTTPException(status_code=503, detail="Health check service not initialized") 95 96 try: 97 health_data = health_service.get_health_data() 98 return JSONResponse(content={"system": health_data.get("system", {})}) 99 except Exception as e: 100 logger.error(f"Error getting system health data: {str(e)}") 101 raise HTTPException(status_code=500, detail=str(e)) 102 103@app.get("/health/dependencies", response_model=Dict[str, Any]) 104async def get_dependencies_health(): 105 """Get dependencies health status""" 106 global health_service 107 if not health_service: 108 raise HTTPException(status_code=503, detail="Health check service not initialized") 109 110 try: 111 health_data = health_service.get_health_data() 112 return JSONResponse(content={"dependencies": health_data.get("dependencies", {})}) 113 except Exception as e: 114 logger.error(f"Error getting dependencies health data: {str(e)}") 115 raise HTTPException(status_code=500, detail=str(e)) 116 117@app.get("/health/pages", response_model=Dict[str, Any]) 118async def get_pages_health(): 119 """Get Streamlit pages health status""" 120 global health_service 121 if not health_service: 122 raise HTTPException(status_code=503, detail="Health check service not initialized") 123 124 try: 125 health_data = health_service.get_health_data() 126 return JSONResponse(content={"streamlit_pages": health_data.get("streamlit_pages", {})}) 127 except Exception as e: 128 logger.error(f"Error getting pages health data: {str(e)}") 129 raise HTTPException(status_code=500, detail=str(e)) 130 131def start_api_server(host: str = "0.0.0.0", port: int = 8000, config: str = "health_check_config.json"): 132 """Start the FastAPI server""" 133 global config_file 134 config_file = config 135 uvicorn.run(app, host=host, port=port) 136 137def parse_args(): 138 """Parse command line arguments""" 139 parser = argparse.ArgumentParser(description="Streamlit Health Check API Server") 140 parser.add_argument("--host", default="0.0.0.0", help="Host address to bind") 141 parser.add_argument("--port", type=int, default=8000, help="Port to run the server on") 142 parser.add_argument( 143 "--config", 144 default="health_check_config.json", 145 help="Path to health check configuration file" 146 ) 147 return parser.parse_args() 148 149if __name__ == "__main__": 150 args = parse_args() 151 logger.info(f"Starting server with config file: {args.config}") 152 start_api_server(host=args.host, port=args.port, config=args.config)
logger =
<Logger streamlit_healthcheck.server (INFO)>
config_file: Optional[str] =
None
@asynccontextmanager
async def
lifespan(app: fastapi.applications.FastAPI):
45@asynccontextmanager 46async def lifespan(app: FastAPI): 47 """Lifespan context manager for FastAPI application""" 48 # Startup 49 global health_service 50 logger.info("Initializing health check service") 51 config_path = config_file if config_file is not None else "health_check_config.json" 52 health_service = HealthCheckService(config_path=config_path) 53 health_service.start() 54 55 yield 56 57 # Shutdown 58 if health_service: 59 logger.info("Stopping health check service") 60 health_service.stop()
Lifespan context manager for FastAPI application
app =
<fastapi.applications.FastAPI object>
@app.get('/health', response_model=Dict[str, Any])
async def
get_health_status():
73@app.get("/health", response_model=Dict[str, Any]) 74async def get_health_status(): 75 """ 76 Get complete health check status including system resources, 77 dependencies, custom checks, and Streamlit pages 78 """ 79 global health_service 80 if not health_service: 81 raise HTTPException(status_code=503, detail="Health check service not initialized") 82 83 try: 84 health_data = health_service.get_health_data() 85 return JSONResponse(content=health_data) 86 except Exception as e: 87 logger.error(f"Error getting health data: {str(e)}") 88 raise HTTPException(status_code=500, detail=str(e))
Get complete health check status including system resources, dependencies, custom checks, and Streamlit pages
@app.get('/health/system', response_model=Dict[str, Any])
async def
get_system_health():
90@app.get("/health/system", response_model=Dict[str, Any]) 91async def get_system_health(): 92 """Get system resource health status (CPU, Memory, Disk)""" 93 global health_service 94 if not health_service: 95 raise HTTPException(status_code=503, detail="Health check service not initialized") 96 97 try: 98 health_data = health_service.get_health_data() 99 return JSONResponse(content={"system": health_data.get("system", {})}) 100 except Exception as e: 101 logger.error(f"Error getting system health data: {str(e)}") 102 raise HTTPException(status_code=500, detail=str(e))
Get system resource health status (CPU, Memory, Disk)
@app.get('/health/dependencies', response_model=Dict[str, Any])
async def
get_dependencies_health():
104@app.get("/health/dependencies", response_model=Dict[str, Any]) 105async def get_dependencies_health(): 106 """Get dependencies health status""" 107 global health_service 108 if not health_service: 109 raise HTTPException(status_code=503, detail="Health check service not initialized") 110 111 try: 112 health_data = health_service.get_health_data() 113 return JSONResponse(content={"dependencies": health_data.get("dependencies", {})}) 114 except Exception as e: 115 logger.error(f"Error getting dependencies health data: {str(e)}") 116 raise HTTPException(status_code=500, detail=str(e))
Get dependencies health status
@app.get('/health/pages', response_model=Dict[str, Any])
async def
get_pages_health():
118@app.get("/health/pages", response_model=Dict[str, Any]) 119async def get_pages_health(): 120 """Get Streamlit pages health status""" 121 global health_service 122 if not health_service: 123 raise HTTPException(status_code=503, detail="Health check service not initialized") 124 125 try: 126 health_data = health_service.get_health_data() 127 return JSONResponse(content={"streamlit_pages": health_data.get("streamlit_pages", {})}) 128 except Exception as e: 129 logger.error(f"Error getting pages health data: {str(e)}") 130 raise HTTPException(status_code=500, detail=str(e))
Get Streamlit pages health status
def
start_api_server( host: str = '0.0.0.0', port: int = 8000, config: str = 'health_check_config.json'):
132def start_api_server(host: str = "0.0.0.0", port: int = 8000, config: str = "health_check_config.json"): 133 """Start the FastAPI server""" 134 global config_file 135 config_file = config 136 uvicorn.run(app, host=host, port=port)
Start the FastAPI server
def
parse_args():
138def parse_args(): 139 """Parse command line arguments""" 140 parser = argparse.ArgumentParser(description="Streamlit Health Check API Server") 141 parser.add_argument("--host", default="0.0.0.0", help="Host address to bind") 142 parser.add_argument("--port", type=int, default=8000, help="Port to run the server on") 143 parser.add_argument( 144 "--config", 145 default="health_check_config.json", 146 help="Path to health check configuration file" 147 ) 148 return parser.parse_args()
Parse command line arguments