77
88from schemas .rfc7807_model import RFC7807ResponseModel
99from .logger import logger , add_file_handler
10- from .exception import APIException , set_default_http_codes , DEFAULT_HTTP_CODES
10+ from .exception import APIException , set_default_http_codes , DEFAULT_HTTP_CODES , set_global_log
1111from custom_enum .enums import ExceptionCode , ExceptionStatus , BaseExceptionCode , ResponseFormat
1212from schemas .response_model import ResponseModel
1313from .response_utils import APIResponse
2727 "logger" ,
2828 "add_file_handler" ,
2929 "APIResponse" ,
30+ "set_global_log"
3031]
3132
3233
3334def register_exception_handlers (app : FastAPI ,
3435 response_format : ResponseFormat = ResponseFormat .RESPONSE_MODEL ,
3536 use_fallback_middleware : bool = True ,
37+ log : bool = True ,
3638 log_traceback : bool = True ,
3739 log_traceback_unhandled_exception : bool = True ,
3840 include_null_data_field_in_openapi : bool = True ):
@@ -59,6 +61,10 @@ def register_exception_handlers(app: FastAPI,
5961 If ResponseFormat.RESPONSE_DICTIONARY, returns plain dictionaries.
6062 use_fallback_middleware : bool, default=True
6163 If True, catches ALL unhandled exceptions (runtime errors, etc.) and logs them.
64+ log : bool, default=True
65+ If False, disables **all logging** (including APIException logs and unhandled exceptions).
66+ Overrides `log_exception` flags inside APIException. Useful for production environments
67+ where you want standardized responses but no logging output.
6268 log_traceback : bool, default=True
6369 If True, logs traceback for APIException errors.
6470 log_traceback_unhandled_exception : bool, default=True
@@ -107,7 +113,8 @@ def register_exception_handlers(app: FastAPI,
107113 register_exception_handlers(
108114 app,
109115 use_fallback_middleware=False, # Let FastAPI's default error pages handle unhandled exceptions
110- use_response_model=ResponseFormat.RESPONSE_MODEL # Return plain dict responses for speed
116+ log=False,
117+ response_format=ResponseFormat.RESPONSE_DICTIONARY # Return plain dict responses for speed
111118 )
112119 ```
113120
@@ -124,8 +131,9 @@ def register_exception_handlers(app: FastAPI,
124131 # Register with all customizations
125132 register_exception_handlers(
126133 app,
127- use_response_model =ResponseFormat.RESPONSE_MODEL,
134+ response_format =ResponseFormat.RESPONSE_MODEL,
128135 use_fallback_middleware=True,
136+ log=True,
129137 log_traceback=True,
130138 log_traceback_unhandled_exception=False, # Don't log tracebacks for runtime errors and/or
131139 # any uncaught errors(db error, 3rd party etc.) (just the message)
@@ -174,7 +182,7 @@ async def user_basic():
174182 # Register with all customizations
175183 register_exception_handlers(
176184 app,
177- use_response_model =ResponseFormat.RFC7807,
185+ response_format =ResponseFormat.RFC7807,
178186 use_fallback_middleware=True,
179187 log_traceback=True,
180188 log_traceback_unhandled_exception=False, # Don't log tracebacks for runtime errors and/or
@@ -203,116 +211,131 @@ def rfc7807():
203211 )
204212 ```
205213 """
214+ set_global_log (log )
206215
207216 @app .exception_handler (APIException )
208217 async def api_exception_handler (request : Request , exc : APIException ):
209- logger .error (f"Exception handled for path: { request .url .path } " )
210- logger .error (f"Method: { request .method } " )
211- logger .error (f"Client IP: { request .client .host if request .client else 'unknown' } " )
212- if log_traceback :
213- tb = traceback .format_exc ()
214- logger .error (f"Traceback:\n { tb } " )
218+ if log :
219+ logger .error (f"Exception handled for path: { request .url .path } " )
220+ logger .error (f"Method: { request .method } " )
221+ logger .error (f"Client IP: { request .client .host if request .client else 'unknown' } " )
222+ if log_traceback :
223+ tb = traceback .format_exc ()
224+ logger .error (f"Traceback:\n { tb } " )
215225
216226 if response_format == ResponseFormat .RESPONSE_MODEL :
217227 content = exc .to_response_model ().model_dump (exclude_none = False )
228+ media_type = "application/json"
218229 elif response_format == ResponseFormat .RFC7807 :
219230 content = exc .to_rfc7807_response ().model_dump (exclude_none = False )
231+ media_type = "application/problem+json"
220232 else :
221233 content = exc .to_response ()
234+ media_type = "application/json"
222235
223236 return JSONResponse (
224237 status_code = exc .http_status_code ,
225238 content = content ,
226- media_type = "application/problem+json" if response_format == ResponseFormat . RFC7807 else None
239+ media_type = media_type
227240 )
228241
229242 if use_fallback_middleware :
230243
231244 @app .exception_handler (RequestValidationError )
232- async def validation_exception_handler (request , exc ):
233- description = exc .errors ()[0 ]["msg" ].replace ("Value error, " , "" ) if exc .errors ()[0 ]["msg" ].startswith (
234- "Value error, " ) else exc .errors ()[0 ]["msg" ]
245+ async def validation_exception_handler (request : Request , exc : RequestValidationError ):
246+ try :
247+ first_err = exc .errors ()[0 ]
248+ msg = first_err .get ("msg" , "Validation error" )
249+ except Exception :
250+ msg = "Validation error"
251+
252+ # Mesajı enum'un description'ına gömelim ama "first error message" bilgisini de koruyalım
253+ err = ExceptionCode .VALIDATION_ERROR
254+ description = msg if msg else err .description
235255
236256 if response_format == ResponseFormat .RFC7807 :
237257 content = RFC7807ResponseModel (
238- title = "Validation Error" ,
239- description = description ,
258+ title = err .message ,
240259 status = HTTP_422_UNPROCESSABLE_ENTITY ,
260+ detail = description ,
261+ type = err .rfc7807_type ,
262+ instance = err .rfc7807_instance ,
241263 ).model_dump (exclude_none = False )
242- else :
264+ media_type = "application/problem+json"
265+ elif response_format == ResponseFormat .RESPONSE_MODEL :
243266 content = ResponseModel (
244267 data = None ,
245268 status = ExceptionStatus .FAIL ,
246- message = "Validation Error" ,
247- error_code = "VAL-422" ,
269+ message = err . message ,
270+ error_code = err . error_code ,
248271 description = description ,
249272 ).model_dump (exclude_none = False )
273+ media_type = "application/json"
274+ else :
275+ content = {
276+ "data" : None ,
277+ "status" : ExceptionStatus .FAIL .value ,
278+ "message" : err .message ,
279+ "error_code" : err .error_code ,
280+ "description" : description ,
281+ }
282+ media_type = "application/json"
250283
251284 return JSONResponse (
252285 status_code = HTTP_422_UNPROCESSABLE_ENTITY ,
253286 content = content ,
287+ media_type = media_type ,
254288 )
255289
256290 @app .middleware ("http" )
257- async def fallback_exception_middleware (request : Request ,
258- call_next : Callable ):
259- """
260- Middleware to catch unhandled exceptions and log them.
261- This middleware acts as a fallback for any unhandled exceptions that occur
262- during request processing.
263- It logs the exception details and returns a standardized error response.
264- This is useful for catching unexpected errors that are not explicitly handled
265- by the APIException handler.
266- Parameters:
267- ----------
268- -----------
269- request: Request
270- The incoming request object.
271- call_next: Callable
272- The next middleware or endpoint to call.
273- -----------
274-
275- Args:
276- request:
277- call_next:
278-
279- Returns:
280- JSONResponse: A standardized error response with status code 500.
281-
282- """
291+ async def fallback_exception_middleware (request : Request , call_next : Callable ):
283292 try :
284293 return await call_next (request )
285294 except Exception as e :
286- tb = traceback .format_exc ()
287- logger .error ("⚡ Unhandled Exception Fallback ⚡" )
288- logger .error (f"📌 Path: { request .url .path } " )
289- logger .error (f"📌 Method: { request .method } " )
290- logger .error (f"📌 Client IP: { request .client .host if request .client else 'unknown' } " )
291- logger .error (f"📌 Exception Args: { e .args } " )
292- logger .error (f"📌 Exception: { str (e )} " )
293-
294- if log_traceback_unhandled_exception :
295- logger .error (f"📌 Traceback:\n { tb } " )
295+ if log :
296+ tb = traceback .format_exc ()
297+ logger .error ("⚡ Unhandled Exception Fallback ⚡" )
298+ logger .error (f"📌 Path: { request .url .path } " )
299+ logger .error (f"📌 Method: { request .method } " )
300+ logger .error (f"📌 Client IP: { request .client .host if request .client else 'unknown' } " )
301+ logger .error (f"📌 Exception Args: { e .args } " )
302+ logger .error (f"📌 Exception: { str (e )} " )
303+ if log_traceback_unhandled_exception :
304+ logger .error (f"📌 Traceback:\n { tb } " )
305+
306+ err = ExceptionCode .INTERNAL_SERVER_ERROR
296307
297308 if response_format == ResponseFormat .RFC7807 :
298309 content = RFC7807ResponseModel (
299- title = "Validation Error" ,
300- description = "An unexpected error occurred. Please try again later." ,
301- status = HTTP_422_UNPROCESSABLE_ENTITY ,
310+ title = err .message ,
311+ status = 500 ,
312+ detail = err .description ,
313+ type = err .rfc7807_type ,
314+ instance = err .rfc7807_instance ,
302315 ).model_dump (exclude_none = False )
303- else :
316+ media_type = "application/problem+json"
317+ elif response_format == ResponseFormat .RESPONSE_MODEL :
304318 content = ResponseModel (
305319 data = None ,
306320 status = ExceptionStatus .FAIL ,
307- message = "Something went wrong." ,
308- error_code = "ISE-500" ,
309- description = "An unexpected error occurred. Please try again later."
321+ message = err . message ,
322+ error_code = err . error_code ,
323+ description = err . description ,
310324 ).model_dump (exclude_none = False )
325+ media_type = "application/json"
326+ else :
327+ content = {
328+ "data" : None ,
329+ "status" : ExceptionStatus .FAIL .value ,
330+ "message" : err .message ,
331+ "error_code" : err .error_code ,
332+ "description" : err .description ,
333+ }
334+ media_type = "application/json"
335+
336+ return JSONResponse (status_code = 500 , content = content , media_type = media_type )
337+
311338
312- return JSONResponse (
313- status_code = 500 ,
314- content = content
315- )
316339 if include_null_data_field_in_openapi :
317340 """
318341 Custom OpenAPI schema generator that injects `data: null` into example error responses
0 commit comments