11"""
2- Simple, reliable logging utilities for the kymflow project .
2+ Simple, reliable logging utilities for the kymflow application .
33
4- - Configure logging once via `setup_logging(...)` at app startup.
4+ - Configure logging via `setup_logging(...)` at app startup.
55- Get module-specific loggers via `get_logger(__name__)`.
6+ - Reconfigure anytime by calling `setup_logging(...)` again.
67
78This uses the *root logger* so it plays nicely with most frameworks.
9+ Logs automatically go to ~/.kymflow/logs/kymflow.log by default.
810"""
911
1012from __future__ import annotations
1618from pathlib import Path
1719from typing import Optional , Union
1820
19- # Internal flag to avoid double-configuration
20- _CONFIGURED = False
21-
2221# Store the log file path for retrieval
2322_LOG_FILE_PATH : Optional [Path ] = None
2423
@@ -29,35 +28,39 @@ def _expand_path(path: Union[str, Path]) -> Path:
2928
3029def setup_logging (
3130 level : Union [str , int ] = "INFO" ,
32- log_file : Optional [Union [str , Path ]] = None ,
31+ log_file : Optional [Union [str , Path ]] = "~/.kymflow/logs/kymflow.log" ,
3332 max_bytes : int = 5_000_000 ,
3433 backup_count : int = 5 ,
3534) -> None :
3635 """
3736 Configure root logging with console + optional rotating file handler.
3837
39- Calling this multiple times is safe; handlers are only added once .
38+ Calling this multiple times will reconfigure logging (removes old handlers first) .
4039
4140 Parameters
4241 ----------
4342 level:
4443 Logging level for console (e.g. "DEBUG", "INFO").
4544 log_file:
46- Optional path to a log file. If None, no file handler is added.
45+ Path to a log file. Defaults to "~/.kymflow/logs/kymflow.log".
46+ Set to None to disable file logging (console only).
4747 max_bytes:
4848 Max size in bytes for rotating log file.
4949 backup_count:
5050 Number of rotated log files to keep.
5151 """
52- global _CONFIGURED
53- if _CONFIGURED :
54- return
55-
5652 # Convert string levels like "INFO" to logging.INFO
5753 if isinstance (level , str ):
5854 level = getattr (logging , level .upper (), logging .INFO )
5955
6056 root = logging .getLogger ()
57+
58+ # Remove existing handlers to allow reconfiguration
59+ # This prevents duplicate handlers while allowing logging to be reconfigured
60+ for handler in root .handlers [:]:
61+ handler .close ()
62+ root .removeHandler (handler )
63+
6164 root .setLevel (level )
6265
6366 # -------- Formatter --------
@@ -87,8 +90,9 @@ def setup_logging(
8790 file_handler .setLevel (logging .DEBUG ) # capture everything to file
8891 file_handler .setFormatter (formatter )
8992 root .addHandler (file_handler )
90-
91- _CONFIGURED = True
93+ else :
94+ # Clear log file path when file logging is disabled
95+ _LOG_FILE_PATH = None
9296
9397
9498def get_logger (name : Optional [str ] = None ) -> logging .Logger :
0 commit comments