This is an automated email from the ASF dual-hosted git repository.
shahar1 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 55b3c58dea7 Return None from relative_path_from_logger for logs
outside base_log_folder (#75572)
55b3c58dea7 is described below
commit 55b3c58dea7406ca53cbad92b40941cdead9324c
Author: Shahar Epstein <[email protected]>
AuthorDate: Tue Oct 27 19:52:47 2026 +0200
Return None from relative_path_from_logger for logs outside base_log_folder
(#75572)
Path.relative_to raised ValueError for log files outside
[logging] base_log_folder (such as a Dag processor log directory configured
elsewhere, or a '<stderr>' handle). upload_to_remote hid this with a broad
except, but remote-log processors that called the function directly
crashed the Dag processor, so each provider had to add its own guard.
---
task-sdk/src/airflow/sdk/log.py | 6 +++++-
task-sdk/tests/task_sdk/test_log.py | 20 ++++++++++++++++++++
2 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/task-sdk/src/airflow/sdk/log.py b/task-sdk/src/airflow/sdk/log.py
index 01a540ed82d..efacfde33c2 100644
--- a/task-sdk/src/airflow/sdk/log.py
+++ b/task-sdk/src/airflow/sdk/log.py
@@ -235,7 +235,11 @@ def relative_path_from_logger(logger) -> Path | None:
from airflow.sdk.configuration import conf
base_log_folder = conf.get("logging", "base_log_folder")
- return Path(fname).relative_to(base_log_folder)
+ path = Path(fname)
+ if not path.is_relative_to(base_log_folder):
+ # Files outside base_log_folder (or non-file names like '<stderr>')
are kept local, not uploaded.
+ return None
+ return path.relative_to(base_log_folder)
def upload_to_remote(logger: FilteringBoundLogger, ti: RuntimeTI | None =
None):
diff --git a/task-sdk/tests/task_sdk/test_log.py
b/task-sdk/tests/task_sdk/test_log.py
index 5d150a0e53f..935f869d146 100644
--- a/task-sdk/tests/task_sdk/test_log.py
+++ b/task-sdk/tests/task_sdk/test_log.py
@@ -28,6 +28,8 @@ import structlog
from airflow.sdk import log as sdk_log
from airflow.sdk.execution_time.comms import MaskSecret
+from tests_common.test_utils.config import conf_vars
+
class TestConfigureLogging:
@pytest.mark.parametrize("sending_to_supervisor", [True, False])
@@ -93,6 +95,24 @@ class TestConfigureLogging:
assert processors == (initial_processor, sdk_log.mask_logs,
final_renderer)
+class TestRelativePathFromLogger:
+ @pytest.mark.parametrize(
+ ("file_name", "fileno"),
+ [
+ pytest.param("/elsewhere/dag_processor/file.log", 5,
id="outside-base-folder"),
+ pytest.param("<stderr>", 2, id="stderr"),
+ ],
+ )
+ def test_returns_none_outside_base_log_folder(self, tmp_path, file_name,
fileno):
+ logger = mock.Mock(spec=["_file"])
+ logger._file = mock.Mock(spec=["name", "fileno"])
+ logger._file.name = file_name
+ logger._file.fileno.return_value = fileno
+
+ with conf_vars({("logging", "base_log_folder"): str(tmp_path)}):
+ assert sdk_log.relative_path_from_logger(logger) is None
+
+
class TestMaskSecret:
def test_does_not_import_task_runner_outside_task_execution(self):
code = textwrap.dedent(