@@ -31,6 +31,9 @@ def configure_logging(
3131 # Convert string level to logging level
3232 numeric_level = getattr (logging , level .upper (), logging .INFO )
3333
34+ # Set root logger level explicitly to ensure all child loggers respect it
35+ logging .getLogger ().setLevel (numeric_level )
36+
3437 if log_format == "json" :
3538 try :
3639 import structlog
@@ -59,41 +62,53 @@ def configure_logging(
5962 format = "%(message)s" ,
6063 stream = sys .stdout ,
6164 level = numeric_level ,
65+ force = True ,
6266 )
6367
6468 # Wrap standard library loggers with structlog
6569 structlog .stdlib .recreate_defaults (log_level = numeric_level )
6670
71+ # Configure uvicorn loggers for JSON format
72+ formatter = logging .Formatter ("%(message)s" )
73+ _configure_uvicorn_loggers (numeric_level , formatter )
74+
6775 except ImportError :
6876 # Fallback to text format if structlog not installed
6977 logging .warning ("structlog not installed, falling back to text format. Install with: pip install structlog" )
7078 _configure_text_logging (numeric_level )
7179 else :
7280 _configure_text_logging (numeric_level )
7381
74- # Apply healthcheck log filter to uvicorn access logger
75- _configure_access_log_filter ()
76-
7782
7883def _configure_text_logging (level : int ) -> None :
7984 """Configure standard text-based logging."""
8085 # Custom format with timestamp, level, name, and message
8186 log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
87+ date_format = "%Y-%m-%d %H:%M:%S"
8288
8389 logging .basicConfig (
8490 format = log_format ,
85- datefmt = "%Y-%m-%d %H:%M:%S" ,
91+ datefmt = date_format ,
8692 stream = sys .stdout ,
8793 level = level ,
94+ force = True ,
8895 )
8996
90- # Reduce noise from some verbose libraries
91- logging .getLogger ( "httpx" ). setLevel ( logging . WARNING )
92- logging . getLogger ( "httpcore" ). setLevel ( logging . WARNING )
93- logging . getLogger ( "asyncio" ). setLevel ( logging . WARNING )
97+ # Configure uvicorn loggers to use the same format
98+ formatter = logging .Formatter ( log_format , datefmt = date_format )
99+ _configure_uvicorn_loggers ( level , formatter )
100+
94101
102+ def _configure_uvicorn_loggers (level : int , formatter : logging .Formatter ) -> None :
103+ """Configure uvicorn loggers to use consistent formatting."""
104+ for logger_name in ("uvicorn" , "uvicorn.error" , "uvicorn.access" ):
105+ logger = logging .getLogger (logger_name )
106+ logger .handlers .clear ()
107+ handler = logging .StreamHandler (sys .stdout )
108+ handler .setFormatter (formatter )
109+ logger .addHandler (handler )
110+ logger .setLevel (level )
111+ logger .propagate = False
95112
96- def _configure_access_log_filter () -> None :
97- """Add filter to suppress healthcheck endpoint logs from uvicorn access logs."""
98- uvicorn_access_logger = logging .getLogger ("uvicorn.access" )
99- uvicorn_access_logger .addFilter (HealthcheckLogFilter ())
113+ # Add healthcheck filter to access logger
114+ logging .getLogger ("uvicorn.access" ).addFilter (HealthcheckLogFilter ())
0 commit comments