← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Traces & Spans

observability PHP 7.0+ Intermediate
debt(d7/e3/b5/t5)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). The detection_hints.tools field is not specified. Missing span propagation, coarse spans, or absent attributes won't be flagged by compilers or default linters. Identifying these issues requires either careful code review or observing the resulting trace UI in a staging/production environment — you only discover that context isn't propagating when the trace tree is broken in the backend.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix notes that auto-instrumentation (open-telemetry/opentelemetry-auto-pdo) can be installed with zero code changes for PDO spans. However, common mistakes like missing trace context propagation across service boundaries or adding meaningful span attributes require touching multiple call sites (forwarding traceparent headers, annotating spans), making it slightly more than a single-line patch but still localised within one component or service boundary.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). Tracing infrastructure applies to both web and CLI contexts (per applies_to). Once adopted, every future developer must understand span naming conventions, attribute requirements, sampling strategy, and context propagation rules. This is a persistent tax across many work streams — new endpoints, new background jobs, new service integrations all require trace-awareness — but it doesn't fundamentally redefine the system's architecture.

t5 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'notable trap — a documented gotcha most devs eventually learn' (t5). The misconception field explicitly states that developers wrongly believe tracing is only for microservices, missing its value in monoliths. Additionally, common mistakes around not propagating traceparent headers and sampling at 100% are well-documented gotchas that most developers encounter after initial adoption. These are notable but not catastrophic — the errors are correctable once discovered.

About DEBT scoring →

Also Known As

trace span distributed trace trace context trace ID span ID parent span

TL;DR

The building blocks of distributed tracing — a trace represents a complete request journey across services, composed of spans that each record one operation with its start time, duration, and parent–child relationship.

Explanation

A trace is a collection of spans that together describe how a single request moved through a distributed system. Each span records: operation name, start timestamp, duration, service name, status (success/error), and attributes (key-value metadata). Spans form a tree — the root span is the initial entry point (HTTP request, queue message, scheduled job); child spans represent downstream calls (database queries, external HTTP calls, cache lookups, sub-function calls). The parent–child relationship is maintained via a trace ID (shared by all spans in a trace) and a span ID (unique per span, referenced by children as their parent ID). In PHP, OpenTelemetry is the standard SDK for creating and exporting spans. Traces are exported to collectors like Jaeger, Zipkin, or Datadog, which visualise the span tree and highlight bottlenecks.

Common Misconception

Distributed tracing is only useful in microservices architectures. Tracing is equally valuable in monolithic PHP applications — a trace shows exactly which database queries, cache calls, and template renders are slow for a specific request, with precise timing and the call hierarchy. A monolith with 50ms database queries inside loops benefits from tracing as much as a microservices system with cross-service latency. The cost of adding spans to a PHP application is low with the OpenTelemetry auto-instrumentation package.

Why It Matters

Logs and metrics tell you that something is slow; traces tell you exactly what is slow and why. A PHP application processing an order might log 'request took 850ms' — a trace shows it was three sequential database queries each taking 280ms, called inside a loop, with a cache miss on every iteration. Without traces, diagnosing this requires adding timing code, re-deploying, and hoping the issue reproduces. With traces, the span tree makes the N+1 pattern immediately visible in the UI. OpenTelemetry auto-instrumentation for PHP instruments PDO, Guzzle, and Redis automatically with zero code changes.

Common Mistakes

  • Creating spans that are too coarse — a single span for 'process order' hides where time is spent; instrument individual database queries and external calls.
  • Not propagating trace context across service boundaries — HTTP headers (traceparent) must be forwarded to downstream services for the trace tree to connect.
  • Sampling 100% of requests in production — at high traffic, full sampling generates enormous data volume; use head-based sampling (1-10%) or tail-based sampling (keep traces with errors).
  • Not adding attributes to spans — a span named 'db.query' with no attributes is useless; add the query, table name, and affected rows.

Code Examples

✗ Vulnerable
// No tracing — request slow, no idea why
public function processOrder(int $orderId): void {
    $order = $this->db->query('SELECT * FROM orders WHERE id = ?', [$orderId]);
    foreach ($order->items as $item) {
        $product = $this->db->query('SELECT * FROM products WHERE id = ?', [$item->product_id]);
        // N+1 — invisible in logs, obvious in traces
    }
}
✓ Fixed
// Manual spans — each operation visible in trace UI
public function processOrder(int $orderId): void {
    $span = $this->tracer->spanBuilder('process_order')
        ->setAttribute('order.id', $orderId)
        ->startSpan();
    $scope = $span->activate();

    try {
        // PDO auto-instrumentation creates child spans automatically
        $order = $this->db->query('SELECT * FROM orders WHERE id = ?', [$orderId]);
        $span->setAttribute('order.item_count', count($order->items));
        // ... rest of processing
        $span->setStatus(StatusCode::STATUS_OK);
    } catch (Throwable $e) {
        $span->recordException($e)->setStatus(StatusCode::STATUS_ERROR);
        throw $e;
    } finally {
        $scope->detach();
        $span->end();
    }
}

Added 23 Mar 2026
Edited 5 Apr 2026
Views 80
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 1 ping F 2 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 2 pings S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 1 ping S 0 pings S 0 pings M 1 ping T 0 pings W
No pings yet today
ChatGPT 1
ChatGPT 51 Amazonbot 19 Perplexity 12 Google 9 Ahrefs 3 Majestic 2 You.com 2 Meta AI 1 Bing 1 Scrapy 1
crawler 98 crawler_json 3
DEV INTEL Tools & Severity
🔵 Info ⚙ Fix effort: Medium
⚡ Quick Fix
Install open-telemetry/opentelemetry-auto-pdo for automatic PDO span creation — zero code changes, all SQL queries become spans with timing
📦 Applies To
PHP 7.0+ web cli

✓ schema.org compliant