Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/uipath/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "uipath"
version = "2.10.39"
version = "2.10.40"
description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools."
readme = { file = "README.md", content-type = "text/markdown" }
requires-python = ">=3.11"
Expand Down
35 changes: 28 additions & 7 deletions packages/uipath/src/uipath/telemetry/_track.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,15 +325,29 @@ def track_event(
_logger.debug(f"Event tracking error for '{name}'", exc_info=True)

@staticmethod
def flush() -> None:
"""Flush any pending telemetry events."""
if _AppInsightsEventClient._client:
def flush(timeout: float = 5.0) -> None:
"""Flush any pending telemetry events.

Blocks for up to ``timeout`` seconds waiting for the flush to complete.
On the happy path (network reachable) the flush finishes well within the
timeout and no data is lost. When the ingestion endpoint is unreachable
(e.g. Automation Suite / CI with restricted egress) the synchronous sender
would otherwise retry indefinitely, preventing the process from exiting and
leaving eval runs stuck in Running state. The flush runs in a daemon thread
so that if it does not finish within ``timeout`` seconds it is abandoned
rather than blocking the process indefinitely.
"""
import threading

if not _AppInsightsEventClient._client:
return

def _do_flush() -> None:
try:
_AppInsightsEventClient._client.flush()
# Check if items remain after flush (indicates send failure)
_AppInsightsEventClient._client.flush() # type: ignore[union-attr]
try:
remaining = (
_AppInsightsEventClient._client.channel.queue._queue.qsize()
_AppInsightsEventClient._client.channel.queue._queue.qsize() # type: ignore[union-attr]
)
if remaining > 0:
_logger.warning(
Expand All @@ -343,10 +357,17 @@ def flush() -> None:
except Exception:
pass
except Exception as e:
# Log but don't raise - telemetry should never break the main application
_logger.warning(f"Failed to flush telemetry events: {e}")
_logger.debug("Telemetry flush error", exc_info=True)

thread = threading.Thread(target=_do_flush, daemon=True)
thread.start()
thread.join(timeout=timeout)
if thread.is_alive():
_logger.warning(
"AppInsights flush: timed out after %.1fs, abandoning send", timeout
)

@staticmethod
def register_atexit_flush() -> None:
"""Register an atexit handler to flush events on process exit."""
Expand Down
2 changes: 1 addition & 1 deletion packages/uipath/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading