After story 1
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
"""
|
||||
Logging configuration for VideoDetect.
|
||||
|
||||
Sets up JSON structured logging via python-json-logger with:
|
||||
- Configurable log levels
|
||||
- Standardized field names
|
||||
- Log rotation
|
||||
- All logs to stdout for Docker capture
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import logging.handlers
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from python_json_logger import json_formatter
|
||||
|
||||
|
||||
def setup_logging(
|
||||
level: str = "INFO",
|
||||
log_format: str = "json",
|
||||
rotation_max_bytes: int = 104857600, # 100MB
|
||||
rotation_backup_count: int = 10,
|
||||
):
|
||||
"""Configure structured logging for the application."""
|
||||
log_level = getattr(logging, level.upper(), logging.INFO)
|
||||
|
||||
# Root logger
|
||||
root_logger = logging.getLogger()
|
||||
root_logger.setLevel(log_level)
|
||||
|
||||
# Remove existing handlers
|
||||
root_logger.handlers.clear()
|
||||
|
||||
# Handler: stdout (for Docker capture)
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
handler.setLevel(log_level)
|
||||
|
||||
if log_format == "json":
|
||||
formatter = JsonFormatter()
|
||||
else:
|
||||
formatter = logging.Formatter(
|
||||
"%(asctime)s %(levelname)s %(name)s %(message)s",
|
||||
datefmt="%Y-%m-%dT%H:%M:%S%z",
|
||||
)
|
||||
|
||||
handler.setFormatter(formatter)
|
||||
root_logger.addHandler(handler)
|
||||
|
||||
# Handler: file rotation (for persistence)
|
||||
file_handler = logging.handlers.RotatingFileHandler(
|
||||
filename="/logs/videodetect.log",
|
||||
maxBytes=rotation_max_bytes,
|
||||
backupCount=rotation_backup_count,
|
||||
encoding="utf-8",
|
||||
)
|
||||
file_handler.setLevel(log_level)
|
||||
file_handler.setFormatter(formatter)
|
||||
root_logger.addHandler(file_handler)
|
||||
|
||||
logging.info("Logging configured: level=%s format=%s", level, log_format)
|
||||
|
||||
|
||||
class JsonFormatter(logging.Formatter):
|
||||
"""JSON log formatter with standardized fields."""
|
||||
|
||||
def format(self, record: logging.LogRecord) -> str:
|
||||
log_data = {
|
||||
"timestamp": datetime.fromtimestamp(record.created, tz=timezone.utc).isoformat(),
|
||||
"level": record.levelname,
|
||||
"logger": record.name,
|
||||
"message": record.getMessage(),
|
||||
"module": record.module,
|
||||
"function": record.funcName,
|
||||
"line": record.lineno,
|
||||
}
|
||||
|
||||
# Add extra fields
|
||||
if hasattr(record, "video_id"):
|
||||
log_data["video_id"] = record.video_id
|
||||
if hasattr(record, "metadata"):
|
||||
log_data["metadata"] = record.metadata
|
||||
|
||||
# Add exception info if present
|
||||
if record.exc_info and record.exc_info[0] is not None:
|
||||
log_data["exception"] = self.formatException(record.exc_info)
|
||||
|
||||
return json.dumps(log_data, default=str)
|
||||
Reference in New Issue
Block a user