import logging import uuid import json from datetime import datetime from fastapi import Request, FastAPI from starlette.responses import JSONResponse from starlette.requests import HTTPConnection from typing import Callable, Awaitable from .context import g def add_request_routes(app: FastAPI): @app.middleware("http") async def before_request(request: Request, call_next: Callable[[Request], Awaitable[JSONResponse]]): # First try to get x-request-id from headers; generate a new one if missing trace_id = request.headers.get('X-Request-ID') if not trace_id: trace_id = "agent_" + str(uuid.uuid4()) # user_id = "unknown user_id" g.trace_id = trace_id # g.user_id = user_id response = await call_next(request) response.headers['X-Request-ID'] = g.trace_id return response class Formatter(logging.Formatter): def formatTime(self, record, datefmt=None): # Convert the timestamp to a datetime object dt = datetime.fromtimestamp(record.created) # Format the timestamp to millisecond precision if datefmt: s = dt.strftime(datefmt) # Drop the last three digits of microseconds, keeping only milliseconds s = s[:-3] else: # Drop the last three digits of microseconds, keeping only milliseconds s = dt.strftime("%H:%M:%S.%f")[:-3] return s def format(self, record): # Handle trace_id - use a default value when there is no request context if not hasattr(record, "trace_id"): try: record.trace_id = getattr(g, "trace_id") except LookupError: record.trace_id = "N/A" # Handle user_id # if not hasattr(record, "user_id"): # record.user_id = getattr(g, "user_id") # Format the timestamp record.timestamp = self.formatTime(record, self.datefmt) return super().format(record) # Register session context tracing here once def init_logger_once(name,level): logger = logging.getLogger(name) logger.setLevel(level=level) formatter = Formatter("%(timestamp)s | %(levelname)-5s | %(trace_id)s | %(name)s:%(funcName)s:%(lineno)s - %(message)s", datefmt='%Y-%m-%d %H:%M:%S.%f') handler = logging.StreamHandler() handler.setFormatter(formatter) logger.addHandler(handler) def init_with_fastapi(app,level=logging.INFO): init_logger_once("app",level) add_request_routes(app) def info(message, *args, **kwargs): app_logger = logging.getLogger('app') app_logger.info(message, *args, **kwargs) def debug(message, *args, **kwargs): app_logger = logging.getLogger('app') app_logger.debug(message, *args, **kwargs) def warning(message, *args, **kwargs): app_logger = logging.getLogger('app') app_logger.warning(message, *args, **kwargs) def error(message, *args, **kwargs): app_logger = logging.getLogger('app') app_logger.error(message, *args, **kwargs) def critical(message, *args, **kwargs): app_logger = logging.getLogger('app') app_logger.critical(message, *args, **kwargs)