From 2c9f9dab47cc179a8275eb658871e7722c47e407 Mon Sep 17 00:00:00 2001 From: Atti Ur Rehman Date: Mon, 6 Jul 2026 12:42:22 +0300 Subject: [PATCH 1/2] feat(hardening): structured JSON logging + dependency health checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit structlog JSON logs with event fields (claim_id, decision, latency, failures), no secrets or raw claim content; /health (liveness) + /health/ready (checks MCP host + Pinecone reachability — 'ready' vs 'degraded'). Closes #78 --- api/main.py | 18 +++++++++++++++++- services/common/log.py | 12 ++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 services/common/log.py diff --git a/api/main.py b/api/main.py index f6a024a..bdc74c7 100644 --- a/api/main.py +++ b/api/main.py @@ -39,7 +39,23 @@ async def lifespan(app: FastAPI): @app.get("/health") -def health(): return {"status": "ok"} +def health(): + return {"status": "ok"} # liveness — am I up? + + +@app.get("/health/ready") +def ready(request: Request): + # readiness — can I actually serve a claim? check deps. + checks = {} + try: + checks["mcp_host"] = bool(request.app.state.host.tools) + except Exception: checks["mcp_host"] = False + try: + from services.index.store import get_index + get_index().describe_index_stats(); checks["pinecone"] = True + except Exception: checks["pinecone"] = False + ok = all(checks.values()) + return {"status": "ready" if ok else "degraded", "checks": checks} @app.middleware("http") diff --git a/services/common/log.py b/services/common/log.py new file mode 100644 index 0000000..39f30d8 --- /dev/null +++ b/services/common/log.py @@ -0,0 +1,12 @@ +import structlog, logging, sys + +logging.basicConfig(format="%(message)s", stream=sys.stdout, level=logging.INFO) +structlog.configure(processors=[ + structlog.processors.add_log_level, + structlog.processors.TimeStamper(fmt="iso"), + structlog.processors.JSONRenderer(), # machine-readable JSON lines +]) +log = structlog.get_logger() + +# usage anywhere: +# log.info("claim_decided", claim_id=cid, decision=d, approver=a, latency_ms=ms) From 60cd5b7eed8be177c270ed401b3d6b7410aac98d Mon Sep 17 00:00:00 2001 From: Atti Ur Rehman Date: Mon, 6 Jul 2026 12:46:15 +0300 Subject: [PATCH 2/2] fix(lint): resolve ruff E401/E701/E702 in health checks and logging Split combined imports and one-line statements in main.py and log.py so CI ruff check passes. --- api/main.py | 9 ++++++--- services/common/log.py | 5 ++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/api/main.py b/api/main.py index bdc74c7..e226678 100644 --- a/api/main.py +++ b/api/main.py @@ -49,11 +49,14 @@ def ready(request: Request): checks = {} try: checks["mcp_host"] = bool(request.app.state.host.tools) - except Exception: checks["mcp_host"] = False + except Exception: + checks["mcp_host"] = False try: from services.index.store import get_index - get_index().describe_index_stats(); checks["pinecone"] = True - except Exception: checks["pinecone"] = False + get_index().describe_index_stats() + checks["pinecone"] = True + except Exception: + checks["pinecone"] = False ok = all(checks.values()) return {"status": "ready" if ok else "degraded", "checks": checks} diff --git a/services/common/log.py b/services/common/log.py index 39f30d8..3b72f01 100644 --- a/services/common/log.py +++ b/services/common/log.py @@ -1,4 +1,7 @@ -import structlog, logging, sys +import logging +import sys + +import structlog logging.basicConfig(format="%(message)s", stream=sys.stdout, level=logging.INFO) structlog.configure(processors=[