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

Extension Propagator B3 Laravel Package

open-telemetry/extension-propagator-b3

Provides B3 (Zipkin-style) context propagation for OpenTelemetry PHP, letting you read and inject single- or multi-header B3 trace headers alongside or instead of the default W3C Trace Context. Useful for services interoperating with Zipkin, Brave, or other B3-based tracing systems.

View on GitHub
Deep Wiki
Context7

Getting Started

This package gives OpenTelemetry PHP a B3 propagator — the Zipkin-native header format — so your Laravel app can join traces with services that speak b3 or X-B3-* rather than W3C traceparent.

Minimal setup:

  1. Install the package and its native counterpart, then confirm it is loaded:

    composer require open-telemetry/extension-propagator-b3
    php -m | grep -i otel
    php --ri opentelemetry
    

    These OTel "extension-propagator-*" packages are bridges over the native opentelemetry PHP extension. If php --ri opentelemetry fails, the propagator classes will not be available regardless of Composer.

  2. Tell the SDK to use it via environment (read at boot):

    OTEL_PROPAGATORS=b3
    # or: b3multi
    # or combine: tracecontext,baggage,b3multi
    
  3. First use case — continue an inbound trace from a Zipkin-instrumented upstream:

    use OpenTelemetry\API\Globals;
    use OpenTelemetry\Extension\Propagator\B3\B3Propagator;
    
    $propagator = B3Propagator::getInstance();      // single `b3` header
    // $propagator = B3MultiPropagator::getInstance(); // X-B3-* headers
    
    $headers = array_map(fn ($v) => $v[0] ?? '', getallheaders());
    $parent  = $propagator->extract($headers);
    
    $span = Globals::tracerProvider()
        ->getTracer('my-service')
        ->spanBuilder('handle-request')
        ->setParent($parent)
        ->startSpan();
    

Look first at the package README, its composer.json (to see the exact ext-* requirement and version constraints), and the unit tests — the tests encode header fixtures and are the best source of truth for edge-case behaviour.

Implementation Patterns

Environment-driven wiring (preferred). Set OTEL_PROPAGATORS before the SDK/autoloader bootstraps. The SDK builds a composite propagator from the registered names, so your application code never touches the propagator directly. This is the pattern to standardise on; hand-constructing propagators in every controller drifts quickly.

Laravel middleware for extract/activate.

public function handle($request, Closure $next)
{
    $carrier = array_map(fn ($v) => $v[0] ?? '', $request->headers->all());
    $parent  = B3Propagator::getInstance()->extract($carrier);

    $span  = Globals::tracerProvider()->getTracer('app')
        ->spanBuilder($request->path())->setParent($parent)->startSpan();
    $scope = $span->activate();

    try {
        return $next($request);
    } finally {
        $scope->detach();
        $span->end();
    }
}

Inject for outbound HTTP. For Guzzle/PSR-18 clients not already covered by OTel middleware:

$carrier = [];
B3Propagator::getInstance()->inject($carrier);
$client->request('GET', $url, ['headers' => $carrier]);

Prefer the official Guzzle/PSR-18 instrumentation middleware when available; use manual injection for bespoke clients (SOAP, raw curl, queue payloads).

Combine propagators for mixed estates. OTEL_PROPAGATORS=tracecontext,baggage,b3multi lets the same service accept W3C from internal services and B3 from Zipkin-side partners. Extraction merges contexts; injection emits both header sets, which is usually fine but see the gotchas.

Match your exporter. If spans go to Zipkin (Zipkin exporter or OTLP into a Zipkin-compatible backend), B3 propagation keeps trace IDs consistent with what Zipkin shows in its UI and dependency graph.

Testing pattern. Assert both directions: a request with a known b3 header must produce a child span sharing traceId/spanId; an outgoing call must contain the expected header names. Unit-test with fixed header arrays — much faster than spinning up a Zipkin instance.

Gotchas and Tips

  • b3 vs b3multi is a compatibility decision, not a preference. Single-header (b3: {trace}-{span}-{sampled}-{parent}) is compact; multi-header (X-B3-TraceId, X-B3-SpanId, X-B3-Sampled, X-B3-ParentSpanId) is what older Zipkin clients emit. Ask the peer; guessing wrong produces two disconnected traces, not an error.
  • The native extension is the real dependency. Composer install succeeding does not mean the propagator works. If OTEL_PROPAGATORS=b3 is set but the extension is missing or version-mismatched, registration can silently fall back — verify by dumping headers at ingress/egress, not by trusting config.
  • Carrier mutability. inject() takes the carrier by reference; passing a copy or an immutable PSR-7 request will lose headers. Build a plain array, then attach it to the request.
  • Header casing and multi-value arrays. HTTP headers are case-insensitive, and Laravel's HeaderBag::all() returns arrays of values. The propagator expects flat strings — flatten before extraction or lookups silently miss.
  • Incoming sampling flags only matter if your sampler reads them. B3 carries Sampled (and the d debug flag). Pair the propagator with a ParentBased sampler; otherwise the parent's sampling decision is ignored and traces fragment at service boundaries.
  • The B3 debug flag is not a force-sample in PHP. Some implementations treat d as "always sample"; the PHP propagator primarily moves context. Don't build sampling logic on it without verifying against your actual installed version.
  • Emit one B3 flavour at a time. Listing both b3 and b3multi injects duplicate header sets; strict peers may reject or double-count. Combine b3 with tracecontext/baggage, not with b3multi.
  • Version-lock the extension to your PHP ABI. Rebuild after PHP upgrades; a stale .so fails to load, and the failure mode is often "no spans" rather than a clear error.
  • Debugging checklist: confirm the header arrives, confirm extract() returns a context containing a remote span context, confirm your span's traceId matches the inbound one. If step two fails, the problem is carrier shape or extension loading — not the exporter.
  • Extension point: if you need a non-standard header dialect, implement TextMapPropagatorInterface yourself and register it explicitly on the SDK builder rather than patching this package.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle