Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Opentelemetry Propagation Traceresponse Laravel Package

open-telemetry/opentelemetry-propagation-traceresponse

Adds TraceResponse propagation support to the OpenTelemetry PHP SDK, letting services extract and inject trace context using the draft TraceResponse format. Useful when integrating with systems that emit or expect this header, keeping distributed traces connected across service boundaries.

View on GitHub
Deep Wiki
Context7

Getting Started

What it is: a contrib propagator that implements the Trace Context: traceresponse draft — the response-side companion to traceparent. Your server emits a traceresponse header on the HTTP response carrying the trace-id and the span-id of the server's root span, so a caller (including a browser) can correlate what it just saw with the server-side trace. It is additive: you still need traceparent/TraceContextPropagator for normal request propagation.

Install and wire up:

composer require open-telemetry/opentelemetry-propagation-traceresponse
use OpenTelemetry\Contrib\Propagation\TraceResponse\TraceResponsePropagator;

// Response side only — do NOT add this to your outgoing-request propagator list.
$propagator = new TraceResponsePropagator();

$carrier = [];
$propagator->inject($carrier);
// ['traceresponse' => '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01']

Then attach it to the outgoing PSR-7 response:

foreach ($carrier as $name => $value) {
    $response = $response->withHeader($name, $value);
}

Where to look first:

  1. src/ — one small propagator class (verify the exact FQCN/namespace against composer.json's autoload block; contrib packages follow the OpenTelemetry\Contrib\Propagation\{Name}\{Name}Propagator convention, and its sibling opentelemetry-propagation-server-timing is structurally identical).
  2. composer.json — the open-telemetry/sdk and api version constraints. This repo is [READONLY] with a single dependent-free release line, so pin versions and read the constraint before upgrading the SDK.
  3. tests/ — the smallest reliable spec of header shape and no-op behaviour.

First use case: put it in a PSR-15 / Symfony / Laravel response middleware after the tracing middleware has started the root span, so a client that received a 500 can quote traceresponse in a bug report and you can jump straight to the trace.

Implementation Patterns

Middleware-first integration (the normal shape). Add a thin response middleware rather than touching every controller:

final class TraceResponseMiddleware implements MiddlewareInterface
{
    public function __construct(private TextMapPropagatorInterface $propagator) {}

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $response = $handler->handle($request);

        $carrier = [];
        $this->propagator->inject($carrier);

        foreach ($carrier as $name => $value) {
            $response = $response->withHeader($name, $value);
        }

        return $response;
    }
}

Keep two propagator sets. Do not register this in TracerProvider::registerPropagator() if that same provider injects into outbound Guzzle/Symfony HTTP client calls — otherwise you'll ship traceresponse on requests, which is meaningless and confuses proxies. In practice:

// Request propagation (client + extract on server)
$requestPropagators = new CompositePropagator([
    new TraceContextPropagator(),
    new BaggagePropagator(),
]);

// Response propagation (a separate instance, used only by response middleware)
$responsePropagator = new TraceResponsePropagator();

Span-order matters. inject() sources the current SpanContext. If your root span ends (and detaches) before the middleware runs, you get nothing. Either keep the root span active until after the response is built, or capture the SpanContext when the root span is created and inject from an explicit Context::withContext(...)/Scope at response time.

Browser correlation. If a frontend reads it with fetch()/XHR, you must add it to CORS:

Access-Control-Expose-Headers: traceresponse

and, when the browser is on another origin, this header is also a custom response header — no preflight implication for responses, but the expose header is mandatory or JS sees null.

Correlation, not causality. If you also want the client side to record the linkage, call extract() and store the returned SpanContext as an attribute/log field (e.g. server.trace_id) on the client span. Do not pass it as a remote parent — that inverts the causal direction and makes your client span a child of a span that happened after it.

Framework wiring (Laravel/Symfony). Register the middleware in the global (not route) stack so 404s, 500s, and exception-handler-rendered responses all get the header. For Symfony, tag it with kernel.response / a high-priority listener; for Laravel, $middleware in app/Http/Kernel.php — the terminate/response path is where the span context is still reachable.

Testing. Assert output with a real SDK provider, not a mock:

$tracerProvider = new TracerProvider(new SimpleSpanProcessor(new InMemoryExporter($spans)));
$tracer = $tracerProvider->getTracer('test');
$scope = $tracer->spanBuilder('root')->activate();
$carrier = [];
(new TraceResponsePropagator())->inject($carrier);
$scope->detach();

$this->assertMatchesRegularExpression('/^00-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$/', $carrier['traceresponse']);

Test the no-active-span case too — that's the one that silently regresses.

Complementary pairs. Use alongside open-telemetry/opentelemetry-propagation-server-timing, which uses the identical response-only propagator pattern; wrapping both in one response middleware keeps the plumbing in a single place.

Gotchas and Tips

  • Silent no-op with no active span. inject() on a propagator with no current SpanContext writes nothing — no exception, no log. If the header is "missing in prod but passes in tests", 90% of the time the span is already detached. Add an assertion/log in the middleware when $carrier is empty.
  • Leaking onto outbound requests. The single most common mistake: putting it in the global propagator list and then finding traceresponse on every Guzzle call. Server-side-only headers belong in a dedicated instance.
  • No traceresponse when the request is unsampled? Behaviour depends on whether the SDK yields a valid non-recording SpanContext vs. none. Check the flags you emit (00 vs 01) against the draft and don't assume sampled-only.
  • Version/format drift. This implements a draft spec; the header grammar and the version byte (00) have changed during drafting. Pin the package, pin open-telemetry/sdk, and re-run your regex test on every bump. Since the repo is [READONLY] with 0 dependents and a single star, budget for maintaining a fork if upstream stalls.
  • Don't try to use it for propagation. extract() exists for correlation convenience; using it as a substitute for TraceContextPropagator gives you broken trace trees. Requests still need traceparent.
  • Header casing and access. Header names are case-insensitive but log pipelines and CORS matchers frequently aren't — normalise to lowercase traceresponse in middleware and in Access-Control-Expose-Headers.
  • PII / cardinality. The header exposes your trace ID to any caller who can see the response. That's usually fine (trace IDs are opaque), but if you consider trace IDs internal identifiers, gate the middleware behind config/env rather than enabling it globally.
  • Debugging order. To debug a missing header: (1) is the middleware in the stack? (2) is a span active at that point? (3) did a later middleware or the exception handler rebuild the response, discarding the header? (4) did a proxy strip it? Emit a temporary traceresponse on a /health route to bisect the stack fast.
  • Extension points. The propagator is a tiny TextMapPropagatorInterface implementation — if you need extras (e.g. an additional custom header, or suppressing emission for certain routes), write a 30-line decorator around it rather than forking; that keeps you upgrade-compatible with contrib.
  • fields(). It declares ['traceresponse']; use it to whitelist the header in any proxy/CDN config that strips unknown response headers, and in log-middleware that records response headers.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource