Skip to main content
use Keepsuit\LaravelOpenTelemetry\Facades\Tracer;
The Tracer facade is the primary entry point for manual tracing. It wraps the underlying Keepsuit\LaravelOpenTelemetry\Tracer class and gives you access to the active span, trace context, propagation headers, and span creation.

traceStarted()

Returns whether an active, valid trace is currently in progress.
bool Tracer::traceStarted()
return
bool
true if there is a currently active span with a valid trace context, false otherwise.
if (Tracer::traceStarted()) {
    // We are inside an active trace
    $traceId = Tracer::traceId();
}

traceId()

Returns the trace ID of the currently active span, or null if no valid trace is active.
?string Tracer::traceId()
return
string | null
The 32-character hex trace ID string, or null when no valid trace is active.
$traceId = Tracer::traceId();
// e.g. "4bf92f3577b34da6a3ce929d0e0e4736"

activeSpan()

Returns the currently active SpanInterface. When no span has been started this returns a no-op span.
SpanInterface Tracer::activeSpan()
return
OpenTelemetry\API\Trace\SpanInterface
The currently active span.
$span = Tracer::activeSpan();
$span->setAttribute('user.id', $userId);

activeScope()

Returns the current ScopeInterface, or null if there is no active scope. A scope represents the lifetime of a span activation and must be detached when you are done.
?ScopeInterface Tracer::activeScope()
return
OpenTelemetry\Context\ScopeInterface | null
The active scope, or null.
$scope = Tracer::activeScope();

// ... later, when cleaning up:
$scope?->detach();

currentContext()

Returns the current ContextInterface. Useful for capturing context to pass across asynchronous boundaries.
ContextInterface Tracer::currentContext()
return
OpenTelemetry\Context\ContextInterface
The current OpenTelemetry context.
$context = Tracer::currentContext();

// Pass $context into a new span later:
Tracer::newSpan('async-work')
    ->setParent($context)
    ->measure(function () {
        // ...
    });

propagationHeaders()

Returns an associative array of HTTP headers that encode the current trace context for propagation to downstream services.
array Tracer::propagationHeaders(?ContextInterface $context = null)
context
OpenTelemetry\Context\ContextInterface | null
default:"null"
The context to inject into the headers. When null, the current active context is used.
return
array
Associative array of propagation headers (e.g. traceparent, tracestate).
// Propagate trace to an outgoing HTTP request
$headers = Tracer::propagationHeaders();

Http::withHeaders($headers)->post('https://api.example.com/process', $data);

extractContextFromPropagationHeaders()

Extracts a ContextInterface from an incoming set of HTTP headers. Use this to continue a trace that originated in an upstream service.
ContextInterface Tracer::extractContextFromPropagationHeaders(array $headers)
headers
array
required
Associative array of HTTP headers received from an upstream service.
return
OpenTelemetry\Context\ContextInterface
The context extracted from the propagation headers.
// In a consumer service receiving a request
$context = Tracer::extractContextFromPropagationHeaders($request->headers->all());

Tracer::newSpan('handle-request')
    ->setParent($context)
    ->measure(function () {
        // This span is a child of the upstream span
    });

newSpan()

Creates a new SpanBuilder for the given span name. Chain builder methods to configure the span, then call measure() or start() to begin recording.
SpanBuilder Tracer::newSpan(string $name)
name
string
required
The name of the span. Must be a non-empty string.
return
Keepsuit\LaravelOpenTelemetry\Support\SpanBuilder
A fluent span builder instance.
Tracer::newSpan('process-order')
    ->setAttribute('order.id', $orderId)
    ->measure(function (SpanInterface $span) use ($order) {
        return $order->process();
    });
See SpanBuilder methods below for all available builder options.

updateLogContext()

Injects the current trace ID into Laravel’s shared log context so that all subsequent log entries carry the trace_id field. Call this after starting a root span manually.
void Tracer::updateLogContext()
When using the built-in instrumentations (HTTP server, queue, etc.) or the otlp log channel, this is called automatically. You only need to call it when starting the root trace manually.
$span = Tracer::newSpan('my-command')->start();
$scope = $span->activate();

Tracer::updateLogContext(); // trace_id now appears in all log records

Log::info('Command started');

$scope->detach();
$span->end();

terminateActiveSpansUpToRoot()

Ends all currently active, recording spans by detaching their scopes and calling end() on each one, walking up the stack until there are no more recording spans or the optional $root span is reached.
void Tracer::terminateActiveSpansUpToRoot(?SpanInterface $root = null)
root
OpenTelemetry\API\Trace\SpanInterface | null
default:"null"
Stop terminating spans when this span is reached. When null, all active recording spans are ended.
// Emergency cleanup — end all open spans
Tracer::terminateActiveSpansUpToRoot();

// End spans down to (but not including) the root span
Tracer::terminateActiveSpansUpToRoot($rootSpan);

SpanBuilder methods

Tracer::newSpan() returns a SpanBuilder instance. All methods except measure() and start() return the builder itself for fluent chaining.

setAttribute()

Sets a single attribute on the span.
SpanBuilder::setAttribute(string $key, mixed $value): SpanBuilder
key
string
required
Attribute key.
value
mixed
required
Attribute value. Scalars, arrays, and booleans are supported.

setAttributes()

Sets multiple attributes at once from any iterable.
SpanBuilder::setAttributes(iterable $attributes): SpanBuilder
attributes
iterable
required
An iterable of string => mixed pairs.

setParent()

Overrides the parent context for this span. Pass null to create a root span with no parent.
SpanBuilder::setParent(?ContextInterface $context): SpanBuilder
context
OpenTelemetry\Context\ContextInterface | null
required
Parent context, or null for a root span.
Adds a link to another span context. Useful for associating spans across unrelated traces.
SpanBuilder::addLink(SpanContextInterface $context, iterable $attributes = []): SpanBuilder
context
OpenTelemetry\API\Trace\SpanContextInterface
required
The span context to link to.
attributes
iterable
default:"[]"
Attributes to attach to the link.

setStartTimestamp()

Sets an explicit start timestamp for the span. Accepts a Carbon instance or a raw nanosecond integer.
SpanBuilder::setStartTimestamp(CarbonInterface|int $timestamp): SpanBuilder
timestamp
CarbonInterface | int
required
A Carbon instance or a Unix timestamp in nanoseconds.
Tracer::newSpan('backfill')
    ->setStartTimestamp(now()->subSeconds(5))
    ->measure(fn () => doWork());

setSpanKind()

Sets the span kind. Accepts any SpanKind::KIND_* constant.
SpanBuilder::setSpanKind(int $spanKind): SpanBuilder
spanKind
int
required
One of SpanKind::KIND_INTERNAL, KIND_SERVER, KIND_CLIENT, KIND_PRODUCER, or KIND_CONSUMER.
use OpenTelemetry\API\Trace\SpanKind;

Tracer::newSpan('publish-message')
    ->setSpanKind(SpanKind::KIND_PRODUCER)
    ->measure(fn () => $queue->publish($message));

start()

Starts the span and returns a SpanInterface. The span is not automatically activated — call $span->activate() to make it the active span for nested operations.
SpanBuilder::start(): SpanInterface
return
OpenTelemetry\API\Trace\SpanInterface
The started span.
$span = Tracer::newSpan('my-span')->start();
$scope = $span->activate();

try {
    doWork();
} catch (Throwable $e) {
    $span->recordException($e);
    throw $e;
} finally {
    $scope->detach();
    $span->end();
}

measure()

Starts the span, activates it as the current span, executes the callback, then ends the span. Exceptions are automatically recorded on the span and re-thrown.
SpanBuilder::measure(Closure $callback): mixed
callback
Closure
required
A closure that receives the active SpanInterface as its first argument. The return value of the closure is returned by measure().
return
mixed
The value returned by the callback. If the callback returns a PendingDispatch (e.g. from dispatch()), null is returned instead to ensure the job is dispatched correctly.
$result = Tracer::newSpan('process-payment')
    ->setAttribute('payment.amount', $amount)
    ->measure(function (SpanInterface $span) use ($payment) {
        $result = $payment->charge();
        $span->setAttribute('payment.status', $result->status);
        return $result;
    });

measure() vs start() — when to use each

Use measure() when the work you want to trace is synchronous and fits inside a closure. The span lifecycle is managed automatically. Use start() when you need fine-grained control: for example, when the span must stay open across multiple steps, when you want to activate the span conditionally, or when you need to attach it to an asynchronous operation.
// measure() — simple, automatic cleanup
Tracer::newSpan('send-email')->measure(function () {
    Mail::to($user)->send(new WelcomeMail());
});

// start() — manual control
$span = Tracer::newSpan('long-running-job')->start();
$scope = $span->activate();

step1();
step2();

$scope->detach();
$span->end();

Distributed tracing example

The following example shows how to propagate a trace from a producer service to a consumer service using propagationHeaders() and extractContextFromPropagationHeaders().
// Producer service — attach propagation headers to an outgoing request
$headers = Tracer::propagationHeaders();

Http::withHeaders($headers)->post('https://service-b.internal/jobs', [
    'payload' => $payload,
]);
// Consumer service — extract context from the incoming request headers
$context = Tracer::extractContextFromPropagationHeaders($request->headers->all());

Tracer::newSpan('handle-job')
    ->setParent($context)
    ->measure(function (SpanInterface $span) use ($payload) {
        // This span is linked to the producer's trace
        processJob($payload);
    });

Build docs developers (and LLMs) love