feat: configure full logging in webmain.py — all API logs to file + console
- RotatingFileHandler: logs/server_YYYY-MM-DD.log (50MB, keep 7 files) - Console handler: colored timestamp + level + logger name + message - webapp.* and rag_eval.* loggers captured at configured level - uvicorn access/error logs also routed to same handlers - File always captures DEBUG; console level controlled by --log-level arg - Added --log-level and --log-file CLI arguments to webmain.py Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
99
webmain.py
99
webmain.py
@@ -5,13 +5,73 @@ and the same runs/ artifacts. Example:
|
||||
|
||||
python webmain.py
|
||||
python webmain.py --host 0.0.0.0 --port 8800
|
||||
python webmain.py --host 0.0.0.0 --port 8800 --log-level debug
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import logging.config
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
import uvicorn
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent
|
||||
|
||||
|
||||
def _build_log_config(log_file: Path, level: str) -> dict:
|
||||
"""Build a uvicorn-compatible logging config dict.
|
||||
|
||||
Writes to both stderr (console) and a rotating daily log file.
|
||||
All webapp.* and rag_eval.* loggers inherit from root so every
|
||||
logger.info() call in the API routes is captured.
|
||||
"""
|
||||
level_upper = level.upper()
|
||||
return {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"detailed": {
|
||||
"format": "%(asctime)s %(levelname)-8s %(name)s %(message)s",
|
||||
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||
},
|
||||
"console": {
|
||||
"format": "%(asctime)s %(levelname)-8s %(name)-30s %(message)s",
|
||||
"datefmt": "%H:%M:%S",
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"class": "logging.StreamHandler",
|
||||
"stream": "ext://sys.stderr",
|
||||
"formatter": "console",
|
||||
"level": level_upper,
|
||||
},
|
||||
"file": {
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": str(log_file),
|
||||
"maxBytes": 50 * 1024 * 1024, # 50 MB per file
|
||||
"backupCount": 7, # keep 7 rotated files
|
||||
"encoding": "utf-8",
|
||||
"formatter": "detailed",
|
||||
"level": "DEBUG", # file always captures everything
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
# Our application loggers — detailed level
|
||||
"webapp": {"handlers": ["console", "file"], "level": level_upper, "propagate": False},
|
||||
"rag_eval": {"handlers": ["console", "file"], "level": level_upper, "propagate": False},
|
||||
# uvicorn access log — captured to file, shown on console
|
||||
"uvicorn.access": {"handlers": ["console", "file"], "level": "INFO", "propagate": False},
|
||||
"uvicorn.error": {"handlers": ["console", "file"], "level": "INFO", "propagate": False},
|
||||
"uvicorn": {"handlers": ["console", "file"], "level": "INFO", "propagate": False},
|
||||
},
|
||||
"root": {
|
||||
"handlers": ["console", "file"],
|
||||
"level": "WARNING", # suppress noisy third-party libs at WARNING
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
@@ -24,17 +84,52 @@ def parse_args() -> argparse.Namespace:
|
||||
action="store_true",
|
||||
help="Enable auto-reload for local development.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--log-level",
|
||||
default="info",
|
||||
choices=["debug", "info", "warning", "error"],
|
||||
help="Console log level (default: info). File always captures DEBUG.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--log-file",
|
||||
default=None,
|
||||
help="Log file path (default: logs/server_YYYY-MM-DD.log).",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Start uvicorn with the configured application."""
|
||||
"""Start uvicorn with the configured application and logging."""
|
||||
import uvicorn
|
||||
|
||||
args = parse_args()
|
||||
|
||||
# Resolve log file path
|
||||
logs_dir = REPO_ROOT / "logs"
|
||||
logs_dir.mkdir(parents=True, exist_ok=True)
|
||||
if args.log_file:
|
||||
log_file = Path(args.log_file)
|
||||
else:
|
||||
date_str = datetime.now().strftime("%Y-%m-%d")
|
||||
log_file = logs_dir / f"server_{date_str}.log"
|
||||
|
||||
log_config = _build_log_config(log_file, args.log_level)
|
||||
|
||||
# Apply config before uvicorn starts so our loggers are ready immediately
|
||||
logging.config.dictConfig(log_config)
|
||||
|
||||
logger = logging.getLogger("webapp.server")
|
||||
logger.info(
|
||||
"Starting RAGAS Console host=%s port=%d log_level=%s log_file=%s",
|
||||
args.host, args.port, args.log_level, log_file,
|
||||
)
|
||||
|
||||
uvicorn.run(
|
||||
"webapp.server:app",
|
||||
host=args.host,
|
||||
port=args.port,
|
||||
reload=args.reload,
|
||||
log_config=log_config, # hand our config to uvicorn so it uses same handlers
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user