Add check_application_logs

This commit is contained in:
dongli 2025-06-16 23:18:26 -07:00
parent c09ad7f5c9
commit 92b2aba960
5 changed files with 99 additions and 5 deletions

View File

@ -23,6 +23,9 @@ class SiteSettings(BaseSettings):
# TODO: confirm with Zhenyu
BASE_RECONSILE_URL: str = "https://reconcile.freeleaps.mathmast.com"
# TODO: modify this with actual Loki URL
BASE_LOKI_URL: str = "http://localhost:3100"
class Config:
env_file = ".devbase-webapi.env"
env_file_encoding = "utf-8"

View File

@ -1,5 +1,5 @@
from datetime import datetime
from typing import Literal
from datetime import datetime, timedelta
from typing import Literal, List
from beanie import Document
from bson import ObjectId
@ -50,6 +50,27 @@ class CheckDeploymentStatusRequest(BaseModel):
target_env: str
user_id: str
class CheckApplicationLogsRequest(BaseModel):
product_id: str
target_env: Literal["alpha", "prod"] = "alpha"
user_id: str = ''
log_level: List[Literal["info", "error", "debug"]] = Field(default_factory=lambda: ["info"])
start_time: datetime = datetime.now() - timedelta(minutes=5)
end_time: datetime = datetime.now()
limit: int = 1000
class CheckApplicationLogsResponse(BaseModel):
product_id: str
target_env: Literal["alpha", "prod"]
user_id: str = ''
log_level: List[Literal["info", "error", "debug"]]
start_time: datetime
end_time: datetime
limit: int
logs: list[str]

View File

@ -5,7 +5,8 @@ from fastapi import APIRouter, Depends
from loguru import logger
from app.common.models import CodeDepotDoc
from app.common.models.deployment.deployment import Deployment, InitDeploymentRequest
from app.common.models.deployment.deployment import Deployment, InitDeploymentRequest, CheckDeploymentStatusRequest, \
CheckApplicationLogsRequest, CheckApplicationLogsResponse
from app.routes.deployment.service import DeploymentService, get_deployment_service
router = APIRouter(prefix="/deployment")
@ -57,3 +58,19 @@ async def create_dummy_code_depot(
except Exception as e:
logger.error(f"Failed to create dummy code depot: {e}")
raise e
@router.post("/checkApplicationLogs")
async def check_application_logs(
request: CheckApplicationLogsRequest,
service: DeploymentService = Depends(get_deployment_service)
) -> CheckApplicationLogsResponse:
"""
Check application logs for a given deployment.
"""
try:
res = await service.check_application_logs(request)
return res
except Exception as e:
logger.error(f"Failed to check application logs: {e}")
raise e

View File

@ -4,12 +4,14 @@ from datetime import datetime, timedelta
from typing import List
import httpx
import requests
from fastapi import HTTPException, Depends
from app.common.config.site_settings import site_settings
from app.common.models import Deployment
from app.common.models.code_depot.code_depot import CodeDepotDoc, DepotStatus
from app.common.models.deployment.deployment import InitDeploymentRequest
from app.common.models.deployment.deployment import InitDeploymentRequest, CheckApplicationLogsRequest, \
CheckApplicationLogsResponse
class DeploymentService:
@ -157,6 +159,53 @@ class DeploymentService:
# raise HTTPException(status_code=response.status_code, detail=response.text)
return True
async def check_application_logs(
self,
request: CheckApplicationLogsRequest,
loki_url: str = site_settings.BASE_LOKI_URL,
) -> CheckApplicationLogsResponse:
# Convert to nanoseconds since epoch
start_ns = int(request.start_time.timestamp() * 1e9)
end_ns = int(request.end_time.timestamp() * 1e9)
# TODO: convert product_id to application name if needed
base_query = f'{{application="{request.product_id}", environment="{request.target_env}"}}'
log_level = '|'.join(request.log_level) if request.log_level else ''
loki_query = f'{base_query} |~ "{log_level}"'
params = {
"query": loki_query,
"limit": request.limit,
"start": start_ns,
"end": end_ns,
}
url = f"{loki_url}/loki/api/v1/query_range"
response = requests.get(url, params=params)
if response.status_code != 200:
raise Exception(f"Query failed: {response.status_code} - {response.text}")
data = response.json()
streams = data.get("data", {}).get("result", [])
logs = []
for stream in streams:
for ts, log in stream.get("values", []):
timestamp = datetime.fromtimestamp(int(ts) / 1e9)
logs.append(f"[{timestamp}] {log.strip()}")
return CheckApplicationLogsResponse(
product_id=request.product_id,
target_env=request.target_env,
user_id=request.user_id,
log_level=request.log_level,
start_time=request.start_time,
end_time=request.end_time,
limit=request.limit,
logs=logs
)
# TODO: dummy test code, remove later
async def create_dummy_code_depot(
self,

View File

@ -7,4 +7,8 @@ pydantic_settings==2.9.1
pytest==7.1.2
starlette==0.46.2
uvicorn==0.34.2
httpx==0.24.0
httpx==0.24.0
pydantic-settings~=2.9.1
pymongo~=4.12.1
pydantic~=2.11.4
requests~=2.32.3