This is a PHP extension for OpenTelemetry, to enable auto-instrumentation. It is based on zend_observer and requires php8+
The extension allows creating pre
and post
hook functions to arbitrary PHP functions and methods, which allows those methods to be wrapped with telemetry.
- PHP 8+
- OpenTelemetry PHP library
The extension can be installed in all of the usual ways:
pecl install opentelemetry
If you are using the official PHP docker images then you can use php-extension-installer
From github:
install-php-extensions open-telemetry/opentelemetry-php-instrumentation@main
Via pecl/pickle:
install-php-extensions opentelemetry[-beta|-stable|-latest]
php --ri opentelemetry
The OpenTelemetry extension does not play nicely with the following other extensions:
- blackfire
You can control conflicts via the opentelemetry.conflicts
ini setting.
If a conflicting extension is found, then the OpenTelemetry extension will disable itself:
php --ri opentelemetry
Notice: PHP Startup: Conflicting extension found (blackfire), disabling OpenTelemetry in Unknown on line 0
opentelemetry
opentelemetry hooks => disabled (conflict)
extension version => 1.0.0beta6
Directive => Local Value => Master Value
opentelemetry.conflicts => blackfire => blackfire
opentelemetry.validate_hook_functions => On => On
Invalid argument types in pre and post callbacks can cause fatal errors. Runtime checking is performed on the hook functions to ensure they are compatible. If not, the hook will not be executed and an error will be logged to error_log.
This feature can be disabled by setting the opentelemetry.validate_hook_functions
ini value to Off
;
Warning Be aware that trivial functions are candidates for optimizations. Optimizer can optimize them out and replace user function call with more optimal set of instructions (inlining). In this case hooks will not be invoked as there will be no function.
The pre
method starts and activates a span. The post
method ends the span after the observed method has finished.
<?php
$tracer = new Tracer(...);
OpenTelemetry\Instrumentation\hook(
DemoClass::class,
'run',
pre: static function (DemoClass $demo, array $params, string $class, string $function, ?string $filename, ?int $lineno) use ($tracer) {
$span = $tracer->spanBuilder($class)
->startSpan();
Context::storage()->attach($span->storeInContext(Context::getCurrent()));
},
post: static function (DemoClass $demo, array $params, $returnValue, ?Throwable $exception) use ($tracer) {
$scope = Context::storage()->scope();
$scope?->detach();
$span = Span::fromContext($scope->context());
$exception && $span->recordException($exception);
$span->setStatus($exception ? StatusCode::STATUS_ERROR : StatusCode::STATUS_OK);
$span->end();
}
);
There are more examples in the tests directory
From a pre
hook function, you may modify the parameters before they are received by the observed function.
The arguments are passed in as a numerically-indexed array. The returned array from the pre
hook is used
to modify (not replace) the existing parameters:
<?php
OpenTelemetry\Instrumentation\hook(
null,
'hello',
function($obj, array $params) {
return [
0 => null, //make first param null
2 => 'baz', //replace 3rd param
3 => 'bat', //add 4th param
];
}
);
function hello($one = null, $two = null, $three = null, $four = null) {
var_dump(func_get_args());
}
hello('a', 'b', 'c');
gives output:
array(4) {
[0]=>
NULL
[1]=>
string(1) "b"
[2]=>
string(3) "baz"
[3]=>
string(3) "bat"
}
post
hook methods can modify the observed function's return value:
<?php
\OpenTelemetry\Instrumentation\hook(null, 'hello', post: fn(mixed $object, array $params, string $return): int => ++$return);
function hello(int $val) {
return $val;
}
var_dump(hello(1));
gives output:
int(2)
Important: the post method must provide a return type-hint, otherwise the return value will be ignored.
post
hook methods can modify an exception thrown from the observed function:
<?php
\OpenTelemetry\Instrumentation\hook(null, 'hello', post: function(mixed $object, array $params, mixed $return, ?Throwable $throwable) {
throw new Exception('new', previous: $throwable);
});
function hello() {
throw new Exception('original');
}
try {
hello();
} catch (\Throwable $t) {
var_dump($t->getMessage());
var_dump($t->getPrevious()?->getMessage());
}
gives output:
string(3) "new"
string(8) "original"
See DEVELOPMENT.md and https://github.com/open-telemetry/opentelemetry-php/blob/main/CONTRIBUTING.md