koraitech / opentelemetry-php-extras
Extra OpenTelemetry auto-instrumentation for PHP: one span per Laravel event and spans for every Redis command (phpredis and Predis).
Package info
github.com/koraitech/opentelemetry-php-extras
pkg:composer/koraitech/opentelemetry-php-extras
Requires
- php: ^8.1
- composer-runtime-api: ^2.0
- open-telemetry/api: ^1.0
Requires (Dev)
- illuminate/events: ^9.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0
- open-telemetry/sdk: ^1.0
- open-telemetry/sem-conv: ^1.27
- phpunit/phpunit: ^10.5 || ^11.0
- predis/predis: ^2.0 || ^3.0
Suggests
- ext-opentelemetry: Required unless the OpenTelemetry PHP distro is installed: provides the hook() function
- ext-redis: To trace phpredis commands
- predis/predis: To trace Predis commands
Provides
None
Conflicts
None
Replaces
None
README
Extra auto-instrumentation for PHP applications traced with OpenTelemetry. It fills in the parts of a request that the official PHP instrumentation leaves blank:
| What | Spans | Works with |
|---|---|---|
| Laravel events | One span per event dispatched through Illuminate\Events\Dispatcher that has listeners, named after the event: App\Events\OrderPlaced, eloquent.saving: App\Models\Page, Illuminate\Auth\Events\Login… Listeners run inside the span, so slow listeners and the queries they run are visible. Every event, as Datadog does, with KORAI_OTEL_LARAVEL_EVENTS=all. |
Laravel 9 to 13 |
| Your own methods | One span per call of the methods you list in KORAI_OTEL_TRACE_METHODS (App\Services\Pricing::quote), with no code change. For your own libraries, where time goes into PHP code no instrumentation knows. |
Any PHP application |
| Redis commands | One client span per command (REDIS GET, REDIS HGETALL, REDIS PCONNECT…), with server address, port and database. |
phpredis (ext-redis, Redis and RedisCluster, PHP 8.2 or later) and Predis, in any PHP application |
Nothing changes in your code: the package registers itself through Composer's autoloader.
Requirements
- PHP 8.1 or later. Tracing the phpredis extension needs PHP 8.2 or later: older versions cannot hook methods of PHP extensions. Predis and Laravel events work on 8.1. Tested on PHP 8.1, 8.2, 8.3, 8.4 and 8.5, and Laravel 9 to 13.
- One of:
- the
opentelemetryPECL extension with the OpenTelemetry SDK, or - the OpenTelemetry PHP distro (
opentelemetry-php-distro).
- the
Without either, the package does nothing.
Install
composer require koraitech/opentelemetry-php-extras
With the OpenTelemetry PHP distro
The distro keeps its own copy of the OpenTelemetry libraries apart from your application's. Enable its bridge so the spans from this package join the distro's traces:
OTEL_PHP_SCOPED_DEPS_BRIDGE_ENABLED=true
Under PHP-FPM, pass environment variables to PHP through the pool (env[NAME] = value, or clear_env = no).
With the PECL extension
Install and configure the OpenTelemetry SDK as usual (OTEL_PHP_AUTOLOAD_ENABLED=true, exporter, service name). No
extra setting is needed.
Configuration
All settings are environment variables and all are optional.
| Variable | Default | What it does |
|---|---|---|
OTEL_PHP_DISABLED_INSTRUMENTATIONS |
Standard OpenTelemetry list. Add korai-laravel-events, korai-redis or korai-methods to turn a part off. |
|
KORAI_OTEL_LARAVEL_EVENTS |
listeners |
Which events get a span. listeners: events with at least one listener of the application (listeners that OpenTelemetry registers itself do not count; cache hits and queries are already in the trace). all: every event, including each cache read and each model retrieved. A busy page can then send ten times more spans. |
KORAI_OTEL_LARAVEL_EVENTS_EXCLUDE |
Comma-separated event names to skip. * matches anything: Illuminate\Log\Events\MessageLogged,eloquent.* |
|
KORAI_OTEL_LARAVEL_EVENTS_REQUIRE_PARENT |
true |
Only create event spans inside an existing trace. Events fired while the framework boots would otherwise each start a trace of their own. |
KORAI_OTEL_TRACE_METHODS |
Comma-separated Class::method to trace: App\Services\Pricing::quote,Vendor\Cms\Renderer::render. Each call gets an INTERNAL span named Class::method. |
|
KORAI_OTEL_REDIS_MIN_DURATION |
0 |
Milliseconds (2, 0.5ms). Redis commands faster than this get no span; failed commands always do. Use it when a page sends hundreds of fast reads. |
KORAI_OTEL_REDIS_KEYS |
false |
Record the command and key in db.query.text (GET session:abc). Values are never recorded. |
Recommended settings for Laravel
These are settings of the OpenTelemetry PHP distro and SDK, not of this package, but they make the biggest difference together with it:
# Fill time not covered by any span with spans sampled from the PHP stack (distro only) OTEL_PHP_INFERRED_SPANS_ENABLED=true OTEL_PHP_INFERRED_SPANS_SAMPLING_INTERVAL=20ms OTEL_PHP_INFERRED_SPANS_MIN_DURATION=10ms # Laravel records cache hits as span events; the default limit of 128 per span drops the rest OTEL_SPAN_EVENT_COUNT_LIMIT=1024 # Each SQL query is traced by both the PDO and the Laravel instrumentation; keep one OTEL_PHP_DISABLED_INSTRUMENTATIONS=pdo
If you use this package, do not call Redis::enableEvents() only to get Redis spans: with Laravel's own Redis
instrumentation also active, each command would be traced twice.
Span volume
A typical Laravel page dispatches hundreds of events (every cache read, every model retrieved). The default only
traces the ones your code listens to. With KORAI_OTEL_LARAVEL_EVENTS=all, use KORAI_OTEL_LARAVEL_EVENTS_EXCLUDE for
events you don't need. Redis spans follow the commands your code sends: a page that reads 500 keys sends 500 spans.
Keep your sampler in mind.
Span reference
Laravel events: kind INTERNAL, name = event name. Attributes: laravel.event.name, code.function.name,
laravel.event.halt (for until()).
Redis: kind CLIENT, name REDIS <COMMAND>. Attributes: db.system.name (and the older db.system) = redis,
db.operation.name, server.address, server.port, db.namespace (database number), db.query.text (only with
KORAI_OTEL_REDIS_KEYS=true), code.function.name.
Spans are created only inside an active trace, and failures record the exception and set the span status to error.
Development
composer install
composer test:unit
# with the opentelemetry and redis extensions loaded and a Redis server on REDIS_HOST:
composer test:integration
Releases
Releases are automatic. When CI passes on main, the next version is worked out from the commit messages since the
last release (Conventional Commits: feat: adds a minor version, fix: and
others a patch, ! or BREAKING CHANGE a major), tagged, published as a GitHub release and sent to Packagist.
Changes that only touch tests, CI or docs don't produce a release.
A daily job also checks php.net and Packagist for new PHP and Laravel versions. When one appears, it runs the full test suite on it and, if it passes, adds it to the tested versions and publishes a new minor release. If the tests fail, it opens an issue instead.
License
Apache-2.0. See LICENSE.