1

Im trying to get the content-length of FastAPI response on the server side for logging purpose. Is this possible? Thanks.

@app.get("/foo")
async def foo(background_tasks: BackgroundTasks):

  data = {"foo": "foo"}

  response_content_length = get_content_length()

  background_tasks.add_task(log, response_content_length ) 

  return data
dasdasd
  • 1,971
  • 10
  • 45
  • 72
  • Related answers can be found [here](https://stackoverflow.com/a/73464007/17865804) and [here](https://stackoverflow.com/a/73443824/17865804). – Chris Jan 07 '23 at 17:34

2 Answers2

3

You can create your own route by inheriting APIRoute class, now you should be able to log everything, without repeating yourself.

from fastapi import FastAPI, Request, Response, Body, BackgroundTasks, APIRouter
from fastapi.routing import APIRoute

from typing import Callable, List



class ContextIncludedRoute(APIRoute):
    def get_route_handler(self) -> Callable:
        original_route_handler = super().get_route_handler()

        async def custom_route_handler(request: Request) -> Response:

            response: Response = await original_route_handler(request)

            content_length = response.headers["content-length"]
            print(content_length)
            
            return response

        return custom_route_handler


app = FastAPI()
router = APIRouter(route_class=ContextIncludedRoute)


@router.post("/dummy")
async def dummy():
    return {"foo":"foo"}


app.include_router(router)
Yagiz Degirmenci
  • 16,595
  • 7
  • 65
  • 85
  • Im talking about the response not the request, but its also helping for partly answer – dasdasd Nov 12 '20 at 17:00
  • Is there a way to get the value in the context of the same server call so Ill can know for which call it was? – dasdasd Nov 12 '20 at 19:06
  • Do you mean getting the `{"foo": "foo"}`? – Yagiz Degirmenci Nov 12 '20 at 19:40
  • I mean associate between the call for dummy() to the content_length so Ill can tight them together into a log line – dasdasd Nov 12 '20 at 20:55
  • do you mean the `response.body` what is associate i did not understand that part, what is the expected log line , maybe I can understand from that. – Yagiz Degirmenci Nov 12 '20 at 21:35
  • I want to know for each request to `dummy` what was the `content-length` related to this request. – dasdasd Nov 12 '20 at 22:32
  • As far as i understand you want to include the endpoint name to your logs, you can try `path = request.url.path` or `path = [route for route in request.scope['router'].routes if route.endpoint == request.scope['endpoint']][0].path` – Yagiz Degirmenci Nov 12 '20 at 23:02
  • My goal is to log what was the content length that was sent to the user. So if I have a path with `do_job` and the user pass parameter `x` and there another path with `do_job_2` and the user pass parameter `y` I want to log 1: what function was invoked. 2: what parameters passed. 3: what was the content length returned – dasdasd Nov 12 '20 at 23:12
  • 2
    check for `await request.body()` first if it is available add it, if it is not it should have Query, check for `request.query_params` and add that to the logs, for the path you can use my previous comment – Yagiz Degirmenci Nov 12 '20 at 23:23
0

You could try

from fastapi.responses import JSONResponse

@app.get("/foo")
async def foo(background_tasks: BackgroundTasks):
    data = {"foo": "foo"}
    response = JSONResponse(data)
    response_content_length = response.headers["content-length"]
    background_tasks.add_task(log, response_content_length ) 
    return response
jbernardes
  • 106
  • 5