Distributed tracing for the PHP agent

New Relic's PHP agent supports distributed tracing (including W3C Trace Context, as of PHP Agent version 9.8), which lets you see the path that requests take as they travel through a distributed system. This document contains PHP-specific tips to enable distributed tracing for a PHP application or service.

Enable distributed tracing for PHP

When distributed tracing is enabled, it implicitly disables cross application tracing. The PHP agent will ignore the cross application tracing configuration value, and act as though this value is set to false.

To enable or disable distributed tracing for the PHP agent:

  1. If applicable, update your PHP agent to support distributed tracing (version 8.4 or higher, although we recommend version 9.8 or higher for the most complete functionality).
  2. Make sure that the transaction tracer configuration option is enabled (default is true):

    newrelic.transaction_tracer.enabled = true
  3. Enable (or disable) distributed tracing for the PHP agent with the distributed_tracing_enabled configuration option:

    newrelic.distributed_tracing_enabled = true
  4. Set the transaction tracer threshold value to a suitable value. Recommendation:

    • If you want to make all transactions eligible for a distributed trace, set this value to 0 seconds.
    • If you are only interested in traces for longer running transactions, or if generating a trace for every transaction creates unacceptable application performance, set this value higher than 0 seconds.

    For example:

    newrelic.transaction_tracer.threshold = 0
  5. Optionally, if only W3C Trace Context tracing is desired, the New Relic Distributed Tracing headers can be disabled with the newrelic.distributed_tracing_exclude_newrelic_header configuration option:

    newrelic.distributed_tracing_exclude_newrelic_header = 1

  6. Enable spans with the configuration setting:

    newrelic.span_events_enabled = true

  7. Be sure to restart your web server (or other PHP SAPI) so the agent will pick up the newly configured values.

Enable Infinite Tracing

Infinite Tracing (PHP agent version 9.12 or later) extends distributed tracing to collect your span data in a trace observer, which runs in a cluster of services in AWS called New Relic Edge. The PHP agent sends all your spans to the trace observer so they can be assembled into traces for you to view in New Relic.

To turn on Infinite Tracing, enable distributed tracing and configure the additional settings below:

newrelic.span_events_enabled = true
newrelic.infinite_tracing.trace_observer_host= YOUR_TRACE_OBSERVER_HOST

Leverage automatic distributed tracing instrumentation

The PHP agent automatically will instrument a number of native PHP functions, as well as some third party HTTP clients, with distributed trace information. These include:

Set trace detail level

Distributed tracing support depends on the PHP agent's transaction tracer. When distributed tracing is enabled, a span is created for each segment seen by the transaction tracer.

As spans are sampled, the PHP agent will prioritize spans related to external calls above other spans, which are then recorded in descending order of their duration.

If you find that there are too many unimportant spans being reported for PHP function calls, you can reduce the detail of the transaction tracer by setting newrelic.transaction_tracer.detail to 0. You may then use the newrelic.transaction_tracer.custom configuration setting or the newrelic_add_custom_tracer API method to add trace segments and spans for the specific PHP function or methods you want to add to your traces.

For PHP agent versions 8.4 to 8.7: When distributed tracing is enabled, these versions behave as if newrelic.transaction_tracer.detail is set to 0 (as described above), which results in PHP function calls not generating spans. To get spans related to PHP function calls, update to version 9.0 or higher.

Manually instrument applications and services

W3C Trace Context support was added in version 9.8. With this, the API for manually instrumenting applications has changed from the JSON payload related functions

newrelic_create_distributed_trace_payload() and newrelic_accept_distributed_trace_payload($payload),

to the header array forms newrelic_insert_distributed_trace_headers($outbound_headers) and newrelic_accept_distributed_trace_headers($inbound_headers).

The JSON functions are now considered deprecated, and will be removed in a future release.

If you're using an unsupported library, or have a non-HTTP based distributed system component (such as messaging queues), you can use the PHP agent API to manually identify transactions to be included in a distributed trace. This is a two step process:

  1. Modify your service or application to create or insert the distributed tracing data
  2. Modify your service or application to accept distributed trace data created by other transactions or requests.

The following example uses a generic message/job queue. While the actual details will vary depending on what sort of system you're trying to add to a distributed trace, the core concepts are the same. Also, while we've used a job queue as an example, you can use these methods with any distributed system component.

When you create a payload or insert headers, you're telling New Relic you want this request or transaction or request to participate in a distributed trace. When you accept them, you're linking the current request or transaction with its parent request or transaction.

For more information about using manual instrumentation to get more detail or to see connections between services, see the documentation about distributed tracing APIs.

Header API

Insert distributed trace headers

Somewhere in your application you'll have code that looks or acts like the following:

// create a job object
$job = new \Generic\Queue\Job;

// set the information needed to run the queue job
$job->setData('info', $info);

// save the job
$job->save();

If you want this job to appear in a distributed trace, you need to insert distributed trace headers into an array by using newrelic_insert_distributed_trace_headers, and then add those headers to the job's data:

$outbound_headers = array();
if (newrelic_insert_distributed_trace_headers($outbound_headers)) {

    // create a job object
    $job = new \Generic\Queue\Job;

    // set the information needed to run the queue job
    $job->setData('info', $info);

    // add the headers to the job data
    $job->setData('nr_dt_headers', $outbound_headers);

    // save the job
    $job->save();
} else {
    echo "Unable to obtain distributed tracing headers";
}

Tip: The headers created via newrelic_insert_distributed_trace_headers() are HTTP safe.

This is step one: You've inserted the distributed trace headers.

Accept the distributed tracing headers

Next, somewhere in your application stack you'll have a queue runner that looks or acts like the following:

// create the job runner
$jobRunner = \Generic\Queue\Runner;

// grab jobs until there aren't any
while($job = $jobRunner->next()) {
    // run the job
    $job->run();
}

In order to accept distributed trace headers, use the newrelic_accept_distributed_trace_headers function

// create the job runner
$jobRunner = \Generic\Queue\Runner;

while($job = $jobRunner->next()) {
    $inbound_headers = $job->getData('nr_dt_headers');
    if($inbound_headers) {
        newrelic_accept_distributed_trace_headers($inbound_headers);
    }
    $job->run();
}

Payload API (deprecated)

Create a distributed trace payload

Somewhere in your application you'll have code that looks or acts like the following:

// create a job object
$job = new \Generic\Queue\Job;

// set the information needed to run the queue job
$job->setData('info', $info);

// save the job
$job->save();

If you want this job to appear in a distributed trace, you need to create a distributed trace payload using newrelic_create_distributed_trace_payload, and then add that payload to the job's data:

$payload = newrelic_create_distributed_trace_payload();

// create a job object
$job = new \Generic\Queue\Job;

// set the information needed to run the queue job
$job->setData('info', $info);

// add the payload data to the job data as a text json string
$job->setData('dt_payload', $payload->Text());

// save the job
$job->save();

This is step one: You've created a distributed trace payload.

Accept the distributed tracing payload

Next, somewhere in your application stack you'll have a queue runner that looks or acts like the following:

// create the job runner
$jobRunner = \Generic\Queue\Runner;

// grab jobs until there aren't any
while($job = $jobRunner->next()) {
    // run the job
    $job->run();
}

In order to accept a distributed trace payload, use the newrelic_accept_distributed_trace_payload function

// create the job runner
$jobRunner = \Generic\Queue\Runner;

while($job = $jobRunner->next()) {
    $payload = $job->getData('dt_payload');
    if($payload) {
        newrelic_accept_distributed_trace_payload($payload);
    }
    $job->run();
}
Optional: Use HTTP safe payload strings

If you need to transport the payload via a mechanism that requires HTTP safe strings, (HTTP headers, URL query strings, POST fields, etc.), the API includes distributed tracing methods and functions that will create and accept HTTP safe versions of the strings.

// create the HTTP safe payload string 
$payload = newrelic_create_distributed_trace_payload();
$httpSafePayload = $payload->httpSafe();

// ...

// accept the HTTP safe payload string
newrelic_accept_distributed_trace_payload_httpsafe($httpSafePayload);

Command line PHP programs

PHP programs run from the PHP command line are always sampled by the agent's distributed tracer. Depending on the programs you run, these processes could see an over-representation in your collection of distributed traces. In these situations, you can opt to disable command line instrumentation by using the per-directory newrelic.enabled setting in your newrelic.ini files.

For more help

If you need more help, check out these support and learning resources: