Compare commits

...

2 Commits

Author SHA1 Message Date
herel b3426f7493 feat: add health check endpoint and suppress health check logs
Added a /health endpoint for application health monitoring

Implemented logging filter to suppress health check requests from logs

Updated Dockerfile and Kubernetes deployment to use the new health check endpoint

Incremented production image tag version
2025-09-04 00:42:53 +02:00
herel cadc34f797 chore: update dependencies and bump version to 1.0.5 2025-09-04 00:37:14 +02:00
5 changed files with 56 additions and 16 deletions
+3 -3
View File
@@ -1,5 +1,5 @@
# Use Python 3.11 slim image as base # Use Python 3.13 slim image as base
FROM python:3.11-slim FROM python:3.13-slim
# Set working directory # Set working directory
WORKDIR /app WORKDIR /app
@@ -42,7 +42,7 @@ EXPOSE 8000
# Health check # Health check
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \ HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/ || exit 1 CMD curl -f http://localhost:8000/health || exit 1
# Run the application # Run the application
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
+23 -6
View File
@@ -14,6 +14,17 @@ from fastapi.templating import Jinja2Templates
from pydantic import BaseModel from pydantic import BaseModel
from fpdf import FPDF from fpdf import FPDF
import logging
# Custom filter to exclude health check logs
class HealthCheckFilter(logging.Filter):
def filter(self, record: logging.LogRecord) -> bool:
# Exclude health check requests from logs
return "GET /health " not in record.getMessage()
# Apply the filter to Uvicorn's access logger
logging.getLogger("uvicorn.access").addFilter(HealthCheckFilter())
app = FastAPI() app = FastAPI()
# S3 Configuration # S3 Configuration
@@ -433,7 +444,7 @@ async def bulk_download(filenames: str = Form(...)):
try: try:
# Parse the JSON string to get the list of filenames # Parse the JSON string to get the list of filenames
filename_list = json.loads(filenames) filename_list = json.loads(filenames)
# Create a temporary zip file # Create a temporary zip file
with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp_zip: with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp_zip:
with zipfile.ZipFile(tmp_zip.name, "w") as zipf: with zipfile.ZipFile(tmp_zip.name, "w") as zipf:
@@ -441,16 +452,16 @@ async def bulk_download(filenames: str = Form(...)):
file_data = download_from_s3(S3_BUCKET_NAME, filename) file_data = download_from_s3(S3_BUCKET_NAME, filename)
if file_data: if file_data:
zipf.writestr(filename, file_data) zipf.writestr(filename, file_data)
# Read the zip file # Read the zip file
with open(tmp_zip.name, "rb") as f: with open(tmp_zip.name, "rb") as f:
zip_data = f.read() zip_data = f.read()
# Clean up temporary file # Clean up temporary file
import os import os
os.unlink(tmp_zip.name) os.unlink(tmp_zip.name)
# Return streaming response with zip data # Return streaming response with zip data
zip_filename = f"math_exercises_{len(filename_list)}_files.zip" zip_filename = f"math_exercises_{len(filename_list)}_files.zip"
return StreamingResponse( return StreamingResponse(
@@ -464,7 +475,13 @@ async def bulk_download(filenames: str = Form(...)):
return {"error": f"Error downloading files: {str(e)}"} return {"error": f"Error downloading files: {str(e)}"}
@app.get("/health")
async def health_check():
"""Health check endpoint that returns the status of the application"""
return {"status": "healthy"}
if __name__ == "__main__": if __name__ == "__main__":
import uvicorn import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000) uvicorn.run(app, host="0.0.0.0", port=8000)
+2 -2
View File
@@ -46,7 +46,7 @@ spec:
# Liveness probe # Liveness probe
livenessProbe: livenessProbe:
httpGet: httpGet:
path: / path: /health
port: 8000 port: 8000
initialDelaySeconds: 90 initialDelaySeconds: 90
periodSeconds: 10 periodSeconds: 10
@@ -55,7 +55,7 @@ spec:
# Readiness probe # Readiness probe
readinessProbe: readinessProbe:
httpGet: httpGet:
path: / path: /health
port: 8000 port: 8000
initialDelaySeconds: 5 initialDelaySeconds: 5
periodSeconds: 10 periodSeconds: 10
@@ -13,7 +13,7 @@ resources:
images: images:
- name: math-exercises - name: math-exercises
newName: harbor.cl1.parano.ch/library/math-exercice newName: harbor.cl1.parano.ch/library/math-exercice
newTag: 1.0.2 newTag: 1.0.7
# Production-specific labels # Production-specific labels
+27 -4
View File
@@ -1,9 +1,32 @@
annotated-types==0.7.0
anyio==4.10.0
boto3==1.26.160
boto3-stubs==1.40.23
botocore==1.29.165
botocore-stubs==1.40.23
click==8.2.1
defusedxml==0.7.1 defusedxml==0.7.1
fastapi==0.116.1
fonttools==4.59.2 fonttools==4.59.2
fpdf2==2.8.4 fpdf2==2.8.4
h11==0.16.0
idna==3.10
Jinja2==3.1.4
jmespath==1.0.1
MarkupSafe==3.0.2
pillow==11.3.0 pillow==11.3.0
fastapi==0.115.0 pydantic==2.11.7
uvicorn==0.30.6 pydantic_core==2.33.2
jinja2==3.1.4 python-dateutil==2.9.0.post0
python-multipart==0.0.9 python-multipart==0.0.9
boto3==1.34.0 s3transfer==0.6.2
six==1.17.0
sniffio==1.3.1
starlette==0.47.3
types-awscrt==0.27.6
types-fpdf2==2.8.4.20250822
types-s3transfer==0.13.1
typing-inspection==0.4.1
typing_extensions==4.15.0
urllib3==1.26.20
uvicorn==0.30.6