Instrument your PHP application with OpenTelemetry
This walkthrough shows how to add observability to your PHP application using the OpenTelemetry PHP libraries and tools.
Feature | Supported |
---|---|
Automatic Instrumentation | Yes |
Automatic OneAgent Ingestion | Yes |
Prerequisites
- Dynatrace version 1.222+
- For tracing, W3C Trace Context is enabled
- From the Dynatrace menu, go to Settings > Preferences > OneAgent features.
- Turn on Send W3C Trace Context HTTP headers.
Choose how to ingest data into Dynatrace
OneAgent currently only ingests traces automatically. If you are recording metrics or logs, choose the OTLP export route.
Apart from the following prerequisites, there are no other steps necessary when using OneAgent to ingest Go OpenTelemetry data.
Prerequisites
- OneAgent version 1.217+
- Traces-only data
- OpenTelemetry Java Instrumentation agent support is enabled
- From the Dynatrace menu, go to Settings > Preferences > OneAgent features.
- Find and turn on OpenTelemetry (PHP).
Determine the API base URL
For details on how to assemble the base OTLP endpoint URL, see Export with OTLP. The URL should end in /api/v2/otlp
.
Get API access token
The access token for ingesting traces, logs, and metrics can be generated in your Dynatrace menu under Access tokens.
For details on the format and the necessary access scopes, see Export with OTLP.
Choose how you want to instrument your application
OpenTelemetry supports on PHP automatic and manual instrumentation, or a combination of both.
It's a good idea to start with automatic instrumentation and add manual instrumentation if the automatic approach doesn't work or doesn't provide enough information.
Automatically instrument your application optional
-
Ensure you have an adequate build environment for your system set up, consisting of GCC, Make, and Autoconfig.
-
Build and install the instrumentation library, using pickle.
php pickle.phar install --source https://github.com/open-telemetry/opentelemetry-php-instrumentation.git#1.0.0beta5
-
Add the newly compiled library as extension to your
php.ini
.extension=opentelemetry.so
-
Restart PHP and verify that the extension was loaded.
- From the command line, with
php -m
- As part of a webserver, by calling
phpinfo()
- From the command line, with
-
Depending on your ingestion type (OneAgent or export), configure the following environment variables.
OTEL_PHP_AUTOLOAD_ENABLED=true OTEL_SERVICE_NAME=php-quickstart OTEL_PROPAGATORS=baggage,tracecontext OTEL_EXPORTER=none
OTEL_PHP_AUTOLOAD_ENABLED=true OTEL_SERVICE_NAME=php-quickstart OTEL_PROPAGATORS=baggage,tracecontext OTEL_EXPORTER=otlp OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf OTEL_EXPORTER_OTLP_ENDPOINT=[URL] OTEL_EXPORTER_OTLP_HEADERS=Authorization=Api-Token [TOKEN]
Substitute
[URL]
and[TOKEN]
with the Dynatrace URL and access token, respectively.
Manually instrument your application
Setup
-
Use composer to install the following two dependencies.
composer require php-http/guzzle7-adapter composer require open-telemetry/opentelemetry
-
Create a new file
otel.php
and save the following code.<?php declare(strict_types=1); require __DIR__ . '/vendor/autoload.php'; use Monolog\Handler\StreamHandler; use Monolog\Logger; use OpenTelemetry\Contrib\Otlp\OtlpHttpTransportFactory; use OpenTelemetry\Contrib\Otlp\SpanExporter; use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor; use OpenTelemetry\SDK\Trace\TracerProvider; use OpenTelemetry\SDK\Resource\ResourceInfoFactory; use OpenTelemetry\SDK\Resource\ResourceInfo; use OpenTelemetry\SDK\Common\Attribute\Attributes; use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator; use OpenTelemetry\SemConv\ResourceAttributes; use OpenTelemetry\SDK\Metrics\MeterProvider; use OpenTelemetry\Contrib\Otlp\MetricExporter; use OpenTelemetry\SDK\Common\Time\ClockFactory; use OpenTelemetry\SDK\Metrics\MetricReader\ExportingReader; // ===== GENERAL SETUP ===== $DT_API_URL = ''; $DT_API_TOKEN = ''; $dtMetadata = []; foreach (['/var/lib/dynatrace/enrichment/dt_metadata.properties', 'dt_metadata_e617c525669e072eebe3d0f08212e8f2.properties'] as $filePath) { try { if (file_exists($filePath)) { $props = str_starts_with($filePath, '/var/') ? parse_ini_file($filePath) : parse_ini_file(trim(file_get_contents($filePath))); $dtMetadata = array_merge($dtMetadata, $props); } } catch (Exception $e) {} } $resource = ResourceInfoFactory::merge( ResourceInfo::create(Attributes::create([ResourceAttributes::SERVICE_NAME => 'php-quickstart']), ResourceAttributes::SCHEMA_URL), ResourceInfo::create(Attributes::create($dtMetadata)), ResourceInfoFactory::defaultResource() ); // ===== TRACING SETUP ===== $transport = (new OtlpHttpTransportFactory())->create($DT_API_URL . '/v1/traces', 'application/x-protobuf', [ 'Authorization' => 'Api-Token ' . $DT_API_TOKEN ]); $exporter = new SpanExporter($transport); $tracerProvider = new TracerProvider(new SimpleSpanProcessor($exporter), null, $resource); // ===== METRIC SETUP ===== $reader = new ExportingReader( new MetricExporter((new OtlpHttpTransportFactory())->create($DT_API_URL . '/v1/metrics', 'application/x-protobuf', [ 'Authorization' => 'Api-Token ' . $DT_API_TOKEN ])), ClockFactory::getDefault() ); $meterProvider = MeterProvider::builder()->setResource($resource)->addReader($reader)->build(); // ===== REGISTRATION ===== Sdk::builder() ->setTracerProvider($tracerProvider) ->setMeterProvider($meterProvider) ->setPropagator(TraceContextPropagator::getInstance()) ->setAutoShutdown(true) ->buildAndRegisterGlobal();
-
Configure the variables
$DT_API_URL
and$DT_API_TOKEN
inotel.php
for the Dynatrace URL and access token. -
Include
otel.php
in all PHP files where you need to initialize OpenTelemetry.require('otel.php');
Add tracing
-
First, we need to get a tracer object.
$tracer = Globals::tracerProvider()->getTracer('my-tracer');
-
With
$tracer
, we can now use a span builder to create and start new spans.$span = $tracer->spanBuilder('Call to /myendpoint')->startSpan(); try { $span->setAttribute('http.method', 'GET'); $span->setAttribute('net.protocol.version', '1.1'); // TODO your code goes here } finally { $span->end(); }
In the above code, we:
- Create a new span and name it "Call to /myendpoint"
- Add two attributes, following the semantic naming convention, specific to the action of this span: information on the HTTP method and version
- Add a
TODO
in place of the eventual business logic - Call the span's
end()
method to close the span (in afinally
block to ensure the method is called)
Collect metrics
-
As with traces, we need to obtain a meter object.
$meter = Globals::meterProvider()->getMeter('my-meter');
-
With
$meter
, we can now create individual instruments, such as a counter.$requestCounter = $meter->createCounter('request_counter');
-
We can now invoke the
add()
method of$requestCounter
to record new values with the counter and save additional attributes.$requestCounter->add(1, [ 'ip' => 'an ip address here' ]);
Connect logs
OpenTelemetry logging is under development for PHP.
Ensure context propagation optional
Context propagation is particularly important when network calls (for example, REST) are involved.
If you are using automatic instrumentation, and your network libraries adhere to PSR-15 (extraction for inbound request) and PSR-18 (injection for outbound requests), context propagation will be automatically handled. Otherwise, your code needs to take this into account.
Extracting the context when receiving a request
In the following example, we assume that we have received an HTTP request with embedded context information, which we are going to extract, to continue the trace.
For this, we first create a request
object with ServerRequestCreator::createFromGlobals()
.
Next, we obtain a propagator object from TraceContextPropagator
and pass our request
object to its extract()
method. This returns a context object (based on the information provided to us via the HTTP call), which we can use subsequently to continue that trace with our own spans.
// Create a request object based on PHP's global arrays (for example, $_SERVER)
$request = ServerRequestCreator::createFromGlobals();
// Obtain propagator instance
$tracePropagator = TraceContextPropagator::getInstance();
// Extract context information from headers and recreate context
$context = $tracePropagator->extract($request->getHeaders());
// Start new span and set received context as parent
$span = $tracer->spanBuilder()
->setParent($context)
->setSpanKind(SpanKind::KIND_SERVER)
->startSpan();
try
{
// TODO your code here
}
finally
{
$span->end();
}
Injecting the context when sending requests
In the following example, we use PHP's cURL library to send an HTTP request to another service and provide our existing context as part of the HTTP headers of our request.
To do so, we first obtain a TraceContextPropagator
instance, on which we call the inject
method and pass the empty array $traceContext
. This call populates the array with the applicable header data in an associative fashion.
As we need a plain string array for the cURL call, we need to convert that before we pass it to cURL. To do so, we loop over $traceContext
in the next step and add the names and values to $contextData
.
Now we are ready to initialize our cURL instance, pass $contextData
, and execute the HTTP call.
$traceContext = []; $contextData = [];
$tracePropagator = TraceContextPropagator::getInstance();
$tracePropagator->inject($traceContext);
// Convert associative array into plain string array
foreach ($traceContext as $name => $value) $contextData[] = "$name: $value";
// Initialize cURL
$ch = curl_init('[URL]');
// Set propagation headers
curl_setopt($ch, CURLOPT_HTTPHEADER, $contextData);
// Execute cURL call
curl_exec($ch);
Configure data capture to meet privacy requirements optional
While Dynatrace automatically captures all OpenTelemetry resource and span attributes, only attribute values specified in the allowlist are stored and displayed in the Dynatrace web UI. This prevents accidental storage of personal data, so you can meet your privacy requirements and control the amount of monitoring data stored.
To view your custom span attributes, you need to allow them in the Dynatrace web UI first.
- Span attributes: In the Dynatrace menu, go to Settings and select Server-side service monitoring > Span attributes.
- Resource attributes: In the Dynatrace menu, go to Settings and select Server-side service monitoring > Resource attributes.
Verify data ingestion into Dynatrace
Once you have finished the instrumentation of your application, perform a couple of test actions to create and send demo traces, metrics, and logs and verify that they were correctly ingested into Dynatrace.
To do that for traces, in the Dynatrace menu, go to Distributed traces and select the Ingested traces tab. If you use OneAgent, select PurePaths instead.
Metrics and logs can be found under their respective entries at Observe and explore.