softwax/correlation-ids-bundle
A PHP/Symfony bundle that generates and propagates correlation IDs across requests, responses, and logs, making it easier to trace distributed transactions and debug issues end-to-end.
What this actually is. softwax/correlation-ids-bundle v0.1 is a single-purpose utility: read or mint a correlation ID at the start of a request, put it in log context, expose it via an injectable service, and carry it on HTTP headers. That is the entire functional surface. The version is 0.1, the last release was August 2024, the repo has zero stars, and the only release artifact is a link to a commit range with no changelog body. The vendor namespace and the -bundle suffix are Symfony conventions.
Architecture fit — poor as a dependency, good as a pattern. The concern being solved (cross-cutting request identity) is a natural fit for Laravel's HTTP kernel, its Context facade, and Monolog processors. The package is not a fit: Symfony bundles register through a DependencyInjection extension and a Bundle subclass, neither of which Laravel's container loads. A Laravel integration therefore requires an adapter layer before any value is delivered — meaning our first engineering task is not "configure and ship" but "write a compatibility shim for an unproven third-party component." That is a poor trade for a component whose entire behaviour can be expressed in roughly a hundred lines of application code we would own outright.
Integration feasibility. Nominally a day: install via Composer, write a service provider to bind the bundle's services, register middleware, publish a config file, add a Monolog processor. The genuine effort sits in the boundaries — queue job payloads, Http client propagation, Octane worker state reset, console invocations, and error paths. None of that is solved by the package; the source read confirms it is request-scoped only. So the shim work is additive to work we must do regardless.
Technical risk — maturity and ownership, not capability.
Key questions to resolve in the spike:
Recommendation. Treat the package as a reference implementation, not a dependency. Read src/, extract the generation/validation/header semantics, and own a small internal middleware. We retain the benefit, drop the Symfony dependency and the vendor risk, and eliminate an upgrade class we cannot forecast.
Stack fit. Laravel 10–12 on PHP 8.1+ (pending confirmation from composer.json). The change is confined to request/response handling and logging; it touches no persistence, auth, or routing. Blast radius is small and well understood.
Build vs. buy, stated plainly. Given the framework mismatch and the negligible code volume, the migration path is port the behaviour, not adopt the package. The internal version needs four pieces: middleware that resolves or generates the ID and sets the response header in a finally block; a Monolog processor that merges it into every record; an outbound HTTP hook that forwards it; and queue dispatch/worker hooks that carry and restore it. Configuration (header name, trust policy, format, excluded paths) lives in config/correlation.php.
Migration path. Fully additive and reversible: no schema change, no backfill, no data movement. Rollback is removing a middleware registration. This supports a cheap, low-commitment pilot.
Compatibility gates to clear before production:
Sequencing with effort:
composer.json and src/; settle the framework question; confirm version support. Exit criterion: a go/no-go on dependency vs. internal build. Expectation is internal build.Version policy. If we do consume the package, pin to an exact version, never a caret range. Pre-1.0 semver plus no changelog means a minor bump can silently rename a header and break propagation while logging keeps working.
Maintenance. Low steady-state effort either way, but the two options differ in predictability. A dependency carries upgrade churn we cannot forecast — and with no changelog we discover breakage at upgrade time or, worse, during an incident. An internal implementation carries churn we control: it moves only when we change it, and the diff is ours to review. Keep the integration surface thin (middleware registration plus config) so either choice remains replaceable.
Support. Community/single-vendor with no LTS, no published security-response process, and a GitHub issue as the escalation path. For a component this small that is tolerable; for a component every service's logs depend on, it is not sufficient on its own. Assign a named internal owner who has read the source and can answer header-trust questions without going upstream.
Scaling. Runtime cost is negligible — a header read and a log field per record. The costs that scale are downstream: log volume grows slightly per line, and ingestion/storage becomes the visible line item. Cardinality is the trap — never promote the correlation ID to a metric label or a database index key, or monitoring cost and index bloat grow with request volume. Sample and retain deliberately.
Failure modes to instrument and monitor:
Ramp-up. Engineering ramp is trivial: one middleware registration and one config file, well under a day per team. The real cost is organisational — agreeing the header name, ID format, trusted-upstream policy, and canonical-ID question across teams, then updating runbooks, dashboards, and alert queries to actually use the field. Budget half a day of engineering plus one short cross-team agreement cycle. Skipping the agreement step is the failure mode that matters: teams adopt divergent conventions, identifiers do not join across service boundaries, and the investment delivers per-service logging hygiene rather than the cross-service traceability that justified it.
How can I help you explore Laravel packages today?