feat: log download endpoints
ref: N25B-401
This commit is contained in:
@@ -48,6 +48,7 @@ handlers:
|
|||||||
class: control_backend.logging.DatedFileHandler
|
class: control_backend.logging.DatedFileHandler
|
||||||
formatter: experiment
|
formatter: experiment
|
||||||
filters: [partial]
|
filters: [partial]
|
||||||
|
# Directory must match config.logging_settings.experiment_log_directory
|
||||||
file_prefix: experiment_logs/experiment
|
file_prefix: experiment_logs/experiment
|
||||||
|
|
||||||
# Level for external libraries
|
# Level for external libraries
|
||||||
@@ -59,6 +60,6 @@ loggers:
|
|||||||
control_backend:
|
control_backend:
|
||||||
level: LLM
|
level: LLM
|
||||||
handlers: [ui]
|
handlers: [ui]
|
||||||
experiment:
|
experiment: # This name must match config.logging_settings.experiment_logger_name
|
||||||
level: DEBUG
|
level: DEBUG
|
||||||
handlers: [ui, file]
|
handlers: [ui, file]
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import logging
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import zmq
|
import zmq
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter, HTTPException
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import FileResponse, StreamingResponse
|
||||||
from zmq.asyncio import Context
|
from zmq.asyncio import Context
|
||||||
|
|
||||||
from control_backend.core.config import settings
|
from control_backend.core.config import settings
|
||||||
@@ -38,3 +39,29 @@ async def log_stream():
|
|||||||
yield f"data: {message}\n\n"
|
yield f"data: {message}\n\n"
|
||||||
|
|
||||||
return StreamingResponse(gen(), media_type="text/event-stream")
|
return StreamingResponse(gen(), media_type="text/event-stream")
|
||||||
|
|
||||||
|
|
||||||
|
LOGGING_DIR = Path(settings.logging_settings.experiment_log_directory).resolve()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/logs/files")
|
||||||
|
@router.get("/api/logs/files")
|
||||||
|
async def log_directory():
|
||||||
|
"""
|
||||||
|
Get a list of all log files stored in the experiment log file directory.
|
||||||
|
"""
|
||||||
|
return [f.name for f in LOGGING_DIR.glob("*.log")]
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/logs/files/{filename}")
|
||||||
|
@router.get("/api/logs/files/{filename}")
|
||||||
|
async def log_file(filename: str):
|
||||||
|
# Prevent path-traversal
|
||||||
|
file_path = (LOGGING_DIR / filename).resolve() # This .resolve() is important
|
||||||
|
if not file_path.is_relative_to(LOGGING_DIR):
|
||||||
|
raise HTTPException(status_code=400, detail="Invalid filename.")
|
||||||
|
|
||||||
|
if not file_path.is_file():
|
||||||
|
raise HTTPException(status_code=404, detail="File not found.")
|
||||||
|
|
||||||
|
return FileResponse(file_path, filename=file_path.name)
|
||||||
|
|||||||
@@ -147,10 +147,12 @@ class LoggingSettings(BaseModel):
|
|||||||
Configuration for logging.
|
Configuration for logging.
|
||||||
|
|
||||||
:ivar logging_config_file: Path to the logging configuration file.
|
:ivar logging_config_file: Path to the logging configuration file.
|
||||||
:ivar experiment_logger_name: Name of the experiment logger, should match the logging config.
|
:ivar experiment_log_directory: Location of the experiment logs. Must match the logging config.
|
||||||
|
:ivar experiment_logger_name: Name of the experiment logger. Must match the logging config.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logging_config_file: str = ".logging_config.yaml"
|
logging_config_file: str = ".logging_config.yaml"
|
||||||
|
experiment_log_directory: str = "experiment_logs"
|
||||||
experiment_logger_name: str = "experiment"
|
experiment_logger_name: str = "experiment"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user