This is an automated email from the ASF dual-hosted git repository.
jason810496 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 87089a53a2e Fix crash when tailing logs of a running task instance
(#69503)
87089a53a2e is described below
commit 87089a53a2e2358e96defae35979b8d271375914
Author: Jason(Zhe-You) Liu <[email protected]>
AuthorDate: Tue Jul 7 14:44:30 2026 +0900
Fix crash when tailing logs of a running task instance (#69503)
FileTaskHandler._read() returns an itertools.islice stream when
resuming a log read via metadata["log_pos"] (introduced in #63531),
but _is_logs_stream_like() only recognized chain and generator
objects, so the public read() wrapper raised
TypeError: Invalid log stream type... Got islice instead
whenever a client polled or tailed a running task's logs.
---
.../src/airflow/utils/log/file_task_handler.py | 2 +-
airflow-core/tests/unit/utils/test_log_handlers.py | 39 ++++++++++++++++++++++
2 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/airflow-core/src/airflow/utils/log/file_task_handler.py
b/airflow-core/src/airflow/utils/log/file_task_handler.py
index 9eec24b1813..81f9a89c458 100644
--- a/airflow-core/src/airflow/utils/log/file_task_handler.py
+++ b/airflow-core/src/airflow/utils/log/file_task_handler.py
@@ -398,7 +398,7 @@ def _interleave_logs(*log_streams: RawLogStream) ->
StructuredLogStream:
def _is_logs_stream_like(log) -> bool:
"""Check if the logs are stream-like."""
- return isinstance(log, (chain, GeneratorType))
+ return isinstance(log, (chain, islice, GeneratorType))
def _get_compatible_log_stream(
diff --git a/airflow-core/tests/unit/utils/test_log_handlers.py
b/airflow-core/tests/unit/utils/test_log_handlers.py
index 24a2e367a38..8248e57e4d5 100644
--- a/airflow-core/tests/unit/utils/test_log_handlers.py
+++ b/airflow-core/tests/unit/utils/test_log_handlers.py
@@ -504,6 +504,31 @@ class TestFileTaskLogHandler:
assert extract_events(log_handler_output_stream) == ["line 3"]
assert metadata == {"end_of_log": True, "log_pos": 3}
+
@patch("airflow.utils.log.file_task_handler.FileTaskHandler._read_from_local")
+ def test_read_respects_log_pos_metadata(self, mock_read_local,
create_task_instance):
+ """The public `read()` wrapper must accept the `islice` stream
`_read()` returns for log_pos reads."""
+ mock_read_local.return_value = (
+ ["the messages"],
+ [convert_list_to_stream(["line 1", "line 2", "line 3"])],
+ )
+ local_log_file_read = create_task_instance(
+ dag_id="dag_for_testing_local_log_read",
+ task_id="task_for_testing_local_log_read",
+ run_type=DagRunType.SCHEDULED,
+ logical_date=DEFAULT_DATE,
+ )
+ fth = FileTaskHandler("")
+
+ log_handler_output_stream, metadata = fth.read(
+ local_log_file_read,
+ try_number=1,
+ metadata={"log_pos": 2},
+ )
+
+ # Should resume from the third line only.
+ assert extract_events(log_handler_output_stream) == ["line 3"]
+ assert metadata == {"end_of_log": True, "log_pos": 3}
+
def test__read_from_local(self, tmp_path):
"""Tests the behavior of method _read_from_local"""
path1 = tmp_path / "hello1.log"
@@ -1179,6 +1204,20 @@ def test__is_sort_key_with_default_timestamp(timestamp,
line_num, expected):
True,
id="chain_log_stream",
),
+ pytest.param(
+ itertools.islice(
+ convert_list_to_stream(
+ [
+ "2022-11-16T00:05:54.278000-08:00",
+ "2022-11-16T00:05:54.457000-08:00",
+ ]
+ ),
+ 1,
+ None,
+ ),
+ True,
+ id="islice_log_stream",
+ ),
pytest.param(
[
"2022-11-16T00:05:54.278000-08:00",