import constants import sys, time import builtins, logging import settings # base logging format log_format = "[%(asctime)s] [%(levelname)s] (%(name)s) %(message)s" # date-time format date_format = constants.DATE_FORMAT # formatter for logger formatter = logging.Formatter(log_format, date_format) # class for redirect STD to Logging class StdLogRedirect: def __init__(self, stream, formatter, level=logging.ERROR, name="stderr"): self.stream = stream self.formatter = formatter self.level = level self.name = name self._buf = "" def _emit_line(self, line: str): rec = logging.LogRecord( name=self.name, level=self.level, pathname="", lineno=0, msg=line, args=(), exc_info=None, ) sys.stdout.write(self.formatter.format(rec) + "\n") def write(self, s): if isinstance(s, bytes): s = s.decode(errors="replace") self._buf += s while True: idx = self._buf.find("\n") if idx == -1: break line = self._buf[:idx+1:] self._buf = self._buf[idx+1:] if line: self._emit_line(line) def flush(self): if self._buf: self._emit_line(self._buf.rstrip("\r")) self._buf = "" self.stream.flush() def isatty(self): return self.stream.isatty() # redirect STDERR to STDOUT using usual Logging format sys.stderr = StdLogRedirect(sys.stderr, formatter, level=logging.ERROR, name="external.stderr") # redirect print to Logging _builtin_print = print def print_as_log(*args, level=logging.NOTSET, name="unknown.print", **kwargs): record = logging.LogRecord( name=name, level=level, pathname=__file__, lineno=0, msg=" ".join(str(a) for a in args), args=(), exc_info=None, ) _builtin_print(formatter.format(record), **kwargs) builtins.print = print_as_log # logging basic config logging.basicConfig( level=settings.LOG_LEVEL_TEXT, format=log_format, datefmt=date_format, stream=sys.stdout, force=True ) # create own logger class to prevent init custom loggers by other libs class GatekeeperLogger(logging.Logger): def addHandler(self, h): # only "root", "__main__" and own loggers will be accepted if self.name == "root" or self.name.startswith((settings.LOG_PREFIX, "__main__")): return super().addHandler(h) logging.setLoggerClass(GatekeeperLogger) # Werkzeug 로거 설정 (Flask 개발 서버용) # 기존 werkzeug 로거의 핸들러를 모두 제거하고 커스텀 포맷 적용 import logging as _logging werkzeug_logger = _logging.getLogger("werkzeug") werkzeug_logger.handlers = [] # 기존 핸들러 제거 werkzeug_logger.setLevel(settings.LOG_LEVEL_TEXT) handler = _logging.StreamHandler(sys.stdout) handler.setFormatter(formatter) # 통일된 포맷 사용 werkzeug_logger.addHandler(handler) werkzeug_logger.propagate = False # Flask의 기본 로거도 동일하게 설정 flask_logger = _logging.getLogger("flask.app") flask_logger.handlers = [] flask_logger.setLevel(settings.LOG_LEVEL_TEXT) flask_handler = _logging.StreamHandler(sys.stdout) flask_handler.setFormatter(formatter) flask_logger.addHandler(flask_handler) flask_logger.propagate = False def custom_logger(log_prefix: str): import logging logger = logging.getLogger(log_prefix) logger.setLevel(settings.LOG_LEVEL_TEXT) return logger