Coverage for src/inheritance_calculator_core/utils/logger.py: 0%

55 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-17 05:31 +0900

1""" 

2ログ管理モジュール 

3 

4アプリケーション全体で使用するロガーを提供します。 

5""" 

6 

7import logging 

8import sys 

9from logging.handlers import RotatingFileHandler 

10from pathlib import Path 

11from typing import Optional 

12 

13from .config import settings 

14from .exceptions import LoggingError 

15 

16 

17def setup_logger( 

18 name: str, 

19 log_file: Optional[Path] = None, 

20 level: Optional[str] = None, 

21) -> logging.Logger: 

22 """ 

23 ロガーをセットアップします。 

24 

25 Args: 

26 name: ロガー名 

27 log_file: ログファイルパス(Noneの場合は設定ファイルから取得) 

28 level: ログレベル(Noneの場合は設定ファイルから取得) 

29 

30 Returns: 

31 設定済みのロガー 

32 

33 Raises: 

34 LoggingError: ロガーのセットアップに失敗した場合 

35 """ 

36 try: 

37 logger = logging.getLogger(name) 

38 

39 # 既にハンドラが設定されている場合はクリア(再設定可能に) 

40 if logger.handlers: 

41 logger.handlers.clear() 

42 

43 # ログレベルの設定 

44 try: 

45 log_level = level or (settings.log.level if settings else "INFO") 

46 logger.setLevel(getattr(logging, log_level.upper())) 

47 except AttributeError: 

48 # settingsがNoneの場合のフォールバック 

49 logger.setLevel(logging.INFO) 

50 log_level = "INFO" 

51 

52 # フォーマッタの設定 

53 formatter = logging.Formatter( 

54 fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s", 

55 datefmt="%Y-%m-%d %H:%M:%S", 

56 ) 

57 

58 # コンソールハンドラの設定 

59 console_handler = logging.StreamHandler(sys.stdout) 

60 console_handler.setLevel(logging.INFO) 

61 console_handler.setFormatter(formatter) 

62 logger.addHandler(console_handler) 

63 

64 # ファイルハンドラの設定(settingsがある場合のみ) 

65 if log_file is None and settings: 

66 try: 

67 log_file = settings.logs_dir / settings.log.file.split("/")[-1] 

68 except AttributeError: 

69 # settingsの属性にアクセスできない場合はファイルハンドラをスキップ 

70 log_file = None 

71 

72 if log_file is not None: 

73 try: 

74 log_file.parent.mkdir(parents=True, exist_ok=True) 

75 except OSError as e: 

76 raise LoggingError(f"Failed to create log directory: {log_file.parent}") from e 

77 

78 try: 

79 max_bytes = settings.log.max_bytes if settings else 10485760 

80 backup_count = settings.log.backup_count if settings else 5 

81 

82 file_handler = RotatingFileHandler( 

83 log_file, 

84 maxBytes=max_bytes, 

85 backupCount=backup_count, 

86 encoding="utf-8", 

87 ) 

88 file_handler.setLevel(getattr(logging, log_level.upper())) 

89 file_handler.setFormatter(formatter) 

90 logger.addHandler(file_handler) 

91 except OSError as e: 

92 raise LoggingError(f"Failed to create log file handler: {log_file}") from e 

93 

94 # プロパゲーションを無効化(重複ログ防止) 

95 logger.propagate = False 

96 

97 return logger 

98 

99 except Exception as e: 

100 if isinstance(e, LoggingError): 

101 raise 

102 raise LoggingError(f"Failed to setup logger '{name}'") from e 

103 

104 

105# ロガーキャッシュ 

106_logger_cache: dict[str, logging.Logger] = {} 

107 

108# デフォルトロガー 

109default_logger = setup_logger("inheritance_calculator") 

110_logger_cache["inheritance_calculator"] = default_logger 

111 

112 

113def get_logger(name: str) -> logging.Logger: 

114 """ 

115 指定された名前のロガーを取得します(キャッシュ付き)。 

116 

117 Args: 

118 name: ロガー名 

119 

120 Returns: 

121 ロガー 

122 """ 

123 if name not in _logger_cache: 

124 _logger_cache[name] = setup_logger(name) 

125 return _logger_cache[name]