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 # TODO: confirm with Zhenyu
BASE_RECONSILE_URL: str = "https://reconcile.freeleaps.mathmast.com" 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: class Config:
env_file = ".devbase-webapi.env" env_file = ".devbase-webapi.env"
env_file_encoding = "utf-8" env_file_encoding = "utf-8"

View File

@ -1,5 +1,5 @@
from datetime import datetime from datetime import datetime, timedelta
from typing import Literal from typing import Literal, List
from beanie import Document from beanie import Document
from bson import ObjectId from bson import ObjectId
@ -50,6 +50,27 @@ class CheckDeploymentStatusRequest(BaseModel):
target_env: str target_env: str
user_id: 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 loguru import logger
from app.common.models import CodeDepotDoc 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 from app.routes.deployment.service import DeploymentService, get_deployment_service
router = APIRouter(prefix="/deployment") router = APIRouter(prefix="/deployment")
@ -57,3 +58,19 @@ async def create_dummy_code_depot(
except Exception as e: except Exception as e:
logger.error(f"Failed to create dummy code depot: {e}") logger.error(f"Failed to create dummy code depot: {e}")
raise 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 from typing import List
import httpx import httpx
import requests
from fastapi import HTTPException, Depends from fastapi import HTTPException, Depends
from app.common.config.site_settings import site_settings from app.common.config.site_settings import site_settings
from app.common.models import Deployment from app.common.models import Deployment
from app.common.models.code_depot.code_depot import CodeDepotDoc, DepotStatus 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: class DeploymentService:
@ -157,6 +159,53 @@ class DeploymentService:
# raise HTTPException(status_code=response.status_code, detail=response.text) # raise HTTPException(status_code=response.status_code, detail=response.text)
return True 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 # TODO: dummy test code, remove later
async def create_dummy_code_depot( async def create_dummy_code_depot(
self, self,

View File

@ -8,3 +8,7 @@ pytest==7.1.2
starlette==0.46.2 starlette==0.46.2
uvicorn==0.34.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