44import logging .handlers
55import os
66import re
7+ import traceback
78from contextlib import asynccontextmanager
89from typing import Any , Optional
910
@@ -363,6 +364,11 @@ async def teardown_database(app: FastAPI):
363364 logger .info (app .state .tagstore_engine .pool .status ())
364365 logger .info ("Closed Tagstore connection." )
365366
367+ # Close Redis client if it exists
368+ if getattr (app .state , "redis_client" , None ):
369+ await app .state .redis_client .aclose ()
370+ logger .info ("Closed Redis connection." )
371+
366372
367373class ConceptsCacheServiceFastAPI (ConceptProtocol ):
368374 """FastAPI-compatible concepts cache service"""
@@ -407,6 +413,9 @@ async def setup_services(app: FastAPI):
407413 redis_client = None
408414 log_tag_access_prefix = None
409415
416+ # Store redis_client on app.state for cleanup during shutdown
417+ app .state .redis_client = redis_client
418+
410419 app .state .services = ServiceContainer (
411420 config = config ,
412421 db = app .state .db ,
@@ -465,6 +474,7 @@ async def setup_plugins(app: FastAPI):
465474 setup_gen = subcl .setup (setup_args )
466475 if hasattr (setup_gen , "__anext__" ):
467476 await setup_gen .__anext__ ()
477+ app .state .plugin_cleanup_generators .append (setup_gen )
468478
469479
470480@asynccontextmanager
@@ -478,21 +488,40 @@ async def lifespan(app: FastAPI):
478488 yield
479489
480490 # Shutdown
491+ # Close plugin cleanup generators
492+ for gen in getattr (app .state , "plugin_cleanup_generators" , []):
493+ try :
494+ await gen .aclose ()
495+ except Exception as e :
496+ logger .warning (f"Error closing plugin generator: { e } " )
497+
481498 await teardown_database (app )
482499
483500
501+ def _get_request_context (request : Request ) -> str :
502+ """Get user and URL context for error logging."""
503+ username = request .headers .get ("X-Consumer-Username" , "unknown" )
504+ return f"URL: { request .url } | User: { username } "
505+
506+
484507def _register_exception_handlers (app : FastAPI ):
485508 """Register common exception handlers on the app"""
486509
487510 @app .exception_handler (NotFoundException )
488511 async def not_found_handler (request : Request , exc : NotFoundException ):
512+ logger .warning (
513+ f"NotFoundException: { exc .get_user_msg ()} | { _get_request_context (request )} "
514+ )
489515 return JSONResponse (
490516 status_code = 404 ,
491517 content = {"detail" : exc .get_user_msg ()},
492518 )
493519
494520 @app .exception_handler (BadUserInputException )
495521 async def bad_input_handler (request : Request , exc : BadUserInputException ):
522+ logger .warning (
523+ f"BadUserInputException: { exc .get_user_msg ()} | { _get_request_context (request )} "
524+ )
496525 return JSONResponse (
497526 status_code = 400 ,
498527 content = {"detail" : exc .get_user_msg ()},
@@ -502,18 +531,31 @@ async def bad_input_handler(request: Request, exc: BadUserInputException):
502531 async def feature_not_available_handler (
503532 request : Request , exc : FeatureNotAvailableException
504533 ):
534+ logger .warning (
535+ f"FeatureNotAvailableException: { exc .get_user_msg ()} | { _get_request_context (request )} "
536+ )
505537 return JSONResponse (
506538 status_code = 400 ,
507539 content = {"detail" : exc .get_user_msg ()},
508540 )
509541
510542 @app .exception_handler (GsTimeoutException )
511543 async def timeout_handler (request : Request , exc : GsTimeoutException ):
544+ logger .warning (f"GsTimeoutException | { _get_request_context (request )} " )
512545 return JSONResponse (
513546 status_code = 408 ,
514547 content = {"detail" : "Request timeout" },
515548 )
516549
550+ @app .exception_handler (Exception )
551+ async def unhandled_exception_handler (request : Request , exc : Exception ):
552+ tb = "" .join (traceback .format_exception (type (exc ), exc , exc .__traceback__ ))
553+ logger .error (f"Unhandled exception | { _get_request_context (request )} \n { tb } " )
554+ return JSONResponse (
555+ status_code = 500 ,
556+ content = {"detail" : "Internal server error" },
557+ )
558+
517559
518560def _register_routers (app : FastAPI ):
519561 """Register all API routers on the app.
0 commit comments