Add check_application_logs
This commit is contained in:
parent
c09ad7f5c9
commit
92b2aba960
@ -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"
|
||||||
|
|||||||
@ -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]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -7,4 +7,8 @@ pydantic_settings==2.9.1
|
|||||||
pytest==7.1.2
|
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
|
||||||
Loading…
Reference in New Issue
Block a user