Skip to content
intermediatePhase 48 · Distributed Systems

Observability

Monitor system health with logs, metrics, and distributed tracing.

45m
0 problems
Topic Progress0%

The Three Pillars

The Three Pillars of Observability

Observability = Logs + Metrics + Traces

The Three Pillars

1. Logs:
   - Discrete events
   - Detailed information
   - Debugging individual requests

2. Metrics:
   - Numerical measurements
   - Aggregated over time
   - Trend analysis

3. Traces:
   - Request journey through system
   - Latency breakdown
   - Service dependency mapping

Comparison

Pillar What When Granularity
Logs Events/details Debugging Individual
Metrics Aggregated numbers Monitoring Aggregated
Traces Request flow Performance Per-request

How They Work Together

Problem: High latency detected

1. Metrics: Latency P99 = 2s (normally 100ms)
2. Traces: Find slow request, see which service is slow
3. Logs: Check that service's logs for errors

Metrics → Traces → Logs (drill down)

Observability Stack

Logs: ELK, Loki, Fluentd
Metrics: Prometheus, Grafana, Datadog
Traces: Jaeger, Zipkin, OpenTelemetry

All three: OpenTelemetry (unified)

Design for Observability

  1. Structured logging
  2. Correlation IDs
  3. Standard metrics
  4. Sampling strategies
  5. Dashboards and alerts

Logs

Logs in Observability

Structured Logging

import json
import logging

class StructuredLogger:
    def __init__(self, service_name):
        self.service = service_name
    
    def log(self, level, message, **kwargs):
        log_entry = {
            'timestamp': datetime.utcnow().isoformat(),
            'level': level,
            'service': self.service,
            'message': message,
            'trace_id': get_trace_id(),
            'span_id': get_span_id(),
            **kwargs
        }
        print(json.dumps(log_entry))
    
    def info(self, message, **kwargs):
        self.log('INFO', message, **kwargs)
    
    def error(self, message, **kwargs):
        self.log('ERROR', message, **kwargs)

# Usage
logger = StructuredLogger('order-service')
logger.info('Order created', order_id='123', user_id='456')

Log Levels

Level Use Case
DEBUG Detailed debugging info
INFO General operational events
WARN Unexpected but handled
ERROR Errors requiring attention
FATAL System-threatening errors

Correlation

def correlation_middleware(next_app):
    def wrapper(request):
        # Generate or extract correlation ID
        correlation_id = request.headers.get('X-Correlation-ID') \
                        or str(uuid.uuid4())
        
        # Set in context
        set_correlation_id(correlation_id)
        
        # Add to response
        response = next_app(request)
        response.headers['X-Correlation-ID'] = correlation_id
        return response
    return wrapper

Best Practices

  1. Use structured logging (JSON)
  2. Include correlation IDs
  3. Log at appropriate levels
  4. Don't log sensitive data
  5. Centralize logs

Metrics

Metrics in Observability

Metric Types

1. Counter:
   - Monotonically increasing
   - Example: requests_total

2. Gauge:
   - Can go up or down
   - Example: temperature, queue_depth

3. Histogram:
   - Distribution of values
   - Example: request_duration

4. Summary:
   - Similar to histogram
   - Client-side calculation

Prometheus Example

from prometheus_client import Counter, Histogram, Gauge

# Define metrics
REQUEST_COUNT = Counter('http_requests_total', 'Total requests', ['method', 'endpoint'])
REQUEST_LATENCY = Histogram('http_request_duration_seconds', 'Request latency', ['endpoint'])
QUEUE_DEPTH = Gauge('queue_depth', 'Current queue depth')

# Use metrics
def handle_request():
    REQUEST_COUNT.labels(method='GET', endpoint='/api').inc()
    
    with REQUEST_LATENCY.labels(endpoint='/api').time():
        result = process_request()
    
    QUEUE_DEPTH.set(get_queue_depth())
    return result

Key Metrics

Category Metrics
Traffic requests_per_second, concurrent_users
Latency p50, p95, p99 duration
Errors error_rate, errors_by_type
Saturation cpu_usage, memory_usage, queue_depth

SLIs/SLOs

SLI (Service Level Indicator):
- Latency P99 < 200ms
- Error rate < 0.1%
- Availability > 99.9%

SLO (Service Level Objective):
- 99.9% of requests < 200ms
- 99.95% success rate

SLA (Service Level Agreement):
- Contractual commitment
- Penalties for missing

Best Practices

  1. Use RED method (Rate, Errors, Duration)
  2. Use USE method (Utilization, Saturation, Errors)
  3. Set SLOs and alert on violations
  4. Dashboard key metrics
  5. Automate alerting

Traces

Distributed Traces

What is a Trace?

Trace: Complete journey of a request

Trace ID: abc123

[Service A] → [Service B] → [Service C]
   50ms         100ms         30ms

Total latency: 180ms

Span

Span: Single operation within a trace

Span ID: span1
Parent: span0
Service: order-service
Operation: create_order
Duration: 50ms
Status: OK

OpenTelemetry Example

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanExporter

# Setup
provider = TracerProvider()
processor = BatchSpanExporter(JaegerExporter())
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)

tracer = trace.get_tracer(__name__)

# Create span
with tracer.start_as_current_span('create_order') as span:
    span.set_attribute('order.id', order_id)
    
    # Child span
    with tracer.start_as_current_span('validate_order'):
        validate(order)
    
    # Another child span
    with tracer.start_as_current_span('save_to_db'):
        db.save(order)

Trace Visualization

Trace: abc123 (180ms)

├── order-service.create_order (180ms)
│   ├── validate_order (20ms)
│   ├── inventory-service.check (50ms)
│   │   └── db.query (30ms)
│   ├── payment-service.charge (80ms)
│   │   └── external_api.call (70ms)
│   └── db.insert (20ms)

Sampling

Sampling Strategies:

1. Always: 100% (expensive)
2. Never: 0% (no traces)
3. Rate-based: 10% of requests
4. Adaptive: More on errors
5. Head-based: Decide at start
6. Tail-based: Decide after completion

Best Practices

  1. Instrument all services
  2. Use correlation IDs
  3. Sample strategically
  4. Capture errors in spans
  5. Visualize trace graphs

Practice Problems

0/3solved
Design Observability System

Design a scalable Observability system. Cover high-level architecture, data model, and API design.

Solution
// Complete system design:
// - Functional + Non-functional requirements
// - Capacity estimation
// - Data model (SQL/NoSQL choice)
// - API endpoints
// - Component architecture
// - Scaling strategy
// - Monitoring & reliability
Observability Scaling

How would you scale Observability to handle 10x the current load? Identify bottlenecks and solutions.

Solution
// Scaling approach:
// 1. Load balancing
// 2. Database sharding/replication
// 3. Cache layer (Redis)
// 4. CDN for static assets
// 5. Async processing (queues)
// 6. Microservices decomposition
Observability Failure Modes

Analyze potential failure modes for Observability and design mitigation strategies.

Solution
// Failure mitigation:
// 1. Redundancy (multi-AZ)
// 2. Circuit breakers
// 3. Retry with backoff
// 4. Dead letter queues
// 5. Health checks
// 6. Graceful degradation

Quiz

1. What are the three pillars of observability?

Question 1 options

2. When should you use metrics vs logs?

Question 2 options

3. What is a distributed trace?

Question 3 options

4. What is SLI in observability?

Question 4 options

5. Why use correlation IDs?

Question 5 options

Flashcards

Question

Three pillars of observability?

Answer

Logs (events), Metrics (numbers), Traces (request flow)

Question

Logs vs Metrics?

Answer

Logs: detailed events for debugging. Metrics: aggregated numbers for monitoring trends.

Question

What is a distributed trace?

Answer

Complete journey of a request through multiple services, showing latency breakdown

Question

What is SLI?

Answer

Service Level Indicator - measurable metric like latency P99 or error rate

Question

Why correlation IDs?

Answer

Link logs, metrics, and traces for same request across services for debugging

Revision Notes

Key Takeaways

  • 1.Three pillars: Logs, Metrics, Traces
  • 2.Logs for debugging, Metrics for monitoring, Traces for request flow
  • 3.Correlation IDs link all telemetry across services
  • 4.SLIs/SLOs define service performance targets
  • 5.Use OpenTelemetry for unified observability

Interview Tips

  • Explain three pillars and when to use each
  • Discuss SLIs/SLOs and their importance
  • Explain how metrics → traces → logs drill-down works
  • Mention OpenTelemetry as unified standard

Cheat Sheet

Cheat Sheet: Observability

Three Pillars

  1. Logs: Discrete events, debugging
  2. Metrics: Aggregated numbers, monitoring
  3. Traces: Request flow, performance

How They Work

Metrics detect → Traces locate → Logs explain

Key Concepts

  • Structured logging
  • Correlation IDs
  • SLIs/SLOs
  • Sampling strategies

Tools

  • Logs: ELK, Loki
  • Metrics: Prometheus, Grafana
  • Traces: Jaeger, Zipkin
  • Unified: OpenTelemetry