Creating a Middleware in FastAPI for Logging Request and Responses

FastAPI is a modern, high-performance web framework for Python. One of its powerful features is the ability to customize behavior using middlewares. In this blog, we will walk through creating a middleware to log every request and response, along with useful metadata like the client’s IP address, HTTP method, endpoint, and status codes.

Why Use Middleware for Logging?

Middleware in FastAPI is a hook that gets executed for every request and response. This makes it an ideal place to:

  • Log Client Details: Capture IP addresses, user agents, etc.
  • Monitor API Usage: Track requests and responses.
  • Debugging and Auditing: Maintain a log of all interactions with your application.

Setting Up FastAPI

Before we begin, ensure you have FastAPI installed. If not, install it using:

pip <span>install </span>fastapi[all]
pip <span>install </span>fastapi[all]
pip install fastapi[all]

Enter fullscreen mode Exit fullscreen mode

Let’s create a basic FastAPI app:

<span>from</span> <span>fastapi</span> <span>import</span> <span>FastAPI</span>
<span>app</span> <span>=</span> <span>FastAPI</span><span>()</span>
<span>@app.get</span><span>(</span><span>"</span><span>/</span><span>"</span><span>)</span>
<span>async</span> <span>def</span> <span>read_root</span><span>():</span>
<span>return</span> <span>{</span><span>"</span><span>message</span><span>"</span><span>:</span> <span>"</span><span>Welcome to FastAPI!</span><span>"</span><span>}</span>
<span>from</span> <span>fastapi</span> <span>import</span> <span>FastAPI</span>
<span>app</span> <span>=</span> <span>FastAPI</span><span>()</span>

<span>@app.get</span><span>(</span><span>"</span><span>/</span><span>"</span><span>)</span>
<span>async</span> <span>def</span> <span>read_root</span><span>():</span>
    <span>return</span> <span>{</span><span>"</span><span>message</span><span>"</span><span>:</span> <span>"</span><span>Welcome to FastAPI!</span><span>"</span><span>}</span>
from fastapi import FastAPI app = FastAPI() @app.get("/") async def read_root(): return {"message": "Welcome to FastAPI!"}

Enter fullscreen mode Exit fullscreen mode

Creating the Middleware

Here’s how to create a middleware for logging.

1. Import Required Modules

We’ll use Python’s built-in logging module to log data into a file.

<span>import</span> <span>logging</span>
<span>from</span> <span>fastapi</span> <span>import</span> <span>FastAPI</span><span>,</span> <span>Request</span>
<span>from</span> <span>starlette.middleware.base</span> <span>import</span> <span>BaseHTTPMiddleware</span>
<span>import</span> <span>logging</span>
<span>from</span> <span>fastapi</span> <span>import</span> <span>FastAPI</span><span>,</span> <span>Request</span>
<span>from</span> <span>starlette.middleware.base</span> <span>import</span> <span>BaseHTTPMiddleware</span>
import logging from fastapi import FastAPI, Request from starlette.middleware.base import BaseHTTPMiddleware

Enter fullscreen mode Exit fullscreen mode

2. Configure Logging

Set up a log file and format the logs.

<span>logging</span><span>.</span><span>basicConfig</span><span>(</span>
<span>filename</span><span>=</span><span>"</span><span>app.log</span><span>"</span><span>,</span>
<span>level</span><span>=</span><span>logging</span><span>.</span><span>INFO</span><span>,</span>
<span>format</span><span>=</span><span>"</span><span>%(asctime)s - %(levelname)s - %(message)s</span><span>"</span>
<span>)</span>
<span>logger</span> <span>=</span> <span>logging</span><span>.</span><span>getLogger</span><span>(</span><span>__name__</span><span>)</span>
<span>logging</span><span>.</span><span>basicConfig</span><span>(</span>
    <span>filename</span><span>=</span><span>"</span><span>app.log</span><span>"</span><span>,</span>
    <span>level</span><span>=</span><span>logging</span><span>.</span><span>INFO</span><span>,</span>
    <span>format</span><span>=</span><span>"</span><span>%(asctime)s - %(levelname)s - %(message)s</span><span>"</span>
<span>)</span>
<span>logger</span> <span>=</span> <span>logging</span><span>.</span><span>getLogger</span><span>(</span><span>__name__</span><span>)</span>
logging.basicConfig( filename="app.log", level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" ) logger = logging.getLogger(__name__)

Enter fullscreen mode Exit fullscreen mode

3. Define the Middleware

Create a custom middleware class that processes every request and response.

<span>class</span> <span>LoggingMiddleware</span><span>(</span><span>BaseHTTPMiddleware</span><span>):</span>
<span>async</span> <span>def</span> <span>dispatch</span><span>(</span><span>self</span><span>,</span> <span>request</span><span>:</span> <span>Request</span><span>,</span> <span>call_next</span><span>):</span>
<span># Log request details </span> <span>client_ip</span> <span>=</span> <span>request</span><span>.</span><span>client</span><span>.</span><span>host</span>
<span>method</span> <span>=</span> <span>request</span><span>.</span><span>method</span>
<span>url</span> <span>=</span> <span>request</span><span>.</span><span>url</span><span>.</span><span>path</span>
<span>logger</span><span>.</span><span>info</span><span>(</span><span>f</span><span>"</span><span>Request: </span><span>{</span><span>method</span><span>}</span><span> </span><span>{</span><span>url</span><span>}</span><span> from </span><span>{</span><span>client_ip</span><span>}</span><span>"</span><span>)</span>
<span># Process the request </span> <span>response</span> <span>=</span> <span>await</span> <span>call_next</span><span>(</span><span>request</span><span>)</span>
<span># Log response details </span> <span>status_code</span> <span>=</span> <span>response</span><span>.</span><span>status_code</span>
<span>logger</span><span>.</span><span>info</span><span>(</span><span>f</span><span>"</span><span>Response: </span><span>{</span><span>method</span><span>}</span><span> </span><span>{</span><span>url</span><span>}</span><span> returned </span><span>{</span><span>status_code</span><span>}</span><span> to </span><span>{</span><span>client_ip</span><span>}</span><span>"</span><span>)</span>
<span>return</span> <span>response</span>
<span>class</span> <span>LoggingMiddleware</span><span>(</span><span>BaseHTTPMiddleware</span><span>):</span>
    <span>async</span> <span>def</span> <span>dispatch</span><span>(</span><span>self</span><span>,</span> <span>request</span><span>:</span> <span>Request</span><span>,</span> <span>call_next</span><span>):</span>
        <span># Log request details </span>        <span>client_ip</span> <span>=</span> <span>request</span><span>.</span><span>client</span><span>.</span><span>host</span>
        <span>method</span> <span>=</span> <span>request</span><span>.</span><span>method</span>
        <span>url</span> <span>=</span> <span>request</span><span>.</span><span>url</span><span>.</span><span>path</span>

        <span>logger</span><span>.</span><span>info</span><span>(</span><span>f</span><span>"</span><span>Request: </span><span>{</span><span>method</span><span>}</span><span> </span><span>{</span><span>url</span><span>}</span><span> from </span><span>{</span><span>client_ip</span><span>}</span><span>"</span><span>)</span>

        <span># Process the request </span>        <span>response</span> <span>=</span> <span>await</span> <span>call_next</span><span>(</span><span>request</span><span>)</span>

        <span># Log response details </span>        <span>status_code</span> <span>=</span> <span>response</span><span>.</span><span>status_code</span>
        <span>logger</span><span>.</span><span>info</span><span>(</span><span>f</span><span>"</span><span>Response: </span><span>{</span><span>method</span><span>}</span><span> </span><span>{</span><span>url</span><span>}</span><span> returned </span><span>{</span><span>status_code</span><span>}</span><span> to </span><span>{</span><span>client_ip</span><span>}</span><span>"</span><span>)</span>

        <span>return</span> <span>response</span>
class LoggingMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): # Log request details client_ip = request.client.host method = request.method url = request.url.path logger.info(f"Request: {method} {url} from {client_ip}") # Process the request response = await call_next(request) # Log response details status_code = response.status_code logger.info(f"Response: {method} {url} returned {status_code} to {client_ip}") return response

Enter fullscreen mode Exit fullscreen mode

4. Add Middleware to the App

Integrate the middleware with your FastAPI app:

<span>import</span> <span>logging</span>
<span>from</span> <span>fastapi</span> <span>import</span> <span>FastAPI</span><span>,</span> <span>Request</span>
<span>from</span> <span>starlette.middleware.base</span> <span>import</span> <span>BaseHTTPMiddleware</span>
<span># Configure logging </span><span>logging</span><span>.</span><span>basicConfig</span><span>(</span>
<span>filename</span><span>=</span><span>"</span><span>app.log</span><span>"</span><span>,</span>
<span>level</span><span>=</span><span>logging</span><span>.</span><span>INFO</span><span>,</span>
<span>format</span><span>=</span><span>"</span><span>%(asctime)s - %(levelname)s - %(message)s</span><span>"</span>
<span>)</span>
<span>logger</span> <span>=</span> <span>logging</span><span>.</span><span>getLogger</span><span>(</span><span>__name__</span><span>)</span>
<span># Create FastAPI app </span><span>app</span> <span>=</span> <span>FastAPI</span><span>()</span>
<span># Define logging middleware </span><span>class</span> <span>LoggingMiddleware</span><span>(</span><span>BaseHTTPMiddleware</span><span>):</span>
<span>async</span> <span>def</span> <span>dispatch</span><span>(</span><span>self</span><span>,</span> <span>request</span><span>:</span> <span>Request</span><span>,</span> <span>call_next</span><span>):</span>
<span># Log request details </span> <span>client_ip</span> <span>=</span> <span>request</span><span>.</span><span>client</span><span>.</span><span>host</span>
<span>method</span> <span>=</span> <span>request</span><span>.</span><span>method</span>
<span>url</span> <span>=</span> <span>request</span><span>.</span><span>url</span><span>.</span><span>path</span>
<span>logger</span><span>.</span><span>info</span><span>(</span><span>f</span><span>"</span><span>Request: </span><span>{</span><span>method</span><span>}</span><span> </span><span>{</span><span>url</span><span>}</span><span> from </span><span>{</span><span>client_ip</span><span>}</span><span>"</span><span>)</span>
<span># Process the request </span> <span>response</span> <span>=</span> <span>await</span> <span>call_next</span><span>(</span><span>request</span><span>)</span>
<span># Log response details </span> <span>status_code</span> <span>=</span> <span>response</span><span>.</span><span>status_code</span>
<span>logger</span><span>.</span><span>info</span><span>(</span><span>f</span><span>"</span><span>Response: </span><span>{</span><span>method</span><span>}</span><span> </span><span>{</span><span>url</span><span>}</span><span> returned </span><span>{</span><span>status_code</span><span>}</span><span> to </span><span>{</span><span>client_ip</span><span>}</span><span>"</span><span>)</span>
<span>return</span> <span>response</span>
<span># Add middleware to the app </span><span>app</span><span>.</span><span>add_middleware</span><span>(</span><span>LoggingMiddleware</span><span>)</span>
<span># Define a sample route </span><span>@app.get</span><span>(</span><span>"</span><span>/</span><span>"</span><span>)</span>
<span>async</span> <span>def</span> <span>read_root</span><span>():</span>
<span>return</span> <span>{</span><span>"</span><span>message</span><span>"</span><span>:</span> <span>"</span><span>Welcome to FastAPI!</span><span>"</span><span>}</span>
<span>@app.get</span><span>(</span><span>"</span><span>/items/{item_id}</span><span>"</span><span>)</span>
<span>async</span> <span>def</span> <span>read_item</span><span>(</span><span>item_id</span><span>:</span> <span>int</span><span>):</span>
<span>return</span> <span>{</span><span>"</span><span>item_id</span><span>"</span><span>:</span> <span>item_id</span><span>}</span>
<span>import</span> <span>logging</span>
<span>from</span> <span>fastapi</span> <span>import</span> <span>FastAPI</span><span>,</span> <span>Request</span>
<span>from</span> <span>starlette.middleware.base</span> <span>import</span> <span>BaseHTTPMiddleware</span>

<span># Configure logging </span><span>logging</span><span>.</span><span>basicConfig</span><span>(</span>
    <span>filename</span><span>=</span><span>"</span><span>app.log</span><span>"</span><span>,</span>
    <span>level</span><span>=</span><span>logging</span><span>.</span><span>INFO</span><span>,</span>
    <span>format</span><span>=</span><span>"</span><span>%(asctime)s - %(levelname)s - %(message)s</span><span>"</span>
<span>)</span>
<span>logger</span> <span>=</span> <span>logging</span><span>.</span><span>getLogger</span><span>(</span><span>__name__</span><span>)</span>

<span># Create FastAPI app </span><span>app</span> <span>=</span> <span>FastAPI</span><span>()</span>

<span># Define logging middleware </span><span>class</span> <span>LoggingMiddleware</span><span>(</span><span>BaseHTTPMiddleware</span><span>):</span>
    <span>async</span> <span>def</span> <span>dispatch</span><span>(</span><span>self</span><span>,</span> <span>request</span><span>:</span> <span>Request</span><span>,</span> <span>call_next</span><span>):</span>
        <span># Log request details </span>        <span>client_ip</span> <span>=</span> <span>request</span><span>.</span><span>client</span><span>.</span><span>host</span>
        <span>method</span> <span>=</span> <span>request</span><span>.</span><span>method</span>
        <span>url</span> <span>=</span> <span>request</span><span>.</span><span>url</span><span>.</span><span>path</span>

        <span>logger</span><span>.</span><span>info</span><span>(</span><span>f</span><span>"</span><span>Request: </span><span>{</span><span>method</span><span>}</span><span> </span><span>{</span><span>url</span><span>}</span><span> from </span><span>{</span><span>client_ip</span><span>}</span><span>"</span><span>)</span>

        <span># Process the request </span>        <span>response</span> <span>=</span> <span>await</span> <span>call_next</span><span>(</span><span>request</span><span>)</span>

        <span># Log response details </span>        <span>status_code</span> <span>=</span> <span>response</span><span>.</span><span>status_code</span>
        <span>logger</span><span>.</span><span>info</span><span>(</span><span>f</span><span>"</span><span>Response: </span><span>{</span><span>method</span><span>}</span><span> </span><span>{</span><span>url</span><span>}</span><span> returned </span><span>{</span><span>status_code</span><span>}</span><span> to </span><span>{</span><span>client_ip</span><span>}</span><span>"</span><span>)</span>

        <span>return</span> <span>response</span>

<span># Add middleware to the app </span><span>app</span><span>.</span><span>add_middleware</span><span>(</span><span>LoggingMiddleware</span><span>)</span>

<span># Define a sample route </span><span>@app.get</span><span>(</span><span>"</span><span>/</span><span>"</span><span>)</span>
<span>async</span> <span>def</span> <span>read_root</span><span>():</span>
    <span>return</span> <span>{</span><span>"</span><span>message</span><span>"</span><span>:</span> <span>"</span><span>Welcome to FastAPI!</span><span>"</span><span>}</span>

<span>@app.get</span><span>(</span><span>"</span><span>/items/{item_id}</span><span>"</span><span>)</span>
<span>async</span> <span>def</span> <span>read_item</span><span>(</span><span>item_id</span><span>:</span> <span>int</span><span>):</span>
    <span>return</span> <span>{</span><span>"</span><span>item_id</span><span>"</span><span>:</span> <span>item_id</span><span>}</span>
import logging from fastapi import FastAPI, Request from starlette.middleware.base import BaseHTTPMiddleware # Configure logging logging.basicConfig( filename="app.log", level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" ) logger = logging.getLogger(__name__) # Create FastAPI app app = FastAPI() # Define logging middleware class LoggingMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): # Log request details client_ip = request.client.host method = request.method url = request.url.path logger.info(f"Request: {method} {url} from {client_ip}") # Process the request response = await call_next(request) # Log response details status_code = response.status_code logger.info(f"Response: {method} {url} returned {status_code} to {client_ip}") return response # Add middleware to the app app.add_middleware(LoggingMiddleware) # Define a sample route @app.get("/") async def read_root(): return {"message": "Welcome to FastAPI!"} @app.get("/items/{item_id}") async def read_item(item_id: int): return {"item_id": item_id}

Enter fullscreen mode Exit fullscreen mode

Testing the Middleware

1. Run the FastAPI App: Start the application by running the script:

uvicorn app:app <span>--reload</span>
uvicorn app:app <span>--reload</span>
uvicorn app:app --reload

Enter fullscreen mode Exit fullscreen mode

2. Send Requests: Use tools like curl, Postman, or a browser to interact with the API. For example:

curl http://127.0.0.1:8000/
curl http://127.0.0.1:8000/items/42
curl http://127.0.0.1:8000/
curl http://127.0.0.1:8000/items/42
curl http://127.0.0.1:8000/ curl http://127.0.0.1:8000/items/42

Enter fullscreen mode Exit fullscreen mode

3. Check the Logs: Open the app.log file to see the logged requests and responses. It will look something like this:

2024-12-04 12:05:00,001 - INFO - Request: GET / from 127.0.0.1
2024-12-04 12:05:00,002 - INFO - Response: GET / returned 200 to 127.0.0.1
2024-12-04 12:06:00,003 - INFO - Request: GET /items/42 from 127.0.0.1
2024-12-04 12:06:00,004 - INFO - Response: GET /items/42 returned 200 to 127.0.0.1
2024-12-04 12:05:00,001 - INFO - Request: GET / from 127.0.0.1
2024-12-04 12:05:00,002 - INFO - Response: GET / returned 200 to 127.0.0.1
2024-12-04 12:06:00,003 - INFO - Request: GET /items/42 from 127.0.0.1
2024-12-04 12:06:00,004 - INFO - Response: GET /items/42 returned 200 to 127.0.0.1
2024-12-04 12:05:00,001 - INFO - Request: GET / from 127.0.0.1 2024-12-04 12:05:00,002 - INFO - Response: GET / returned 200 to 127.0.0.1 2024-12-04 12:06:00,003 - INFO - Request: GET /items/42 from 127.0.0.1 2024-12-04 12:06:00,004 - INFO - Response: GET /items/42 returned 200 to 127.0.0.1

Enter fullscreen mode Exit fullscreen mode

Conclusion

With this middleware, you can easily log all API requests and responses in your FastAPI application. This is incredibly useful for debugging, auditing, and monitoring the usage of your APIs. You can further customize the middleware to log headers, request bodies, or response payloads if needed.

Logging is an essential part of any production-grade application, and FastAPI’s middleware support makes it easy to implement.

Creating a Middleware in FastAPI for Logging Requests and Responses | Python & Rest

Learn how to create a custom middleware in FastAPI to log all incoming requests and outgoing responses, including client IP and other details, into a log file.

blog.python.rest

Follow me on LinkedIn

原文链接:Creating a Middleware in FastAPI for Logging Request and Responses

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
The greatest test of courage on earth is to bear defeat without losing heart.
世界上对勇气的最大考验是忍受失败而不丧失信心
评论 抢沙发

请登录后发表评论

    暂无评论内容