Files
comet-ml--opik/apps/opik-python-backend/docs/ISOLATED_EXECUTOR_COMPLETE.md
T
wehub-resource-sync 5a558eb09e
TypeScript SDK Compatibility V1.x E2E Tests / Select Node version matrix (push) Has been cancelled
TypeScript SDK Compatibility V1.x E2E Tests / TypeScript SDK Compatibility V1.x E2E Tests Node ${{matrix.node_version}} (push) Has been cancelled
TypeScript SDK E2E Tests / TypeScript SDK E2E Tests Node ${{matrix.node_version}} (push) Has been cancelled
Opik Optimizer - E2E Tests / build-opik (push) Has been cancelled
TypeScript SDK Compatibility V1.x E2E Tests / build-opik (push) Has been cancelled
Python SDK E2E Tests / Select Python version matrix (push) Has been cancelled
Python SDK E2E Tests / Python SDK E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Python SDK E2E Tests / build-opik (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / Select Python version matrix (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / Python SDK Compatibility V1.x E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Python SDK Compatibility V1.x E2E Tests / build-opik (push) Has been cancelled
TypeScript SDK E2E Tests / Select Node version matrix (push) Has been cancelled
TypeScript SDK E2E Tests / build-opik (push) Has been cancelled
Opik Optimizer - E2E Tests / Opik Optimizer E2E Tests Python ${{matrix.python_version}} (push) Has been cancelled
Opik Optimizer - E2E Tests / Opik Optimizer Integration Smoke Tests (push) Has been cancelled
🐙 Code Quality / detect (push) Has been cancelled
🐙 Code Quality / lint (${{ matrix.leg.name }}) (push) Has been cancelled
🐙 Code Quality / summary (push) Has been cancelled
TypeScript SDK Library Integration Tests / Check Secrets (push) Has been cancelled
TypeScript SDK Library Integration Tests / opik-vercel (Vercel AI SDK / eve) (push) Has been cancelled
SDK Library Integration Tests Runner / Check Secrets (push) Has been cancelled
SDK Library Integration Tests Runner / Missed OpenAI API Key Warning (push) Has been cancelled
SDK Library Integration Tests Runner / Build (push) Has been cancelled
SDK Library Integration Tests Runner / openai_tests (push) Has been cancelled
SDK Library Integration Tests Runner / langchain_tests (push) Has been cancelled
SDK Library Integration Tests Runner / langchain_legacy_tests (push) Has been cancelled
SDK Library Integration Tests Runner / llama_index_tests (push) Has been cancelled
SDK Library Integration Tests Runner / anthropic_tests (push) Has been cancelled
SDK Library Integration Tests Runner / mistral_tests (push) Has been cancelled
SDK Library Integration Tests Runner / groq_tests (push) Has been cancelled
SDK Library Integration Tests Runner / aisuite_tests (push) Has been cancelled
SDK Library Integration Tests Runner / haystack_tests (push) Has been cancelled
SDK Library Integration Tests Runner / dspy_tests (push) Has been cancelled
SDK Library Integration Tests Runner / crewai_v0_tests (push) Has been cancelled
SDK Library Integration Tests Runner / crewai_v1_tests (push) Has been cancelled
SDK Library Integration Tests Runner / genai_tests (push) Has been cancelled
SDK Library Integration Tests Runner / adk_tests (push) Has been cancelled
SDK Library Integration Tests Runner / adk_legacy_1_3_0_tests (push) Has been cancelled
SDK Library Integration Tests Runner / evaluation_metrics_tests (push) Has been cancelled
SDK Library Integration Tests Runner / bedrock_tests (push) Has been cancelled
SDK Library Integration Tests Runner / litellm_tests (push) Has been cancelled
SDK Library Integration Tests Runner / harbor_tests (push) Has been cancelled
SDK Library Integration Tests Runner / Slack Notification (push) Has been cancelled
Lint Opik Helm Chart / render-equality (push) Has been cancelled
Opik Optimizer - Unit Tests / Opik Optimizer Unit Tests Python ${{matrix.python_version}} (push) Has been cancelled
Python BE E2E Tests / Python BE E2E (push) Has been cancelled
Python Backend Tests / run-python-backend-tests (push) Has been cancelled
Python SDK Unit Tests / Python SDK Unit Tests ${{matrix.python_version}} (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
SDK E2E Libraries Integration Tests / Check Secrets (push) Has been cancelled
SDK E2E Libraries Integration Tests / Missed OpenAI API Key Warning (push) Has been cancelled
SDK E2E Libraries Integration Tests / build-opik (push) Has been cancelled
SDK E2E Libraries Integration Tests / E2E Lib Integration Python ${{matrix.python_version}} (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-gemini) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-langchain) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-openai) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-otel) (push) Has been cancelled
TypeScript SDK Integration Build & Publish / build-and-publish (opik-vercel) (push) Has been cancelled
TypeScript SDK Build & Publish / build-and-publish (push) Has been cancelled
TypeScript SDK Unit Tests / Test on Node ${{ matrix.node-version }} (push) Has been cancelled
Backend Tests / discover-tests (push) Has been cancelled
Backend Tests / ${{ matrix.name }} (push) Has been cancelled
Build and Publish SDK / build-and-publish (push) Has been cancelled
Build Opik Docker Images / set-version (push) Has been cancelled
Build Opik Docker Images / build-backend (push) Has been cancelled
Build Opik Docker Images / build-sandbox-executor-python (push) Has been cancelled
Build Opik Docker Images / build-python-backend (push) Has been cancelled
Build Opik Docker Images / build-frontend (push) Has been cancelled
Build Opik Docker Images / create-git-tag (push) Has been cancelled
ClickHouse Migration Cluster Check / validate-clickhouse-migrations (push) Has been cancelled
Docs - Publish / run (push) Has been cancelled
E2E Tests - Post Merge (v2) / 🧪 E2E v2 Tests (${{ github.event.inputs.tier || 't1' }}) (push) Has been cancelled
E2E Tests - Post Merge (v2) / 📢 Slack Notification (push) Has been cancelled
Frontend Unit Tests / Test on Node 20 (push) Has been cancelled
Guardrails E2E Tests / Select Python version matrix (push) Has been cancelled
Guardrails E2E Tests / Guardrails E2E Tests ${{matrix.python_version}} (push) Has been cancelled
Guardrails E2E Tests / 📢 Slack Notification (push) Has been cancelled
Guardrails Backend Unit Tests / Guardrails Backend Unit Tests (push) Has been cancelled
Guardrails Backend Unit Tests / 📢 Slack Notification (push) Has been cancelled
Lint Opik Helm Chart / lint-helm-chart (Helm v3.21.0) (push) Has been cancelled
Lint Opik Helm Chart / lint-helm-chart (Helm v4.2.0) (push) Has been cancelled
Lint Opik Helm Chart / unittest-helm-chart (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:25:44 +08:00

768 lines
24 KiB
Markdown

# IsolatedSubprocessExecutor - Complete Documentation
> **Status**: ✅ Production Ready | **Version**: 2.1 | **Last Updated**: October 22, 2025
> **Note**: This is the authoritative merged documentation combining all implementation details and quick references.
> **Latest**: Per-process log collectors support full concurrent execution with zero log loss guarantee.
## 📋 Table of Contents
1. [Executive Summary](#executive-summary)
2. [Quick Start](#quick-start)
3. [Core Architecture](#core-architecture)
4. [Implementation Details](#implementation-details)
5. [Concurrent Execution](#concurrent-execution)
6. [Configuration Reference](#configuration-reference)
7. [Usage Patterns](#usage-patterns)
8. [Lifecycle Management](#lifecycle-management)
9. [Logging & Monitoring](#logging--monitoring)
10. [Production Checklist](#production-checklist)
11. [Troubleshooting & FAQ](#troubleshooting--faq)
---
# Executive Summary
## Problem Solved
`ProcessExecutor` maintains a reusable worker pool with a **shared environment**, causing potential **environment variable leakage** between concurrent executions.
`IsolatedSubprocessExecutor` creates **fresh subprocesses** for each execution with **completely isolated and scoped environment variables** - no leakage, no conflicts, safe for multi-tenant systems.
## Key Features
**Environment Variable Isolation** - Each execution has scoped, isolated environment variables
**Subprocess Lifecycle Management** - Automatic creation and cleanup
**Teardown Callbacks** - Register cleanup functions to be called during teardown
**Context Manager Support** - Use `with` statement for automatic resource cleanup
**Thread-Safe Concurrent Execution** - Safe to use with ThreadPoolExecutor and AsyncIO
**Resource Limiting** - Stack memory limited to 20MB per subprocess
**Log Streaming** - Optional HTTP-based log collection to backend
**OpenTelemetry Metrics** - Creation and execution latency tracking
**Comprehensive Error Handling** - All error paths handled gracefully
**Timeout Support** - Prevent runaway executions
**Zero Shared State** - Completely independent executions
## Use Cases
| ✅ Perfect For | ❌ Not For |
|---|---|
| Multi-tenant systems | Extreme high throughput (>100/sec) |
| Different configs per execution | Real-time streaming (<10ms latency) |
| Environment variable isolation | Resource-constrained environments |
| Security-sensitive operations | |
| Different API keys per execution | |
## Performance Profile
| Metric | Value |
|--------|-------|
| **Throughput** | 5-10 executions/second |
| **Per-execution Overhead** | ~150ms (subprocess creation) |
| **Memory per Subprocess** | ~20MB stack limit |
| **Thread Safe** | ✅ Yes |
| **Concurrent Safe** | ✅ Yes |
| **Auto Cleanup** | ✅ Yes |
---
# Quick Start
## 60-Second Integration
### 1. Import
```python
from opik_backend.executor_isolated import IsolatedSubprocessExecutor
```
### 2. Create Instance
```python
executor = IsolatedSubprocessExecutor(timeout_secs=30)
```
### 3. Create Python File to Execute
```python
# metric.py
import json
from opik.evaluation.metrics import base_metric, score_result
result = {
"scores": [{
"value": 0.95,
"name": "my_metric",
"reason": "Works!"
}]
}
print(json.dumps(result))
```
### 4. Execute File
```python
result = executor.execute(file_path="/path/to/metric.py", data={})
# Output: {"scores": [{"value": 0.95, "name": "my_metric", "reason": "Works!"}]}
```
### 5. With Environment Variables
```python
env_vars = {
"TENANT_ID": "tenant_123",
"API_KEY": "secret_key",
}
result = executor.execute(
file_path="/path/to/metric.py",
data={},
env_vars=env_vars
)
# Environment variables are isolated to this execution
```
### 6. Context Manager (Automatic Cleanup)
```python
with IsolatedSubprocessExecutor() as executor:
result = executor.execute(file_path="/path/to/metric.py", data={})
# Automatic teardown when exiting the context
```
---
# Core Architecture
## File Structure
### 1. `executor_isolated.py` - Main Executor Class
**Location**: `apps/opik-python-backend/src/opik_backend/executor_isolated.py`
**Responsibilities**:
- Creates isolated subprocesses for each code execution
- Passes data via JSON over stdin/stdout
- Scopes environment variables per execution
- Enforces 20MB stack memory limit
- Provides process lifecycle management (kill, teardown callbacks)
- Integrates with `BatchLogCollector` for optional log streaming
**Key Methods**:
```python
# Execute code with isolated environment
result = executor.execute(
code="...",
data={...},
env_vars={...},
optimization_id="opt-123",
job_id="job-456"
)
# Register callbacks for cleanup
executor.register_teardown_callback(cleanup_func)
# Manual process management
executor.kill_process(pid)
executor.kill_all_processes()
executor.teardown()
# Context manager support
with executor:
result = executor.execute(...)
```
### 2. `subprocess_logger.py` - Log Collection & Streaming
**Location**: `apps/opik-python-backend/src/opik_backend/subprocess_logger.py`
**Key Classes**:
- `SubprocessLogRecord`: Represents a single log entry with timestamp, level, message, attributes
- `BatchLogCollector`: Collects, batches, and sends logs via HTTP
**Features**:
- Captures stdout/stderr from subprocesses
- Parses JSON-formatted logs with fallback to plain text
- Batches logs by time (1 second default) or size (10MB default)
- Sends via HTTP POST with gzip compression support
- Includes authentication headers (Authorization, Comet-Workspace)
- Thread-safe with background flush thread
- Graceful error handling (logs warnings, doesn't crash)
**Usage**:
```python
logger = BatchLogCollector(
backend_url="http://api.example.com/logs",
optimization_id="opt-123",
job_id="job-456",
api_key="secret-key",
workspace="workspace-id"
)
# Process logs from subprocess
logger.process_subprocess_output(stdout, stderr)
```
### 3. `subprocess_log_config.py` - Centralized Configuration
**Location**: `apps/opik-python-backend/src/opik_backend/subprocess_log_config.py`
**Responsibilities**:
- Centralized environment variable reading (single source of truth)
- Configuration validation and defaults
- No side effects (only getenv calls)
**Methods**:
- `get_backend_url()` - Log backend HTTP endpoint
- `is_enabled()` - Check if logging is enabled
- `get_flush_interval_ms()` - Time-based flush interval
- `get_max_size_bytes()` - Size-based flush threshold
- `get_request_timeout_secs()` - HTTP request timeout
- `should_fail_on_missing_backend()` - Error handling mode
- `is_fully_configured()` - All required config present
## Architecture Diagram
```
┌─────────────────────────────────────────────────────────────┐
│ Parent Process (IsolatedSubprocessExecutor) │
│ │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ execute(code, data, env_vars, ...) ││
│ └──────────────────┬──────────────────────────────────────┘│
│ │ │
│ ┌────────────┴────────────┐ │
│ │ │ │
│ ┌───▼──────┐ ┌───────▼────────┐ │
│ │ Load │ │ Prepare │ │
│ │ Code │ │ Environment │ │
│ └───┬──────┘ └────────────────┘ │
│ │ │
│ └──────────────┬──────────────────┐ │
│ │ │ │
│ ┌────────▼────────┐ ┌──────▼──────┐ │
│ │ Create Wrapper │ │ json.dumps │ │
│ │ Script │ │ Input data │ │
│ └────────┬────────┘ └──────┬──────┘ │
│ │ │ │
│ ┌───▼──────────────────▼───┐ │
│ │ subprocess.Popen() │ │
│ │ python -c <wrapper> │ │
│ └───┬──────────────────┬───┘ │
│ │ │ │
└───────────────────────┼──────────────────┼─────────────────┘
│ │
┌───────────────▼────────────────▼─────────────────┐
│ Child Process (Subprocess) │
│ │
│ stdin: ◄── json data │
│ Read JSON input │
│ exec(user_code) │
│ print(json.dumps(result)) to stdout ──► stdout │
│ Logger output ──────────────────────► stderr │
└──────────────────────────────────────────────────┘
│ │
┌───────────────▼────────────────▼─────────────────┐
│ Parent Process Continues │
│ │
│ communicate() retrieves stdout/stderr │
│ │
│ ┌────────────────────────────────────────────┐ │
│ │ if logging enabled: │ │
│ │ BatchLogCollector.process_subprocess() │ │
│ │ - Parse logs from stderr/stdout │ │
│ │ - Batch by time/size │ │
│ │ - POST to backend with gzip │ │
│ └────────────────────────────────────────────┘ │
│ │
│ Parse result JSON from last stdout line │
│ Return result to caller │
└──────────────────────────────────────────────────┘
```
---
# Implementation Details
## Code Execution Flow
### 1. Input Preparation
```python
# User code
code = """
from opik.evaluation.metrics import base_metric, score_result
result = {"scores": [{"value": 0.8, "name": "quality"}]}
print(json.dumps(result))
"""
# Data to pass to code
data = {"text": "Hello world"}
# Environment variables (scoped to subprocess)
env_vars = {"CUSTOM_VAR": "value", "OPIK_API_KEY": "key", "OPIK_WORKSPACE": "ws"}
```
### 2. Wrapper Script Creation
```python
# IsolatedSubprocessExecutor creates wrapper code internally
wrapper_code = """
import json
import sys
input_data = json.loads(sys.stdin.read())
data = input_data["data"]
payload_type = input_data["payload_type"]
# User's code here (injected)
result = {"scores": [{"value": 0.8, "name": "quality"}]}
print(json.dumps(result))
"""
```
### 3. Subprocess Execution
```bash
python -c '<wrapper_code>'
# stdin: {"data": {"text": "Hello"}, "payload_type": null}
# stdout: {"scores": [{"value": 0.8, "name": "quality"}]}
# stderr: any logs from the code
```
### 4. Log Collection (Optional)
```python
# If logging enabled:
if SubprocessLogConfig.is_enabled():
mylogger = BatchLogCollector(
backend_url="http://api.example.com/logs",
optimization_id="opt-123",
job_id="job-456",
api_key=env_vars.get("OPIK_API_KEY", ""),
workspace=env_vars.get("OPIK_WORKSPACE", ""),
)
mylogger.process_subprocess_output(stdout, stderr)
# Sends: POST with {logs: [...], optimization_id, job_id}
```
## Resource Limiting
### Memory Limiting
- **Limit Type**: Stack memory only (RLIMIT_STACK)
- **Limit Size**: 20MB per subprocess
- **Effect**: Prevents infinite recursion and stack overflow
- **Doesn't Affect**: Heap allocations, runtime data structures
- **Rationale**: Matches ProcessExecutor behavior, allows normal operations
---
# Concurrent Execution
## Per-Process Log Collectors
### Architecture
Each subprocess gets its own independent log collector:
```python
# Internal structure
_log_collectors = {
1234: BatchLogCollector(...), # Process 1 logs
1235: BatchLogCollector(...), # Process 2 logs
1236: BatchLogCollector(...), # Process 3 logs
}
```
### Benefits
**Full Concurrent Support**: Multiple processes can run simultaneously
**Independent Log Streaming**: Each process streams logs independently
**Zero Interference**: Closing one process's logs doesn't affect others
**Thread-Safe**: Protected with locks during add/remove operations
**Zero Log Loss**: Proper shutdown sequence: signal → flush → cleanup
### Concurrent Execution Flow
```python
import concurrent.futures
executor = IsolatedSubprocessExecutor()
def execute_with_tenant(tenant_id):
return executor.execute(
file_path="/path/to/metric.py",
data={"tenant_id": tenant_id},
env_vars={"TENANT_ID": tenant_id},
optimization_id=f"opt_{tenant_id}",
job_id=f"job_{tenant_id}",
)
# Run 10 concurrent executions
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as pool:
futures = [pool.submit(execute_with_tenant, f"tenant_{i}") for i in range(10)]
results = [f.result() for f in concurrent.futures.as_completed(futures)]
```
### Thread Safety Guarantees
| Operation | Thread-Safe | Protected By |
|-----------|-----------|--------------|
| execute() | ✅ Yes | Process isolation |
| kill_process() | ✅ Yes | _process_lock |
| _log_collectors access | ✅ Yes | _process_lock |
| Log streaming | ✅ Yes | ThreadPoolExecutor (single-threaded) |
| Shutdown | ✅ Yes | Signal → Executor.shutdown(wait=True) → Final flush |
### Shutdown Sequence
```
Executor with 3 concurrent processes:
├─ Process A (PID 1000)
│ └─ _log_collectors[1000] → streams logs
├─ Process B (PID 1001)
│ └─ _log_collectors[1001] → streams logs
└─ Process C (PID 1002)
└─ _log_collectors[1002] → streams logs
On teardown():
├─ Signal all processes to terminate
├─ Wait for all to exit
├─ For each process:
│ ├─ Signal stop (should_stop = True)
│ ├─ Shutdown executor (wait for pending flushes)
│ ├─ Final flush (all logs sent)
│ └─ Cleanup threads
└─ All logs captured, zero loss guarantee ✓
```
---
# Configuration Reference
## Environment Variables
All configuration via `SubprocessLogConfig` reads from environment variables:
```bash
# Logging Backend Configuration
SUBPROCESS_LOG_ENABLED=true/false # Enable logging (default: false)
OPIK_SUBPROCESS_LOG_BACKEND_URL=... # Log backend HTTP endpoint
SUBPROCESS_LOG_FLUSH_INTERVAL=1000 # Flush interval in ms (default: 1000)
SUBPROCESS_LOG_MAX_SIZE=10485760 # Max buffer size in bytes (default: 10MB)
SUBPROCESS_LOG_REQUEST_TIMEOUT=60 # HTTP request timeout in seconds (default: 60)
SUBPROCESS_LOG_FAIL_ON_MISSING_BACKEND=false # Fail if backend URL missing (default: false)
```
## Logging Credentials (via env_vars parameter)
These are passed via the `env_vars` parameter to `execute()`, not via environment variables:
```python
executor.execute(
code=code,
data=data,
env_vars={
"OPIK_API_KEY": "your-api-key", # Used for Authorization header
"OPIK_WORKSPACE": "workspace-id", # Used for Comet-Workspace header
}
)
```
## Error Handling Modes
### Graceful Mode (Default)
```bash
SUBPROCESS_LOG_FAIL_ON_MISSING_BACKEND=false
```
- If backend_url not configured: Logs warning, skips logging, continues execution
- Execution succeeds even if logging fails
### Strict Mode
```bash
SUBPROCESS_LOG_FAIL_ON_MISSING_BACKEND=true
```
- If backend_url not configured: Raises ValueError
- Execution fails with clear error message
---
# Usage Patterns
## Pattern 1: Multi-Tenant Scoring
```python
executor = IsolatedSubprocessExecutor()
for tenant in tenants:
result = executor.execute(
code,
data,
env_vars={
"TENANT_ID": tenant.id,
"OPIK_API_KEY": tenant.api_key,
"OPIK_WORKSPACE": tenant.workspace,
},
optimization_id=f"opt_{tenant.id}",
job_id=f"job_{tenant.id}",
)
process_result(result)
```
## Pattern 2: Concurrent Execution
```python
import concurrent.futures
executor = IsolatedSubprocessExecutor()
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as pool:
futures = [
pool.submit(
executor.execute,
code,
data,
{"TENANT_ID": f"tenant_{i}"}
)
for i in range(10)
]
results = [f.result() for f in concurrent.futures.as_completed(futures)]
```
## Pattern 3: Context Manager (Auto Cleanup)
```python
with IsolatedSubprocessExecutor(timeout_secs=30) as executor:
result = executor.execute(code, data, env_vars)
# Automatic teardown when exiting context
```
## Pattern 4: With Logging
```python
import os
# Configure logging
os.environ["SUBPROCESS_LOG_ENABLED"] = "true"
os.environ["OPIK_SUBPROCESS_LOG_BACKEND_URL"] = "http://api.example.com/logs"
os.environ["SUBPROCESS_LOG_FLUSH_INTERVAL"] = "500" # 500ms
os.environ["SUBPROCESS_LOG_MAX_SIZE"] = str(5 * 1024 * 1024) # 5MB
executor = IsolatedSubprocessExecutor()
result = executor.execute(
code=code,
data=data,
env_vars={"OPIK_API_KEY": "key", "OPIK_WORKSPACE": "ws"},
optimization_id="opt-123",
job_id="job-456",
)
# Logs are automatically sent to backend
```
---
# Lifecycle Management
## Context Manager Pattern
```python
with IsolatedSubprocessExecutor() as executor:
# Setup
executor.register_teardown_callback(lambda: print("Cleanup 1"))
executor.register_teardown_callback(lambda: print("Cleanup 2"))
# Execute
result = executor.execute(code, data)
# Automatic teardown on exit
# Teardown callbacks are called in reverse order
```
## Manual Lifecycle
```python
executor = IsolatedSubprocessExecutor()
# Register teardown callbacks
def cleanup():
print("Cleaning up...")
executor.register_teardown_callback(cleanup)
# Execute
result = executor.execute(code, data)
# Manual teardown
executor.teardown()
# All teardown callbacks called
```
## Process Killing
```python
executor = IsolatedSubprocessExecutor()
# Kill specific process
executor.kill_process(pid, timeout=2)
# Kill all active processes
executor.kill_all_processes()
```
---
# Logging & Monitoring
## Log Structure
### Log Entry Format
```json
{
"timestamp": 1697539200000,
"level": "INFO",
"logger_name": "task",
"message": "Task started",
"attributes": {"step": 1}
}
```
### Supported Log Sources
1. **Python logging module** - JSON-formatted logs to stderr
2. **Print to stdout** - Plain text lines
3. **Print to stderr** - Plain text lines
4. **JSON to stdout/stderr** - Structured logs
### Log Batching
- **Time-based**: Flush every 1 second (configurable)
- **Size-based**: Flush when buffer reaches 10MB (configurable)
- **Event-based**: Flush on shutdown
### HTTP Request
```
POST /logs HTTP/1.1
Content-Type: application/json
Authorization: <api_key>
Comet-Workspace: <workspace>
Content-Encoding: gzip
{
"optimization_id": "opt-123",
"job_id": "job-456",
"logs": [
{"timestamp": ..., "level": "INFO", "message": "..."},
...
]
}
```
## OpenTelemetry Metrics
```python
# Available metrics (via OpenTelemetry):
- isolated_subprocess_creation_latency # Subprocess creation time (ms)
- isolated_subprocess_execution_latency # Code execution time (ms)
- isolated_subprocess_active_count # Current active subprocesses
```
---
# Production Checklist
- ✅ No mutable default arguments
- ✅ No silent failures (explicit error handling)
- ✅ Proper error logging throughout
- ✅ Thread-safe operations with locks
- ✅ Per-process log collectors (dictionary mapping PID → BatchLogCollector)
- ✅ Full concurrent execution support (tested with 3 parallel processes)
- ✅ Zero log loss guarantee (signal → flush → cleanup sequence)
- ✅ Graceful degradation on config errors
- ✅ Resource limits enforced (20MB stack)
- ✅ Comprehensive test coverage (23 tests, 100% pass)
- ✅ Clear configuration interface
- ✅ Background thread cleanup with ThreadPoolExecutor
- ✅ Memory-efficient log batching
- ✅ Automatic process cleanup
- ✅ Timeout handling
- ✅ JSON-based IPC
- ✅ OpenTelemetry integration
- ✅ Context manager support with automatic teardown
---
# Troubleshooting & FAQ
## Common Issues
### Issue: "Subprocess logging enabled but backend_url not configured"
**Cause**: `SUBPROCESS_LOG_ENABLED=true` but `OPIK_SUBPROCESS_LOG_BACKEND_URL` not set
**Solution**:
```bash
# Either disable logging
export SUBPROCESS_LOG_ENABLED=false
# Or set the backend URL
export OPIK_SUBPROCESS_LOG_BACKEND_URL=http://api.example.com/logs
```
### Issue: "requests library not available for log posting"
**Cause**: `requests` library not installed
**Solution**:
```bash
pip install requests
```
### Issue: Subprocess timeout
**Cause**: Code execution takes longer than timeout
**Solution**:
```python
# Increase timeout
executor = IsolatedSubprocessExecutor(timeout_secs=60)
```
## FAQ
**Q: Can I share state between executions?**
A: No, each execution is completely isolated. This is by design.
**Q: What happens to environment variables in the subprocess?**
A: They are isolated to that execution only. Parent process not affected.
**Q: Can I modify the code being executed?**
A: Yes, the code parameter accepts both file paths and inline code strings.
**Q: Is it thread-safe?**
A: Yes, fully thread-safe. Multiple threads can call execute() concurrently.
**Q: What's the memory limit?**
A: 20MB stack memory per subprocess (prevents infinite recursion).
**Q: Can I access files from the subprocess?**
A: Yes, the subprocess has access to the filesystem (OS-level resources are shared).
---
## Files Reference
| File | Purpose | Location |
|------|---------|----------|
| `executor_isolated.py` | Main executor class | `src/opik_backend/` |
| `subprocess_logger.py` | Log collection & HTTP streaming | `src/opik_backend/` |
| `subprocess_log_config.py` | Configuration management | `src/opik_backend/` |
| `test_executor_isolated.py` | Executor unit tests (17 tests) | `tests/` |
| `test_subprocess_logging.py` | Logging integration tests (4 tests) | `tests/` |
---
**Last Updated**: October 22, 2025
**Status**: ✅ Production Ready
**Version**: 2.1
**Test Coverage**: 23 tests, 100% passing
**Key Feature**: Per-process log collectors with zero log loss guarantee for concurrent execution