Skip to content

fix: isolate per-execution log buffers for parallel eval runs#102

Open
smflorentino wants to merge 3 commits intomainfrom
fix/parallel-eval-log-isolation
Open

fix: isolate per-execution log buffers for parallel eval runs#102
smflorentino wants to merge 3 commits intomainfrom
fix/parallel-eval-log-isolation

Conversation

@smflorentino
Copy link

@smflorentino smflorentino commented Mar 26, 2026

Summary

Fixes two bugs that caused jumbled and truncated log output when UIPATH_JOB_KEY is set and eval runs execute in parallel.

Bug 1: Interleaved output

When eval runs N items in parallel, each item creates a child UiPathRuntimeLogsInterceptor via UiPathExecutionRuntime.execute(). The old code called _redirect_stdout_stderr() in child mode, which did:

sys.stdout = LoggerWriter(stdout_logger, ...)
sys.stderr = LoggerWriter(stderr_logger, ...)

sys.stdout is a single global. Each child overwrites it with a new LoggerWriter instance. If 4 eval items run concurrently:

  • Child 1 sets sys.stdout to LoggerWriter_1
  • Child 2 sets sys.stdout to LoggerWriter_2 (LoggerWriter_1 is now orphaned)
  • Child 3 sets sys.stdout to LoggerWriter_3 (LoggerWriter_2 is now orphaned)
  • Child 4 sets sys.stdout to LoggerWriter_4 (LoggerWriter_3 is now orphaned)

All 4 tasks call print(), which writes to sys.stdout — but that's LoggerWriter_4. One buffer, four writers. Lines merge. When earlier children tear down and restore sys.stdout, the later children's LoggerWriter instances are orphaned along with any buffered content.

Bug 2: Truncated output

LoggerWriter.buffer was never flushed before teardown. Partial lines (no trailing \n) were silently discarded when the handler was closed.

Changes

  • LoggerWriter now maintains per-context buffers keyed by current_execution_id — concurrent tasks never share a buffer
  • Child interceptors no longer replace sys.stdout/sys.stderr — only the master owns global streams. Children register their handler on the stdout/stderr loggers; the existing filter system routes records correctly.
  • Teardown flushes buffers before clearing context and removing handlers

Test plan

  • Existing tests pass (86/86)
  • New unit tests for per-context buffer isolation
  • New test verifying child interceptors don't replace global streams
  • New async end-to-end test: 4 concurrent executions with interleaved logging.info() + print() — verified zero cross-contamination

🤖 Generated with Claude Code

When UIPATH_JOB_KEY is set and eval runs execute in parallel, two bugs
caused jumbled and truncated log output in the execution log file:

1. **Interleaved output**: Each child interceptor replaced the global
   sys.stdout/sys.stderr with a new LoggerWriter, stomping the previous
   one. All concurrent async tasks wrote to the last-installed writer's
   single shared buffer, merging partial lines from different executions.

2. **Truncated output**: LoggerWriter.buffer was never flushed before
   teardown. Partial lines (without a trailing newline) were silently
   discarded when the handler was closed.

Fix:
- LoggerWriter now maintains per-context buffers keyed by
  current_execution_id, so concurrent tasks never share a buffer.
- Child interceptors no longer replace sys.stdout/sys.stderr — only the
  master interceptor owns the global streams. Children register their
  handler on the stdout/stderr loggers; the existing filter system
  routes records to the correct handler.
- Teardown flushes buffers before clearing context and removing handlers,
  so partial lines are never lost.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
smflorentino and others added 2 commits March 26, 2026 08:45
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
chore: regenerate uv.lock after version bump to 0.10.1

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@smflorentino smflorentino force-pushed the fix/parallel-eval-log-isolation branch from c1895a9 to 8987807 Compare March 26, 2026 16:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant