How to log request body on ValidationError? #7441
-
|
The default log output when a ValidationError occurs looks like this pydantic.error_wrappers.ValidationError: 1 validation error for User
response -> email
value is not a valid email address (type=value_error.email)I would like to make these easier to debug by applying middleware to log the request body so I can see the actual value that did not match the schema. This is was my naive attempt: @app.exception_handler(ValidationError)
async def exception_handler(request, exc: ValidationError):
print(request.json())
raise excBut I ran into the same issue outlined in issue #394. And since this is for a |
Beta Was this translation helpful? Give feedback.
Replies: 10 comments 1 reply
-
|
How about this way? async def http422_error_handler(
_: Request, exc: Union[RequestValidationError, ValidationError]
) -> JSONResponse:
print(_.json())
return JSONResponse(
{"errors": exc.errors()}, status_code=HTTP_422_UNPROCESSABLE_ENTITY
)
app.add_exception_handler(ValidationError, http422_error_handler)
app.add_exception_handler(RequestValidationError, http422_error_handler)Add a middleware to catch the error and handle it. |
Beta Was this translation helpful? Give feedback.
-
|
See this: #853 |
Beta Was this translation helpful? Give feedback.
-
That looks like a nice addition, but I don't think it's applicable to my case. I'm trying to log Here's an example: class Article(BaseModel):
title: str
body: str
@app.get("/test", response_model=Article)
def test():
return "hello"pydantic.error_wrappers.ValidationError: 1 validation error for Article
response
value is not a valid dict (type=type_error.dict)I'd like to see what was actually returned so it's easier to debug why it doesn't match the expected |
Beta Was this translation helpful? Give feedback.
-
I don't think that does what I'm after. It gives an error for the print statement, and it returns a response to the client similar to @app.get("/test", response_model=Article)
def test():
return "hello"
async def http422_error_handler(_, exc):
print(_.json())
return JSONResponse(
{"errors": exc.errors()}
)
app.add_exception_handler(ValidationError, http422_error_handler)RuntimeWarning: coroutine 'Request.json' was never awaited
print(_.json())
RuntimeWarning: Enable tracemalloc to get the object allocation traceback |
Beta Was this translation helpful? Give feedback.
-
|
This is how I log. Use background tasks as you dont need to wait for logging before you return the response. async def http422_error_handler(
_: Request, exc: Union[RequestValidationError, ValidationError]
) -> JSONResponse:
# log the invalid message body into table
tasks = BackgroundTask(create_log_entry, data={"request": exc.body, "response": exc.errors()})
return JSONResponse(
{"errors": exc.errors()}, status_code=HTTP_422_UNPROCESSABLE_ENTITY, background=tasks
) |
Beta Was this translation helpful? Give feedback.
-
|
Okay I just realized the problem of consuming the request body when reading it isn't an issue inside error handlers, only middlware. Thanks for everyone's advice, this is what I ended up doing @app.exception_handler(ValidationError)
async def _(request: Request,
exc: ValidationError):
try:
print(await request.json())
except json.decoder.JSONDecodeError:
# Request had invalid or no body
pass
raise exc |
Beta Was this translation helpful? Give feedback.
-
|
Thanks for reporting back and closing the issue @adamerose! 🤓 For what is worth, I want to improve how those validation errors from response data are handled. Let's hope I can manage to do that soon. 🤞
|
Beta Was this translation helpful? Give feedback.
-
|
FTR Since this is a kind of works-for-me thread ;-) and the above did not work for me, I add here my slightly different one (which I found somewhere and modified it): from fastapi import exceptions
from fastapi.responses import JSONResponse
from pydantic import ValidationError
# ...
@app.exception_handler(exceptions.RequestValidationError)
@app.exception_handler(ValidationError)
async def validation_exception_handler(request, exc):
print(f"The client sent invalid data!: {exc}")
exc_json = json.loads(exc.json())
response = {"message": [], "data": None}
for error in exc_json:
response['message'].append(f"{error['loc']}: {error['msg']}")
return JSONResponse(response, status_code=422) |
Beta Was this translation helpful? Give feedback.
-
|
I like the The logging topic in general is a tricky one - we had to completely customize the uvicorn source code to log Header, Request and Response params. Since uvicorn changed completely, getting rid of atom() functions, we cannot update to uvicorn later than uvicorn==0.13.0 without completely re-desigining our logging. It would be great, if FastAPI allowed easy, standardized way to log everything at any point with custom formats:
` ` |
Beta Was this translation helpful? Give feedback.
-
|
Current version of docs suggests the following approach: @app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}),
)https://fastapi.tiangolo.com/tutorial/handling-errors/#use-the-requestvalidationerror-body |
Beta Was this translation helpful? Give feedback.
Current version of docs suggests the following approach:
https://fastapi.tiangolo.com/tutorial/handling-errors/#use-the-requestvalidationerror-body