This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 462ce9a6746 Fix module loading in logging config (#54555)
462ce9a6746 is described below
commit 462ce9a674690e3157e68f70381008947f33418d
Author: Vic Wen <[email protected]>
AuthorDate: Wed Aug 20 06:11:31 2025 +0800
Fix module loading in logging config (#54555)
* fix(logging): fix module loading by using `import_module()`
- Replace `import_string()` with `import_module()` in
`load_logging_config()`
- Fix issue where simple module paths like `log_config.LOGGING_CONFIG`
failed
- Add `importlib` import inside function to avoid circular imports
* test(logging): add tests for logging config module path handling
- Add parametrized test for simple and nested module paths
- Add test for graceful fallback when remote logging vars are missing
- Create helper method to reduce test code duplication
- Test both log_config.LOGGING_CONFIG and nested.module.LOGGING_CONFIG
scenarios
Ensures logging config works with various module path structures
* fix(logging): fix module loading by using `import_module()`
- Replace `import_string()` with `import_module()` in
`load_logging_config()`
- Fix issue where simple module paths like `log_config.LOGGING_CONFIG`
failed
- Add `importlib` import inside function to avoid circular imports
* test(logging): add tests for logging config module path handling
- Add parametrized test for simple and nested module paths
- Add test for graceful fallback when remote logging vars are missing
- Create helper method to reduce test code duplication
- Test both log_config.LOGGING_CONFIG and nested.module.LOGGING_CONFIG
scenarios
Ensures logging config works with various module path structures
* fix(logging): fix module loading by using `import_module()`
- Replace `import_string()` with `import_module()` in
`load_logging_config()`
- Fix issue where simple module paths like `log_config.LOGGING_CONFIG`
failed
- Add `importlib` import inside function to avoid circular imports
* test(logging): add tests for logging config module path handling
- Add parametrized test for simple and nested module paths
- Add test for graceful fallback when remote logging vars are missing
- Create helper method to reduce test code duplication
- Test both log_config.LOGGING_CONFIG and nested.module.LOGGING_CONFIG
scenarios
Ensures logging config works with various module path structures
* refactor(logging): move `import_module` to the top level
* test(logging): optimize test constants to reduce code duplication
Replace `SETTINGS_FILE_NESTED_MODULE` replicate part with string
replacement from `SETTINGS_FILE_SIMPLE_MODULE`
* test(logging): use shared helper method for logging config validation
Replace manual assertions with `self._verify_basic_logging_config` in
fallback test to ensure consistent validation across all logging config tests.
* refactor(logging_config): simplify, improve test config vars
- Remove redundant `SETTINGS_FILE_NESTED_MODULE`
- Rename `SETTINGS_FILE_SIMPLE_MODULE` to `SETTINGS_FILE_WITH_REMOTE_VARS`
* refactor(logging): imporve & simplify logging testing file
1. remove `SETTING_FILE_NO_REMOTE_VARS` bcz redundant var
2. add return typo with `_verify_basic_logging_config`
3. add unittest.mock `call`
---
airflow-core/src/airflow/logging_config.py | 5 +-
.../tests/unit/core/test_logging_config.py | 57 +++++++++++++++++++++-
2 files changed, 60 insertions(+), 2 deletions(-)
diff --git a/airflow-core/src/airflow/logging_config.py
b/airflow-core/src/airflow/logging_config.py
index e6d837bc220..a391c1c6361 100644
--- a/airflow-core/src/airflow/logging_config.py
+++ b/airflow-core/src/airflow/logging_config.py
@@ -19,6 +19,7 @@ from __future__ import annotations
import logging
import warnings
+from importlib import import_module
from logging.config import dictConfig
from typing import TYPE_CHECKING, Any
@@ -73,7 +74,9 @@ def load_logging_config() -> tuple[dict[str, Any], str]:
else:
modpath = logging_class_path.rsplit(".", 1)[0]
try:
- mod = import_string(modpath)
+ mod = import_module(modpath)
+
+ # Load remote logging configuration from the custom module
REMOTE_TASK_LOG = getattr(mod, "REMOTE_TASK_LOG")
DEFAULT_REMOTE_CONN_ID = getattr(mod, "DEFAULT_REMOTE_CONN_ID",
None)
except Exception as err:
diff --git a/airflow-core/tests/unit/core/test_logging_config.py
b/airflow-core/tests/unit/core/test_logging_config.py
index 72864b066b3..b92a7f0bcfb 100644
--- a/airflow-core/tests/unit/core/test_logging_config.py
+++ b/airflow-core/tests/unit/core/test_logging_config.py
@@ -24,7 +24,7 @@ import os
import pathlib
import sys
import tempfile
-from unittest.mock import patch
+from unittest.mock import call, patch
import pytest
@@ -96,6 +96,11 @@ SETTINGS_FILE_EMPTY = """
# Other settings here
"""
+SETTINGS_FILE_WITH_REMOTE_VARS = f"""{SETTINGS_FILE_VALID}
+REMOTE_TASK_LOG = None
+DEFAULT_REMOTE_CONN_ID = "test_conn_id"
+"""
+
SETTINGS_DEFAULT_NAME = "custom_airflow_local_settings"
@@ -191,6 +196,15 @@ class TestLoggingSettings:
importlib.reload(airflow_local_settings)
configure_logging()
+ def _verify_basic_logging_config(
+ self, logging_config: dict, logging_class_path: str, expected_path: str
+ ) -> None:
+ """Helper method to verify basic logging config structure"""
+ assert isinstance(logging_config, dict)
+ assert logging_config["version"] == 1
+ assert "airflow.task" in logging_config["loggers"]
+ assert logging_class_path == expected_path
+
# When we try to load an invalid config file, we expect an error
def test_loading_invalid_local_settings(self):
from airflow.logging_config import configure_logging, log
@@ -365,3 +379,44 @@ class TestLoggingSettings:
airflow.logging_config.configure_logging()
assert isinstance(airflow.logging_config.REMOTE_TASK_LOG,
HdfsRemoteLogIO)
+
+ @pytest.mark.parametrize(
+ "settings_content,module_structure,expected_path",
+ [
+ (SETTINGS_FILE_WITH_REMOTE_VARS, None,
f"{SETTINGS_DEFAULT_NAME}.LOGGING_CONFIG"),
+ (
+ SETTINGS_FILE_WITH_REMOTE_VARS,
+ "nested.config.module",
+ f"nested.config.module.{SETTINGS_DEFAULT_NAME}.LOGGING_CONFIG",
+ ),
+ ],
+ )
+ def test_load_logging_config_module_paths(
+ self, settings_content: str, module_structure: str, expected_path: str
+ ):
+ """Test that load_logging_config works with different module path
structures"""
+ dir_structure = module_structure.replace(".", "/") if module_structure
else None
+
+ with settings_context(settings_content, dir_structure):
+ from airflow.logging_config import load_logging_config
+
+ logging_config, logging_class_path = load_logging_config()
+ self._verify_basic_logging_config(logging_config,
logging_class_path, expected_path)
+
+ def test_load_logging_config_fallback_behavior(self):
+ """Test that load_logging_config falls back gracefully when remote
logging vars are missing"""
+ with settings_context(SETTINGS_FILE_VALID):
+ from airflow.logging_config import load_logging_config
+
+ with patch("airflow.logging_config.log") as mock_log:
+ logging_config, logging_class_path = load_logging_config()
+
+ self._verify_basic_logging_config(
+ logging_config, logging_class_path,
f"{SETTINGS_DEFAULT_NAME}.LOGGING_CONFIG"
+ )
+
+ mock_log.info.mock_calls = [
+ call(
+ "Remote task logs will not be available due to an
error: %s",
+ )
+ ]