PHP Observability
debt(d9/e5/b5/t7)
Closest to 'silent in production until users hit it' (d9), detection_hints.automated is no — missing observability is invisible until incidents occur; no linter flags absence of OpenTelemetry/Datadog instrumentation.
Closest to 'touches multiple files / significant refactor in one component' (e5), quick_fix suggests OpenTelemetry auto-instrumentation which is relatively easy, but adding correlation IDs, structured logging, and metric labels across the app is a multi-file effort.
Closest to 'persistent productivity tax' (b5), applies to web/cli/queue-worker contexts — observability tooling becomes load-bearing across the codebase, but it's additive infrastructure rather than defining system shape.
Closest to 'serious trap' (t7), misconception that error logs are sufficient contradicts the three-pillars model (metrics/traces/logs) — devs from log-only backgrounds reliably guess wrong about what's needed for production visibility.
Also Known As
TL;DR
Explanation
PHP observability toolkit: Logs (Monolog with structured JSON handlers — Loki, Elasticsearch, Datadog), Metrics (prometheus/client_php — expose /metrics for Prometheus scraping; StatsD for push-based metrics), Traces (opentelemetry-php or Datadog ddtrace auto-instrumentation — distributed tracing across services). PHP-specific considerations: PHP-FPM process-per-request model means metrics must persist across requests (shared memory via APCu or push to StatsD). Correlation IDs: generate a UUID per request, pass it in all log entries and outgoing HTTP headers — enables correlating logs, metrics, and traces for a single request.
Common Misconception
Why It Matters
Common Mistakes
- No correlation IDs — cannot connect related events across log lines and services.
- Logging without structure — plain text logs cannot be queried for specific fields.
- Metrics without labels — unlabelled counters cannot distinguish between endpoints.
- No request duration histogram — cannot calculate p95/p99 latency without it.
Code Examples
// Plain text logs — unsearchable:
error_log('Error processing order ' . $id . ' for user ' . $userId);
// Cannot query: all errors for user 42
// Cannot correlate with database traces
// Cannot alert when error rate exceeds threshold
// Structured observability:
// 1. Structured logging:
$logger->error('order.processing.failed', [
'order_id' => $id,
'user_id' => $userId,
'trace_id' => $this->traceId,
'duration_ms' => $elapsed,
]);
// 2. Metrics:
$counter->labels(['endpoint' => 'checkout', 'status' => '500'])->inc();
$histogram->labels(['endpoint' => 'checkout'])->observe($duration);
// 3. Trace propagation:
$span = $tracer->spanBuilder('order.process')->startSpan();
$span->setAttribute('order.id', $id);
finally { $span->end(); }