import hashlib import logging import os import re import uuid from contextvars import ContextVar from datetime import datetime import colorlog import pytz from dotenv import load_dotenv load_dotenv() _request_id = ContextVar("request_id", default=None) VIETNAM_TZ = pytz.timezone("Asia/Ho_Chi_Minh") def get_colored_uuid(uuid_str: str) -> str: colors = [ "\033[97;41m", "\033[97;42m", "\033[97;43m", "\033[97;44m", "\033[97;45m", "\033[97;46m", "\033[30;102m", "\033[30;103m", "\033[30;104m", "\033[30;105m", "\033[30;106m", "\033[97;100m", "\033[97;101m", "\033[97;102m", "\033[97;104m", "\033[97;105m", ] hash_value = int(hashlib.md5(uuid_str.encode()).hexdigest()[:8], 16) return f"{colors[hash_value % len(colors)]}{uuid_str}\033[0m" class ColoredFormatter(colorlog.ColoredFormatter): def formatTime(self, record, datefmt=None): dt = datetime.fromtimestamp(record.created, tz=VIETNAM_TZ) return dt.strftime(datefmt) if datefmt else dt.isoformat() def format(self, record): rid = getattr(record, "request_id", None) if rid: record.request_id = get_colored_uuid(rid) return super().format(record) class FileFormatter(logging.Formatter): def formatTime(self, record, datefmt=None): dt = datetime.fromtimestamp(record.created, tz=VIETNAM_TZ) return dt.strftime(datefmt) if datefmt else dt.isoformat() def format(self, record): rid = getattr(record, "request_id", None) if rid: ansi = re.compile(r"\x1B[@-_][0-?]*[ -/]*[@-~]") record.request_id = ansi.sub("", str(rid)) return super().format(record) class UUIDFilter(logging.Filter): def filter(self, record): rid = _request_id.get() if rid is None: rid = str(uuid.uuid4())[:] _request_id.set(rid) record.request_id = rid return True def setup_logger(name="APP", log_file="logs/log.log") -> logging.Logger: logger = colorlog.getLogger(name) if logger.handlers: return logger logger.setLevel(logging.INFO) logger.propagate = False os.makedirs(os.path.dirname(log_file), exist_ok=True) console_handler = colorlog.StreamHandler() console_handler.setFormatter( ColoredFormatter( "%(asctime)s-%(log_color)s%(levelname)s%(reset)s-%(request_id)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S", log_colors={ "DEBUG": "cyan", "INFO": "green", "WARNING": "yellow", "ERROR": "red,bold", "CRITICAL": "white,bg_red,bold", }, ) ) file_handler = logging.FileHandler(log_file, encoding="utf-8") file_handler.setFormatter( FileFormatter("%(asctime)s-%(levelname)s-%(request_id)s - %(message)s") ) logger.addFilter(UUIDFilter()) logger.addHandler(console_handler) logger.addHandler(file_handler) return logger logger = setup_logger() request_id_context = _request_id