Comprehensive observability tools for distributed systems - tracing, correlation, and metrics
Project description
Distributed Observability Tools
A comprehensive Python package for observability in distributed systems, providing distributed tracing with correlation ID propagation and framework integrations.
🚀 Quick Start
Installation
# Basic installation (core tracing only)
pip install distributed-observability-tools
# With FastAPI support (REQUIRED for HTTP request tracing)
pip install distributed-observability-tools[fastapi]
# With HTTP client support
pip install distributed-observability-tools[httpx]
# Full installation (all features)
pip install distributed-observability-tools[all]
⚠️ Important: For FastAPI applications, you must install with the
[fastapi]extras to enable HTTP request tracing. Without this, only database and other instrumentations will work, but HTTP spans will not be created.
Basic Usage
from distributed_observability import TracingConfig, setup_tracing
# Configure tracing
config = TracingConfig(
service_name="my-service",
collector_url="http://otel-collector:4317"
)
# Setup tracing
tracer_manager, middleware = setup_tracing(config)
FastAPI Integration
New in v0.1.3: Automatic FastAPI instrumentation! 🎉
from distributed_observability import TracingConfig, FastAPIConfig, setup_tracing
from fastapi import FastAPI
# Configure tracing
config = TracingConfig(
service_name="my-service",
collector_url="http://otel-collector:4317"
)
# Configure header capture (optional)
fastapi_config = FastAPIConfig(
capture_request_headers=["x-correlation-id", "user-agent"]
)
# Create FastAPI app
app = FastAPI()
# Setup tracing with automatic instrumentation (recommended)
tracer_manager, middleware = setup_tracing(
config,
app=app, # Pass app for auto-instrumentation
fastapi_config=fastapi_config
)
# Add middleware for correlation ID propagation
middleware_class, middleware_kwargs = middleware
app.add_middleware(middleware_class, **middleware_kwargs)
What this does:
- ✅ Creates HTTP request spans (GET /api/users, POST /api/orders, etc.)
- ✅ Captures HTTP method, status code, URL path
- ✅ Extracts and propagates correlation IDs from headers
- ✅ Instruments database queries (SQLAlchemy, Redis, etc.)
- ✅ Creates complete distributed trace trees
Manual instrumentation (advanced users):
from distributed_observability import TracingConfig, setup_tracing
from distributed_observability.tracing.tracer import instrument_fastapi_app
from fastapi import FastAPI
config = TracingConfig(service_name="my-service")
# Setup tracing first
tracer_manager, middleware = setup_tracing(config)
# Create app
app = FastAPI()
# Manually instrument FastAPI
instrument_fastapi_app(app, config, fastapi_config)
# Add middleware
middleware_class, middleware_kwargs = middleware
app.add_middleware(middleware_class, **middleware_kwargs)
Advanced Configuration
from distributed_observability import TracingConfig, CorrelationConfig
from fastapi import FastAPI
# Advanced configuration
config = TracingConfig(
service_name="my-service",
service_version="1.2.3",
collector_url="http://otel-collector:4317",
sampling_rate=0.1, # 10% sampling
correlation=CorrelationConfig(
headers=["x-correlation-id", "x-request-id"],
propagation=True
),
environment="production"
)
# Setup with custom middleware
from distributed_observability.framework import RequestTracingMiddleware
from distributed_observability.core import FastAPIConfig
fastapi_config = FastAPIConfig(
enable_middleware=True,
record_exceptions=True
)
app = FastAPI()
app.add_middleware(RequestTracingMiddleware, tracing_config=config, fastapi_config=fastapi_config)
Custom HTTP Header Capture
Configure which HTTP request headers are captured as OpenTelemetry span attributes:
from distributed_observability import TracingConfig, FastAPIConfig, setup_tracing
from distributed_observability.tracing.tracer import instrument_fastapi_app
from fastapi import FastAPI
# Configure tracing
tracing_config = TracingConfig(
service_name="my-service",
collector_url="http://otel-collector:4317"
)
# Configure custom header capture
fastapi_config = FastAPIConfig(
enable_middleware=True,
# Explicit list of headers to capture
capture_request_headers=[
"x-correlation-id",
"x-request-id",
"user-agent",
"x-tenant-id", # Custom business header
"x-user-role", # Custom business header
],
# Headers to redact (captured but value masked)
redact_headers=[
"authorization",
"cookie",
"x-api-key"
],
# Wildcard patterns for header matching
header_patterns=[
"x-*", # Capture all headers starting with "x-"
"*-id", # Capture all headers ending with "-id"
"cf-*" # Capture all CloudFlare headers
]
)
# Setup tracing
tracer_manager, middleware_config = setup_tracing(tracing_config)
middleware_class, middleware_kwargs = middleware_config
# Add custom FastAPIConfig to middleware
middleware_kwargs['fastapi_config'] = fastapi_config
app = FastAPI()
app.add_middleware(middleware_class, **middleware_kwargs)
# Instrument FastAPI with custom config
instrument_fastapi_app(app, tracing_config, fastapi_config)
Header Pattern Matching:
The header_patterns field supports wildcard patterns using fnmatch syntax:
x-*- Matches all headers starting with "x-" (e.g.,x-correlation-id,x-tenant-id)*-id- Matches all headers ending with "-id" (e.g.,tenant-id,request-id)cf-*- Matches all CloudFlare headers (e.g.,cf-ray,cf-connecting-ip)cloudfront-*- Matches all CloudFront headers
Header Redaction:
Headers listed in redact_headers will be captured as span attributes but their values will be replaced with [REDACTED] for security. This is useful for sensitive headers like:
authorization- OAuth tokens, API keyscookie- Session cookiesx-api-key- API authentication keysx-secret-*- Any custom secret headers
Default Configuration:
If no FastAPIConfig is provided, the following defaults are used:
# Default headers captured
capture_request_headers = [
"x-correlation-id",
"x-request-id",
"correlation-id",
"user-agent",
"x-forwarded-for",
"x-real-ip",
"x-edge-location",
"x-amz-cf-id"
]
# Default headers redacted
redact_headers = [
"authorization",
"cookie",
"x-api-key",
"api-key"
]
# Default patterns (empty - no wildcard matching)
header_patterns = []
📦 Installation
Basic Installation
pip install distributed-observability-tools
With HTTP Client Support
pip install distributed-observability-tools[httpx]
Development Installation
git clone https://github.com/your-org/distributed-observability-tools.git
cd distributed-observability-tools
poetry install # or pip install -e .
🔧 Features
✅ Distributed Tracing
- OpenTelemetry-based tracing with SigNoz compatibility
- Automatic span creation for HTTP requests
- Configurable sampling and resource attributes
- Graceful degradation when collector unavailable
✅ Correlation ID Propagation
- Automatic correlation ID extraction from headers
- Cross-service correlation ID propagation
- SigNoz-compatible span attributes (
correlation_id) - Multiple header format support
✅ Configurable Header Capture
- Explicit Header Lists: Specify exact headers to capture
- Wildcard Pattern Matching: Use patterns like
x-*to capture multiple headers - Header Redaction: Automatically redact sensitive headers (authorization, cookies, API keys)
- Per-Framework Configuration: Different settings for FastAPI and HTTP clients
- Backward Compatible: Sensible defaults that work out of the box
✅ Framework Integration
- FastAPI: Automatic middleware for request tracing
- HTTP Clients: httpx instrumentation for call propagation
- Extensible: Clean architecture for adding frameworks
✅ Production Ready
- Comprehensive error handling and logging
- Environment-based configuration
- Clean resource management and shutdown
- Performance optimized with minimal overhead
🏗️ Architecture
distributed_observability/
├── core/ # Configuration and base classes
├── tracing/ # Core tracing functionality
├── framework/ # Framework-specific integrations
└── utils/ # Utility functions and helpers
📚 API Reference
Core Classes
TracingConfig
from distributed_observability import TracingConfig
config = TracingConfig(
service_name="my-service", # Required: Your service name
collector_url="http://otel-collector:4317", # Default OTLP endpoint
collector_protocol="grpc", # or "http"
sampling_rate=1.0, # Optional: 0.0-1.0 sampling
environment="development", # Optional: deployment env
resource_attributes={"team": "backend"} # Optional: custom attributes
)
CorrelationConfig
from distributed_observability.core import CorrelationConfig
correlation = CorrelationConfig(
headers=["x-correlation-id", "x-request-id"], # Headers to check
propagation=True, # Propagate IDs
generate_id=True # Generate ID if missing
)
Setup Functions
setup_tracing()
from distributed_observability import setup_tracing
# Returns (TracingManager, RequestTracingMiddleware)
tracer_manager, middleware = setup_tracing(config)
Framework Integration
from distributed_observability.framework.fastapi import RequestTracingMiddleware
# Add custom middleware
app.add_middleware(RequestTracingMiddleware,
tracing_config=config,
fastapi_config=FastAPIConfig())
HTTP Client Instrumentation
from distributed_observability.utils import instrument_httpx_client
from distributed_observability import HTTPClientConfig
# Create correlated client with custom header capture
http_config = HTTPClientConfig(
enable_httpx=True,
capture_headers=[
"x-correlation-id",
"x-request-id",
"user-agent"
],
redact_headers=["authorization", "cookie"],
header_patterns=["x-*"] # Capture all x- headers
)
client = instrument_httpx_client()
# All requests will include correlation headers automatically
response = await client.get("http://api.example.com/users")
FastAPIConfig
Configure header capture for incoming requests:
from distributed_observability import FastAPIConfig
fastapi_config = FastAPIConfig(
enable_middleware=True,
record_exceptions=True,
# Headers to capture as span attributes
capture_request_headers=[
"x-correlation-id",
"x-request-id",
"user-agent",
"x-tenant-id",
"x-user-role"
],
# Headers to redact (capture but mask value)
redact_headers=[
"authorization",
"cookie",
"x-api-key"
],
# Wildcard patterns for header matching
header_patterns=[
"x-*", # All headers starting with x-
"*-id", # All headers ending with -id
"cf-*" # All CloudFlare headers
]
)
HTTPClientConfig
Configure header capture for outgoing requests:
from distributed_observability import HTTPClientConfig
http_config = HTTPClientConfig(
enable_httpx=True,
# Headers to capture in outgoing request spans
capture_headers=[
"x-correlation-id",
"x-request-id",
"user-agent",
"content-type"
],
# Headers to redact in outgoing requests
redact_headers=[
"authorization",
"cookie",
"x-api-key"
],
# Wildcard patterns (default includes x-*)
header_patterns=["x-*"]
)
🎯 Usage Examples
FastAPI Service
from fastapi import FastAPI, Request, Response
from distributed_observability import TracingConfig, setup_tracing
from distributed_observability.framework.fastapi import get_current_correlation_id
# Setup tracing
config = TracingConfig(service_name="order-service")
tracer_manager, middleware = setup_tracing(config)
app = FastAPI()
app.add_middleware(middleware)
@app.get("/orders")
async def get_orders(request: Request):
# Access correlation ID in handlers
correlation_id = get_current_correlation_id()
# Your business logic here
return {"orders": [], "correlation_id": correlation_id}
Inter-Service Communication
import httpx
from distributed_observability.utils import CorrelatedClient, patch_httpx
# Option 1: Auto-patch httpx (all clients get correlation headers)
patch_httpx()
# Now all httpx requests include correlation headers
async with httpx.AsyncClient() as client:
response = await client.get("http://inventory-service/api/items")
# Option 2: Explicit correlated client
from distributed_observability.utils import instrument_httpx_client
client = instrument_httpx_client()
response = await client.post("http://user-service/api/users", json={"name": "John"})
Environment Configuration
Create a configuration file .env:
SERVICE_NAME=my-microservice
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
ENVIRONMENT=production
SAMPLING_RATE=0.1
from distributed_observability.core import ObservabilityConfig
# Load from environment
config = ObservabilityConfig.from_env()
runner = setup_tracing(config.tracing)
🔍 SigNoz Dashboard Integration
Query for Correlated Requests
-- Find all spans with a specific correlation ID
SELECT *
FROM signoz_traces.distributed_signoz_index_v3
WHERE mapContains(attributes_string, 'correlation_id')
AND attributes_string['correlation_id'] = 'your-correlation-id'
ORDER BY timestamp ASC
Service Timeline Dashboard
-- Timeline view of correlated requests
SELECT
timestamp AS timestamp_datetime,
trace_id,
name AS operation_name,
duration_nano / 1000000 AS duration_ms,
attributes_string['correlation_id'] AS correlation_id,
multiIf(resource.`service.name` IS NOT NULL,
resource.`service.name`::String,
resources_string['service.name'],
'unknown') AS service_name
FROM signoz_traces.distributed_signoz_index_v3
WHERE mapContains(attributes_string, 'correlation_id')
AND attributes_string['correlation_id'] != ''
ORDER BY timestamp ASC
Error Correlation Analysis
-- Errors grouped by correlation ID
SELECT
attributes_string['correlation_id'] AS correlation_id,
countIf(status_code = 2) AS error_count,
arrayStringConcat(groupUniqArray(if(status_code = 2, name, null)), ', ') AS error_operations
FROM signoz_traces.distributed_signoz_index_v3
WHERE mapContains(attributes_string, 'correlation_id')
AND status_code = 2
GROUP BY correlation_id
ORDER BY error_count DESC
⚙️ Configuration Options
TracingConfig Options
All available configuration options:
TracingConfig(
service_name="required-service-name",
service_version="1.0.0", # Default: "1.0.0"
collector_url="http://otel-collector:4317", # Default: "http://host.docker.internal:4317"
collector_protocol="grpc", # "grpc" or "http"
sampling_rate=1.0, # 0.0 to 1.0, None for default
correlation=CorrelationConfig(...), # Correlation configuration
environment="development", # Environment name
resource_attributes={"team": "backend"}, # Custom resource attributes
)
Environment Variables
# Tracing Configuration
SERVICE_NAME=my-service
SERVICE_VERSION=1.0.0
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317
OTEL_EXPORTER_OTLP_PROTOCOL=grpc
SAMPLING_RATE=1.0
ENVIRONMENT=production
# Correlation Configuration
CORRELATION_HEADERS=x-correlation-id,x-request-id
CORRELATION_PROPAGATION=true
CORRELATION_GENERATE_ID=true
🔧 Development
Running Tests
# Install development dependencies
poetry install --with dev
# Run tests
poetry run pytest
# Run with coverage
poetry run pytest --cov=distributed_observability tests/
Code Quality
# Format code
poetry run black .
# Type checking
poetry run mypy distributed_observability
# Sort imports
poetry run isort .
Building and Publishing
# Build package
poetry build
# Publish to PyPI
poetry publish
📋 Dependencies
Core Dependencies
opentelemetry-sdk >= 1.21.0- OpenTelemetry coreopentelemetry-exporter-otlp-proto-grpc >= 1.21.0- OTLP exportopentelemetry-instrumentation-fastapi >= 0.42b0- FastAPI integrationopentelemetry-instrumentation-httpx >= 0.42b0- HTTP client integrationpydantic >= 2.5.0- Configuration validation
Optional Dependencies
httpx >= 0.25.0- HTTP client wrapper support
🤝 Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙋 Support
- 📚 Documentation
- 🐛 Issues
- 💬 Discussions
🎉 Acknowledgments
- OpenTelemetry community for the excellent observability standards
- FastAPI community for the amazing web framework
- SigNoz for the comprehensive observability platform
Ready to make your distributed systems observable! 🚀
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file distributed_observability_tools-1.0.0.tar.gz.
File metadata
- Download URL: distributed_observability_tools-1.0.0.tar.gz
- Upload date:
- Size: 24.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
52125b7ccb31a5ae7ade09ed5dd327e45a52fde502b566e61e39c6e05b3b045f
|
|
| MD5 |
ef96b085ffce6505fac4af20bae90bdd
|
|
| BLAKE2b-256 |
4f05c4f77814e9cacb116941959981ea6bce84fb6ef4dabf5a313d65f27abae1
|
File details
Details for the file distributed_observability_tools-1.0.0-py3-none-any.whl.
File metadata
- Download URL: distributed_observability_tools-1.0.0-py3-none-any.whl
- Upload date:
- Size: 26.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
016f52f5b5362f0830c86430506221f18d7c8a6d34dd83f76ed7c36145f633ec
|
|
| MD5 |
c0fbfb89394399ba98e717f40a808e76
|
|
| BLAKE2b-256 |
10cbdc6e1ea7a710633dcb45923b47f64b98d7177a1a93a55d589914bdf4cb71
|