You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
iTi-Flask/iti/logging_config.py

85 lines
2.8 KiB
Python

from __future__ import annotations
import logging
from logging.handlers import RotatingFileHandler
from pathlib import Path
from typing import Any
from iti.config import BaseConfig
class SafeFormatter(logging.Formatter):
def format(self, record: logging.LogRecord) -> str:
for key in ("trace_id", "request_id", "actor_type", "actor_id", "response_code"):
if not hasattr(record, key):
setattr(record, key, "-")
return super().format(record)
def configure_logging(config: BaseConfig) -> None:
level = getattr(logging, config.log_level.upper(), logging.INFO)
formatter = SafeFormatter(
"%(asctime)s %(levelname)s %(name)s "
"trace=%(trace_id)s actor=%(actor_type)s:%(actor_id)s code=%(response_code)s - %(message)s"
)
root_logger = logging.getLogger("iti")
root_logger.setLevel(level)
root_logger.handlers.clear()
root_logger.propagate = False
console_handler = logging.StreamHandler()
console_handler.setLevel(level)
console_handler.setFormatter(formatter)
root_logger.addHandler(console_handler)
error_logger = logging.getLogger("iti.error")
error_logger.setLevel(logging.ERROR)
error_logger.handlers.clear()
error_logger.propagate = False
if config.log_file_enabled:
log_dir = Path(config.log_dir)
log_dir.mkdir(parents=True, exist_ok=True)
app_handler = RotatingFileHandler(
log_dir / "app.log",
encoding="utf-8",
maxBytes=config.log_max_bytes,
backupCount=config.log_backup_count,
)
app_handler.setLevel(level)
app_handler.setFormatter(formatter)
root_logger.addHandler(app_handler)
error_handler = RotatingFileHandler(
log_dir / "error.log",
encoding="utf-8",
maxBytes=config.log_max_bytes,
backupCount=config.log_backup_count,
)
error_handler.setLevel(logging.ERROR)
error_handler.setFormatter(formatter)
root_logger.addHandler(error_handler)
error_logger.addHandler(error_handler)
def log_extra(request: Any | None = None) -> dict[str, Any]:
if request is None:
return {
"trace_id": "-",
"request_id": "-",
"actor_type": "-",
"actor_id": "-",
"response_code": "-",
}
actor = getattr(request.state, "actor", None)
principal = getattr(request.state, "principal", None)
return {
"trace_id": getattr(request.state, "trace_id", "-"),
"request_id": getattr(request.state, "request_id", "-"),
"actor_type": getattr(actor, "type", None) or ("user" if principal else "-"),
"actor_id": getattr(actor, "id", None) or getattr(principal, "id", "-"),
"response_code": getattr(request.state, "response_code", "-"),
}