87 lines
2.4 KiB
Python
87 lines
2.4 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
import logging.config
|
|
import re
|
|
from collections.abc import Mapping, Sequence
|
|
from typing import Any
|
|
|
|
SENSITIVE_KEYS = {
|
|
"password",
|
|
"password_hash",
|
|
"token",
|
|
"access_token",
|
|
"refresh_token",
|
|
"authorization",
|
|
"email",
|
|
}
|
|
TOKEN_PATTERN = re.compile(r"Bearer\s+[A-Za-z0-9\-._~+/]+=*", re.IGNORECASE)
|
|
|
|
|
|
def _sanitize_value(value: Any) -> Any:
|
|
if isinstance(value, str):
|
|
redacted = TOKEN_PATTERN.sub("Bearer [REDACTED]", value)
|
|
for key in SENSITIVE_KEYS:
|
|
redacted = re.sub(
|
|
rf"({key}\s*=\s*)([^,\s]+)",
|
|
r"\1[REDACTED]",
|
|
redacted,
|
|
flags=re.IGNORECASE,
|
|
)
|
|
return redacted
|
|
|
|
if isinstance(value, Mapping):
|
|
return {
|
|
key: "[REDACTED]" if str(key).lower() in SENSITIVE_KEYS else _sanitize_value(item)
|
|
for key, item in value.items()
|
|
}
|
|
|
|
if isinstance(value, tuple):
|
|
return tuple(_sanitize_value(item) for item in value)
|
|
|
|
if isinstance(value, list):
|
|
return [_sanitize_value(item) for item in value]
|
|
|
|
if isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)):
|
|
return [_sanitize_value(item) for item in value]
|
|
|
|
return value
|
|
|
|
|
|
class SensitiveDataFilter(logging.Filter):
|
|
def filter(self, record: logging.LogRecord) -> bool:
|
|
record.msg = _sanitize_value(record.msg)
|
|
if record.args:
|
|
record.args = _sanitize_value(record.args)
|
|
return True
|
|
|
|
|
|
def setup_logging(log_level: str) -> None:
|
|
logging.config.dictConfig(
|
|
{
|
|
"version": 1,
|
|
"disable_existing_loggers": False,
|
|
"filters": {
|
|
"sensitive_data_filter": {
|
|
"()": "app.core.logging.SensitiveDataFilter",
|
|
}
|
|
},
|
|
"formatters": {
|
|
"standard": {
|
|
"format": "%(asctime)s %(levelname)s [%(name)s] %(message)s",
|
|
}
|
|
},
|
|
"handlers": {
|
|
"console": {
|
|
"class": "logging.StreamHandler",
|
|
"filters": ["sensitive_data_filter"],
|
|
"formatter": "standard",
|
|
}
|
|
},
|
|
"root": {
|
|
"level": log_level.upper(),
|
|
"handlers": ["console"],
|
|
},
|
|
}
|
|
)
|