Signed-off-by: phernandez <paul@basicmachines.co> Co-authored-by: Claude <noreply@anthropic.com>
8.2 KiB
SPEC-12: OpenTelemetry Observability
Why
We need comprehensive observability for basic-memory-cloud to:
- Track request flows across our multi-tenant architecture (MCP → Cloud → API services)
- Debug performance issues and errors in production
- Understand user behavior and system usage patterns
- Correlate issues to specific tenants for targeted debugging
- Monitor service health and latency across the distributed system
Currently, we only have basic logging without request correlation or distributed tracing capabilities.
What
Implement OpenTelemetry instrumentation across all basic-memory-cloud services with:
Core Requirements
- Distributed Tracing: End-to-end request tracing from MCP gateway through to tenant API instances
- Tenant Correlation: All traces tagged with tenant_id, user_id, and workos_user_id
- Service Identification: Clear service naming and namespace separation
- Auto-instrumentation: Automatic tracing for FastAPI, SQLAlchemy, HTTP clients
- Grafana Cloud Integration: Direct OTLP export to Grafana Cloud Tempo
Services to Instrument
- MCP Gateway (basic-memory-mcp): Entry point with JWT extraction
- Cloud Service (basic-memory-cloud): Provisioning and management operations
- API Service (basic-memory-api): Tenant-specific instances
- Worker Processes (ARQ workers): Background job processing
Key Trace Attributes
tenant.id: UUID from UserProfile.tenant_iduser.id: WorkOS user identifieruser.email: User email for debuggingservice.name: Specific service identifierservice.namespace: Environment (development/production)operation.type: Business operation (provision/update/delete)tenant.app_name: Fly.io app name for tenant instances
How
Phase 1: Setup OpenTelemetry SDK
-
Add OpenTelemetry dependencies to each service's pyproject.toml:
"opentelemetry-distro[otlp]>=1.29.0", "opentelemetry-instrumentation-fastapi>=0.50b0", "opentelemetry-instrumentation-httpx>=0.50b0", "opentelemetry-instrumentation-sqlalchemy>=0.50b0", "opentelemetry-instrumentation-logging>=0.50b0", -
Create shared telemetry initialization module (
apps/shared/telemetry.py) -
Configure Grafana Cloud OTLP endpoint via environment variables:
OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gateway-prod-us-east-2.grafana.net/otlp OTEL_EXPORTER_OTLP_HEADERS=Authorization=Basic[token] OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
Phase 2: Instrument MCP Gateway
- Extract tenant context from AuthKit JWT in middleware
- Create root span with tenant attributes
- Propagate trace context to downstream services via headers
Phase 3: Instrument Cloud Service
- Continue trace from MCP gateway
- Add operation-specific attributes (provisioning events)
- Instrument ARQ worker jobs for async operations
- Track Fly.io API calls and latency
Phase 4: Instrument API Service
- Extract tenant context from JWT
- Add machine-specific metadata (instance ID, region)
- Instrument database operations with SQLAlchemy
- Track MCP protocol operations
Phase 5: Configure and Deploy
- Add OTLP configuration to
.env.exampleand.env.example.secrets - Set Fly.io secrets for production deployment
- Update Dockerfiles to use
opentelemetry-instrumentwrapper - Deploy to development environment first for testing
How to Evaluate
Success Criteria
- End-to-end traces visible in Grafana Cloud showing complete request flow
- Tenant filtering works - Can filter traces by tenant_id to see all requests for a user
- Service maps accurate - Grafana shows correct service dependencies
- Performance overhead < 5% - Minimal latency impact from instrumentation
- Error correlation - Can trace errors back to specific tenant and operation
Testing Checklist
- Single request creates connected trace across all services
- Tenant attributes present on all spans
- Background jobs (ARQ) appear in traces
- Database queries show in trace timeline
- HTTP calls to Fly.io API tracked
- Traces exported successfully to Grafana Cloud
- Can search traces by tenant_id in Grafana
- Service dependency graph shows correct flow
Monitoring Success
- All services reporting traces to Grafana Cloud
- No OTLP export errors in logs
- Trace sampling working correctly (if implemented)
- Resource usage acceptable (CPU/memory)
Dependencies
- Grafana Cloud account with OTLP endpoint configured
- OpenTelemetry Python SDK v1.29.0+
- FastAPI instrumentation compatibility
- Network access from Fly.io to Grafana Cloud
Implementation Assignment
Recommended Agent: python-developer
- Requires Python/FastAPI expertise
- Needs understanding of distributed systems
- Must implement middleware and context propagation
- Should understand OpenTelemetry SDK and instrumentation
Follow-up Tasks
Enhanced Log Correlation
While basic trace-to-log correlation works automatically via OpenTelemetry logging instrumentation, consider adding structured logging for improved log filtering:
- Structured Logging Context: Add
logger.bind()calls to inject tenant/user context directly into log records - Custom Loguru Formatter: Extract OpenTelemetry span attributes for better log readability
- Direct Log Filtering: Enable searching logs directly by tenant_id, workflow_id without going through traces
This would complement the existing automatic trace correlation and provide better log search capabilities.
Alternative Solution: Logfire
After implementing OpenTelemetry with Grafana Cloud, we discovered limitations in the observability experience:
- Traces work but lack useful context without correlated logs
- Setting up log correlation with Grafana is complex and requires additional infrastructure
- The developer experience for Python observability is suboptimal
Logfire Evaluation
Pydantic Logfire offers a compelling alternative that addresses your specific requirements:
Core Requirements Match
- ✅ User Activity Tracking: Automatic request tracing with business context
- ✅ Error Monitoring: Built-in exception tracking with full context
- ✅ Performance Metrics: Automatic latency and performance monitoring
- ✅ Request Tracing: Native distributed tracing across services
- ✅ Log Correlation: Seamless trace-to-log correlation without setup
Key Advantages
- Python-First Design: Built specifically for Python/FastAPI applications by the Pydantic team
- Simple Integration:
pip install logfire+logfire.configure()vs complex OTLP setup - Automatic Correlation: Logs automatically include trace context without manual configuration
- Real-time SQL Interface: Query spans and logs using SQL with auto-completion
- Better Developer UX: Purpose-built observability UI vs generic Grafana dashboards
- Loguru Integration:
logger.configure(handlers=[logfire.loguru_handler()])maintains existing logging
Pricing Assessment
- Free Tier: 10M spans/month (suitable for development and small production workloads)
- Transparent Pricing: $1 per million spans/metrics after free tier
- No Hidden Costs: No per-host fees, only usage-based metering
- Production Ready: Recently exited beta, enterprise features available
Migration Path
The existing OpenTelemetry instrumentation is compatible - Logfire uses OpenTelemetry under the hood, so the current spans and attributes would work unchanged.
Recommendation
Consider migrating to Logfire for the following reasons:
- It directly addresses the "next to useless" traces problem by providing integrated logs
- Dramatically simpler setup and maintenance compared to Grafana Cloud + custom log correlation
- Better ROI on observability investment with purpose-built Python tooling
- Free tier sufficient for current development needs with clear scaling path
The current Grafana Cloud implementation provides a solid foundation and could remain as a backup/export target, while Logfire becomes the primary observability platform.
Status
Created: 2024-01-28 Status: Completed (OpenTelemetry + Grafana Cloud) Next Phase: Evaluate Logfire migration Priority: High - Critical for production observability