feat(v0.1.0): project foundation with logging and config
This commit is contained in:
104
src/logging/formatters.py
Normal file
104
src/logging/formatters.py
Normal file
@@ -0,0 +1,104 @@
|
||||
"""Custom log formatters."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict
|
||||
|
||||
try:
|
||||
import colorlog
|
||||
except ImportError:
|
||||
colorlog = None
|
||||
|
||||
|
||||
class JSONFormatter(logging.Formatter):
|
||||
"""JSON formatter for structured logging."""
|
||||
|
||||
def format(self, record: logging.LogRecord) -> str:
|
||||
"""
|
||||
Format log record as JSON.
|
||||
|
||||
Args:
|
||||
record: Log record to format
|
||||
|
||||
Returns:
|
||||
JSON string
|
||||
"""
|
||||
log_data: Dict[str, Any] = {
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"level": record.levelname,
|
||||
"logger": record.name,
|
||||
"message": record.getMessage(),
|
||||
"module": record.module,
|
||||
"function": record.funcName,
|
||||
"line": record.lineno,
|
||||
}
|
||||
|
||||
# Add exception info if present
|
||||
if record.exc_info:
|
||||
log_data["exception"] = self.formatException(record.exc_info)
|
||||
|
||||
# Add extra fields
|
||||
if hasattr(record, "extra_fields"):
|
||||
log_data.update(record.extra_fields)
|
||||
|
||||
return json.dumps(log_data)
|
||||
|
||||
|
||||
class DetailedFormatter(logging.Formatter):
|
||||
"""Detailed text formatter with full context."""
|
||||
|
||||
def __init__(self, *args: Any, **kwargs: Any):
|
||||
"""Initialize formatter."""
|
||||
super().__init__(
|
||||
fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s - [%(pathname)s:%(lineno)d]",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
class ColoredFormatter(logging.Formatter):
|
||||
"""Colored console formatter."""
|
||||
|
||||
def __init__(self, *args: Any, **kwargs: Any):
|
||||
"""Initialize formatter."""
|
||||
if colorlog is None:
|
||||
# Fallback to standard formatter if colorlog not available
|
||||
super().__init__(
|
||||
fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
else:
|
||||
# Use colorlog if available
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def format(self, record: logging.LogRecord) -> str:
|
||||
"""
|
||||
Format log record with colors.
|
||||
|
||||
Args:
|
||||
record: Log record to format
|
||||
|
||||
Returns:
|
||||
Formatted string with colors
|
||||
"""
|
||||
if colorlog is None:
|
||||
return super().format(record)
|
||||
|
||||
# Create colored formatter
|
||||
formatter = colorlog.ColoredFormatter(
|
||||
"%(log_color)s%(asctime)s - %(name)s - %(levelname)s - %(message)s%(reset)s",
|
||||
datefmt="%Y-%m-%d %H:%M:%S",
|
||||
log_colors={
|
||||
"DEBUG": "cyan",
|
||||
"INFO": "green",
|
||||
"WARNING": "yellow",
|
||||
"ERROR": "red",
|
||||
"CRITICAL": "red,bg_white",
|
||||
},
|
||||
)
|
||||
return formatter.format(record)
|
||||
|
||||
Reference in New Issue
Block a user