dragon-code/request-tracker
A Laravel package for tracking incoming HTTP requests. It captures request details, logs activity, and helps you inspect traffic through a simple, configurable setup, making it easy to add request auditing and monitoring to your application.
dragon-code/request-tracker records the hops of a request as it travels between your own services: what called what, how long each leg took, and what came back. Treat it as a lightweight, self-hosted alternative to a full APM agent — it is scoped to Laravel's HTTP client and HTTP kernel, not to database queries or queue workers on their own.
Minimal setup:
composer require "dragon-code/request-tracker:^1.5"
php artisan vendor:publish --tag=request-tracker
php artisan migrate
You will get a config file (storage driver, header names, sampling, payload capture toggles) and one or more migrations for the trace/span tables. Read the config file top to bottom before touching anything else — the defaults are conservative and several features (payload capture, sampling) are off until you switch them on.
Then wire the two halves:
api group (or a dedicated group) in bootstrap/app.php / Kernel.php. This is what creates the trace for a request arriving from another service.Http:: call made from your app is recorded and stamped with the correlation header.The two classes you will actually touch are TrackerHeader (which resolves the correlation header name) and TrackerUuid (which generates/reads the request identifier). Since 1.5.0 both are initialized through the container rather than being hardcoded statics, which means they are the right seam for customization — look there before you look at a fork.
The first use case worth validating end to end: a single user action in Service A that fans out to Service B. Fire the request, then look at the tracked records for that trace. If you see two rows sharing a trace identifier and each row has the correct duration, the package is doing its job. If you see only one row, the outbound half is not registered or the header is being stripped — see the gotchas below.
Because this package has a very small user base, budget an hour for reading the actual source in vendor/dragon-code/request-tracker/src before you rely on it. The README covers the happy path; the source is short and is the real documentation. Start with TrackerHeader and TrackerUuid — they are the smallest files and they define the contract everything else assumes.
Correlation header as the contract. Everything hinges on one header (a X-Request-Id-style constant resolved by TrackerHeader) being propagated. Keep the header name in config or in a container binding of TrackerHeader — never hardcode the string in your own code — so that if you later swap the package out you change one line. If you already emit a request ID of your own, point the tracker at that header instead of adopting a second one.
Let the container decide the ID format. Because TrackerUuid is dynamic since 1.5.0, bind your own implementation when you need a specific format (ULID, prefixed tenant ID, an ID your gateway already generates). This is cleaner than post-processing: the value written to storage and the value sent on the outbound header are then guaranteed identical.
Stamp the trace onto queued jobs. The middleware only covers the HTTP life of a request; a job dispatched inside it will start with no trace and produce an orphaned span. Add the resolved identifier to your job payload (a small base job class that reads it from TrackerUuid in its constructor is the cleanest way) and re-bind it before the job runs. Without this, any asynchronous fan-out is invisible.
Scope outbound tracking deliberately. Global tracking of every Http:: call is the right default in a small mesh, but it will also record calls to Stripe, S3 and health checks. Use the config's ignore list, or wrap third-party calls in a client that opts out, so your trace tables stay about your services. A trace that is 90% payment-gateway latency is noise.
Record only what you will read. Payload capture is the feature people enable first and regret later. Turn it on per-environment (staging yes, production no) or restrict it to a header allowlist. The useful middle ground is capturing method, path, status, duration and the correlation ID, and nothing else.
Group by trace, not by row. Build your own query layer for reading: a Trace model relation that eager-loads spans ordered by start time gives you the waterfall view that makes the data worth having. The package's storage is a flat log; the value comes from how you present it. If the package ships a dashboard route, gate it behind auth and a non-production check immediately — it is not designed to be public.
Write through a queue in high-traffic services. Synchronous inserts on every inbound and outbound hop add latency to the request path. If the config exposes a queue/async storage option, use it; if not, wrap the storage driver in your own decorator that pushes a job, and accept that you will lose records when the queue backs up.
Complement, do not duplicate, Telescope. Telescope is for local debugging of one app; request-tracker is for a request crossing two or more apps. Run them side by side in staging and make sure the same event is not being written twice into different tables.
StartSession if you attach user context. Register it explicitly rather than relying on the group's existing order.curl -H against the service before you debug any PHP.TrackerUuid being dynamic cuts both ways under long-lived workers. Under Octane, Swoole or a queue worker, verify with a two-request test that a second request gets a new identifier. If the resolved value is cached on a singleton, every request in the worker shares one ID and your traces collapse into a single blob. Bind it as a scoped/singleton-cleared instance and confirm.TrackerHeader is initialized once. If you change the header name at runtime (a feature flag, a per-tenant override) the already-initialized instance will not pick it up. Rebinding in the container and forgetInstance() is the reliable route; editing config after boot is not.Http:: call is a new outgoing span. Decide up front whether that is a feature (you see the retry) or a bug (your p95 is skewed); if the latter, correlate retries to the same logical span in your reporting rather than trying to suppress the second row.^1.5 is a sane constraint, but 1.x at this adoption level still moves fast — 1.5.0 already changed how two core classes are constructed. Test minor bumps in staging, especially anything touching TrackerHeader/TrackerUuid, because those changes are runtime-behavioural, not just cosmetic.Log), container bindings for TrackerHeader and TrackerUuid (now first-class swap points as of 1.5.0), an event or resolver for attaching user/tenant context, and a callback for redacting payloads before persistence. If any of these are missing, the practical workaround is a decorator around the driver rather than a fork — the package is small enough that a fork is also a reasonable option, and you may end up maintaining it.How can I help you explore Laravel packages today?