[ GLOSSARY ]

What is a span?

QUICK ANSWER

What is a span in observability?

A span is one unit of work within a trace — an HTTP request, a DB query, a function call. Each span has a name, start and end timestamps, parent/child pointers to other spans, and attributes (the SQL query, HTTP status, controller name). A trace is a tree of spans. In Laravel, every DB query, cache call, and outgoing HTTP request fires one span when instrumented via laravel/nightwatch or OpenTelemetry.

Updated · 2026-04-13

Anatomy of a span

A single DB query span

text
name:         db.query
trace_id:     0af7651916cd43dd8448eb211c80319c
span_id:      b7ad6b7169203331
parent_id:    0f9e8d7c6b5a4392
start_time:   2026-04-13T14:23:05.240Z
end_time:     2026-04-13T14:23:05.260Z
duration_ms:  20

attributes:
  db.system:     postgresql
  db.statement:  SELECT * FROM orders WHERE user_id = ? LIMIT 10
  db.rows:       10
  laravel.connection: pgsql

status:
  code: OK

The span tells you everything you need to reconstruct that one operation's timing, inputs, and outcome — but nothing about the surrounding request. That's what the parent span is for.

Parent-child relationships

Spans form a tree via parent_id. The root span (the HTTP request) has no parent. Every other span points to its parent. The tracing backend reconstructs the tree from these pointers to render the waterfall.

Attributes

Spans carry structured key-value metadata — attributes. OpenTelemetry defines semantic conventions so every vendor uses the same attribute names for the same concepts:

  • http.method, http.status_code, http.route — HTTP spans
  • db.system, db.statement, db.operation — DB spans
  • messaging.system, messaging.destination — queue spans
  • exception.type, exception.message, exception.stacktrace — error spans

Creating custom spans in Laravel

For an expensive internal operation that isn't captured by auto-instrumentation:

php
use OpenTelemetry\API\Globals;

$tracer = Globals::tracerProvider()->getTracer('app');

$span = $tracer->spanBuilder('image.resize')
    ->setAttribute('image.width', $width)
    ->setAttribute('image.height', $height)
    ->startSpan();

$scope = $span->activate();

try {
    $resized = $this->resize($image, $width, $height);
    $span->setStatus(StatusCode::STATUS_OK);
} catch (\Throwable $e) {
    $span->recordException($e);
    $span->setStatus(StatusCode::STATUS_ERROR);
    throw $e;
} finally {
    $scope->detach();
    $span->end();
}

Laravel-idiomatic alternative: wrap the operation in a macro or service method and have NightOwl's Nightwatch integration surface it via its own span abstraction.

Span events vs child spans

A span event is a timestamped annotation within a span — not a child span, just a marker. Use events for point-in-time signals (cache.hit, retry.attempted). Use child spans when the thing has its own duration worth tracking. Events are cheaper but less queryable.

Frequently asked questions

What is a span?

A span is one unit of work within a trace — an HTTP request, a database query, a function call, an external API call. Each span has a name, start/end timestamps, parent/child pointers to other spans, and a set of attributes (key-value metadata like the SQL query, HTTP status, controller name). A trace is a tree of spans.

What's the difference between a span and a transaction?

Nothing technical — it's terminology. 'Transaction' is older APM language for a top-level span (the root of a request trace). 'Span' is OpenTelemetry's term for any unit, including the root. New Relic, Datadog, and older Sentry use 'transaction'; modern OTel-aligned tools use 'span' uniformly.

What attributes should I put on a Laravel span?

OpenTelemetry semantic conventions define standard attributes: http.method, http.status_code, db.system, db.statement, net.peer.name. Add Laravel-specific context as needed — laravel.controller, laravel.route, user.id (if privacy allows). Keep spans lean — attributes are stored per span and multiply storage cost.

Should I create custom spans in Laravel?

For expensive internal operations, yes. A slow image-resizing function in a controller won't show up as a span unless you wrap it. Auto-instrumentation catches the framework's common operations (HTTP, DB, cache, queues) — custom spans fill in the pieces that are specific to your code.

What's a span event?

A timestamped annotation within a span — not a child span, but a point-in-time marker. Useful for 'something happened mid-span' signals: cache.miss, queue.job.released, exception.caught. Span events are cheaper than full spans but still attach to the span's attribute storage.

How long should a span be?

Granular enough to isolate bottlenecks, coarse enough to not drown in data. Typical useful granularity: one span per SQL query, one per external HTTP call, one per queued job dispatch, one per view render. Per-line spans are overkill and inflate storage. A good heuristic: if a span wouldn't reveal a useful decision, it probably shouldn't be a span.

PRICING

Flat pricing. No event caps. No per-seat fees.

14-day free trial, no credit card. Your PostgreSQL, your data.

HOBBY

$5 /month

1 app · 14 days lookback · all Laravel events

TEAM

$15 /month

Up to 3 connected apps · unlimited environments · all Laravel events

AGENCY

$69 /month

Unlimited apps · unlimited agent instances · same flat rate at any traffic