Repository: kedro-org/kedro
File: kedro/framework/project/__init__.py (Python)

Code before:
    __str__ = _load_data_wrapper(str)


class _ProjectLogging(UserDict):
    def __init__(self) -> None:
        """Initialise project logging. The path to logging configuration is given in
        environment variable KEDRO_LOGGING_CONFIG (defaults to conf/logging.yml)."""

        # Check if a user path is set in the environment variable
        user_logging_path = os.environ.get("KEDRO_LOGGING_CONFIG")

        # Check if the default logging configuration exists
        default_logging_path = Path("conf/logging.yml")
        if not default_logging_path.exists():
            default_logging_path = Path(__file__).parent / "default_logging.yml"

        # Use the user path if available, otherwise, use the default path
        if user_logging_path and Path(user_logging_path).exists():
            path = Path(user_logging_path)
        else:
            path = default_logging_path
            logger = logging.getLogger(__name__)
            logger.warning(
                "Using `conf/logging.yml` as logging configuration. You can change this by setting the "
                "KEDRO_LOGGING_CONFIG environment variable accordingly."
            )

        # Load and apply the logging configuration
        logging_config = Path(path).read_text(encoding="utf-8")
        self.configure(yaml.safe_load(logging_config))

    def configure(self, logging_config: dict[str, Any]) -> None:
        """Configure project logging using ``logging_config`` (e.g. from project
        logging.yml). We store this in the UserDict data so that it can be reconfigured
        in _bootstrap_subprocess.
        """
        logging.config.dictConfig(logging_config)
        self.data = logging_config

    def set_project_logging(self, package_name: str) -> None:
        """Add the project level logging to the loggers upon provision of a package name.
        Checks if project logger already exists to prevent overwriting, if none exists
        it defaults to setting project logs at INFO level."""
        if package_name not in self.data["loggers"]:
            self.data["loggers"][package_name] = {"level": "INFO"}
            self.configure(self.data)


PACKAGE_NAME = None
LOGGING = _ProjectLogging()


Review comment (suggestion):
I'm on the fence on whether this should be `logger.warning` or `warnings.warn`... From https://docs.python.org/3/howto/logging.html#when-to-use-logging,

> [warnings.warn()](https://docs.python.org/3/library/warnings.html#warnings.warn) in library code if the issue is avoidable and the client application should be modified to eliminate the warning
> 
> A logger’s [warning()](https://docs.python.org/3/library/logging.html#logging.Logger.warning) method if there is nothing the client application can do about the situation, but the event should still be noted

Code after:
    __str__ = _load_data_wrapper(str)


class _ProjectLogging(UserDict):
    def __init__(self) -> None:
        """Initialise project logging. The path to logging configuration is given in
        environment variable KEDRO_LOGGING_CONFIG (defaults to conf/logging.yml)."""

        # Check if a user path is set in the environment variable
        user_logging_path = os.environ.get("KEDRO_LOGGING_CONFIG")

        # Check if the default logging configuration exists
        default_logging_path = Path("conf/logging.yml")
        if not default_logging_path.exists():
            default_logging_path = Path(__file__).parent / "default_logging.yml"

        # Use the user path if available, otherwise, use the default path
        if user_logging_path and Path(user_logging_path).exists():
            path = Path(user_logging_path)
        else:
            path = default_logging_path

        # Load and apply the logging configuration
        logging_config = Path(path).read_text(encoding="utf-8")
        self.configure(yaml.safe_load(logging_config))

        # Log info about the logging configuration
        if not user_logging_path and default_logging_path == Path("conf/logging.yml"):
            logger = logging.getLogger(__name__)
            logger.info(
                f"Using `{path}` as logging configuration. "
                f"You can change this by setting the KEDRO_LOGGING_CONFIG environment variable accordingly."
            )

    def configure(self, logging_config: dict[str, Any]) -> None:
        """Configure project logging using ``logging_config`` (e.g. from project
        logging.yml). We store this in the UserDict data so that it can be reconfigured
        in _bootstrap_subprocess.
        """
        logging.config.dictConfig(logging_config)
        self.data = logging_config

    def set_project_logging(self, package_name: str) -> None:
        """Add the project level logging to the loggers upon provision of a package name.
        Checks if project logger already exists to prevent overwriting, if none exists
        it defaults to setting project logs at INFO level."""
        if package_name not in self.data["loggers"]:
            self.data["loggers"][package_name] = {"level": "INFO"}
            self.configure(self.data)