Skip to content

Commit 381945f

Browse files
refactor: use context manager for request logging
1 parent c7fecb8 commit 381945f

File tree

11 files changed

+109
-6
lines changed

11 files changed

+109
-6
lines changed

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Always run `pytest {{cookiecutter.project_slug}}/tests` and ensure all tests pass after any code changes.
2+
- Run tests even if not explicitly requested.

{{cookiecutter.project_slug}}/.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ DEBUG=True
33
MODEL_PATH={{cookiecutter.machine_learn_model_path}}
44
MODEL_NAME={{cookiecutter.machine_learn_model_name}}
55
MEMOIZATION_FLAG=False
6+
DATABASE_URL=postgresql://postgres:postgres@db:5432/app

{{cookiecutter.project_slug}}/app/api/routes/predictor.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
from core.config import INPUT_EXAMPLE
66
from fastapi import APIRouter, HTTPException
77
from fastapi.concurrency import run_in_threadpool
8+
from loguru import logger
9+
from db import SessionLocal
10+
from models.log import RequestLog
811
from models.prediction import (
912
HealthResponse,
1013
MachineLearningDataInput,
@@ -44,10 +47,24 @@ async def predict(data_input: MachineLearningDataInput):
4447
except Exception as err:
4548
raise HTTPException(status_code=500, detail=f"Exception: {err}") from err
4649

47-
return MachineLearningResponse(
50+
response = MachineLearningResponse(
4851
prediction=prediction, prediction_label=prediction_label
4952
)
5053

54+
try:
55+
with SessionLocal() as db:
56+
db.add(
57+
RequestLog(
58+
request=json.dumps(data_input.model_dump()),
59+
response=json.dumps(response.model_dump()),
60+
)
61+
)
62+
db.commit()
63+
except Exception:
64+
logger.exception("failed to log request")
65+
66+
return response
67+
5168

5269
@router.get(
5370
"/health",

{{cookiecutter.project_slug}}/app/core/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
MIN_CONNECTIONS_COUNT: int = config("MIN_CONNECTIONS_COUNT", cast=int, default=10)
1616
SECRET_KEY: Secret = config("SECRET_KEY", cast=Secret, default="")
1717
MEMOIZATION_FLAG: bool = config("MEMOIZATION_FLAG", cast=bool, default=True)
18+
DATABASE_URL: str = config("DATABASE_URL", default="sqlite:///./app.db")
1819

1920
PROJECT_NAME: str = config("PROJECT_NAME", default="{{cookiecutter.project_name}}")
2021

{{cookiecutter.project_slug}}/app/core/events.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import joblib
44
from fastapi import FastAPI
5+
from core.config import MEMOIZATION_FLAG
6+
from db import Base, engine
57

68

79
def preload_model():
@@ -15,6 +17,8 @@ def preload_model():
1517

1618
def create_start_app_handler(app: FastAPI) -> Callable:
1719
def start_app() -> None:
18-
preload_model()
20+
if MEMOIZATION_FLAG:
21+
preload_model()
22+
Base.metadata.create_all(bind=engine)
1923

2024
return start_app
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from sqlalchemy import create_engine
2+
from sqlalchemy.orm import sessionmaker, declarative_base
3+
4+
from core.config import DATABASE_URL
5+
6+
engine = create_engine(DATABASE_URL)
7+
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
8+
Base = declarative_base()

{{cookiecutter.project_slug}}/app/main.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
def get_application() -> FastAPI:
88
application = FastAPI(title=PROJECT_NAME, debug=DEBUG, version=VERSION)
99
application.include_router(api_router, prefix=API_PREFIX)
10-
if MEMOIZATION_FLAG:
11-
application.add_event_handler("startup", create_start_app_handler(application))
10+
application.add_event_handler("startup", create_start_app_handler(application))
1211
return application
1312

1413

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from sqlalchemy import Column, Integer, Text
2+
3+
from db import Base
4+
5+
6+
class RequestLog(Base):
7+
__tablename__ = "request_logs"
8+
9+
id = Column(Integer, primary_key=True, index=True)
10+
request = Column(Text, nullable=False)
11+
response = Column(Text, nullable=False)

{{cookiecutter.project_slug}}/docker-compose.yml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,19 @@ services:
1313
command: uvicorn main:app --reload --host 0.0.0.0 --port 8080
1414
volumes:
1515
- ./app:/app/
16-
- ./ml/model/:/app/ml/model/
16+
- ./ml/model/:/app/ml/model/
17+
depends_on:
18+
- db
19+
db:
20+
image: postgres:16
21+
environment:
22+
POSTGRES_USER: postgres
23+
POSTGRES_PASSWORD: postgres
24+
POSTGRES_DB: app
25+
ports:
26+
- "5432:5432"
27+
volumes:
28+
- postgres_data:/var/lib/postgresql/data
29+
30+
volumes:
31+
postgres_data:

{{cookiecutter.project_slug}}/pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ dependencies = [
1515
"joblib>=1.2.0",
1616
"scikit-learn>=1.1.3",
1717
"pandas>=2.2.3",
18-
"httpx>=0.27.0"
18+
"httpx>=0.27.0",
19+
"sqlalchemy>=2.0.0",
20+
"psycopg2-binary>=2.9.0"
1921
]
2022

2123
[project.optional-dependencies]

0 commit comments

Comments
 (0)