This is a deprecation notice for using dd_trace() for custom instrumentation. This function is the legacy API for instrumenting function and method calls. Custom instrumentation is supported via dd_trace_function() and dd_trace_method() documented below.
If you already have existing custom instrumentations using dd_trace(), see the "Upgrade guide" section below. The dd_trace() function will become noop in 3-4 weeks with the release of 0.48.0.
If you do not have any custom instrumentation or you do not use the dd_trace() function, you do not need to read any further.
UPDATE (2020-07-09): The official documentation has been updated with these changes. Please refer to it as it contains the most up-to-date information.
PHP tracer API
Custom instrumentations of function and method calls are implemented via dd_trace_function() and dd_trace_method() respectively. These functions will create a span for each call and run a closure (called a tracing closure) that provides write-access to the span properties via an instance of DDTrace\SpanData. By default the tracing closure will run immediately after the instrumented call.
Tracing function calls
Function calls are instrumented with dd_trace_function() and the tracing closure is executed after the instrumented call is made.
<?php
use DDTrace\SpanData;
function addNums($a, $b) {
$sum = $a + $b;
printf("%d + %d = %d\n", $a, $b, $sum);
return $sum;
}
dd_trace_function(
'addNums',
function(SpanData $span, $args, $retval) {
echo "Traced" . PHP_EOL;
}
);
var_dump(addNums(2, 8));
// 2 + 8 = 10
// Traced
// int(10)
Tracing method calls
Methods are instrumented with dd_trace_method() which provides the same functionality as dd_trace_function(). One key difference is that the tracing closure is bound to the instrumented class which exposes an instance of the instrumented class via $this.
<?php
use DDTrace\SpanData;
class Calc {
public $foo = 'bar';
public function addNums($a, $b) {
$sum = $a + $b;
printf("%d + %d = %d\n", $a, $b, $sum);
return $sum;
}
}
dd_trace_method(
'Calc', 'addNums',
function(SpanData $span, $args, $retval) {
echo '$this->foo: ' . $this->foo . PHP_EOL;
}
);
$calc = new Calc();
var_dump($calc->addNums(2, 8));
// 2 + 8 = 10
// $this->foo: bar
// int(10)
Parameters of the tracing closure
The tracing closure has four parameters:
function(
DDTrace\SpanData $span,
array $args,
mixed $retval,
Exception|null $exception
);
- $span: An instance of
DDTrace\SpanData to write to the span properties
- $args: An
array of arguments from the instrumented call
- $retval: The return value of the instrumented call
- $exception: An instance of the exception that was thrown in the instrumented call or
null if no exception was thrown
Parameter 1: DDTrace\SpanData $span
The DDTrace\SpanData instance contains the same span information that the Agent expects. A few exceptions are trace_id, span_id, parent_id, start, and duration which are set at the C level and not exposed to userland via DDTrace\SpanData. Exceptions from the instrumented call are automatically attached to the span and the error field is managed automatically.
| Property |
Type |
Description |
SpanData::$name |
string |
The span name |
SpanData::$resource |
string |
The resource you are tracing |
SpanData::$service |
string |
The service you are tracing |
SpanData::$type |
string |
(Optional) The type of request which can be set to: web, db, cache, or custom |
SpanData::$meta |
string[] |
(Optional) An array of key-value span metadata; keys and values must be strings |
SpanData::$metrics |
float[] |
(Optional) An array of key-value span metrics; keys must be strings and values must be floats |
<?php
use DDTrace\SpanData;
function myRandFunc($min, $max) {
return mt_rand($min, $max);
}
dd_trace_function(
'myRandFunc',
function(SpanData $span, $args, $retval) {
// The following properties are required but will be optional in future releases.
// See the "Future considerations" section below.
$span->name = 'myRandFunc';
$span->resource = 'myRandFunc';
$span->service = 'php';
// The following are optional
$span->type = 'web';
$span->meta = [
'rand.range' => $args[0] . ' - ' . $args[1],
'rand.value' => $retval,
];
$span->metrics = [
'_sampling_priority_v1' => 0.9,
];
}
);
Parameter 2: array $args
The second parameter to the tracing closure is an array of arguments from the instrumented call. It functions similarly to func_get_args().
By default the tracing closure is executed after the instrumented call which means any arguments passed by reference could be a different value when they reach the tracing closure.
<?php
use DDTrace\SpanData;
function argsByRef(&$a) {
return ++$a;
}
dd_trace_function(
'argsByRef',
function(SpanData $span, $args) {
var_dump($args);
}
);
$foo = 10;
var_dump(argsByRef($foo));
// array(1) {
// [0]=>
// int(11)
// }
// int(11)
On PHP 7, the tracing closure has access to the same arguments passed to the instrumented call. If the instrumented call mutates an argument, including arguments passed by value, the posthook tracing closure will receive the mutated argument.
This is the expected behavior of arguments in PHP 7 as illustrated in the following example:
<?php
function foo($a) {
var_dump(func_get_args());
$a = 'Dogs';
var_dump(func_get_args());
}
foo('Cats');
/*
array(1) {
[0]=>
string(4) "Cats"
}
array(1) {
[0]=>
string(4) "Dogs"
}
*/
The following example demonstrates this effect on posthook tracing closures.
<?php
function foo($a) {
$a = 'Dogs';
}
dd_trace_function('foo', function ($span, array $args) {
var_dump($args[0]);
});
foo('Cats');
// string(4) "Dogs"
If an argument needs to be accessed before mutation, the tracing closure can be marked as prehook to access the arguments before the instrumented call. (See the "Running the tracing closure before the instrumented call" section below.)
Parameter 3: mixed $retval
The third parameter of the tracing closure is the return value of the instrumented call. Functions or methods that declare a void return type or ones that do not return a value will have a value of null.
<?php
use DDTrace\SpanData;
function message(): void {
echo "Hello!\n";
}
dd_trace_function(
'message',
function(SpanData $span, $args, $retval) {
echo "Traced\n";
var_dump($retval);
}
);
var_dump(message());
// Hello!
// Traced
// NULL
// NULL
Parameter 4: Exception|null $exception
The final parameter of the tracing closure is an instance of the exception that was thrown in the instrumented call or null if no exception was thrown.
<?php
use DDTrace\SpanData;
function mightThrowException() {
throw new Exception('Oops!');
return 'Hello';
}
dd_trace_function(
'mightThrowException',
function(SpanData $span, $args, $retval, $ex) {
if ($ex) {
echo 'Exception from instrumented call: ';
echo $ex->getMessage() . PHP_EOL;
}
}
);
mightThrowException();
/*
Exception from instrumented call: Oops!
NULL
PHP Fatal error: Uncaught Exception: Oops! ...
*/
As exceptions are attached to spans automatically, there is no need to manually set $span->meta['error.*'] metadata. But having access to the exception instance enables you to check for a thrown exception before accessing the return value.
<?php
use DDTrace\SpanData;
dd_trace_function(
'mightThrowException',
function(SpanData $span, $args, $retval, $ex) {
if (null === $ex) {
// Do something with $retval
}
}
);
Tracing internal functions and methods
An optimization was added starting in 0.46.0 to ignore all internal functions and methods for instrumentation. Internal functions and methods can still be instrumented by setting the DD_TRACE_TRACED_INTERNAL_FUNCTIONS environment variable. This takes a CSV of functions or methods that will be instrumented e.g. DD_TRACE_TRACED_INTERNAL_FUNCTIONS=array_sum,mt_rand,DateTime::add. Once a function or method has been added to the list, it can be instrumented using dd_trace_function() and dd_trace_method() respectively.
Running the tracing closure before the instrumented call
By default, tracing closures are treated as posthook closures meaning they will be executed after the instrumented call. Some cases require running the tracing closure before the instrumented call. In that case, tracing closures are marked as prehook using an associative configuration array.
dd_trace_function('foo', [
'prehook' => function (\DDTrace\SpanData $span, array $args) {
// ...
}
]);
Sandboxing
Tracing closures are "sandboxed" in that exceptions thrown and errors raised inside of them do no impact the instrumented call.
<?php
function my_func() {
echo 'Hello!' . PHP_EOL;
}
dd_trace_function(
'my_func',
function() {
throw new \Exception('Oops!');
}
);
my_func();
echo 'Done.' . PHP_EOL;
/*
Hello!
Done.
*/
Whereas this is handy behavior in production, it can make it a bit tricky to debug when things go wrong. Set the environment variable DD_TRACE_DEBUG=1 to expose any exceptions or errors that may have occurred in a tracing closure.
/*
Hello!
Exception thrown in tracing closure for my_func: Oops!
Done.
*/
Future considerations
At the time of writing, there are no defaults to the SpanData properties. SpanData::$name, SpanData::$resource, and SpanData::$service, are required properties and must be set explicitly.
With #923 (scheduled for release in 0.47.0), SpanData::$name will default to the fully qualified called name, and SpanData::$resource will default to the value of SpanData::$name.
In a future release SpanData::$service will default to the value of the parent span's SpanData::$service, or DD_SERVICE for top-level spans.
Upgrade guide
Custom instrumentations implemented using the legacy dd_trace() API should be updated soon as the legacy API will become noop in a future release of ddtrace.
There is an important paradigm distinction to understand between the legacy API and the "sandbox" API. The legacy API forwards the instrumented call from inside the tracing closure using dd_trace_forward_call().

The sandbox API runs the tracing closure after the instrumented call so there is no need to forward the original call along with dd_trace_forward_call().

Contrary to the legacy API, the sandbox API handles the following tasks automatically:
- Creating the span
- Forwarding the original call
- Attaching exceptions to the span
Upgrading example
The sandbox API significantly reduces the amount of boilerplate required to instrument a call. Below is a side-by-side comparison of a full legacy API example and the sandbox API equivalent.
# Legacy API
dd_trace('CustomDriver', 'doWork', function (...$args) {
// Start a new span
$scope = \DDTrace\GlobalTracer::get()->startActiveSpan('CustomDriver.doWork');
$span = $scope->getSpan();
// Access object members via $this
$span->setTag(\DDTrace\Tag::RESOURCE_NAME, $this->workToDo);
try {
// Execute the original method. Note: dd_trace_forward_call() - handles any parameters automatically
$result = dd_trace_forward_call();
// Set a tag based on the return value
$span->setTag('doWork.size', count($result));
return $result;
} catch (Exception $e) {
// Inform the tracer that there was an exception thrown
$span->setError($e);
// Bubble up the exception
throw $e;
} finally {
// Close the span
$span->finish();
}
});
# Sandbox API
dd_trace_method('CustomDriver', 'doWork', function (\DDTrace\SpanData $span, array $args, $result) {
// Span was already created before the original call
$span->name = 'CustomDriver.doWork';
$span->service = 'php';
// Access object members via $this
$span->resource = $this->workToDo;
// No need to explicitly forward the call with dd_trace_forward_call()
// No need to explicitly catch/attach exceptions
$span->meta['doWork.size'] = count($result);
// No need to explicitly close/finish the span
});
This is a deprecation notice for using
dd_trace()for custom instrumentation. This function is the legacy API for instrumenting function and method calls. Custom instrumentation is supported viadd_trace_function()anddd_trace_method()documented below.If you already have existing custom instrumentations using
dd_trace(), see the "Upgrade guide" section below. Thedd_trace()function will become noop in 3-4 weeks with the release of 0.48.0.If you do not have any custom instrumentation or you do not use the
dd_trace()function, you do not need to read any further.UPDATE (2020-07-09): The official documentation has been updated with these changes. Please refer to it as it contains the most up-to-date information.
PHP tracer API
Custom instrumentations of function and method calls are implemented via
dd_trace_function()anddd_trace_method()respectively. These functions will create a span for each call and run a closure (called a tracing closure) that provides write-access to the span properties via an instance ofDDTrace\SpanData. By default the tracing closure will run immediately after the instrumented call.Tracing function calls
Function calls are instrumented with
dd_trace_function()and the tracing closure is executed after the instrumented call is made.Tracing method calls
Methods are instrumented with
dd_trace_method()which provides the same functionality asdd_trace_function(). One key difference is that the tracing closure is bound to the instrumented class which exposes an instance of the instrumented class via$this.Parameters of the tracing closure
The tracing closure has four parameters:
DDTrace\SpanDatato write to the span propertiesarrayof arguments from the instrumented callnullif no exception was thrownParameter 1:
DDTrace\SpanData $spanThe
DDTrace\SpanDatainstance contains the same span information that the Agent expects. A few exceptions aretrace_id,span_id,parent_id,start, anddurationwhich are set at the C level and not exposed to userland viaDDTrace\SpanData. Exceptions from the instrumented call are automatically attached to the span and theerrorfield is managed automatically.SpanData::$namestringSpanData::$resourcestringSpanData::$servicestringSpanData::$typestringSpanData::$metastring[]SpanData::$metricsfloat[]Parameter 2:
array $argsThe second parameter to the tracing closure is an array of arguments from the instrumented call. It functions similarly to
func_get_args().By default the tracing closure is executed after the instrumented call which means any arguments passed by reference could be a different value when they reach the tracing closure.
On PHP 7, the tracing closure has access to the same arguments passed to the instrumented call. If the instrumented call mutates an argument, including arguments passed by value, the
posthooktracing closure will receive the mutated argument.This is the expected behavior of arguments in PHP 7 as illustrated in the following example:
The following example demonstrates this effect on
posthooktracing closures.If an argument needs to be accessed before mutation, the tracing closure can be marked as
prehookto access the arguments before the instrumented call. (See the "Running the tracing closure before the instrumented call" section below.)Parameter 3:
mixed $retvalThe third parameter of the tracing closure is the return value of the instrumented call. Functions or methods that declare a
voidreturn type or ones that do not return a value will have a value ofnull.Parameter 4:
Exception|null $exceptionThe final parameter of the tracing closure is an instance of the exception that was thrown in the instrumented call or
nullif no exception was thrown.As exceptions are attached to spans automatically, there is no need to manually set
$span->meta['error.*']metadata. But having access to the exception instance enables you to check for a thrown exception before accessing the return value.Tracing internal functions and methods
An optimization was added starting in 0.46.0 to ignore all internal functions and methods for instrumentation. Internal functions and methods can still be instrumented by setting the
DD_TRACE_TRACED_INTERNAL_FUNCTIONSenvironment variable. This takes a CSV of functions or methods that will be instrumented e.g.DD_TRACE_TRACED_INTERNAL_FUNCTIONS=array_sum,mt_rand,DateTime::add. Once a function or method has been added to the list, it can be instrumented usingdd_trace_function()anddd_trace_method()respectively.Running the tracing closure before the instrumented call
By default, tracing closures are treated as
posthookclosures meaning they will be executed after the instrumented call. Some cases require running the tracing closure before the instrumented call. In that case, tracing closures are marked asprehookusing an associative configuration array.Sandboxing
Tracing closures are "sandboxed" in that exceptions thrown and errors raised inside of them do no impact the instrumented call.
Whereas this is handy behavior in production, it can make it a bit tricky to debug when things go wrong. Set the environment variable
DD_TRACE_DEBUG=1to expose any exceptions or errors that may have occurred in a tracing closure.Future considerations
At the time of writing, there are no defaults to the
SpanDataproperties.SpanData::$name,SpanData::$resource, andSpanData::$service, are required properties and must be set explicitly.With #923 (scheduled for release in 0.47.0),
SpanData::$namewill default to the fully qualified called name, andSpanData::$resourcewill default to the value ofSpanData::$name.In a future release
SpanData::$servicewill default to the value of the parent span'sSpanData::$service, orDD_SERVICEfor top-level spans.Upgrade guide
Custom instrumentations implemented using the legacy
dd_trace()API should be updated soon as the legacy API will become noop in a future release of ddtrace.There is an important paradigm distinction to understand between the legacy API and the "sandbox" API. The legacy API forwards the instrumented call from inside the tracing closure using
dd_trace_forward_call().The sandbox API runs the tracing closure after the instrumented call so there is no need to forward the original call along with
dd_trace_forward_call().Contrary to the legacy API, the sandbox API handles the following tasks automatically:
Upgrading example
The sandbox API significantly reduces the amount of boilerplate required to instrument a call. Below is a side-by-side comparison of a full legacy API example and the sandbox API equivalent.