상태 JSON 파일의 요청 통계 및 가동 시간을 업데이트하고, 새로운 모델 정보 API를 추가하였습니다. 모니터링 포트를 8080에서 8888로 변경하였으며, .gitignore 파일에 로그 파일 패턴을 추가하였습니다. 스크립트에서 가상환경 경로 자동 감지 기능을 개선하였습니다.

This commit is contained in:
AGX 2025-08-29 01:42:13 +09:00
parent 0cb1596be1
commit 29f0de65e1
15 changed files with 573 additions and 1608 deletions

6
.gitignore vendored
View File

@ -1,3 +1,4 @@
logs/
bin/ bin/
include/ include/
lib/ lib/
@ -17,6 +18,7 @@ pyvenv.cfg
*.pyzw *.pyzw
*.pyzwz *.pyzwz
*.pyzwzw *.pyzwzw
logs/
status.json status.json
*.logs
*.log
*.pid

View File

@ -352,6 +352,47 @@ async def remove_background(
@router.get("/api/v1/model")
async def get_model_info():
"""모델 정보 반환 (클라이언트 헬스체크 호환)"""
try:
# 현재 사용 가능한 모델 목록
models = [
{
"name": "simple-lama",
"type": "inpainting",
"status": "available",
"description": "Simple LAMA 인페인팅 모델"
},
{
"name": "migan",
"type": "inpainting",
"status": "available",
"description": "MIGAN 인페인팅 모델"
},
{
"name": "rembg",
"type": "rembg",
"status": "available",
"description": "Rembg 배경 제거 모델"
}
]
return {
"success": True,
"models": models,
"server_status": "running",
"version": "1.0.0",
"device": "cuda" if settings.IS_JETSON else "cuda",
"is_jetson": settings.IS_JETSON,
"timestamp": datetime.now().isoformat()
}
except Exception as e:
logger.error(f"모델 정보 조회 실패: {e}")
raise HTTPException(status_code=500, detail=f"모델 정보 조회 실패: {str(e)}")
@router.get("/api/v1/samplers") @router.get("/api/v1/samplers")
async def get_samplers(): async def get_samplers():
"""사용 가능한 샘플러 목록 반환 (iopaint 호환)""" """사용 가능한 샘플러 목록 반환 (iopaint 호환)"""

View File

@ -54,7 +54,7 @@ class Settings(BaseSettings):
# Monitoring # Monitoring
ENABLE_MONITORING: bool = True ENABLE_MONITORING: bool = True
MONITORING_PORT: int = 8080 MONITORING_PORT: int = 8888
# Jetson performance settings # Jetson performance settings
JETSON_GPU_FREQ: int = 1200 # MHz JETSON_GPU_FREQ: int = 1200 # MHz

View File

@ -16,6 +16,7 @@ from fastapi.staticfiles import StaticFiles
from fastapi.responses import HTMLResponse from fastapi.responses import HTMLResponse
import uvicorn import uvicorn
from fastapi import APIRouter, Request from fastapi import APIRouter, Request
import websockets.exceptions
from ..core.worker_manager import worker_manager from ..core.worker_manager import worker_manager
from ..core.session_pool import session_pool from ..core.session_pool import session_pool
@ -571,6 +572,50 @@ HTML_TEMPLATE = """
font-size: 0.9em; font-size: 0.9em;
margin-top: 20px; margin-top: 20px;
} }
.status {
padding: 2px 8px;
border-radius: 4px;
font-weight: bold;
font-size: 0.8em;
}
.status.connected {
background-color: #d4edda;
color: #155724;
}
.status.connecting {
background-color: #fff3cd;
color: #856404;
}
.status.reconnecting {
background-color: #f8d7da;
color: #721c24;
animation: pulse 1s infinite;
}
.status.disconnected {
background-color: #f8d7da;
color: #721c24;
}
.status.error {
background-color: #f5c6cb;
color: #491217;
}
.status.failed {
background-color: #d1ecf1;
color: #0c5460;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
</style> </style>
</head> </head>
<body> <body>
@ -793,7 +838,8 @@ HTML_TEMPLATE = """
</div> </div>
<div class="refresh-time"> <div class="refresh-time">
마지막 업데이트: <span id="last-update">-</span> 마지막 업데이트: <span id="last-update">-</span> |
연결 상태: <span id="connection-status" class="status connecting">연결 ...</span>
</div> </div>
</div> </div>
@ -852,20 +898,100 @@ HTML_TEMPLATE = """
} }
}); });
// WebSocket 연결 // WebSocket 연결 관리
const ws = new WebSocket(`ws://${window.location.host}/ws`); let ws;
let reconnectAttempts = 0;
const maxReconnectAttempts = 5;
const reconnectInterval = 3000; // 3
let lastHeartbeat = Date.now();
const heartbeatTimeout = 10000; // 10 타임아웃
ws.onmessage = function(event) { function connectWebSocket() {
const data = JSON.parse(event.data); try {
updateDashboard(data); ws = new WebSocket(`ws://${window.location.host}/ws`);
};
ws.onopen = function() {
console.log('WebSocket 연결이 성공했습니다.');
reconnectAttempts = 0;
// 연결 상태 표시 업데이트
document.getElementById('connection-status').textContent = '연결됨';
document.getElementById('connection-status').className = 'status connected';
};
ws.onmessage = function(event) {
try {
const data = JSON.parse(event.data);
// heartbeat 체크
if (data.heartbeat) {
lastHeartbeat = Date.now();
}
updateDashboard(data);
} catch (e) {
console.error('데이터 파싱 오류:', e);
}
};
ws.onclose = function(event) {
console.log(`WebSocket 연결이 종료되었습니다. 코드: ${event.code}, 이유: ${event.reason}`);
document.getElementById('connection-status').textContent = '연결 끊어짐';
document.getElementById('connection-status').className = 'status disconnected';
// 자동 재연결 시도
if (reconnectAttempts < maxReconnectAttempts) {
reconnectAttempts++;
console.log(`재연결 시도 ${reconnectAttempts}/${maxReconnectAttempts} in ${reconnectInterval/1000}...`);
document.getElementById('connection-status').textContent = `재연결 ... (${reconnectAttempts}/${maxReconnectAttempts})`;
document.getElementById('connection-status').className = 'status reconnecting';
setTimeout(connectWebSocket, reconnectInterval);
} else {
console.log('최대 재연결 시도 횟수를 초과했습니다. 페이지를 새로고침합니다.');
document.getElementById('connection-status').textContent = '연결 실패';
document.getElementById('connection-status').className = 'status failed';
setTimeout(() => {
location.reload();
}, 5000);
}
};
ws.onerror = function(error) {
console.error('WebSocket 오류:', error);
document.getElementById('connection-status').textContent = '연결 오류';
document.getElementById('connection-status').className = 'status error';
};
} catch (error) {
console.error('WebSocket 연결 생성 오류:', error);
document.getElementById('connection-status').textContent = '연결 생성 실패';
document.getElementById('connection-status').className = 'status error';
}
}
ws.onclose = function() { // 페이지 가시성 변경 감지
console.log('WebSocket 연결이 종료되었습니다.'); document.addEventListener('visibilitychange', function() {
setTimeout(() => { if (document.visibilityState === 'visible') {
location.reload(); // 탭이 다시 활성화되면 연결 상태 확인
}, 5000); if (ws.readyState !== WebSocket.OPEN) {
}; console.log('탭이 활성화되어 WebSocket 재연결을 시도합니다.');
connectWebSocket();
}
}
});
// 초기 연결
connectWebSocket();
// heartbeat 모니터링
setInterval(function() {
const now = Date.now();
if (now - lastHeartbeat > heartbeatTimeout && ws && ws.readyState === WebSocket.OPEN) {
console.log('heartbeat 타임아웃 - 연결을 다시 시도합니다.');
ws.close();
}
}, 5000); // 5초마다 체크
function updateDashboard(data) { function updateDashboard(data) {
console.log("받은 데이터:", data); // 디버깅용 로그 추가 console.log("받은 데이터:", data); // 디버깅용 로그 추가
@ -1199,17 +1325,39 @@ async def websocket_endpoint(websocket: WebSocket):
"""WebSocket 연결을 처리합니다.""" """WebSocket 연결을 처리합니다."""
await websocket.accept() await websocket.accept()
connected_clients.append(websocket) connected_clients.append(websocket)
logger.info(f"WebSocket 클라이언트 연결됨: {websocket.client}")
try: try:
while True: while True:
# 주기적으로 데이터 전송 # 주기적으로 데이터 전송
data = await monitoring_data.collect_data() data = await monitoring_data.collect_data()
await websocket.send_json(data)
# heartbeat 메시지 추가
data['heartbeat'] = time.time()
data['server_status'] = 'running'
try:
await websocket.send_json(data)
except (websockets.exceptions.ConnectionClosedOK,
websockets.exceptions.ConnectionClosedError,
RuntimeError) as e:
logger.info(f"WebSocket 연결이 끊어짐: {e}")
break
except Exception as e:
logger.error(f"데이터 전송 오류: {e}")
break
await asyncio.sleep(2) # 2초마다 업데이트 await asyncio.sleep(2) # 2초마다 업데이트
except WebSocketDisconnect: except WebSocketDisconnect:
connected_clients.remove(websocket) logger.info("클라이언트가 연결을 끊음")
logger.info("클라이언트 연결 해제") except Exception as e:
logger.error(f"WebSocket 오류: {e}")
finally:
# 연결된 클라이언트 목록에서 제거
if websocket in connected_clients:
connected_clients.remove(websocket)
logger.info(f"WebSocket 클라이언트 연결 해제됨: {websocket.client}")
async def broadcast_data(): async def broadcast_data():
@ -1225,7 +1373,11 @@ async def broadcast_data():
for client in connected_clients: for client in connected_clients:
try: try:
await client.send_text(message) await client.send_text(message)
except Exception: except (websockets.exceptions.ConnectionClosedOK,
websockets.exceptions.ConnectionClosedError,
RuntimeError,
Exception) as e:
logger.debug(f"브로드캐스트 중 클라이언트 연결 끊어짐: {e}")
disconnected.append(client) disconnected.append(client)
for client in disconnected: for client in disconnected:

File diff suppressed because it is too large Load Diff

View File

@ -1 +1 @@
1312285 1328887

View File

@ -1,10 +1,6 @@
INFO: Started server process [1312316] INFO: Started server process [1328918]
INFO: Waiting for application startup. INFO: Waiting for application startup.
Fan control not available Fan control not available
INFO: Application startup complete. INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit) INFO: Uvicorn running on http://0.0.0.0:8888 (Press CTRL+C to quit)
INFO: 127.0.0.1:35348 - "GET /api/simple HTTP/1.1" 200 OK INFO: 127.0.0.1:42340 - "GET /api/simple HTTP/1.1" 200 OK
INFO: 127.0.0.1:37590 - "GET / HTTP/1.1" 200 OK
INFO: 127.0.0.1:37590 - "GET /favicon.ico HTTP/1.1" 404 Not Found
INFO: ('127.0.0.1', 37632) - "WebSocket /ws" [accepted]
INFO: connection open

View File

@ -1 +1 @@
1312316 1328918

View File

@ -30,9 +30,44 @@ log_error() {
echo -e "${RED}[ERROR]${NC} $1" echo -e "${RED}[ERROR]${NC} $1"
} }
# 기본 설정 # 기본 설정 - 동적 경로 처리
PROJECT_ROOT="/home/ckh08045/work/inpaintServer" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VENV_PATH="$PROJECT_ROOT" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
# 가상환경 경로 자동 감지
detect_venv_path() {
local possible_paths=(
"$PROJECT_ROOT/venv" # 일반적인 venv 경로
"$PROJECT_ROOT/.venv" # 숨김 venv 경로
"$PROJECT_ROOT/env" # env 경로
"$PROJECT_ROOT/.env" # 숨김 env 경로
"$PROJECT_ROOT" # 프로젝트 루트 (Jetson 방식)
)
for path in "${possible_paths[@]}"; do
if [ -f "$path/bin/python" ] || [ -f "$path/bin/python3" ]; then
VENV_PATH="$path"
log_info "가상환경 경로 감지: $VENV_PATH"
return 0
fi
done
# 현재 활성화된 가상환경 확인
if [ -n "$VIRTUAL_ENV" ]; then
VENV_PATH="$VIRTUAL_ENV"
log_info "활성화된 가상환경 사용: $VENV_PATH"
return 0
fi
# 가상환경을 찾지 못한 경우 시스템별 기본값 사용
if [ "$(uname -m)" = "aarch64" ] && uname -a | grep -q "tegra"; then
VENV_PATH="$PROJECT_ROOT"
log_warning "가상환경을 찾을 수 없어 Jetson 기본값 사용: $VENV_PATH"
else
VENV_PATH="$PROJECT_ROOT/venv"
log_warning "가상환경을 찾을 수 없어 x86 기본값 사용: $VENV_PATH"
fi
}
# 시스템 감지 # 시스템 감지
detect_system() { detect_system() {
@ -687,6 +722,7 @@ main() {
log_info "인페인팅 서버 의존성 설치 시작" log_info "인페인팅 서버 의존성 설치 시작"
detect_system detect_system
detect_venv_path
check_system_requirements check_system_requirements
check_cuda_installation check_cuda_installation
activate_venv activate_venv

View File

@ -176,8 +176,14 @@ install_dependencies() {
fi fi
# requirements.txt 확인 # requirements.txt 확인
if [ ! -f "requirements.txt" ]; then if [ "$SYSTEM_TYPE" = "x86" ]; then
log_error "requirements.txt 파일을 찾을 수 없습니다" REQ_FILE="requirements_x86.txt"
else
REQ_FILE="requirements.txt"
fi
if [ ! -f "$REQ_FILE" ]; then
log_error "$REQ_FILE 파일을 찾을 수 없습니다"
exit 1 exit 1
fi fi
@ -188,7 +194,7 @@ install_dependencies() {
fi fi
# 기본 패키지 설치 # 기본 패키지 설치
pip install -r requirements.txt pip install -r "$REQ_FILE"
log_success "의존성 설치 완료" log_success "의존성 설치 완료"
} }
@ -282,7 +288,7 @@ print_completion_info() {
echo "🚀 서버 접속 정보:" echo "🚀 서버 접속 정보:"
echo " - 메인 API 서버: http://localhost:8008" echo " - 메인 API 서버: http://localhost:8008"
echo " - API 문서: http://localhost:8008/docs" echo " - API 문서: http://localhost:8008/docs"
echo " - 모니터링 대시보드: http://localhost:8080" echo " - 모니터링 대시보드: http://localhost:8888"
echo " - 헬스 체크: http://localhost:8008/health" echo " - 헬스 체크: http://localhost:8008/health"
else else
echo "" echo ""

View File

@ -141,7 +141,7 @@ echo " bash scripts/setup_and_run.sh"
echo "" echo ""
log_info "서버 포트:" log_info "서버 포트:"
echo " 메인 서버: http://localhost:8008" echo " 메인 서버: http://localhost:8008"
echo " 모니터링: http://localhost:8080" echo " 모니터링: http://localhost:8888"
echo "" echo ""
log_info "GPU 설정:" log_info "GPU 설정:"
echo " RTX 3060 12GB 권장 설정이 자동으로 적용됩니다" echo " RTX 3060 12GB 권장 설정이 자동으로 적용됩니다"

View File

@ -30,13 +30,48 @@ log_error() {
echo -e "${RED}[ERROR]${NC} $1" echo -e "${RED}[ERROR]${NC} $1"
} }
# 기본 설정 # 기본 설정 - 동적 경로 처리
PROJECT_ROOT="/home/ckh08045/work/inpaintServer" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VENV_PATH="$PROJECT_ROOT" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
MAIN_SERVER_PORT=8008 MAIN_SERVER_PORT=8008
MONITORING_PORT=8080 MONITORING_PORT=8888
LOG_DIR="$PROJECT_ROOT/logs" LOG_DIR="$PROJECT_ROOT/logs"
# 가상환경 경로 자동 감지
detect_venv_path() {
local possible_paths=(
"$PROJECT_ROOT/venv" # 일반적인 venv 경로
"$PROJECT_ROOT/.venv" # 숨김 venv 경로
"$PROJECT_ROOT/env" # env 경로
"$PROJECT_ROOT/.env" # 숨김 env 경로
"$PROJECT_ROOT" # 프로젝트 루트 (Jetson 방식)
)
for path in "${possible_paths[@]}"; do
if [ -f "$path/bin/python" ] || [ -f "$path/bin/python3" ]; then
VENV_PATH="$path"
log_info "가상환경 경로 감지: $VENV_PATH"
return 0
fi
done
# 현재 활성화된 가상환경 확인
if [ -n "$VIRTUAL_ENV" ]; then
VENV_PATH="$VIRTUAL_ENV"
log_info "활성화된 가상환경 사용: $VENV_PATH"
return 0
fi
# 가상환경을 찾지 못한 경우 시스템별 기본값 사용
if [ "$(uname -m)" = "aarch64" ] && uname -a | grep -q "tegra"; then
VENV_PATH="$PROJECT_ROOT"
log_warning "가상환경을 찾을 수 없어 Jetson 기본값 사용: $VENV_PATH"
else
VENV_PATH="$PROJECT_ROOT/venv"
log_warning "가상환경을 찾을 수 없어 x86 기본값 사용: $VENV_PATH"
fi
}
# 시스템 감지 # 시스템 감지
detect_system() { detect_system() {
if [ "$(uname -m)" = "aarch64" ] && uname -a | grep -q "tegra"; then if [ "$(uname -m)" = "aarch64" ] && uname -a | grep -q "tegra"; then
@ -129,6 +164,7 @@ check_environment() {
fi fi
# 가상환경 확인 # 가상환경 확인
log_info "가상환경 경로 확인: $VENV_PATH"
if [ ! -f "$VENV_PATH/bin/activate" ]; then if [ ! -f "$VENV_PATH/bin/activate" ]; then
log_error "가상환경을 찾을 수 없습니다: $VENV_PATH" log_error "가상환경을 찾을 수 없습니다: $VENV_PATH"
exit 1 exit 1
@ -506,6 +542,7 @@ main() {
log_info "인페인팅 서버 시작 스크립트 실행" log_info "인페인팅 서버 시작 스크립트 실행"
detect_system detect_system
detect_venv_path
check_environment check_environment
activate_venv activate_venv
check_ports check_ports

View File

@ -30,11 +30,12 @@ log_error() {
echo -e "${RED}[ERROR]${NC} $1" echo -e "${RED}[ERROR]${NC} $1"
} }
# 기본 설정 # 기본 설정 - 동적 경로 처리
PROJECT_ROOT="/home/ckh08045/work/inpaintServer" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
LOG_DIR="$PROJECT_ROOT/logs" LOG_DIR="$PROJECT_ROOT/logs"
MAIN_PORT=8008 MAIN_PORT=8008
MONITORING_PORT=8080 MONITORING_PORT=8888
# 옵션 파싱 # 옵션 파싱
DETAILED=false DETAILED=false

View File

@ -29,8 +29,9 @@ log_error() {
echo -e "${RED}[ERROR]${NC} $1" echo -e "${RED}[ERROR]${NC} $1"
} }
# 기본 설정 # 기본 설정 - 동적 경로 처리
PROJECT_ROOT="/home/ckh08045/work/inpaintServer" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
PID_FILE="$PROJECT_ROOT/server.pid" PID_FILE="$PROJECT_ROOT/server.pid"
LOG_FILE="$PROJECT_ROOT/logs/server.log" LOG_FILE="$PROJECT_ROOT/logs/server.log"
@ -70,8 +71,8 @@ find_server_processes() {
done done
fi fi
# 포트 8080에서 실행 중인 프로세스 찾기 (모니터링) # 포트 8888에서 실행 중인 프로세스 찾기 (모니터링)
local monitor_processes=$(lsof -ti:8080 2>/dev/null || echo "") local monitor_processes=$(lsof -ti:8888 2>/dev/null || echo "")
if [ -n "$monitor_processes" ]; then if [ -n "$monitor_processes" ]; then
for pid in $monitor_processes; do for pid in $monitor_processes; do
if [[ ! " ${processes[@]} " =~ " ${pid} " ]]; then if [[ ! " ${processes[@]} " =~ " ${pid} " ]]; then
@ -135,10 +136,10 @@ stop_server() {
log_success "포트 8008 해제됨" log_success "포트 8008 해제됨"
fi fi
if lsof -ti:8080 > /dev/null 2>&1; then if lsof -ti:8888 > /dev/null 2>&1; then
log_warning "포트 8080이 여전히 사용 중입니다" log_warning "포트 8888이 여전히 사용 중입니다"
else else
log_success "포트 8080 해제됨" log_success "포트 8888 해제됨"
fi fi
log_success "서버 정지 완료" log_success "서버 정지 완료"
@ -165,10 +166,10 @@ force_stop() {
kill -KILL $port_8008 2>/dev/null || true kill -KILL $port_8008 2>/dev/null || true
fi fi
local port_8080=$(lsof -ti:8080 2>/dev/null || echo "") local port_8888=$(lsof -ti:8888 2>/dev/null || echo "")
if [ -n "$port_8080" ]; then if [ -n "$port_8888" ]; then
log_info "포트 8080 사용 프로세스 강제 종료 중..." log_info "포트 8888 사용 프로세스 강제 종료 중..."
kill -KILL $port_8080 2>/dev/null || true kill -KILL $port_8888 2>/dev/null || true
fi fi
# PID 파일 정리 # PID 파일 정리
@ -204,10 +205,10 @@ check_status() {
log_info "포트 8008: 사용 안함" log_info "포트 8008: 사용 안함"
fi fi
if lsof -ti:8080 > /dev/null 2>&1; then if lsof -ti:8888 > /dev/null 2>&1; then
log_info "포트 8080: 사용 중 (모니터링 대시보드)" log_info "포트 8888: 사용 중 (모니터링 대시보드)"
else else
log_info "포트 8080: 사용 안함" log_info "포트 8888: 사용 안함"
fi fi
} }

View File

@ -6,7 +6,7 @@
"workers_by_status": { "workers_by_status": {
"idle": [ "idle": [
{ {
"id": "worker_8aca7695", "id": "worker_f42e10e1",
"status": "idle", "status": "idle",
"task_count": 0, "task_count": 0,
"error_count": 0, "error_count": 0,
@ -37,62 +37,38 @@
} }
}, },
"api_stats": { "api_stats": {
"total_requests": 7, "total_requests": 4,
"successful_requests": 6, "successful_requests": 4,
"failed_requests": 1, "failed_requests": 0,
"success_rate": 85.71428571428571, "success_rate": 100.0,
"endpoint_usage": { "endpoint_usage": {
"GET /health": 1, "GET /health": 2,
"GET /": 1, "GET /api/v1/model": 2
"GET /favicon.ico": 1,
"GET /docs": 2,
"GET /openapi.json": 2
}, },
"endpoint_stats": { "endpoint_stats": {
"GET /health": { "GET /health": {
"count": 1,
"avg_time": 0.0018818378448486328,
"min_time": 0.0018818378448486328,
"max_time": 0.0018818378448486328,
"current_concurrent": 0
},
"GET /": {
"count": 1,
"avg_time": 0.0030472278594970703,
"min_time": 0.0030472278594970703,
"max_time": 0.0030472278594970703,
"current_concurrent": 0
},
"GET /favicon.ico": {
"count": 1,
"avg_time": 0.002054452896118164,
"min_time": 0.002054452896118164,
"max_time": 0.002054452896118164,
"current_concurrent": 0
},
"GET /docs": {
"count": 2, "count": 2,
"avg_time": 0.003388047218322754, "avg_time": 0.001588582992553711,
"min_time": 0.0019888877868652344, "min_time": 0.0013887882232666016,
"max_time": 0.0047872066497802734, "max_time": 0.0017883777618408203,
"current_concurrent": 0 "current_concurrent": 0
}, },
"GET /openapi.json": { "GET /api/v1/model": {
"count": 2, "count": 2,
"avg_time": 0.02791738510131836, "avg_time": 0.0014955997467041016,
"min_time": 0.002294301986694336, "min_time": 0.0014739036560058594,
"max_time": 0.05354046821594238, "max_time": 0.0015172958374023438,
"current_concurrent": 0 "current_concurrent": 0
} }
}, },
"average_response_time": 0.009942054748535156, "average_response_time": 0.0015420913696289062,
"min_response_time": 0.0018818378448486328, "min_response_time": 0.0013887882232666016,
"max_response_time": 0.05354046821594238, "max_response_time": 0.0017883777618408203,
"current_concurrent": 0, "current_concurrent": 0,
"max_concurrent": 1, "max_concurrent": 1,
"requests_per_second": 0.004146781630742803, "requests_per_second": 0.018707118250962246,
"uptime": 1688.056093454361, "uptime": 213.82235074043274,
"recent_errors": [] "recent_errors": []
}, },
"timestamp": 1756394399.330041 "timestamp": 1756399332.7971911
} }