Observability Overview
RamAPI provides comprehensive observability features built on OpenTelemetry, enabling you to monitor, trace, and optimize your API in production.
Table of Contents
- What is Observability?
- Why Observability Matters
- RamAPI Observability Features
- Quick Start
- OpenTelemetry Integration
- The Three Pillars
- Configuration Overview
- Use Cases
What is Observability?
Observability is the ability to understand the internal state of your system by examining its outputs. Unlike traditional monitoring that tells you when something breaks, observability helps you understand why it broke.
Observability vs Monitoring
| Monitoring | Observability |
|---|---|
| Predefined metrics | Explore any question |
| Known unknowns | Unknown unknowns |
| Dashboards and alerts | Interactive investigation |
| "Is it broken?" | "Why is it broken?" |
Why Observability Matters
Production Debugging
// Traditional approach: Add console.logs and redeploy
console.log('User ID:', userId);
console.log('Request:', request);
// Observability approach: Query existing traces
// No redeployment needed - data is already therePerformance Optimization
Identify bottlenecks without guessing:
- Which database queries are slow?
- Which external APIs add latency?
- Where is memory being consumed?
User Experience
Understand real user impact:
- P95 and P99 latency for actual users
- Error rates by region or device
- Full request context for errors
Cost Optimization
Find inefficiencies:
- Unnecessary database queries
- Redundant API calls
- Resource-intensive operations
RamAPI Observability Features
1. Distributed Tracing
Track requests across your entire system:
const app = createApp({
observability: {
tracing: {
enabled: true,
serviceName: 'my-api',
exporter: 'jaeger',
},
},
});Features:
- W3C Trace Context propagation
- Automatic span creation for all requests
- Support for REST, GraphQL, and gRPC
- Integration with Jaeger, Zipkin, and more
2. Performance Profiling
Identify bottlenecks automatically:
const app = createApp({
observability: {
profiling: {
enabled: true,
slowThreshold: 1000, // Alert on >1s requests
autoDetectBottlenecks: true,
},
},
});Features:
- Automatic bottleneck detection
- Timeline visualization
- Memory profiling
- Performance budgets
3. Metrics Collection
Track key metrics automatically:
const app = createApp({
observability: {
metrics: {
enabled: true,
},
},
});Metrics Collected:
- Request rate (req/s)
- Latency (avg, p50, p95, p99)
- Error rate
- By protocol (REST, GraphQL, gRPC)
- By status code
4. Structured Logging
Context-aware logging:
const app = createApp({
observability: {
logging: {
enabled: true,
level: 'info',
format: 'json',
},
},
});Features:
- Trace ID correlation
- Structured JSON output
- PII redaction
- Multiple log levels
Quick Start
Minimal Setup
import { createApp } from 'ramapi';
const app = createApp({
observability: {
tracing: {
enabled: true,
serviceName: 'my-api',
exporter: 'console', // Start with console
},
},
});
app.get('/api/users', async (ctx) => {
ctx.json({ users: [] });
});
app.listen(3000);View Traces
Traces will be logged to console:
{
"traceId": "3f2504e04f8911edb13900505634b5f1",
"spanId": "8a3c60f7d188f8fa",
"name": "GET /api/users",
"attributes": {
"http.method": "GET",
"http.url": "/api/users",
"http.status_code": 200
}
}Production Setup with Jaeger
const app = createApp({
observability: {
tracing: {
enabled: true,
serviceName: 'my-api',
exporter: 'otlp',
endpoint: 'http://jaeger:4318/v1/traces',
sampleRate: 0.1, // Sample 10% in production
},
metrics: {
enabled: true,
},
profiling: {
enabled: true,
slowThreshold: 1000,
},
},
});OpenTelemetry Integration
RamAPI is built on OpenTelemetry (opens in a new tab), the industry-standard observability framework.
Benefits
- Vendor-Neutral: Not locked into any specific vendor
- Standard Protocol: Works with any OTLP-compatible backend
- Rich Ecosystem: Extensive tooling and integrations
- Future-Proof: Backed by CNCF
Supported Exporters
- Console: Development/debugging
- OTLP: Universal exporter (Jaeger, Tempo, etc.)
- Jaeger: Direct Jaeger integration
- Zipkin: Direct Zipkin integration
Trace Propagation
RamAPI automatically propagates trace context using W3C Trace Context headers:
GET /api/users HTTP/1.1
traceparent: 00-3f2504e04f8911edb13900505634b5f1-8a3c60f7d188f8fa-01
tracestate: vendor1=value1,vendor2=value2The Three Pillars
1. Traces
Distributed traces show the complete journey of a request:
Request → API Gateway → Auth Service → Database → Response
| | | |
20ms 5ms 50ms 100msSee: Distributed Tracing Guide
2. Metrics
Aggregated statistics about your system:
Requests/sec: 1,234
P95 Latency: 125ms
Error Rate: 0.02%3. Logs
Structured, searchable log events:
{
"level": "error",
"traceId": "3f2504e04f8911edb13900505634b5f1",
"message": "Database connection failed",
"error": "ETIMEDOUT"
}Configuration Overview
ObservabilityConfig Interface
interface ObservabilityConfig {
enabled?: boolean; // Global toggle
tracing?: TracingConfig;
logging?: LoggingConfig;
metrics?: MetricsConfig;
profiling?: ProfilingConfig;
health?: HealthConfig;
metricsEndpoint?: MetricsEndpointConfig;
}Tracing Configuration
interface TracingConfig {
enabled: boolean;
serviceName: string;
serviceVersion?: string;
exporter?: 'console' | 'otlp' | 'memory';
endpoint?: string;
sampleRate?: number; // 0-1, default 1.0
// Advanced options
captureStackTraces?: boolean;
maxSpanAttributes?: number;
redactHeaders?: string[];
captureRequestBody?: boolean;
captureResponseBody?: boolean;
}Profiling Configuration
interface ProfilingConfig {
enabled: boolean;
captureMemory?: boolean;
bufferSize?: number;
slowThreshold?: number; // ms
enableBudgets?: boolean;
autoDetectBottlenecks?: boolean;
captureStacks?: boolean;
}Metrics Configuration
interface MetricsConfig {
enabled: boolean;
collectInterval?: number; // ms
endpoint?: string;
prefix?: string; // Default: 'ramapi'
}See: Configuration Guide
Use Cases
Use Case 1: Find Slow Endpoints
// Enable profiling
const app = createApp({
observability: {
profiling: {
enabled: true,
slowThreshold: 500, // Alert on >500ms
},
},
});
// Access profiling data
app.get('/debug/profiles', async (ctx) => {
const profiles = await getProfiles({ slowOnly: true });
ctx.json({ profiles });
});Use Case 2: Distributed Tracing Across Services
// Service A
const serviceA = createApp({
observability: {
tracing: {
enabled: true,
serviceName: 'service-a',
exporter: 'otlp',
},
},
});
// Service B
const serviceB = createApp({
observability: {
tracing: {
enabled: true,
serviceName: 'service-b',
exporter: 'otlp',
},
},
});
// Traces automatically propagate between servicesUse Case 3: Production Monitoring
const app = createApp({
observability: {
tracing: {
enabled: true,
serviceName: 'my-api',
exporter: 'otlp',
endpoint: process.env.OTLP_ENDPOINT,
sampleRate: 0.1, // 10% sampling
redactHeaders: ['authorization', 'cookie'],
},
metrics: {
enabled: true,
endpoint: process.env.METRICS_ENDPOINT,
},
profiling: {
enabled: true,
slowThreshold: 2000,
autoDetectBottlenecks: true,
},
},
});
// Access metrics endpoint
app.get('/metrics', async (ctx) => {
const metrics = getMetrics();
ctx.text(exportPrometheusMetrics());
});Use Case 4: Debug Production Issues
// 1. Find slow traces in Jaeger
// Filter: service=my-api duration>5s
// 2. Examine trace details
// See which spans are slow
// 3. Check profiling data
GET /debug/profiles?traceId=3f2504e04f8911edb13900505634b5f1
// 4. View waterfall chart
GET /debug/flows/3f2504e04f8911edb13900505634b5f1Environment-Based Configuration
Development
const app = createApp({
observability: {
tracing: {
enabled: true,
serviceName: 'my-api',
exporter: 'console', // Console for dev
sampleRate: 1.0, // Sample everything
},
profiling: {
enabled: true,
slowThreshold: 100, // Alert on >100ms
},
},
});Production
const app = createApp({
observability: {
tracing: {
enabled: true,
serviceName: 'my-api',
exporter: 'otlp',
endpoint: process.env.OTLP_ENDPOINT,
sampleRate: 0.1, // 10% sampling
redactHeaders: ['authorization', 'cookie', 'x-api-key'],
captureRequestBody: false, // Don't capture bodies
},
profiling: {
enabled: true,
slowThreshold: 2000, // Alert on >2s
autoDetectBottlenecks: true,
},
metrics: {
enabled: true,
},
},
});Performance Impact
Observability has minimal performance overhead:
| Feature | Overhead | Notes |
|---|---|---|
| Tracing (sampled) | <1% | With 10% sampling |
| Tracing (100%) | ~2-3% | Full sampling |
| Metrics | <0.5% | In-memory aggregation |
| Profiling | ~1-2% | Enabled by default |
| Logging | Variable | Depends on log volume |
Recommendation: Use 10-20% sampling in production for optimal balance.
Next Steps
- Distributed Tracing - Set up tracing with Jaeger/Zipkin
- Flow Visualization - Visualize request flows
- Performance Profiling - Identify bottlenecks
- Metrics & Logging - Configure metrics and logs
- Monitoring Integration - Integrate with monitoring tools
Common Questions
Do I need observability?
Yes, if you want to:
- Debug production issues efficiently
- Optimize performance based on data
- Understand user experience
- Monitor system health
Is OpenTelemetry required?
RamAPI's observability features use OpenTelemetry, but you can:
- Disable observability entirely
- Use only metrics or logs without tracing
- Use console exporter for development
What's the cost?
Observability is free in RamAPI. Backend costs depend on:
- Self-hosted (Jaeger): Free but requires infrastructure
- Managed (Datadog, New Relic): Varies by plan
Can I use my existing monitoring?
Yes! RamAPI supports:
- Prometheus metrics export
- OTLP for any compatible backend
- Custom exporters
Need help? Check the Troubleshooting Guide or GitHub Issues (opens in a new tab).