Observability Monitoring And Incident Response

Datadog-Style Infrastructure And Service Monitoring

Error tracking watches your code; infrastructure and service monitoring watches everything the code runs on and depends on — hosts, containers, databases, queues, and the request paths through them. Datadog is a common commercial example, and the open-source Prometheus-and-Grafana stack does the same job; "Datadog-style" here means the whole category of metrics-collection, dashboarding, and alerting platforms. This lesson is about turning the golden signals from the orientation into a living system of dashboards and alerts.

Collecting Metrics

Metrics come from two directions. Infrastructure metrics — CPU, memory, disk, network, and per-service resource use — are gathered by an agent running on each host (the Datadog agent, or Prometheus node/exporter processes). Application metrics — request rate, latency percentiles, business counters like signups or orders — are emitted by your code. There are two collection models worth knowing: push, where the app sends metrics to a collector (StatsD-style, Datadog), and pull/scrape, where the platform periodically fetches a metrics endpoint your app exposes (Prometheus). PHP's request-per-process model makes the scrape model slightly awkward — a short-lived request cannot hold counters in memory between requests — so PHP apps commonly push to a local agent or write to a shared store the exporter reads. The mechanism matters less than the discipline of emitting the right metrics.

Two vocabulary points that prevent mistakes. Metrics have types: counters (monotonic totals like requests served), gauges (a value that goes up and down like queue depth), and histograms (distributions, which is how latency percentiles are computed). And metrics carry tags/labels (endpoint, status, region) that let you slice one metric many ways — but every distinct tag combination is a separate stored series, so putting a high-cardinality value like a user id or a raw URL in a tag explodes storage and cost. This is the exact same cardinality warning as the security-monitoring lesson: use bounded labels (route templates, not filled-in paths).

Dashboards That Answer Questions

A good dashboard is designed around the questions you ask under stress, not around every metric you can plot. The golden signals per service go up top; dependencies (database, cache, queue, external APIs) below; saturation of the resources most likely to run out (connection pools, queue depth, disk) alongside. The test of a dashboard is whether, during an incident, it answers "is it us or a dependency, and what is saturating?" in one glance. Sprawling walls of forty graphs fail that test — they are collected, not designed. Keep a small number of purpose-built dashboards over a museum of everything.

Alerts Worth Waking For

An alert is a promise to interrupt a human, so the bar is high: every alert should be actionable (there is something to do), urgent (it cannot wait for business hours if it pages), and real (low false-positive rate). The failure mode this prevents is alert fatigue — when a channel cries wolf often enough, humans start ignoring it, and the one real alert is missed inside the noise. This is not hypothetical; it is the most common way monitoring fails in practice, and you saw its shape in the security track's dead-inbox incident.

Alerting design principles that keep the bar high:

  • Alert on symptoms, not causes. Page on "checkout error rate above 2%" (user pain) rather than "CPU at 90%" (which may be perfectly fine under load). High CPU that is not hurting anyone is not an emergency; a failing checkout is.
  • Use severity tiers. Page (wake someone) for user-facing outages; notify (a channel message) for degradations to look at soon; ticket for slow trends. Not everything deserves a 3 a.m. phone call.
  • Set thresholds from data, with duration. A threshold derived from normal behavior, requiring the condition to persist for a few minutes, filters transient blips. "Error rate above 2% for 5 minutes" beats "any single failed request."
  • Every paging alert has a runbook. If there is nothing to do, it should not page — and if there is, the runbook lesson says write it down.
PHP example
<?php

declare(strict_types=1);

$alerts = [
    ['symptom' => 'checkout 5xx > 2% for 5m', 'severity' => 'page'],
    ['symptom' => 'p99 latency > 2s for 10m', 'severity' => 'page'],
    ['symptom' => 'queue depth rising 30m',   'severity' => 'notify'],
    ['symptom' => 'disk > 80%',               'severity' => 'ticket'],
];

foreach ($alerts as $a) {
    echo str_pad($a['severity'], 7) . ' <- ' . $a['symptom'] . PHP_EOL;
}

// Prints:
// page    <- checkout 5xx > 2% for 5m
// page    <- p99 latency > 2s for 10m
// notify  <- queue depth rising 30m
// ticket  <- disk > 80%

SLIs, SLOs, And Error Budgets

The mature framing ties monitoring to a reliability target. A service level indicator (SLI) is a measured signal (say, the fraction of requests served under 300 ms and without error). A service level objective (SLO) is the target for it (99.9% over 30 days). The gap between the SLO and 100% is the error budget — the amount of unreliability you are allowed to spend. This reframes alerting productively: page when the error budget is burning fast, not on every blip, and use the remaining budget to decide whether to ship features or stabilize. It turns "how reliable is reliable enough?" from an argument into a number.

What To Check

Before moving on, make sure you can:

  • distinguish infrastructure from application metrics and push from scrape collection
  • name metric types (counter, gauge, histogram) and the cardinality trap in tags
  • design a dashboard that answers "us or dependency, and what is saturating?"
  • state the actionable/urgent/real bar and what alert fatigue costs
  • alert on symptoms not causes, with severity tiers and data-derived thresholds
  • define SLI, SLO, and error budget and how they reframe alerting

What You Should Be Able To Do

After this lesson, you should be able to instrument a PHP service's infrastructure and application metrics with bounded cardinality, build purpose-designed dashboards, and write a small set of symptom-based, tiered alerts that stay actionable instead of decaying into fatigue.

Practice

Practice: Design Service Monitoring And Alerts

Design metrics collection, one primary dashboard, and the alert set for a PHP application (web + Redis queue + MySQL). Specify metric types and tags, the dashboard's contents, and each alert's symptom, threshold, and severity.

Show solution

Primary dashboard: golden signals for web (p50/p95/p99 latency, req/sec, 5xx rate, php-fpm saturation) and for the queue (job rate, job latency, failure rate, depth) up top; dependencies below (MySQL connections and slow queries, Redis memory and depth); the resources likeliest to exhaust — DB connection pool, queue depth, disk — highlighted. It is built to answer "us or a dependency, and what's saturating?" at a glance, not to plot everything.

Alerts, symptom-based and tiered:

  • Page: checkout/API 5xx > 2% for 5m; p99 latency > 2s for 10m; queue not draining (depth rising) for 30m; any host down.
  • Notify: MySQL slow-query rate up; Redis memory > 75%; error-budget burn elevated.
  • Ticket: disk > 80%; steady traffic-growth trends.

Each paging alert names a runbook. Thresholds come from measured normal behavior with a duration clause to filter blips. An SLO (99.9% of requests < 300ms, error-free, over 30 days) defines the error budget that governs the burn-rate alerts.

Practice: Diagnose Alert Fatigue

A team's on-call receives ~60 alerts a night, almost all "CPU > 80%" and "single request failed" that resolve themselves. One night a real database outage's alert arrives and is acknowledged-and-ignored like the rest; the outage runs two hours. Separately, their metrics bill tripled after adding a per-user-id tag.

Task

Diagnose the alerting and cardinality failures and redesign.

Show solution
  • Alerting on causes, not symptoms: "CPU > 80%" fires constantly under normal load and usually hurts no one. Replace with the symptom that matters — latency or error rate crossing a user-pain threshold. High CPU is only an emergency if users feel it.
  • No duration clause: "single request failed" pages on transient blips. Require the condition to persist (error rate > 2% for 5 minutes) so noise self-filters.
  • No severity tiers: everything paged. Move degradations and trends to notify/ticket and reserve pages for user-facing outages, so the page channel stays rare and therefore trusted.

Redesign: a handful of symptom-based, duration-gated, tiered alerts, each with a runbook and each genuinely actionable. The night's volume should drop from ~60 to near-zero, which is what makes the real database alert legible when it comes. Consider error-budget burn-rate alerting so the page fires on fast reliability loss rather than instantaneous blips.

Cardinality/cost: the per-user-id tag created a new stored time series for every user, exploding series count and tripling the bill — the high-cardinality trap. User id belongs in logs or traces (where per-request detail lives), never in a metric tag. Remove it; tag by bounded values only (route template, status class). This is the same bounded-label rule as the security-monitoring lesson, and it protects both cost and query performance.

Practice: Define Monitoring Review Checks
Show solution
  • Metric cardinality is tracked, and a series-count spike alerts — the early warning for an accidental high-cardinality tag before the bill or query latency reflects it.
  • Golden-signal metrics are asserted present and non-stale per service; a metric that stops reporting is itself an alert (a dead exporter looks like a healthy quiet system otherwise).
  • Alert definitions are linted: every paging alert has a linked runbook and a duration clause, and none page on a raw resource-cause metric without a symptom justification.

Human, periodically:

  • Review alert volume and the acknowledge/action ratio per alert rule. Any rule that fires often and is routinely dismissed is retired or downgraded — rising volume with falling action predicts the next missed-real-alert incident.
  • Run the dashboard test: during a game-day or drill, can responders answer "us or dependency, what's saturating?" in one glance? Dashboards that fail get pruned and redesigned, not expanded.
  • Re-derive alert thresholds from current traffic so growth does not turn yesterday's threshold into today's noise, and confirm the SLO/error-budget still reflects the reliability the business actually wants.