Coverage for railway / core / logging.py: 93%

29 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-11 00:06 +0900

1"""Logging initialization for Railway Framework.""" 

2 

3import sys 

4from typing import TYPE_CHECKING 

5 

6from loguru import logger 

7 

8if TYPE_CHECKING: 

9 from railway.core.settings import LoggingSettings 

10 

11 

12def _add_console_handler( 

13 settings: "LoggingSettings", 

14 handler_level: str, 

15) -> None: 

16 """Add console handler to logger.""" 

17 logger.add( 

18 sys.stderr, 

19 level=handler_level, 

20 format=settings.format, 

21 colorize=True, 

22 ) 

23 

24 

25def _add_file_handler( 

26 settings: "LoggingSettings", 

27 path: str, 

28 level: str, 

29 rotation: str | None, 

30 retention: str | None, 

31) -> None: 

32 """Add file handler to logger.""" 

33 logger.add( 

34 path, 

35 level=level, 

36 format=settings.format, 

37 rotation=rotation, 

38 retention=retention, 

39 encoding="utf-8", 

40 ) 

41 

42 

43def _add_handler(settings: "LoggingSettings", handler_config: "LoggingSettings") -> None: 

44 """Add a single handler based on configuration.""" 

45 from railway.core.settings import LoggingHandlerSettings 

46 

47 if not isinstance(handler_config, LoggingHandlerSettings): 47 ↛ 48line 47 didn't jump to line 48 because the condition on line 47 was never true

48 return 

49 

50 if handler_config.type == "console": 

51 _add_console_handler(settings, handler_config.level) 

52 elif handler_config.type == "file" and handler_config.path: 52 ↛ exitline 52 didn't return from function '_add_handler' because the condition on line 52 was always true

53 _add_file_handler( 

54 settings, 

55 handler_config.path, 

56 handler_config.level, 

57 handler_config.rotation, 

58 handler_config.retention, 

59 ) 

60 

61 

62def _add_default_handler(settings: "LoggingSettings") -> None: 

63 """Add default console handler when no handlers configured.""" 

64 logger.add( 

65 sys.stderr, 

66 level=settings.level, 

67 format=settings.format, 

68 colorize=True, 

69 ) 

70 

71 

72def init_logging(settings: "LoggingSettings") -> None: 

73 """ 

74 Initialize loguru based on configuration. 

75 

76 Steps: 

77 1. Remove default handler 

78 2. Add configured handlers (console, file) 

79 3. Apply log level and format 

80 

81 Args: 

82 settings: LoggingSettings instance 

83 """ 

84 # Remove default handler 

85 logger.remove() 

86 

87 # Add handlers from config (functional approach using map-like pattern) 

88 if settings.handlers: 

89 for handler_config in settings.handlers: 

90 _add_handler(settings, handler_config) # type: ignore[arg-type] 

91 else: 

92 # If no handlers configured, add default console handler 

93 _add_default_handler(settings) 

94 

95 logger.debug(f"Logging initialized (level={settings.level})") 

96 

97 

98def get_logger(name: str | None = None) -> "logger": # type: ignore[valid-type] 

99 """ 

100 Get loguru logger instance. 

101 

102 Args: 

103 name: Optional context name (for future use) 

104 

105 Returns: 

106 loguru.logger instance 

107 """ 

108 if name: 

109 return logger.bind(context=name) 

110 return logger 

111 

112 

113# Export logger for convenience 

114__all__ = ["init_logging", "get_logger", "logger"]