Logging and Request Correlation
ActingWeb provides comprehensive logging capabilities with automatic request correlation, making it easy to trace requests across distributed actor-to-actor communication.
Overview
ActingWeb’s logging system automatically adds context to every log statement:
Request ID: Unique identifier for each request
Actor ID: The actor handling the request
Peer ID: The peer making the request (when authenticated)
This context appears in every log line, enabling easy grepping and request tracing.
Quick Start
Basic Configuration
from actingweb.logging_config import configure_actingweb_logging
import logging
# Development: verbose logging
configure_actingweb_logging(logging.DEBUG)
# Production: minimal logging
configure_actingweb_logging(logging.WARNING, db_level=logging.ERROR)
With Context Filter (Recommended)
from actingweb.logging_config import (
configure_actingweb_logging,
enable_request_context_filter
)
import logging
# Configure base logging
configure_actingweb_logging(logging.INFO)
# Enable automatic context injection
enable_request_context_filter()
After enabling the context filter, all log statements automatically include request context:
import logging
logger = logging.getLogger(__name__)
# This simple log statement...
logger.info("Property updated")
# ...produces this output:
# 2024-01-15 10:23:45,123 [a1b2c3d4:actor123:peer456] myapp.handlers:INFO: Property updated
Log Format
Default Format
When the context filter is enabled, logs use this format:
%(asctime)s %(context)s %(name)s:%(levelname)s: %(message)s
Example output:
2024-01-15 10:23:45,123 [a1b2c3d4:actor123:peer456] actingweb.handlers.properties:INFO: Property 'email' updated
2024-01-15 10:23:45,456 [a1b2c3d4:actor123:peer456] actingweb.auth:DEBUG: Checking authentication
Context Format
The %(context)s field shows:
[short_request_id:actor_id:short_peer_id]
Where:
short_request_id: Last 8 characters of the request UUID (sufficient for correlation)
actor_id: Full actor identifier
short_peer_id: Last segment after final colon in peer ID
-: Used when a field is not available
Examples:
[a1b2c3d4:actor123:peer456] # Authenticated peer request
[a1b2c3d4:actor123:-] # Creator/trustee request (no peer)
[-:actor123:-] # No request context set
Request Correlation
How It Works
ActingWeb automatically tracks requests through multiple layers:
Incoming Requests: Extract or generate request ID from
X-Request-IDheaderRequest Context: Store request ID, actor ID, and peer ID in thread-local context
Logging: Automatically inject context into every log statement
Outgoing Requests: Propagate request IDs to peer actors with parent tracking
Request Flow Example
When Actor A calls Actor B, which then calls Actor C:
Client → Actor A (request_id: UUID-A)
├─ Actor A logs: [UUID-A:actorA:-]
└─ Actor A → Actor B
├─ Headers: X-Request-ID: UUID-B, X-Parent-Request-ID: UUID-A
├─ Actor B logs: [UUID-B:actorB:actorA]
└─ Actor B → Actor C
├─ Headers: X-Request-ID: UUID-C, X-Parent-Request-ID: UUID-B
└─ Actor C logs: [UUID-C:actorC:actorB]
This creates a traceable chain: UUID-A → UUID-B → UUID-C
Peer-to-Peer Correlation
When actors communicate, ActingWeb automatically:
Generates a new request ID for the outgoing request
Includes the current request ID as the parent
Logs the correlation for debugging
Example from actingweb.aw_proxy:
DEBUG actingweb.aw_proxy:aw_proxy.py:113 Peer request correlation: new_id=fa5959b7... parent_id=a1b2c3d4...
Request bodies sent to a peer are logged as a summary — the sorted key names (at most 20) and the serialised size — never their values (since 3.15):
DEBUG actingweb.aw_proxy:aw_proxy.py:251 Creating trust peer resource at (https://peer.example.com/abc/methods/notify) with data(keys=[message, priority] bytes=42)
Key names of a /properties write are the property names. The MCP
authorize endpoint and the FastAPI trust routes use the same summary.
Grepping Logs
The context format makes it easy to filter logs using standard tools.
Find All Logs for a Request
# Find all logs for request ID starting with "a1b2c3d4"
grep "a1b2c3d4" application.log
Example output:
2024-01-15 10:23:45,123 [a1b2c3d4:actor123:peer456] actingweb.handlers:INFO: Request received
2024-01-15 10:23:45,234 [a1b2c3d4:actor123:peer456] actingweb.auth:DEBUG: Authentication successful
2024-01-15 10:23:45,345 [a1b2c3d4:actor123:peer456] actingweb.handlers:INFO: Property updated
Find All Logs for an Actor
# Find all logs for a specific actor
grep ":actor123:" application.log
Find All Logs for Actor-Peer Interaction
# Find logs for specific actor-peer combination
grep ":actor123:peer456" application.log
Trace Request Chains
To trace a complete request chain across actors:
# Step 1: Find logs for the parent request
grep "a1b2c3d4" application.log
# Step 2: Look for child requests in peer logs
grep "parent_id=a1b2c3d4" application.log
# Step 3: Find the child request ID and repeat
grep "new_id=fa5959b7" application.log
Advanced Filtering
Combine grep patterns for precise filtering:
# Find ERROR logs for a specific request
grep "a1b2c3d4" application.log | grep "ERROR"
# Find all property operations for an actor
grep ":actor123:" application.log | grep "properties"
# Find authentication logs for peer requests
grep "peer456" application.log | grep "auth"
Configuration Options
Convenience Functions
ActingWeb provides pre-configured logging setups:
from actingweb.logging_config import (
configure_production_logging,
configure_development_logging,
configure_testing_logging
)
# Production: WARNING level, minimal output
configure_production_logging()
# Development: DEBUG level, verbose output
configure_development_logging()
# Testing: WARNING level, suppress most output
configure_testing_logging()
Per-Logger Configuration
Control logging levels for specific components:
configure_actingweb_logging(
level=logging.INFO, # Default level
db_level=logging.WARNING, # Database operations
auth_level=logging.INFO, # Authentication
handlers_level=logging.INFO, # Request handlers
proxy_level=logging.DEBUG # Peer communication
)
Environment Variables
Configure logging via environment variables:
# Set default log level
export ACTINGWEB_LOG_LEVEL=INFO
# Set database log level
export ACTINGWEB_DB_LOG_LEVEL=WARNING
Performance Considerations
Context injection has minimal overhead:
Setting context: ~1-2 microseconds per request
Getting context: ~0.5 microseconds per log statement
Filter processing: ~2-3 microseconds per log statement
Total impact: <1% for typical INFO-level logging
Best Practices for Production
Use WARNING level for default logging:
configure_actingweb_logging(logging.WARNING)
Suppress verbose loggers:
configure_actingweb_logging( level=logging.INFO, db_level=logging.ERROR, # Database very verbose auth_level=logging.WARNING, # Auth moderately verbose proxy_level=logging.WARNING # Proxy moderately verbose )
Enable context filter for easier debugging:
enable_request_context_filter()
Integration with Frameworks
Flask Integration
ActingWeb’s Flask integration automatically manages request context:
from actingweb.interface import ActingWebApp
from actingweb.interface.integrations.flask_integration import FlaskIntegration
from flask import Flask
flask_app = Flask(__name__)
aw_app = ActingWebApp(...)
# Context is automatically managed
integration = FlaskIntegration(aw_app, flask_app)
Context lifecycle:
Before request: Extract/generate request ID, extract actor ID from URL
During request: Context available in all log statements
After request: Add
X-Request-IDto response headers, clear context
FastAPI Integration
ActingWeb’s FastAPI integration uses middleware for async context:
from actingweb.interface import ActingWebApp
from actingweb.interface.integrations.fastapi_integration import FastAPIIntegration
from fastapi import FastAPI
fastapi_app = FastAPI()
aw_app = ActingWebApp(...)
# Context automatically managed via middleware
integration = FastAPIIntegration(aw_app, fastapi_app)
Context is automatically propagated across await boundaries.
Manual Context Management
For custom integrations, manage context manually:
from actingweb import request_context
# Set up context at request start
request_context.set_request_context(
request_id="550e8400-e29b-41d4-a716-446655440000",
actor_id="actor123"
)
# During authentication, add peer ID
request_context.set_peer_id("peer456")
# Clear context at request end
request_context.clear_request_context()
Context API
Available context functions:
from actingweb import request_context
# Set full context
request_context.set_request_context(
request_id="uuid",
actor_id="actor123",
peer_id="peer456" # Optional
)
# Set individual fields
request_context.set_request_id("uuid")
request_context.set_actor_id("actor123")
request_context.set_peer_id("peer456")
# Get context
request_id = request_context.get_request_id()
actor_id = request_context.get_actor_id()
peer_id = request_context.get_peer_id()
# Generate new request ID
new_id = request_context.generate_request_id()
# Clear all context
request_context.clear_request_context()
Troubleshooting
No Context in Logs
Problem: Log statements don’t show context information.
Solution: Enable the context filter:
from actingweb.logging_config import enable_request_context_filter
enable_request_context_filter()
Context Shows Dashes
Problem: Context shows [-:-:-] or similar.
Explanation: This means context fields are not set:
[-:-:-]: No context set at all[uuid:-:-]: Request ID set, but no actor or peer[uuid:actor:-]: No peer ID (normal for creator/trustee requests)
Solution: Ensure context is set at request start. For framework integrations, this happens automatically.
Context Not Cleared Between Requests
Problem: Context from previous request appears in new request.
Solution: Ensure clear_request_context() is called at request end. Framework integrations do this automatically in their cleanup hooks.
Missing Parent Request ID
Problem: Peer request logs don’t show parent request ID.
Explanation: Parent request ID only appears when:
A request context exists (parent request)
That request makes a peer-to-peer call
Solution: Normal behavior. Requests without a parent context won’t have a parent ID.
Examples
Complete Flask Application
from flask import Flask
from actingweb.interface import ActingWebApp
from actingweb.interface.integrations.flask_integration import FlaskIntegration
from actingweb.logging_config import (
configure_actingweb_logging,
enable_request_context_filter
)
import logging
# Configure logging
configure_actingweb_logging(logging.INFO, db_level=logging.WARNING)
enable_request_context_filter()
# Set up Flask and ActingWeb
flask_app = Flask(__name__)
aw_app = (
ActingWebApp(
aw_type="urn:actingweb:example.com:myapp",
database="dynamodb",
fqdn="myapp.example.com",
proto="https://"
)
.with_oauth(client_id="...", client_secret="...")
.with_web_ui(enable=True)
)
# Context is automatically managed
integration = FlaskIntegration(aw_app, flask_app)
if __name__ == "__main__":
flask_app.run()
Custom Context in Request Handler
import logging
from actingweb import request_context
logger = logging.getLogger(__name__)
def my_handler(actor_id, data):
# Context is already set by framework integration
logger.info("Processing request") # Shows context automatically
# Make peer request (correlation automatic)
from actingweb.aw_proxy import AwProxy
proxy = AwProxy(peer_target={"id": actor_id, "peerid": "peer123"}, config=config)
result = proxy.get_resource(path="resource")
logger.info("Request completed")
return result
Log Analysis Script
#!/bin/bash
# analyze_request.sh - Trace a request through logs
REQUEST_ID=$1
echo "=== Request: $REQUEST_ID ==="
echo ""
echo "Main request logs:"
grep "$REQUEST_ID" application.log
echo ""
echo "Child requests:"
grep "parent_id=${REQUEST_ID:0:8}" application.log | \
sed -n 's/.*new_id=\([a-f0-9]*\).*/\1/p' | \
while read child_id; do
echo " Child: $child_id"
grep "$child_id" application.log | head -3
done
See Also
Configuration Reference - General configuration guide
ActingWeb Authentication System - Authentication and authorization
actingweb code - Full API reference including request_context and logging_config modules