niladam/laravel-tracing

W3C Trace Context for Laravel: one id follows a request through its logs, its queued jobs and the services it calls.

Maintainers

Package info

github.com/niladam/laravel-tracing

pkg:composer/niladam/laravel-tracing

Transparency log

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 2

Open Issues: 0

v2.0.0 2026-08-24 17:38 UTC

This package is auto-updated.

Last update: 2026-08-24 18:18:09 UTC


README

Laravel TracingA request traced through its jobs and the services they call, every log line carrying the same trace id

Latest Version on Packagist Tests Code Style Total Downloads

One id follows a request through its own log lines, the queued jobs it dispatches, the jobs those dispatch, and the internal services it calls.

Built on the W3C Trace Context standard, so it speaks the same traceparent header as OpenTelemetry, Sentry, Datadog and Honeycomb — no APM dependency required, and nothing to rewrite if you add one later.

grep one trace_id for the whole tree. parent_span_id tells you who triggered what.

Installation

composer require niladam/laravel-tracing

That is the whole setup — the provider is auto-discovered, requests are traced, jobs inherit the trace and log lines carry it. Publish the config only when you want to change something:

php artisan vendor:publish --tag=laravel-tracing-config

What you get without configuring anything

Key
trace_id 32 hex. The root, minted once at the origin and never regenerated as it travels.
span_id 16 hex. This one unit of work — this request, this job run, this command.
parent_span_id The span_id that caused this one. null at the root.
channel http, console or queue.
user_id The moment any guard answers — session and stateless alike.
ip, url, method On a request.
command On a console command.
job.* name, connection, queue, attempts, uuid — inside a job.

Crossing every boundary on the way:

Boundary How
Inbound HTTP Reads traceparent / tracestate and continues the caller's trace
Queued jobs Context dehydrate/hydrate, with a fresh child span per job run
Console subprocesses Laravel rehydrates __LARAVEL_CONTEXT, so Artisan::call children join in
Outgoing HTTP traceparent injected on your own hosts only — never a third party
Saloon Registered separately, since it ships its own sender. Optional.
Logs Merged into each record's context, so a line stays one JSON object

Adding your own context

Every key has a moment — the instant it becomes true. Record it then and you never think about middleware order or whether the user has logged in yet.

Pick the lightest rung that fits:

Your key is… Do this
a constant context.additional in config — no code
a value or two, computed Tracing::always(…)
tied to who is logged in Tracing::authenticated('web', …)
tied to a moment Tracing::on(SomeEvent::class, …)
growing, or needs dependencies a Recorder class listed in record
use Niladam\LaravelTracing\Facades\Tracing;

// in a service provider's boot()
Tracing::always(fn () => ['host' => gethostname()]);

Tracing::authenticated('web', fn (User $user) => [
    'team_id' => $user->current_team_id,
]);

Tracing::on(TeamSwitched::class, fn ($e) => ['team_id' => $e->team->id]);

That last line is the part a middleware cannot do: Context::add overwrites, so the key corrects itself the moment the team changes. Snapshot it once at the start of a request and it is wrong for the rest of it.

Anything you put in Laravel's own Context is traced too — there is no second store and nothing new to learn:

Context::add(['order_id' => $order->id]);
Context::addHidden('idempotency_key', $key);   // travels to jobs, never logged

Read the trace back through the facade, which knows what you have named the keys:

Tracing::traceId();       // 4bf92f3577b34da6a3ce929d0e0e4736
Tracing::traceparent();   // to hand to a client this package does not cover

Keeping secrets out

Four mechanisms, narrowest first — the guide covers which to reach for:

#[\SensitiveParameter] public string $cardToken,             // never recorded at all
Context::addHidden('key', $value);                           // travels, never logged
'logs'    => ['redact' => ['keys' => ['*password*']]],       // safety net, descends into nested values
'context' => ['local_only' => ['body.*']],                   // stays in this process, never written to your queue

Documentation

Recording context The moments, the built-in recorders, and plugging in your own
Adding your own context Using Laravel's Context — anywhere, any time, including mid-job
Registering the middleware Groups, global, or a route alias — plus ordering and manual wiring
Jobs Child spans, job.* context, and opt-in job arguments
Keeping secrets out #[\SensitiveParameter], hidden context, redaction, and your queue
Handing the trace back Response headers, and showing an id to a user
Interoperability The wire format, renaming keys, upstream ids, Saloon, APMs

Configuration at a glance

Five groups, each answering one question:

'middleware'  => [...],   // where a trace begins
'record'      => [...],   // what gets recorded
'context'     => [...],   // what lands in the context
'logs'        => [...],   // what reaches a log line
'propagation' => [...],   // what leaves the application

'enabled' => false turns the lot off.

Requirements

PHP 8.2+ (8.3+ on Laravel 13) tested
Laravel 12 ^12.1 tested
Laravel 13 ^13.0 tested
Saloon ^4.0, optional tested, and tested absent

The floors are where the APIs this package needs first appeared, nothing more. Keeping your framework patched is your application's business and composer audit's — a package that pins a security floor only goes stale on the next advisory.

Why not Laravel 11

Illuminate\Contracts\Log\ContextLogProcessor arrived in v12.1.0 — it exists in no Laravel 11 release. Without it there is no supported way to put the trace on a log record, which is most of what this package does, so Laravel 11 is not a matter of testing effort: there is nothing to hook into. That is also why the Laravel 12 floor is ^12.1 rather than ^12.0.

Saloon v3 is unsupported for a different reason: every release carries an unpatched advisory (<4.0.0), so there is no safe floor to point at.

Testing

composer test

Changelog

See CHANGELOG.md.

Credits

The logo and header image were generated with AI.

License

MIT. See LICENSE.md.