kinetis / telemetry
OpenTelemetry tracing for Kinetis — request spans, framework instrumentation hooks, decorators for the cache, session, HTTP and search boundaries, and OTLP export over the Revolt-backed client.
Requires
- php: ^8.4
- kinetis/framework: ^1.8.1
- kinetis/revolt-http-client: ^1.4.1
- nyholm/psr7: ^1.8.2
- open-telemetry/exporter-otlp: ^1.4.0
- open-telemetry/sdk: ^1.15.0
- psr/log: ^3.0.2
- symfony/http-client: ^8.1.4
- symfony/http-client-contracts: ^3.7.1
Requires (Dev)
- infection/infection: ^0.35.0
- kinetis/cache-redis: ^1.2.1
- kinetis/persistence: ^1.2.2
- kinetis/redis: ^1.0.1
- kinetis/session: ^1.2.3
- phpstan/phpstan: ^2.2.8
- phpunit/phpunit: ^13.3.3
- vimeo/psalm: ^6.17
Suggests
- kinetis/cache-redis: Enables the TracingSimpleCache decorator — a span per cache operation.
- kinetis/persistence: Query and transaction spans, reported from inside the drivers through the framework's instrumentation hooks.
- kinetis/queue: Producer and consumer job spans, reported by the queue backends and the worker through the framework's instrumentation hooks.
- kinetis/search: Gives TracingSearchTransport the transportDecorator seam each engine factory exposes — a span per search call, on OpenSearch or Elasticsearch.
- kinetis/session: Enables the TracingSessionStore decorator — a span per session read, create, update and destroy.
Provides
None
Conflicts
None
Replaces
None
README
kinetis/telemetry
OpenTelemetry tracing for Kinetis
Part of Kinetis, a non-blocking PHP framework for API-first applications, developed in the kinetis-dev/kinetis monorepo.
A span per request, per SQL query, per queue job, and per outgoing
HTTP call, exported over OTLP to any tracing backend. Each export
request goes through kinetis/revolt-http-client's Fiber-suspending transport;
the exporter waits between retries with a blocking sleep, which blocks
the worker for each delay.
The distinctive trace this produces: spans that overlap in time. A
request running two queries and an HTTP call through concurrently()
shows all three side by side inside the request span — what
non-blocking I/O actually did for that request, visible.
Set one environment variable and requests start tracing:
OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318
Provides
Installing this package auto-registers, via extra.kinetis:
- Global middleware
RequestSpanMiddleware— one server span per request (method, status,php.memory.usage; an incomingtraceparentjoins the caller's trace). - A container binding for
OpenTelemetry\API\Trace\TracerProviderInterface— the OTLP-exporting provider whenOTEL_EXPORTER_OTLP_ENDPOINTis set, a no-op provider otherwise, so an unconfigured install costs near nothing. - The framework's instrumentation hooks, turned on — when the OTLP
endpoint is set, the bootstrap swaps an OTel backend into core's
Kinetis\Instrumentation\Telemetryholder, so the spans the framework reports from inside itself start exporting with no further wiring: boot phases, per-middleware timing, route match, hydration, controller,concurrently()tasks, events, MCP calls, and — this is the whole of Kinetis-owned SQL and queue tracing, with nothing to wrap by hand — a span per query split at the pool boundary, a transaction span carrying its outcome, and producer and consumer job spans joined into one trace across processes by atraceparentthe push hook stores with the job.
The decorators below are explicit opt-ins wired in your own
bootstrap.php, for the boundaries the framework reports nothing from.
Scope ownership across Fibers
An active span's scope belongs to the Fiber that started it —
OpenTelemetry's default Fiber-bound context storage stays in place — so
two overlapping concurrently() tasks each keep their own stack and
neither can detach the other's span. Parentage across a Fiber boundary
is passed rather than read: the batch hook's token reaches each task
hook and parents the task span to its batch, and a span starting on a
Fiber that carries no context (a request span, a worker's job span)
names its parent explicitly — the propagated traceparent, or the
trace root.
Decorators
TracingHttpClient— a client span per outgoing request withtraceparentinjection, ending when the response is consumed rather than whenrequest()returns. Carries the URL's scheme, host and port. Hand it toHttpas its transport.TracingSimpleCache— a span per cache operation, wrapping any PSR-16CacheInterface. A key-list fingerprint and a batch size travel; neither the keys nor the values do.TracingSessionStore— a span perread/create/update/destroy, wrapping anykinetis/sessionSessionStoreInterface. The session id never travels verbatim (it's a bearer credential) — only its fingerprint does.TracingSearchTransport— a span per search call on either engine, wrapping the PSR-18 client via each engine factory'stransportDecoratorparameter.TraceAwareLogger— wraps any PSR-3 logger, adding the active span'strace_id/span_idto every entry's context.
What never reaches a span
A trace is exported to a third-party backend, retained there, and readable by everyone with access to it — a wider audience than the database, cache, or upstream service an operation's input was addressed to. So a span here describes an operation and never the data it carried. Every decorator and hook routes an operation's inputs through one internal policy point, and there is no setting that turns it off.
A SQL statement and its parameters, a cache key and its value, a URL's
userinfo/path/query/fragment, an incoming request's path, a search
index name or document id, a session id and its payload, and a
failure's message and stack trace all stay behind. What travels in
their place is an unkeyed 128-bit SHA-256 fingerprint — enough for a
backend to group two spans over the same value, never the value — plus
the operation's own name drawn from a closed vocabulary. A failing
operation's exception propagates unchanged, so an application that
wants the message logs it where its own redaction policy applies, and
TraceAwareLogger puts the trace id on that log line.
The full table of what is dropped and what replaces it, and what each vocabulary falls back to, is stated once at kinetis.dev/docs/telemetry.html.
Configuration
| Key | Default | Purpose |
|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT |
— | Collector's OTLP/HTTP base URL. Unset = tracing off (no-op provider). |
OTEL_SERVICE_NAME |
kinetis |
The service.name resource attribute. |
OTEL_EXPORTER_OTLP_HEADERS |
— | Export headers, key=value,key2=value2 — a hosted backend's auth. |
OTEL_TRACES_SAMPLER |
parentbased_always_on |
Standard sampler names; traceidratio + OTEL_TRACES_SAMPLER_ARG for a rate. |
OTEL_TRACES_SAMPLER_ARG |
1.0 |
Ratio for the traceidratio samplers, 0–1. |
Export never follows a redirect: the exporter retries a redirect response against the configured endpoint up to its retry limit, then reports an export failure — see kinetis.dev/docs/telemetry.html.
Installation
composer require kinetis/telemetry
Requires PHP 8.4+, kinetis/framework,
and kinetis/revolt-http-client.
Full documentation:
kinetis.dev/docs/telemetry.html.
License
MIT — see LICENSE.