Observability

Observability

Part 9: Observability - Logs, Metrics, Traces - Complete Guide

Part 9: Observability (3 Pillars)

Logs, Metrics, and Traces for Debugging Microservices - Complete Guide

πŸ“Œ Quick Summary (30 seconds):
Three pillars of observability: Logs (detailed events, "what happened?"), Metrics (aggregated numeric data, "how much/how often?"), Traces (request flow across services, "where did it go?"). Together they provide complete visibility into system behavior. In microservices, observability is NOT optional β€” you cannot debug distributed systems without it.

❓ The Problem: Debugging Distributed Systems

Why Observability is CRITICAL in Microservices:
  • Request spans multiple services (hard to track)
  • Failure could be anywhere in the chain
  • No single log file to grep
  • Different teams own different services
  • Traditional debugging doesn't work
Without Observability:

"Payment failed" error message β†’ Which service? Which instance? Why?
β†’ 50 services Γ— 10 instances = 500 log files to search manually!
β†’ Hours wasted, customer unhappy

With Observability:

Trace ID links all services β†’ Logs show the error β†’ Metrics show pattern
β†’ Root cause found in minutes β†’ Happy customer

πŸ“Š The Three Pillars of Observability

πŸ“ LOGS
πŸ“„ Detailed, structured events
❓ "What happened?"
πŸ› οΈ ELK Stack, Loki, Splunk
πŸ“‹ Example: "Payment failed for order 123: insufficient funds"
πŸ“Š METRICS
πŸ“ˆ Aggregated numeric data
❓ "How much? How often?"
πŸ› οΈ Prometheus, Grafana, Datadog
πŸ“‹ Example: "Error rate = 15% in last 5 minutes"
πŸ” TRACES
πŸ—ΊοΈ Request flow across services
❓ "Where did it go?"
πŸ› οΈ Jaeger, Zipkin, OpenTelemetry
πŸ“‹ Example: "Request: Gateway β†’ Order β†’ Payment β†’ Inventory"

πŸ“ Pillar 1: Logs (Deep Dive)

What are Logs?
  • Timestamped records of events
  • Structured (JSON) or unstructured (text)
  • Include context (request ID, user ID, etc.)
  • Every service writes to stdout/stderr
  • Collected by log aggregator (Fluentd, Logstash)
Log Levels (Importance)
  • ERROR - Something failed, needs attention
  • WARN - Something unexpected but working
  • INFO - Normal application events
  • DEBUG - Detailed troubleshooting info
  • TRACE - Very detailed (only during debugging)
// Good structured log (JSON) - Searchable, parseable
{
  "timestamp": "2024-01-15T10:30:00Z",
  "level": "ERROR",
  "service": "payment-service",
  "trace_id": "abc-123-def",
  "user_id": "user-456",
  "order_id": "order-789",
  "message": "Payment failed",
  "error": "insufficient_funds",
  "duration_ms": 145
}

// Bad log (unstructured) - Hard to search
"Payment failed at 10:30 for order 789 - insufficient funds"

πŸ“Š Pillar 2: Metrics (Deep Dive)

Types of Metrics
  • Counter - Only increases (request count, errors)
  • Gauge - Goes up and down (memory, connections)
  • Histogram - Distribution (latency percentiles)
  • Summary - Like histogram, client-side
Key Metrics to Monitor
  • Request Rate (RPS) - Traffic volume
  • Error Rate - Failure percentage
  • Latency (p50, p95, p99) - Response times
  • Resource Usage - CPU, Memory, Disk, Network
  • Business Metrics - Orders, Payments, Users
// Prometheus metrics example
# HELP http_requests_total Total number of HTTP requests
# TYPE http_requests_total counter
http_requests_total{method="POST", endpoint="/payments", status="200"} 15234
http_requests_total{method="POST", endpoint="/payments", status="500"} 42

# HELP http_request_duration_seconds HTTP request latency
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.1"} 10000
http_request_duration_seconds_bucket{le="0.5"} 14500
http_request_duration_seconds_bucket{le="1.0"} 15000

πŸ” Pillar 3: Distributed Tracing (Deep Dive)

Distributed Trace Example

Trace ID: 4bf92f3577b34da6a3ce929d0e0e4736

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Service: API Gateway β”‚
β”‚ Span ID: 001 | Parent: null | Duration: 5ms β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Service: Order Service β”‚
β”‚ Span ID: 002 | Parent: 001 | Duration: 50ms β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ β”‚
β–Ό β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Service: Payment Service β”‚ β”‚ Service: Inventory Service β”‚
β”‚ Span ID: 003 | Parent:002β”‚ β”‚ Span ID: 004 | Parent: 002 β”‚
β”‚ Duration: 200ms β”‚ β”‚ Duration: 30ms β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Total Request Time = 5 + 50 + max(200, 30) = 255ms
Bottleneck identified: Payment Service (200ms)
// OpenTelemetry tracing example
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;

@Service
public class PaymentService {
    
    @Autowired
    private Tracer tracer;
    
    public PaymentResponse processPayment(PaymentRequest request) {
        // Start a new span
        Span span = tracer.spanBuilder("processPayment")
            .setAttribute("order.id", request.getOrderId())
            .setAttribute("amount", request.getAmount())
            .startSpan();
            
        try (Scope scope = span.makeCurrent()) {
            // Business logic
            PaymentResponse response = paymentProcessor.charge(request);
            span.setAttribute("payment.status", response.getStatus());
            return response;
        } catch (Exception e) {
            span.recordException(e);
            span.setStatus(StatusCode.ERROR, e.getMessage());
            throw e;
        } finally {
            span.end();
        }
    }
}

πŸ”„ How Logs, Metrics, and Traces Work Together

Unified Observability Flow

User Request β†’ Trace ID: abc-123
β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”‚ β”‚
β–Ό β–Ό β–Ό
Gateway Order Service Payment Service
Span: 5ms Span: 50ms Span: 200ms
Log: "Received" Log: "Processing" Log: "FAILED - insufficient funds"
Metrics: RPS++ Metrics: RPS++ Metrics: ERROR++

Alert: Error rate > 10% at 10:30:05
↓
Query traces for that time period β†’ Find trace abc-123
↓
View logs for trace abc-123 β†’ "insufficient funds" error
↓
Root cause identified in 2 minutes!

πŸ“‹ When to Use Each Pillar

ScenarioPrimary ToolSecondaryWhy
"Why did this specific order fail?" Logs Traces Logs contain detailed error message
"Is the system healthy right now?" Metrics Health Checks Metrics show current error rate, latency
"Which service is slow?" Traces Metrics Traces show latency per service
"How many users are active?" Metrics Logs Request rate metric aggregates across all instances
"We need to scale up" Metrics None CPU, memory, request rate show capacity needs
"What happened between 2-3 PM yesterday?" Logs + Metrics Traces Historical data from both pillars

πŸ› οΈ Popular Observability Tools

.=εΏ—Commercial, developer-friendly .=εΏ—Future-proof instrumentation
CategoryToolKey FeaturesBest For
Logs ELK Stack (Elasticsearch, Logstash, Kibana) Full-text search, dashboards, alerting Large-scale logging
Loki (Grafana Labs) Tight Grafana integration, cheaper storage Kubernetes environments
Splunk Enterprise features, machine learning Enterprise, compliance-heavy
Metrics Prometheus + Grafana Pull model, powerful querying (PromQL) Most popular, cloud-native
Datadog SaaS, APM integration, easy setup Commercial, all-in-one
New Relic SaaS, user-friendly dashboards
Traces Jaeger OpenTracing compatible, UI for trace search Uber open source, popular choice
Zipkin Lightweight, simple deployment Smaller deployments
OpenTelemetry Unified standard, vendor-neutral

πŸ”— Correlation ID - The Glue That Binds Everything

What is Correlation ID? A unique identifier passed through all services in a request chain. It ties logs, metrics, and traces together.
Correlation ID Flow

Client: POST /orders (Header: X-Correlation-ID: abc-123)
β”‚
β–Ό
Gateway: Logs with correlation_id=abc-123
β”‚
β–Ό
Order Service: Logs with correlation_id=abc-123
β”‚
β–Ό
Payment Service: Logs with correlation_id=abc-123
β”‚
β–Ό
All logs for a single request can be found by searching "correlation_id=abc-123"!
// Spring Boot - MDC (Mapped Diagnostic Context) for correlation ID
@Configuration
public class CorrelationIdFilter implements Filter {
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                         FilterChain chain) {
        HttpServletRequest req = (HttpServletRequest) request;
        
        // Extract or generate correlation ID
        String correlationId = req.getHeader("X-Correlation-Id");
        if (correlationId == null || correlationId.isEmpty()) {
            correlationId = UUID.randomUUID().toString();
        }
        
        // Add to MDC - will appear in all logs
        MDC.put("correlation_id", correlationId);
        
        try {
            chain.doFilter(request, response);
        } finally {
            MDC.clear();
        }
    }
}

// In logback configuration
// <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg - %X{correlation_id}%n</pattern>

⚑ Pros & Cons of Observability

βœ… BENEFITS
  • Fast root cause analysis (minutes vs hours)
  • Proactive alerting before customers notice
  • Capacity planning with real data
  • Debug distributed transactions easily
  • Identify performance bottlenecks
  • Historical analysis for compliance
❌ CHALLENGES
  • Storage costs (logs, traces add up)
  • Performance overhead (tracing adds latency)
  • Complexity to set up and maintain
  • Noise (too many logs/metrics)
  • Sampling decisions (not all traces)
  • Cardinality issues with metrics

βœ… Observability Best Practices

πŸ“ Logs Best Practices
  • Use structured logging (JSON format)
  • Always include correlation ID
  • Use appropriate log levels
  • Don't log sensitive data (PII, passwords)
  • Centralize logs (ELK, Loki, Splunk)
πŸ“Š Metrics Best Practices
  • Monitor RED method: Rate, Errors, Duration
  • Monitor USE method: Utilization, Saturation, Errors
  • Set alerts with appropriate thresholds
  • Use histograms for latency
  • Avoid high-cardinality labels (user_id)
πŸ” Traces Best Practices
  • Propagate trace ID across all services
  • Use head-based sampling for production
  • Add business context as attributes
  • Instrument all HTTP/DB/messaging calls
  • Set meaningful span names

πŸ“Š RED Method vs USE Method

πŸ”΄ RED Method (Service Level)
  • Rate - Requests per second
  • Errors - Percentage of failed requests
  • Duration - Time to process requests
Best for: User-facing services
βš™οΈ USE Method (Resource Level)
  • Utilization - % time resource is busy
  • Saturation - Queue length, waiting work
  • Errors - Error events
Best for: Infrastructure (CPU, memory, disk)

πŸ’‘ Interview Power Phrases

"Observability is not optional in microservices. Logs tell you what happened, metrics tell you how much, and traces tell you where the request went. You need all three."
"We use correlation IDs to link logs, metrics, and traces across all services. A single request ID lets us trace the entire journey from API gateway to database."
"Our monitoring uses the RED method: Rate, Errors, and Duration for every service. We set alerts for error rate spikes and latency increases beyond p99 thresholds."
"We use OpenTelemetry for vendor-neutral instrumentation and Prometheus + Grafana for metrics, Jaeger for tracing, and Loki for logs."

πŸ“š Quick Memory Reference

πŸ”‘ Key Terms
  • Logs - Detailed event records
  • Metrics - Aggregated numeric data
  • Traces - Request flow across services
  • Correlation ID - Links everything together
  • RED Method - Rate, Errors, Duration
  • USE Method - Utilization, Saturation, Errors
🧠 Mental Models
  • Logs = Diary entries (what happened)
  • Metrics = Heartbeat monitor (how healthy)
  • Traces = GPS route map (where it went)
  • Correlation ID = DNA linking evidence
  • RED method = Service health dashboard
πŸ“Œ One-Line Summary

"Three pillars: Logs (details), Metrics (aggregates), Traces (flow) β€” together they make distributed systems debugable."

βœ… Key Takeaways (Remember These 5 Points)

  • Three pillars - Logs, Metrics, Traces (need all three)
  • Correlation ID - Links everything together across services
  • RED Method - Rate, Errors, Duration for service health
  • USE Method - Utilization, Saturation, Errors for infrastructure
  • Observability is NOT optional - Without it, microservices are un-debugable
🎯 Remember This:
Without Observability β†’ "Payment failed" β†’ Search 500 log files β†’ Hours wasted
With Observability β†’ Trace ID links it all β†’ 2 minutes β†’ Problem fixed