diff --git a/.gitignore b/.gitignore index eba74f4..2f1494f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -venv/ \ No newline at end of file +venv/ +__pycache__ \ No newline at end of file diff --git a/example/debug.py b/example/debug.py new file mode 100644 index 0000000..57ae8ac --- /dev/null +++ b/example/debug.py @@ -0,0 +1,19 @@ +import sys +import os +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "src"))) + +from libs.custom_logger import get_logger +from libs.time_watch import TimeWatch + +# level = 10 # DEBUG level +logger = get_logger(level=10) +logger.debug("Debug message") + +@TimeWatch.debug(logger=logger) +def app_run(): + import time + time.sleep(2) + return "Function executed." + +if __name__ == "__main__": + app_run() diff --git a/src/libs/__init__.py b/src/libs/__init__.py new file mode 100644 index 0000000..2a9a790 --- /dev/null +++ b/src/libs/__init__.py @@ -0,0 +1,11 @@ +from .custom_logger import get_logger, CustomLogger +from .time_watch import TimeWatch +from .singleton import Singleton + +__all__ = [ + "get_logger", + "CustomLogger", + "TimeWatch", + "Singleton", +] + diff --git a/src/libs/custom_logger.py b/src/libs/custom_logger.py new file mode 100644 index 0000000..48e219e --- /dev/null +++ b/src/libs/custom_logger.py @@ -0,0 +1,48 @@ +import logging +import functools +from .singleton import Singleton + +class CustomLogger(Singleton): + def __init__(self, name='main', log_file=None, level=logging.INFO): + if hasattr(self, '_initialized') and self._initialized: + self.logger.setLevel(level) + return # すでに初期化済みなら何もしない + + self.logger = logging.getLogger(name) + self.logger.setLevel(level) + self.logger.propagate = False + + formatter = logging.Formatter( + '%(asctime)s %(levelname)s [%(filename)s:%(lineno)3d]: %(message)s' + ) + + # Console handler + ch = logging.StreamHandler() + ch.setFormatter(formatter) + self.logger.addHandler(ch) + + # File handler + if log_file: + fh = logging.FileHandler(log_file, encoding='utf-8') + fh.setFormatter(formatter) + self.logger.addHandler(fh) + + self._initialized = True + + + def get_logger(self): + return self.logger + + def log_entry_exit(self, func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + self.logger.info(f"Enter: {func.__qualname__}") + result = func(*args, **kwargs) + self.logger.info(f"Exit: {func.__qualname__}") + return result + return wrapper + + +def get_logger(name='main', log_file=None, level=logging.INFO): + custom_logger = CustomLogger(name, log_file, level) + return custom_logger.get_logger() diff --git a/src/libs/singleton.py b/src/libs/singleton.py new file mode 100644 index 0000000..2313286 --- /dev/null +++ b/src/libs/singleton.py @@ -0,0 +1,20 @@ +"""Singleton pattern implementation in Python. +This implementation is thread-safe and ensures that only one instance of the class is created. + +Singleton が提供するのは「同じインスタンスを返す仕組み」 +* __init__() は毎回呼ばれる(多くの人が意図しない動作) +* __init__の2回目は_initialized というフラグは 使う側で管理する必要がある。 +""" + +import threading + +class Singleton(object): + _instances = {} + _lock = threading.Lock() + + def __new__(cls, *args, **kwargs): + if cls not in cls._instances: + with cls._lock: + if cls not in cls._instances: # ダブルチェック + cls._instances[cls] = super(Singleton, cls).__new__(cls) + return cls._instances[cls] diff --git a/src/libs/time_watcg.py b/src/libs/time_watch.py similarity index 68% rename from src/libs/time_watcg.py rename to src/libs/time_watch.py index 7933e1a..39f359c 100644 --- a/src/libs/time_watcg.py +++ b/src/libs/time_watch.py @@ -36,20 +36,22 @@ class TimeWatch: self.end_time = None self.logger.debug("Timer reset.") - @classmethod - def debug(cls, func): - def wrapper(*args, **kwargs): - instance = cls() # インスタンスを作成 - instance.start() - result = func(*args, **kwargs) - instance.stop() - instance.elapsed_time() - return result - return wrapper + @staticmethod + def debug(logger=None): + def decorator(func): + def wrapper(*args, **kwargs): + timer = TimeWatch(logger=logger) + timer.start() + result = func(*args, **kwargs) + timer.stop() + timer.elapsed_time() + return result + return wrapper + return decorator if __name__ == "__main__": - @TimeWatch.debug + @TimeWatch.debug() def test_function(): time.sleep(2) return "Function executed."