Skip to content

Commit e45d675

Browse files
committed
fix: log a warning on Actor log-stream timeout instead of ending silently
1 parent 29fcef6 commit e45d675

1 file changed

Lines changed: 12 additions & 12 deletions

File tree

src/apify_client/_streamed_log.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,16 @@
2323
class StreamedLogBase:
2424
"""Base class for streaming and buffering chunked Actor run logs."""
2525

26-
# Test related flag to enable propagation of logs to the `caplog` fixture during tests.
2726
_force_propagate = False
27+
"""Test related flag to enable propagation of logs to the `caplog` fixture during tests."""
2828

29-
# The log stream is a long-poll request that should stay open for the whole Actor run; the server ends it
30-
# with EOF once the run finishes. impit applies its `timeout` to the whole request (streamed body included),
31-
# so any bounded value truncates a longer run mid-stream, which raised `impit.TimeoutException` here (#1040).
32-
# `no_timeout` is the only tier above `DEFAULT_TIMEOUT_MAX`; it maps to impit's ~24h cap, which is effectively
33-
# unbounded for real runs and mirrors the JS client, which sets no body timeout on its log stream.
3429
_stream_timeout: ClassVar[Timeout] = 'no_timeout'
30+
"""Timeout for the log-stream long-poll request, which stays open for the whole Actor run.
31+
32+
impit applies its `timeout` to the whole request including the streamed body, so any bounded value truncates a
33+
longer run mid-stream and raises `impit.TimeoutException` (#1040). `no_timeout` maps to impit's ~24h cap, which
34+
is effectively unbounded for real runs and mirrors the JS client.
35+
"""
3536

3637
def __init__(self, to_logger: logging.Logger, *, from_start: bool = True) -> None:
3738
if self._force_propagate:
@@ -162,10 +163,9 @@ def _stream_log(self) -> None:
162163
# Flush the last buffered part even if the read timed out or was stopped.
163164
self._log_buffer_content(include_last_part=True)
164165
except impit.TimeoutException:
165-
# With `no_timeout` the stream is bounded only by impit's ~24h whole-request cap, so this fires only
166-
# if the run outlives that cap or the connection stalls. The stream cannot continue either way, so the
167-
# thread ends quietly rather than surfacing a traceback via `threading.excepthook` (#1040).
168-
pass
166+
# With `no_timeout` this fires only if the run outlives impit's ~24h cap or the connection stalls.
167+
# The stream cannot continue, so warn and let the thread end instead of leaking a traceback (#1040).
168+
self._to_logger.warning('Log streaming stopped: the log stream request timed out.')
169169
except Exception:
170170
# Any other failure in log redirection must not escape the background thread; log it instead.
171171
self._to_logger.exception('Log redirection stopped due to unexpected error:')
@@ -243,8 +243,8 @@ async def _stream_log(self) -> None:
243243
self._log_buffer_content(include_last_part=True)
244244
except impit.TimeoutException:
245245
# As in `StreamedLog._stream_log`, impit's whole-request timeout on the long-lived stream is an
246-
# expected terminal condition, not an error, so end the task quietly instead of logging a traceback.
247-
pass
246+
# expected terminal condition, not an error, so log a warning and end the task instead of a traceback.
247+
self._to_logger.warning('Log streaming stopped: the log stream request timed out.')
248248
except Exception:
249249
# Exception in log redirection should not propagate further.
250250
self._to_logger.exception('Log redirection stopped due to unexpected error:')

0 commit comments

Comments
 (0)