Local state management for installed MCP servers in ~/.hive/mcp_registry/. Supports install from registry index, add_local for running servers, resolve_for_agent with include/tags/exclude/profile/max_tools/versions selection, health checks via MCPConnectionManager, and JSON type validation at the mcp_registry.json boundary. Integration points: AgentRunner, queen orchestrator, credential tester all load mcp_registry.json with error handling. ToolRegistry gains load_registry_servers() with retry and structured DX-4 logging.
Observability - Structured Logging
Configuration via Environment Variables
Control logging format using environment variables:
# JSON logging (production) - Machine-parseable, one line per log
export LOG_FORMAT=json
python -m my_agent run
# Human-readable (development) - Color-coded, easy to read
# Default if LOG_FORMAT is not set
python -m my_agent run
Alternative: Set ENV=production to automatically use JSON format:
export ENV=production
python -m my_agent run
Overview
The Hive framework provides automatic structured logging with trace context propagation. Logs include correlation IDs (trace_id, execution_id) that automatically follow your agent execution flow.
Features:
- Zero developer friction: Standard
logger.info()calls automatically get trace context - ContextVar-based propagation: Thread-safe and async-safe for concurrent executions
- Dual output modes: JSON for production, human-readable for development
- Automatic correlation:
trace_idandexecution_idpropagate through all logs
Quick Start
Logging is automatically configured when you use AgentRunner. No setup required:
from framework.runner import AgentRunner
runner = AgentRunner(graph=my_graph, goal=my_goal)
result = await runner.run({"input": "data"})
# Logs automatically include trace_id, execution_id, agent_id, etc.
Programmatic Configuration
Configure logging explicitly in your code:
from framework.observability import configure_logging
# Human-readable (development)
configure_logging(level="DEBUG", format="human")
# JSON (production)
configure_logging(level="INFO", format="json")
# Auto-detect from environment
configure_logging(level="INFO", format="auto")
Configuration Options
- level:
"DEBUG","INFO","WARNING","ERROR","CRITICAL" - format:
"json"- Machine-parseable JSON (one line per log entry)"human"- Human-readable with colors"auto"- Detects fromLOG_FORMATenv var orENV=production
Log Format Examples
JSON Format (Machine-parseable)
{"timestamp": "2026-01-28T15:01:02.671126+00:00", "level": "info", "logger": "framework.runtime", "message": "Starting agent execution", "trace_id": "54e80d7b5bd6409dbc3217e5cd16a4fd", "execution_id": "b4c348ec54e80d7b5bd6409dbc3217e50", "agent_id": "sales-agent", "goal_id": "qualify-leads"}
Features:
trace_idandexecution_idare 32 hex chars (W3C/OTel-aligned, no prefixes)- Compact single-line format (easy to stream/parse)
- All trace context fields included automatically
Human-Readable Format (Development / Terminal)
[INFO ] [agent:sales-agent] Starting agent execution
[INFO ] [agent:sales-agent] Processing input data [node_id:input-processor]
[INFO ] [agent:sales-agent] LLM call completed [latency_ms:1250] [tokens_used:450]
Features:
- Color-coded log levels
- Terminal output omits trace_id and execution_id for readability
- For full traceability (e.g. debugging), use
ENV=productionto get JSON file logs with trace_id and execution_id
Trace Context Fields
When the framework sets trace context, these fields are included in all logs. IDs are 32 hex (W3C/OTel-aligned, no prefixes).
- trace_id: Trace identifier
- execution_id: Run/session correlation
- agent_id: Agent/graph identifier
- goal_id: Goal being pursued
- node_id: Current node (when set)
Custom Log Fields
Add custom fields using the extra parameter:
import logging
logger = logging.getLogger("my_module")
# Add custom fields
logger.info("LLM call completed", extra={
"latency_ms": 1250,
"tokens_used": 450,
"model": "claude-3-5-sonnet-20241022",
"node_id": "web-search"
})
These fields appear in both JSON and human-readable formats.
Usage in Your Code
Standard Logging (Recommended)
Just use Python's standard logging - context is automatic:
import logging
logger = logging.getLogger(__name__)
def my_function():
# This log automatically includes trace_id, execution_id, etc.
logger.info("Processing data")
try:
result = do_work()
logger.info("Work completed", extra={"result_count": len(result)})
except Exception as e:
logger.error("Work failed", exc_info=True)
Framework-Managed Context
The framework automatically sets trace context at key points:
- Runtime.start_run(): Sets
trace_id,execution_id,goal_id - GraphExecutor.execute(): Adds
agent_id - Node execution: Adds
node_id
Propagation is automatic via ContextVar.
Advanced Usage
Manual Context Management
If you need to set trace context manually (rare):
from framework.observability import set_trace_context, get_trace_context
# Set context (32-hex, no prefixes)
set_trace_context(
trace_id="54e80d7b5bd6409dbc3217e5cd16a4fd",
execution_id="b4c348ec54e80d7b5bd6409dbc3217e50",
agent_id="my-agent"
)
# Get current context
context = get_trace_context()
print(context["execution_id"])
# Clear context (usually not needed)
from framework.observability import clear_trace_context
clear_trace_context()
Testing
For tests, you may want to configure logging explicitly:
import pytest
from framework.observability import configure_logging
@pytest.fixture(autouse=True)
def setup_logging():
configure_logging(level="DEBUG", format="human")
Best Practices
- Production: Use JSON format (
LOG_FORMAT=jsonorENV=production) - Development: Use human-readable format (default)
- Don't manually set context: Let the framework manage it
- Use standard logging: No special APIs needed - just
logger.info() - Add custom fields: Use
extradict for additional metadata
Troubleshooting
Logs missing trace context
Ensure configure_logging() has been called (usually automatic via AgentRunner._setup()).
JSON logs not appearing
Check environment variables:
echo $LOG_FORMAT
echo $ENV
Or explicitly set:
configure_logging(format="json")
Context not propagating
ContextVar automatically propagates through async calls. If context seems lost, check:
- Are you in the same async execution context?
- Has
set_trace_context()been called for this execution?
See Also
- Logging Implementation - Source code
- AgentRunner - Where logging is configured
- Runtime Core - Where trace context is set