Skip to content

Commit

Permalink
fix app when DB not configured => skip db logging
Browse files Browse the repository at this point in the history
  • Loading branch information
mki-c2c committed Feb 6, 2025
1 parent 4f838dd commit 3768d4c
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 28 deletions.
12 changes: 12 additions & 0 deletions backend/maelstro/common/types.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
from collections import namedtuple
from dataclasses import dataclass


Credentials = namedtuple("Credentials", ["login", "password"])

GsLayer = namedtuple("GsLayer", ["workspace_name", "layer_name"])
GsLayer.__str__ = lambda l: ":".join(el for el in l if el is not None) # type: ignore


@dataclass
class DbConfig:
host: str = "database"
port: int = 5432
login: str = "georchestra"
password: str = "georchestra"
database: str = "georchestra"
schema: str = "maelstro"
table: str = "logs"
8 changes: 7 additions & 1 deletion backend/maelstro/config/config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os
from typing import Any
import yaml
from maelstro.common.types import Credentials
from maelstro.common.types import Credentials, DbConfig


class ConfigError(Exception):
Expand Down Expand Up @@ -71,6 +71,12 @@ def get_destinations(self) -> list[dict[str, str]]:
for k, v in self.config["destinations"].items()
]

def has_db_logging(self) -> bool:
return "db_logging" in self.config

def get_db_config(self) -> DbConfig:
return DbConfig(**self.config.get("db_logging", {}))

def get_access_info(
self, is_src: bool, is_geonetwork: bool, instance_id: str
) -> dict[str, Any]:
Expand Down
13 changes: 7 additions & 6 deletions backend/maelstro/core/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
def setup_exception_handlers(app: FastAPI) -> None:
@app.exception_handler(HTTPException)
async def handle_fastapi_exception(request: Request, err: HTTPException) -> Any:
log_request_to_db(
err.status_code,
request,
log_handler.properties,
log_handler.get_json_responses(),
)
if "/copy/" in str(request.url):
log_request_to_db(
err.status_code,
request,
log_handler.properties,
log_handler.get_json_responses(),
)
return await http_exception_handler(request, err)

@app.exception_handler(GnException)
Expand Down
50 changes: 35 additions & 15 deletions backend/maelstro/logging/psql_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,17 @@
from sqlalchemy.orm import Session
from pydantic import TypeAdapter
from maelstro.config import app_config as config
from maelstro.common.types import DbConfig


class DbNotSetup(Exception):
pass


Base = declarative_base()


DB_CONFIG = {
DB_DEFAULT_CONFIG = {
"host": "database",
"port": 5432,
"login": "georchestra",
Expand All @@ -31,18 +36,13 @@
"schema": "maelstro",
"table": "logs",
}
DB_CONFIG.update(config.config.get("db_logging", {}))

SCHEMA = str(DB_CONFIG.get("schema"))
DB_URL = (
f"postgresql://{DB_CONFIG['login']}:{DB_CONFIG['password']}@{DB_CONFIG['host']}:"
f"{DB_CONFIG['port']}/{DB_CONFIG['database']}"
)
LOGGING_ACTIVE = config.has_db_logging()
DB_CONFIG = config.get_db_config()


class Log(Base): # type: ignore
__tablename__ = "logs"
__table_args__ = {"schema": SCHEMA}
__table_args__ = {"schema": DB_CONFIG.schema}

id = Column(Integer, primary_key=True, autoincrement=True)
start_time = Column(DateTime, nullable=False, default=datetime.now())
Expand Down Expand Up @@ -99,7 +99,17 @@ def log_request_to_db(
log_to_db(record)


def log_to_db(record: dict[str, Any]) -> None:
if not LOGGING_ACTIVE:
return
with Session(get_engine()) as session:
session.add(Log(**record))
session.commit()


def get_logs(size: int, offset: int, get_details: bool = False) -> list[dict[str, Any]]:
if not LOGGING_ACTIVE:
raise DbNotSetup
with Session(get_engine()) as session:
return [
row.to_dict(get_details)
Expand Down Expand Up @@ -128,6 +138,8 @@ def format_log(row: Log) -> str:


def format_logs(size: int, offset: int) -> list[str]:
if not LOGGING_ACTIVE:
raise DbNotSetup
with Session(get_engine()) as session:
return [
format_log(row)
Expand All @@ -138,19 +150,27 @@ def format_logs(size: int, offset: int) -> list[str]:
]


def log_to_db(record: dict[str, Any]) -> None:
with Session(get_engine()) as session:
session.add(Log(**record))
session.commit()
def build_url(db_config: DbConfig) -> str:
return (
f"postgresql://{db_config.login}:{db_config.password}@{db_config.host}:"
f"{db_config.port}/{db_config.database}"
)


def get_engine() -> Engine:
return create_engine(DB_URL)
return create_engine(build_url(DB_CONFIG))


def read_db_table(name: str = "logs") -> Table:
engine = get_engine()
return Table("logs", MetaData(schema=SCHEMA), autoload_with=engine)
return Table("logs", MetaData(schema=DB_CONFIG.schema), autoload_with=engine)


def setup_db_logging() -> None:
if not LOGGING_ACTIVE:
return
# this call is safe in init: by default sqlalchemy checks first if the table exists
create_db_table()


def create_db_table() -> None:
Expand Down
16 changes: 10 additions & 6 deletions backend/maelstro/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@
from maelstro.core import CloneDataset
from maelstro.core.operations import log_handler, setup_exception_handlers
from maelstro.logging.psql_logger import (
create_db_table,
setup_db_logging,
log_request_to_db,
get_logs,
format_logs,
DbNotSetup,
)
from maelstro.common.models import SearchQuery


app = FastAPI(root_path="/maelstro-backend")
setup_exception_handlers(app)
# this call is safe: by default sqlalchemy checks first if the table exists
create_db_table()
setup_db_logging()

app.state.health_countdown = 5

Expand Down Expand Up @@ -176,6 +176,7 @@ def put_dataset_copy(
"/logs",
responses={
200: {"content": {"text/plain": {}, "application/json": {}}},
500: {"content": {"text/plain": {}, "application/json": {}}},
},
)
def get_user_logs(
Expand All @@ -184,9 +185,12 @@ def get_user_logs(
get_details: bool = False,
accept: Annotated[str, Header(include_in_schema=False)] = "text/plain",
) -> Any:
if accept == "application/json":
return get_logs(size, offset, get_details)
return PlainTextResponse("\n".join(format_logs(size, offset)))
try:
if accept == "application/json":
return get_logs(size, offset, get_details)
return PlainTextResponse("\n".join(format_logs(size, offset)))
except DbNotSetup as err:
raise HTTPException(500, "DB logging not configured") from err


@app.get("/health")
Expand Down

0 comments on commit 3768d4c

Please sign in to comment.