Imagine there is a code like that:
app.py
import logging
from contextvars import ContextVar, Token
from typing import Any
from uuid import uuid4
from fastapi.requests import Request
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.responses import Response
_logging_context: ContextVar[dict[Any, Any]] = ContextVar(
"logging_context"
)
from fastapi import FastAPI
class LoggingMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
request_id = request.headers.get("X-Request-ID", uuid4().hex)
data = {
"request_id": request_id,
"method": request.method,
"ip": request.client.host,
"url": str(request.url),
}
token: Token = _logging_context.set(data)
response = await call_next(request)
_logging_context.reset(token)
response.headers["X-Request-ID"] = request_id
return response
default_record_factory = logging.getLogRecordFactory()
def record_factory(*args, **kwargs):
record = default_record_factory(*args, **kwargs)
context = _logging_context.get({})
record.request_id = context.get("request_id")
record.method = context.get("method")
record.ip = context.get("ip")
record.url = context.get("url")
return record
def my_function():
logger = logging.getLogger(__name__)
logger.info("Hello world")
logging.setLogRecordFactory(record_factory)
logging.basicConfig(level=logging.DEBUG, format="%(message)s %(ip)s")
app = FastAPI()
app.add_middleware(LoggingMiddleware)
@app.get("/")
async def test(request: Request):
my_function()
When I run FastAPI application everything works as expected and I get a log:
Hello world 127.0.0.1
But if I run into shell:
>> python
from app import my_function
my_function()
then I get a log:
Hello world None
It is expected but I'd like it is not included into log. I don't want None in log. Is it possible to exclude %(ip)s if the code is not in request-response cycle?