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

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

2

3 import sys

4 from typing import TYPE_CHECKING

5

6 from loguru import logger

7

8 if TYPE_CHECKING:

9 from railway.core.settings import LoggingSettings

10

11

12 def _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

25 def _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

43 def _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

62 def _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

72 def 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

98 def 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"]