-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.py
257 lines (222 loc) · 9.01 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# main.py
from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi import Response
from vlm_model.exceptions import (
VideoImportingError,
MediapipeHandlingError,
PromptImportingError,
VideoProcessingError,
ImageEncodingError
)
from vlm_model.routers.upload_video import router as upload_video_router
from vlm_model.routers.send_feedback import router as send_feedback_router
from vlm_model.routers.delete_files import router as delete_files_router
from pathlib import Path
from dotenv import load_dotenv
import json
import uvicorn
import logging
import logging.config
import uuid
import traceback
import os
import sentry_sdk
# Sentry Logging Integration
from sentry_sdk.integrations.logging import LoggingIntegration
# Context variables import
from vlm_model.context_var import request_id_ctx_var
# Middleware import
from vlm_model.middleware import RequestIDMiddleware
# 환경 변수 로드
load_dotenv()
# 환경 변수 가져오기
SENTRY_DSN = os.getenv("SENTRY_DSN")
TRACE_SAMPLE_RATE = float(os.getenv("TRACE_SAMPLE_RATE", 1.0)) # 기본값 1.0
# Sentry 로깅 통합 설정
sentry_logging = LoggingIntegration(
level=logging.INFO, # Sentry가 기록할 최소 로깅 레벨 (Breadcrumbs)
event_level=logging.ERROR # Sentry 이벤트로 기록할 최소 로깅 레벨
)
# Sentry 초기화
sentry_sdk.init(
dsn=SENTRY_DSN,
traces_sample_rate=TRACE_SAMPLE_RATE,
integrations=[sentry_logging],
_experiments={
"continuous_profiling_auto_start": True,
},
)
app = FastAPI()
# JSON 기반 로깅 설정 적용
logging_config_path = Path(__file__).resolve().parent / "logging_config.json" # 프로젝트 루트에 위치한 파일 경로
with open(logging_config_path, "r") as f:
logging_config = json.load(f)
# Sentry 핸들러 추가 대신 LoggingIntegration 사용
# 기존에 Sentry 핸들러가 있다면 제거
if "sentry" in logging_config["handlers"]:
del logging_config["handlers"]["sentry"]
for logger in logging_config["loggers"].values():
if "sentry" in logger.get("handlers", []):
logger["handlers"].remove("sentry")
logging.config.dictConfig(logging_config)
logger = logging.getLogger("vlm_model")
# 모든 출처를 허용하는 CORS 설정 (자격 증명 포함 불가)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 모든 출처 허용
allow_methods=["*"],
allow_headers=["*"],
allow_credentials=False, # credentials를 반드시 False로 설정
)
# Request ID 미들웨어 추가
app.add_middleware(RequestIDMiddleware)
# 라우터 포함
app.include_router(upload_video_router, prefix="/api/video", tags=["Video Upload"])
app.include_router(send_feedback_router, prefix="/api/video", tags=["Feedback Retrieval"])
app.include_router(delete_files_router, prefix="/api/video", tags=["File Deletion"])
# 정적 파일을 제공할 디렉토리 설정 (선택 사항)
app.mount("/static", StaticFiles(directory="storage/output_feedback_frame"), name="static")
# 루트 엔드포인트
@app.get("/")
def read_root():
logger.info("Root endpoint accessed")
return {"message": "Hello, Toby!"}
# 요청 로깅 미들웨어: 모든 요청과 응답을 로깅
@app.middleware("http")
async def log_requests(request: Request, call_next):
logger.debug("Request received")
try:
response = await call_next(request)
# responseTime 계산: 요청 처리 시간
# FastAPI 자체적으로 responseTime을 제공하지 않으므로, 직접 측정 필요
# 여기서는 단순 예시로 timestamp 차이를 사용
# 실제로는 start_time을 기록하고 response 후에 차이를 계산해야 함
response_time = response.headers.get("X-Response-Time", "unknown") # 필요 시 설정
logger.info("Response sent", extra={
"errorType": "",
"error_message": ""
})
return response
except Exception as e:
# 예외 발생 위치 추출
tb = traceback.extract_tb(e.__traceback__)
if tb:
filename, lineno, func, text = tb[-1] # 가장 마지막 스택 프레임
else:
filename, lineno, func, text = "unknown", 0, "unknown", "unknown"
logger.error("Error processing request", extra={
"errorType": type(e).__name__,
"error_message": str(e)
})
raise e
# 예외 처리 핸들러: VideoImportingError 발생 시
@app.exception_handler(VideoImportingError)
async def video_importing_exception_handler(request: Request, exc: VideoImportingError):
"""
VideoImportingError 발생 시 400 Bad Request 응답을 반환합니다.
"""
logger.error("Video importing error", extra={
"errorType": "VideoImportingError",
"error_message": exc.detail
})
return JSONResponse(
status_code=400, # 비디오 파일이 유효하지 않을 때 사용됩니다. 예를 들어, 잘못된 비디오 형식이나 손상된 파일을 업로드한 경우
content={"detail": exc.detail},
)
# 예외 처리 핸들러: MediapipeHandlingError 발생 시
@app.exception_handler(MediapipeHandlingError)
async def mediapipe_handling_exception_handler(request: Request, exc: MediapipeHandlingError):
"""
MediapipeHandlingError 발생 시 400 Bad Request 응답을 반환합니다.
"""
logger.error("Mediapipe Handling error", extra={
"errorType": "MediapipeHandlingError",
"error_message": exc.detail
})
return JSONResponse(
status_code=500, # Mediapipe로 CV 행동 탐지를 처리하는 중 예상치 못한 오류가 발생했을 때 사용
content={"detail": exc.detail},
)
# 예외 처리 핸들러: PromptImportingError 발생 시
@app.exception_handler(PromptImportingError)
async def prompt_importing_exception_handler(request: Request, exc: PromptImportingError):
"""
PromptImportingError 발생 시 400 Bad Request 응답을 반환합니다.
"""
logger.error("PromptImportingError", extra={
"errorType": "PromptImportingError",
"error_message": exc.detail
})
return JSONResponse(
status_code=400, # 프롬프트 파일의 형식이 잘못되었거나, 필수 데이터가 누락된 경우
content={"detail": exc.detail},
)
# 예외 처리 핸들러: VideoProcessingError 발생 시
@app.exception_handler(VideoProcessingError)
async def video_processing_exception_handler(request: Request, exc: VideoProcessingError):
"""
VideoProcessingError 발생 시 500 Internal Server Error 응답을 반환합니다.
"""
logger.error("Video processing error", extra={
"errorType": "VideoProcessingError",
"error_message": exc.detail
})
return JSONResponse(
status_code=500, # 서버 내부에서 비디오를 처리하는 중 예상치 못한 오류가 발생했을 때 사용
content={"detail": exc.detail},
)
# 예외 처리 핸들러: ImageEncodingError 발생 시
@app.exception_handler(ImageEncodingError)
async def image_encoding_exception_handler(request: Request, exc: ImageEncodingError):
"""
ImageEncodingError 발생 시 500 Internal Server Error 응답을 반환합니다.
"""
logger.error("Image encoding error", extra={
"errorType": "ImageEncodingError",
"error_message": exc.detail
})
return JSONResponse(
status_code=500, # 이미지 인코딩 과정에서 서버 내부적인 문제가 발생했을 때 사용
content={"detail": exc.detail},
)
# 예외 처리 핸들러: 일반 예외 발생 시
@app.exception_handler(Exception)
async def general_exception_handler(request: Request, exc: Exception):
"""
예상치 못한 예외 발생 시 500 Internal Server Error 응답을 반환합니다.
"""
logger.error("Unhandled exception", extra={
"errorType": type(exc).__name__,
"error_message": str(exc)
})
return JSONResponse(
status_code=500,
content={"detail": "서버 내부 오류가 발생했습니다."},
)
# Sentry 테스트 엔드포인트 추가
@app.get("/sentry-debug")
async def trigger_error():
division_by_zero = 1 / 0
# 테스트 엔드포인트 추가
@app.get("/test-logging")
def test_logging():
logger.debug("디버그 레벨 로그 테스트")
logger.info("정보 레벨 로그 테스트")
logger.error("오류 레벨 로그 테스트")
return {"message": "로깅 테스트 완료"}
# 서버 실행 (uvicorn.run()에서 log_config 지정)
if __name__ == "__main__":
# 현재 작업 디렉터리를 스크립트의 디렉터리로 설정
# os.chdir(os.path.dirname(os.path.abspath(__file__)))
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
reload=True,
log_config="logging_config.json" # 로깅 설정 파일 지정
)
# 실행 명령어 (터미널에서 실행 시):
# uvicorn main:app --host 0.0.0.0 --port 8000 --reload --log-config logging_config.json