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

Symfony Span Bundle Laravel Package

roqmeu/symfony-span-bundle

A Symfony bundle that integrates the Span library into Symfony applications, providing framework-native configuration, dependency injection wiring, and bundle setup so you can add span-based functionality with minimal boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and confirm what actually ships. composer require roqmeu/symfony-span-bundle — but read the repo root before writing code. It is a Symfony bundle, so the first question is whether the package exposes a framework-agnostic core (plain PHP classes for spans/exporters) or whether all wiring lives in DependencyInjection/. If the latter, you will be hand-registering services in a Laravel service provider rather than relying on config/bundles.php.

Where to look first, in order:

  1. README.md — the only reliable source for config keys and the intended integration point.
  2. src/DependencyInjection/Configuration.php (or equivalent) — this is the authoritative list of options; the README usually drifts.
  3. composer.json — check the symfony/* constraints against your PHP version. A bundle written against Symfony 6/7 contracts will pull in symfony/http-kernel even though Laravel never boots it.
  4. src/ for a Span, SpanInterface, or exporter class you can instantiate directly.

First use case. Don't start with the bundle's auto-wiring. Start with the smallest possible span around a single route so you can see output end-to-end:

Route::get('/orders/{order}', function (Order $order, SpanFactory $spans) {
    $span = $spans->start('orders.show', ['order.id' => $order->id]);

    try {
        return OrderResource::make($order->load('lines'));
    } finally {
        $span->finish();
    }
});

If that produces a timed span in your log/exporter, the core works and the remaining work is wiring (middleware, decorators).

Version note: v1.0.1 is a patch release. The changelog is a bare compare link with no commit summary, so treat it as "no documented behaviour change" rather than "verified safe." Diff v1.0.0...v1.0.1 yourself before upgrading — a one-commit patch on a v1.0.0 package is often a tagging or CI fix, but it can equally be a signature change that nobody wrote up.

Implementation Patterns

HTTP: middleware, not controllers. Wrap the request lifecycle once so you never touch individual controllers:

final class SpanMiddleware
{
    public function __construct(private SpanFactory $spans) {}

    public function handle($request, Closure $next)
    {
        $span = $this->spans->start('http.request', [
            'http.method' => $request->method(),
            'http.route'  => optional($request->route())->getName(),
        ]);

        try {
            $response = $next($request);
            $span->setAttribute('http.status_code', $response->getStatusCode());
            return $response;
        } catch (\Throwable $e) {
            $span->recordException($e);
            throw $e;
        } finally {
            $span->finish();
        }
    }
}

Register it in bootstrap/app.php (->withMiddleware(...)) on Laravel 11+, or Kernel::$middleware on 10 and below. Keep it in the global stack only if you want spans for every request including health checks — otherwise put it in web/api and exclude what you don't care about.

Nesting via a scoped context manager. Spans are only useful if they nest. If the bundle ships an active-span/context concept, use try/finally with a scope object rather than manual setParent calls; hand-rolled parenting leaks the wrong parent as soon as an exception unwinds.

Queues: job middleware. Illuminate\Queue\Middleware is the clean insertion point — a span that starts when the job is popped and finishes when it's released/deleted. Propagate a trace/span identifier in the job payload so the queued span links back to the HTTP span that dispatched it; otherwise your queue work is a disconnected island.

Decorators over if statements. Rather than sprinkling span calls through services, decorate the ones you care about in a service provider:

$this->app->extend(PricingClient::class, function ($inner, $app) {
    return new TracedPricingClient($inner, $app->make(SpanFactory::class));
});

This keeps tracing out of domain code and makes it trivially removable in tests.

Console and scheduler. Wrap Artisan::starting / a base command, and tag scheduled tasks with the command name as the span name. This is where tracing pays for itself fastest — slow overnight jobs are exactly what nobody instruments.

Naming conventions. Pick a fixed scheme (http.request, queue.{JobClass}, db.query, http.client.{host}) on day one. Ad-hoc names produce traces you can't aggregate across.

Gotchas and Tips

  • It's a Symfony bundle in a Laravel app. Bundle, Extension, and CompilerPass classes are inert here. Anything loaded through DependencyInjection will not run. Budget time for a manual service provider — and expect the bundle's own README examples to be Symfony YAML that doesn't translate.
  • No documented changes in v1.0.1. The release notes are just a compare URL. Read the diff; don't infer "patch = safe." On a 1.0.x package the API surface is still settling.
  • Long-running processes leak spans. Under Octane, Horizon, or a queue worker, anything pushed onto a static stack must be popped in finally. A single early return that skips finish() will grow memory until the worker recycles. If the bundle uses a static/singleton context, assert count() === 0 after each request in a test.
  • config:cache and env(). If the bundle's config file calls env() outside Laravel's config layer, values vanish after php artisan config:cache. Move them into config/span.php and publish/override there.
  • Sampling is not optional in production. Per-request spans plus per-query spans on a busy app will dominate your exporter's cost. Configure head sampling, or at minimum gate the middleware on a config flag and default it off in local.
  • Sensitive attributes. HTTP spans naturally capture URLs, headers, and query strings. Scrub authorization, cookie, and any ?token=-shaped query parameter inside the middleware before the attribute is set — retroactively cleaning an exporter is much harder.
  • Extension points worth checking first: an exporter interface (so you can log in dev and ship elsewhere in prod), a clock/timer abstraction (so span durations are testable with travel()), and a NullSpan implementation for tests. If NullSpan is absent, wrap span creation behind your own factory so tests don't assert on timing.
  • Debugging missing spans: verify the middleware is actually in the resolved stack (php artisan route:list -v shows middleware for a route), then confirm the exporter is bound — a common failure is a silently-null logger receiving spans that go nowhere.
  • Pin the version. With one patch release and a thin changelog, a ^1.0 constraint gives you very little information when something breaks. Pin to 1.0.1 and upgrade deliberately.
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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource