class AppLogger:
_log_dir = "logs"
_structured_log_file: str | None = None
@classmethod
def initialize(cls, log_dir: str = "logs") -> None:
cls._log_dir = log_dir
if not os.path.exists(log_dir):
os.makedirs(log_dir)
log_filename = f"{log_dir}/{datetime.now().strftime('%Y-%m-%d')}.log"
cls._structured_log_file = f"{log_dir}/{datetime.now().strftime('%Y-%m-%d')}.jsonl"
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.FileHandler(log_filename, encoding="utf-8"),
logging.NullHandler(),
],
)
@classmethod
def _write_structured(cls, record: StructuredLogRecord) -> None:
if cls._structured_log_file:
try:
with open(cls._structured_log_file, "a", encoding="utf-8") as f:
f.write(json.dumps(record.to_dict()) + "\n")
except Exception:
pass
@classmethod
def info(cls, msg: str, **meta: Any) -> None:
safe = redact_secrets(msg)
record = StructuredLogRecord("INFO", safe, **meta)
logging.info(safe)
cls._write_structured(record)
print(f"{CLR_INFO}[INFO] [{datetime.now().strftime('%H:%M:%S')}] {safe}{CLR_RESET}")
@classmethod
def success(cls, msg: str, **meta: Any) -> None:
safe = redact_secrets(msg)
record = StructuredLogRecord("SUCCESS", safe, **meta)
logging.info(f"SUCCESS: {safe}")
cls._write_structured(record)
print(f"{CLR_SUCCESS}[SUCC] [{datetime.now().strftime('%H:%M:%S')}] {safe}{CLR_RESET}")
@classmethod
def warn(cls, msg: str, **meta: Any) -> None:
safe = redact_secrets(msg)
record = StructuredLogRecord("WARN", safe, **meta)
logging.warning(safe)
cls._write_structured(record)
print(f"{CLR_WARN}[WARN] [{datetime.now().strftime('%H:%M:%S')}] {safe}{CLR_RESET}")
@classmethod
def error(cls, msg: str, **meta: Any) -> None:
safe = redact_secrets(msg)
record = StructuredLogRecord("ERROR", safe, **meta)
logging.error(safe, exc_info=True)
cls._write_structured(record)
print(f"{CLR_FAIL}[ERR!] [{datetime.now().strftime('%H:%M:%S')}] {safe}{CLR_RESET}")
@classmethod
def debug(cls, msg: str) -> None:
print(f"{CLR_MAGENTA}[DEBG] {redact_secrets(msg)}{CLR_RESET}")