Coverage for kwai/api/app.py: 58%
62 statements
« prev ^ index » next coverage.py v7.3.0, created at 2023-09-05 17:55 +0000
« prev ^ index » next coverage.py v7.3.0, created at 2023-09-05 17:55 +0000
1"""Module that implements a factory method for a FastAPI application."""
2import os
3import sys
4import uuid
5from contextlib import asynccontextmanager
7from fastapi import FastAPI, Request, status
8from fastapi.middleware.cors import CORSMiddleware
9from fastapi.responses import JSONResponse
10from loguru import logger
12from kwai.api.v1.auth.api import api_router as auth_api_router
13from kwai.api.v1.portal.api import api_router as portal_api_router
14from kwai.api.v1.trainings.api import api_router as training_api_router
15from kwai.core.dependencies import container
16from kwai.core.settings import LoggerSettings, Settings, SettingsException
19@asynccontextmanager
20async def lifespan(app: FastAPI):
21 """Log the start/stop of the application."""
22 logger.info("kwai is starting")
23 yield
24 logger.warning("kwai has ended!")
27def configure_logger(settings: LoggerSettings):
28 """Configure the logger."""
29 try:
30 logger.remove(0) # Remove the default logger
31 except ValueError:
32 pass
34 def log_format(record):
35 """Change the format when a request_id is set in extra."""
36 if "request_id" in record["extra"]:
37 new_format = (
38 "{time} - {level} - ({extra[request_id]}) - {message}" + os.linesep
39 )
40 else:
41 new_format = "{time} - {level} - {message}" + os.linesep
42 if record["exception"]:
43 new_format += "{exception}" + os.linesep
44 return new_format
46 logger.add(
47 settings.file or sys.stderr,
48 format=log_format,
49 level=settings.level,
50 colorize=True,
51 retention=settings.retention,
52 rotation=settings.rotation,
53 backtrace=False,
54 diagnose=False,
55 )
58def create_app(settings: Settings | None = None) -> FastAPI:
59 """Create the FastAPI application.
61 Args:
62 settings: Settings to use in this application.
64 When settings is None (the default), the dependency container will
65 load the settings.
66 """
67 if settings is None:
68 try:
69 settings = container[Settings]
70 except SettingsException as ex:
71 logger.error(f"Could not load settings: {ex}")
72 sys.exit(0)
74 app = FastAPI(title="kwai", lifespan=lifespan)
76 # Setup CORS
77 if settings.cors:
78 app.add_middleware(
79 CORSMiddleware,
80 allow_origins=settings.cors.origins,
81 allow_credentials=True,
82 allow_methods=settings.cors.methods,
83 allow_headers=settings.cors.headers,
84 )
86 # Setup the logger.
87 if settings.logger:
88 configure_logger(settings.logger)
90 app.include_router(auth_api_router, prefix="/api/v1")
91 app.include_router(portal_api_router, prefix="/api/v1")
92 app.include_router(training_api_router, prefix="/api/v1")
94 @app.middleware("http")
95 async def log(request: Request, call_next):
96 """Middleware for logging the requests."""
97 request_id = str(uuid.uuid4())
98 with logger.contextualize(request_id=request_id):
99 logger.info(f"{request.url} - {request.method} - Request started")
101 response = None # Make pylint happy...
102 try:
103 response = await call_next(request)
104 except Exception as ex:
105 logger.error(f"{request.url} - Request failed: {ex}")
106 logger.exception(ex)
107 response = JSONResponse(
108 content={
109 "detail": str(ex),
110 },
111 status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
112 )
113 finally:
114 response.headers["X-Request-ID"] = request_id
115 logger.info(
116 f"{request.url} - {request.method } - Request ended: "
117 f"{response.status_code}"
118 )
120 return response
122 return app