open-telemetry/context
Immutable, execution-scoped context propagation for PHP based on the OpenTelemetry specification. Activate contexts with scopes and detach them safely, with optional fiber support and guidance for event loops.
Architecture fit. open-telemetry/context is not an observability product; it is the propagation primitive underneath one. Its entire surface is a small set of contracts: an immutable Context, an ambient "current context" slot, activate() returning a Scope, and detach(). That is deliberately narrow, which is good for a TPM: it is a stable, low-coupling dependency you adopt once and rarely revisit, not a feature surface you negotiate requirements against. It is framework-agnostic and PSR-friendly, so it slots underneath Laravel rather than competing with Laravel's container, middleware, or logging stack.
The important architectural nuance for planning is that this package is necessary but not sufficient. It moves trace/span/baggage state across execution boundaries, but it does not create spans, sample them, export them, or instrument Laravel. Value only materializes when paired with the OpenTelemetry PHP API/SDK, exporters (OTLP/Zipkin), and Laravel-specific instrumentation. A TPM should treat context as a line item in an observability program of work, not as a deliverable.
The monorepo layout reinforces this: API/ holds interfaces and no-op implementations, SDK/ holds trace/metrics/logs/propagation/resource implementations, Context/ holds storage, scope management, and fiber support, and Contrib/ holds exporters. context is a shared substrate for the SDK layers, which is why it is consumed transitively.
Integration feasibility. Technically trivial: composer require open-telemetry/context, and in practice most teams get it pulled in by the SDK rather than requiring it explicitly. The integration risk is not installation — it is discipline. Every activate() must be paired with detach() in a finally block:
$scope = $context->activate();
try {
// run within new context
} finally {
$scope->detach();
}
The package mitigates this with debug scopes that warn on invalid or missing detach() calls in non-production environments. That is a genuine onboarding asset — it turns an invisible class of bug into a visible warning during development — and a TPM can lean on it as the primary safety net during rollout.
Technical risk. Ranked by what would actually affect a roadmap:
| Risk | Assessment | Mitigation |
|---|---|---|
Scope leakage (activate without detach) |
Moderate — causes wrong parent spans and context bleed between requests/jobs | Keep debug scopes on in dev/staging; code review checklist; wrap in middleware/job decorators |
| Async/fiber support constraints | Moderate — requires NTS build, ext-ffi, OTEL_PHP_FIBERS_ENABLED, and autoloader preloading for non-CLI SAPIs when ffi.enable=preload; fiber observers are explicitly unsafe on ZTS |
Validate the runtime (Octane/Swoole/RoadRunner/FrankenPHP) before committing to async coverage; fall back to manual event-loop binding |
| Event loops do not propagate automatically | High effort, low ambiguity — must wrap callbacks (a documented bindContext pattern) |
Budget engineering time; this is instrumentation work, not a config flag |
| PHP version floor | Unclear from public metadata — release notes show PHP 7.4 and 8.0 dropped, one summary claims 8.1+, while the monorepo's own agent guidance states PHP 8.2 is the minimum and default | Confirm the floor empirically against the pinned tag before planning the upgrade path |
| Release/cadence signal quality | Metadata reports a last release of 2023-09-07, yet the release notes include 1.5.0 and monorepo guidance references PHP 8.5 testing | Do not trust the split-package metadata; track the monorepo's tags and its SDK compatibility matrix |
exit/die skip detach() |
Low but real — produces unavoidable notices | OTEL_PHP_DEBUG_SCOPES_DISABLED exists but is only recommended for apps that use exit/die; prefer fixing the call sites |
| Zero listed dependents | Not a real risk — a false signal from subtree splitting | Judge adoption via the SDK, not this counter |
Key questions for engineering before commitment.
Stack fit. The package is a natural fit for a Laravel application that has decided on OpenTelemetry as its observability standard. It is not a fit for a monolith whose only requirement is local request logging — Laravel's logging, Monolog context, and Debugbar solve that with far less machinery. The decision to adopt is therefore a decision about the tracing architecture, and context should be presented to stakeholders that way.
Concretely, it underpins trace continuity across the boundaries where Laravel applications actually lose correlation:
Migration path. Do not require open-telemetry/context directly. Adopt open-telemetry/sdk (plus open-telemetry/exporter-otlp or equivalent) and let context arrive transitively; pin the SDK and treat context as a resolved transitive version. This avoids the classic failure mode of a split package drifting from the SDK it was cut from. Contribution and issue tracking go to the monorepo — the split repo is read-only — so internal upgrade notes should always reference monorepo tags.
For an existing application, the migration is additive rather than disruptive: no schema changes, no data migration, no user-facing behavior change. The real "migration" is a codebase-wide convention that context is scoped and always detached.
Compatibility. Apache-2.0, PSR-friendly, no framework lock-in, no service dependency, no network calls. The compatibility edges that matter are PHP version, thread safety (NTS vs ZTS), the FFI extension for fiber support, and ffi.enable=preload requiring the Composer autoloader to be preloaded for non-CLI SAPIs. Each is discoverable in a short spike and should be explicitly recorded as a platform prerequisite rather than assumed.
Sequencing. Align delivery with the roadmap already implied by the package's value profile:
An important TPM guardrail: each phase must produce an observable artifact (a trace someone actually used to debug something). "Adopt the context package" is not a phase; it is a dependency of every phase and a milestone nobody should be asked to sign off on.
Maintenance. Low churn on the code itself — the API is a handful of methods and the release history is dominated by small fixes, CI hygiene, and PHP-version compatibility work rather than behavioral change. The maintenance burden sits with the ecosystem: keeping SDK, exporter, and instrumentation versions aligned with the monorepo, and re-testing the fiber/event-loop paths whenever the PHP runtime or a long-running server (Octane, Swoole) is upgraded. Assign one named owner; a package with no direct dependents and no commercial support will not maintain itself by accident.
Support. Community-maintained, CNCF-governed, Apache-2.0, no vendor SLA. The split repo is read-only and contribution flows to the monorepo, so internal tickets cannot be filed upstream in the place engineers will naturally look. Expect to solve our own integration problems and to treat upstream only as a source of fixes for spec-level behavior. The debug-scope warnings are the best first-line support tool we get for free.
Scaling. The context layer itself is negligible: an immutable context object and a scope stack, with per-activation cost dominated by PHP object allocation, not by the package. Real scaling cost comes from span volume, sampling decisions, and exporter throughput — all owned by the SDK and backend choices, not by context. The one scaling caveat worth writing down is fiber propagation: it depends on FFI hooking, is explicitly unsafe on ZTS builds, and requires autoloader preloading under ffi.enable=preload. If horizontal scaling means moving to a persistent-worker runtime, re-validate this path rather than assuming it carries over.
Failure modes. Enumerate these in a runbook, because most are silent:
activate() without detach() leaves stale context active; downstream spans get the wrong parent and correlation becomes misleading rather than broken, which is worse. Detect via non-production debug-scope warnings; prevent via middleware and job decorators that own the try/finally.exit/die bypasses detach() by design; this is the documented reason OTEL_PHP_DEBUG_SCOPES_DISABLED exists, and it should only be used where exit/die is unavoidable.Ramp-up. The API is learnable in an hour; the mental model is not. Engineers must internalize implicit vs. explicit propagation, the scope stack, and why an immutable context plus a detached scope is the correct pattern — otherwise they will pass context explicitly "to be safe," which defeats the point, or forget to detach, which corrupts traces quietly. Budget for a short internal enablement session, a documented convention (middleware and job wrappers as the only places that call activate()), a code-review checklist, and a working reference implementation in one service before asking other teams to follow. The debug scopes do most of the teaching for free if left enabled outside production.
Net TPM position: adopt this as the context primitive when OpenTelemetry is the chosen observability standard, fund the Laravel instrumentation around it as a separate and larger workstream, sequence queues ahead of async/fiber work, and validate the runtime matrix before promising coverage of long-running workers. The package is small, cheap, and standards-aligned; the program it enables is neither small nor cheap, and the roadmap should be honest about which one is being approved.
How can I help you explore Laravel packages today?