Chapter 15 — Monitoring, Logging & Observability
If you can't see what's happening in production, you can't fix it. Observability is the ability to understand your system's internal state from its external outputs.
The Three Pillars
Pillar What Tools Answers
Metrics Numeric measurements over time Prometheus, CloudWatch, Datadog "How much?" "How fast?" "How often?"
Logs Discrete events with context Loki, ELK, CloudWatch Logs "What happened?" "Why did it fail?"
Traces Request flow across services Jaeger, Zipkin, AWS X-Ray "Where is the bottleneck?" "Which service is slow?"
Prometheus + Grafana (The Open-Source Standard)
# docker-compose.yml — Monitoring stack
services:
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
ports:
- "9090:9090"
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
grafana:
image: grafana/grafana:latest
volumes:
- grafana_data:/var/lib/grafana
ports:
- "3001:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: changeme
node-exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
volumes:
prometheus_data:
grafana_data:
# prometheus.yml — Scrape configuration
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "alerts.yml"
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
scrape_configs:
- job_name: 'node'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'myapp'
static_configs:
- targets: ['myapp:3000']
metrics_path: '/metrics'
Application Metrics (What to Measure)
The RED method for services and USE method for resources:
Method Metric What It Tells You
RED (Services) Rate Requests per second
Errors Failed requests per second
Duration Response time (p50, p95, p99)
USE (Resources) Utilization % of resource capacity used
Saturation Queue depth, waiting work
Errors Error count on the resource
# Example: Exposing metrics in Node.js (using prom-client)
const client = require('prom-client');
// Default metrics (CPU, memory, event loop)
client.collectDefaultMetrics();
// Custom metrics
const httpRequestDuration = new client.Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status_code'],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 5]
});
// Middleware to track requests
app.use((req, res, next) => {
const end = httpRequestDuration.startTimer();
res.on('finish', () => {
end({ method: req.method, route: req.route?.path || req.path, status_code: res.statusCode });
});
next();
});
// Metrics endpoint for Prometheus to scrape
app.get('/metrics', async (req, res) => {
res.set('Content-Type', client.register.contentType);
res.end(await client.register.metrics());
});
Alerting Rules
# alerts.yml — Prometheus alerting rules
groups:
- name: webapp
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status_code=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate (> 5%)"
description: "{{ $labels.instance }} has {{ $value | humanizePercentage }} error rate"
- alert: HighLatency
expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 2
for: 5m
labels:
severity: warning
annotations:
summary: "High p95 latency (> 2s)"
- alert: InstanceDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Instance {{ $labels.instance }} is down"
SLIs, SLOs, and SLAs
Term Definition Example
SLI (Indicator)A measurable metric of service quality 99.2% of requests complete in <500ms
SLO (Objective)Target value for an SLI (internal goal) "99.9% availability over 30 days"
SLA (Agreement)Contract with customers (with consequences) "99.9% uptime or we credit your bill"
Error budgets: If your SLO is 99.9% uptime (43 minutes downtime/month), you have a 0.1% "error budget." Use it for deployments, experiments, and maintenance. When the budget is exhausted, freeze deployments and focus on reliability.
← Chapter 14 — Infrastructure as Code
Next: Chapter 16 — Security & Hardening →