diff --git a/backend/app/main.py b/backend/app/main.py index 459466d..3f89071 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -8,6 +8,8 @@ storefronts domain (SLICE-3) replaces the _storefront_for seam. """ from __future__ import annotations +import logging +import sys from contextlib import asynccontextmanager from typing import Any @@ -47,6 +49,23 @@ def _storefront_for(conn: psycopg.Connection, account: accounts.Account) -> dict return None +def _ensure_app_logging() -> None: + """Surface the app's own `ecomm.*` INFO logs on stderr (idempotent). + + uvicorn configures only its own loggers, leaving the root with no INFO handler — so + without this the `ecomm.mailer` line (PUC-10's local dev channel: the one-time code in + the backend log) would be swallowed. A dedicated handler with propagate=False keeps it + out of uvicorn's stream and avoids double-logging. + """ + lg = logging.getLogger("ecomm") + if not lg.handlers: + handler = logging.StreamHandler(sys.stderr) + handler.setFormatter(logging.Formatter("%(levelname)s:%(name)s: %(message)s")) + lg.addHandler(handler) + lg.setLevel(logging.INFO) + lg.propagate = False + + def _set_session_cookie(response: Response, account: accounts.Account) -> None: token = session_mod.sign({"account_id": account.id, "email": account.email}, config.session_secret()) response.set_cookie( @@ -60,6 +79,7 @@ def _set_session_cookie(response: Response, account: accounts.Account) -> None: def create_app(database_url: str | None = None) -> FastAPI: + _ensure_app_logging() dsn = database_url or config.database_url() @asynccontextmanager