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(

Reply via email to