Framework-Agnostic PHP Integration
PHP Language Integration
Add distributed tracing to any PHP application - vanilla PHP, Symfony, Slim, or any other framework. Built on OpenTelemetry for industry-standard observability.
Prerequisites
- PHP 8.1 or higher
- Composer package manager
- TraceKit API key from your dashboard
🔍 What Gets Traced Automatically?
| Component | Automatic | Details |
|---|---|---|
| HTTP Requests | Manual | Add tracing in your request handler |
| Database Queries | Manual | Instrument your database calls |
| External APIs | Manual | Wrap HTTP client calls with spans |
| Exceptions | Built-in | Use recordException() method |
📦 Installation
1. Install via Composer
composer require tracekit/php-apm2. Configure Environment
Create a .env file or set environment variables:
TRACEKIT_API_KEY=ctxio_your_generated_api_key_here
TRACEKIT_ENDPOINT=https://app.tracekit.dev/v1/traces
TRACEKIT_SERVICE_NAME=my-php-app🚀 Basic Usage
Simple HTTP Request Tracing
<?php
require 'vendor/autoload.php';
use TraceKit\PHP\TracekitClient;
// Initialize TraceKit
$tracekit = new TracekitClient([
'api_key' => getenv('TRACEKIT_API_KEY'),
'service_name' => 'my-php-app',
'endpoint' => 'https://app.tracekit.dev/v1/traces',
]);
// Start a trace
$span = $tracekit->startTrace('http-request', [
'http.method' => $_SERVER['REQUEST_METHOD'],
'http.url' => $_SERVER['REQUEST_URI'],
]);
try {
// Your application logic here
echo "Hello World!";
$tracekit->endSpan($span, [
'http.status_code' => 200,
]);
} catch (Exception $e) {
$tracekit->recordException($span, $e);
$tracekit->endSpan($span, [], 'ERROR');
throw $e;
}
// Important: Flush traces before exit
$tracekit->flush();⚡ Framework Integration
🎵 Symfony
Create an event listener to automatically trace all HTTP requests:
<?php
namespace App\EventListener;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use TraceKit\PHP\TracekitClient;
class TracekitListener
{
private TracekitClient $tracekit;
private $currentSpan;
public function __construct()
{
$this->tracekit = new TracekitClient([
'api_key' => $_ENV['TRACEKIT_API_KEY'],
'service_name' => 'symfony-app',
]);
}
public function onKernelRequest(RequestEvent $event)
{
if (!$event->isMainRequest()) {
return;
}
$request = $event->getRequest();
$this->currentSpan = $this->tracekit->startTrace('http-request', [
'http.method' => $request->getMethod(),
'http.url' => $request->getRequestUri(),
'http.route' => $request->attributes->get('_route'),
]);
}
public function onKernelResponse(ResponseEvent $event)
{
if (!$event->isMainRequest() || !$this->currentSpan) {
return;
}
$this->tracekit->endSpan($this->currentSpan, [
'http.status_code' => $event->getResponse()->getStatusCode(),
]);
$this->tracekit->flush();
}
public function onKernelException(ExceptionEvent $event)
{
if ($this->currentSpan) {
$this->tracekit->recordException($this->currentSpan, $event->getThrowable());
$this->tracekit->endSpan($this->currentSpan, [], 'ERROR');
$this->tracekit->flush();
}
}
}⚡ Slim Framework
Use middleware to trace requests:
<?php
use Slim\Factory\AppFactory;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use TraceKit\PHP\TracekitClient;
require 'vendor/autoload.php';
$tracekit = new TracekitClient([
'api_key' => getenv('TRACEKIT_API_KEY'),
'service_name' => 'slim-app',
'endpoint' => 'https://app.tracekit.dev/v1/traces',
]);
$app = AppFactory::create();
// Tracing middleware
$app->add(function (Request $request, $handler) use ($tracekit) {
$span = $tracekit->startTrace('http-request', [
'http.method' => $request->getMethod(),
'http.url' => (string) $request->getUri(),
]);
try {
$response = $handler->handle($request);
$tracekit->endSpan($span, [
'http.status_code' => $response->getStatusCode(),
]);
$tracekit->flush();
return $response;
} catch (Exception $e) {
$tracekit->recordException($span, $e);
$tracekit->endSpan($span, [], 'ERROR');
$tracekit->flush();
throw $e;
}
});
$app->get('/hello/{name}', function (Request $request, Response $response, $args) {
$response->getBody()->write("Hello, " . $args['name']);
return $response;
});
$app->run();🔧 Vanilla PHP
For vanilla PHP applications, wrap your application logic:
<?php
require 'vendor/autoload.php';
use TraceKit\PHP\TracekitClient;
$tracekit = new TracekitClient([
'api_key' => getenv('TRACEKIT_API_KEY'),
'service_name' => 'my-app',
'endpoint' => 'https://app.tracekit.dev/v1/traces',
]);
$span = $tracekit->startTrace('http-request', [
'http.method' => $_SERVER['REQUEST_METHOD'],
'http.url' => $_SERVER['REQUEST_URI'],
]);
try {
// Your application logic
echo "Hello World!";
$tracekit->endSpan($span, [
'http.status_code' => 200,
]);
} catch (Exception $e) {
$tracekit->recordException($span, $e);
$tracekit->endSpan($span, [], 'ERROR');
}
$tracekit->flush();🤖 Auto-Instrumentation
PHP tracing typically requires manual instrumentation for most operations.
🔧 Manual Instrumentation
Database Queries
<?php
function getUserById($tracekit, $userId) {
$span = $tracekit->startSpan('db.query.users', [
'db.system' => 'mysql',
'db.operation' => 'SELECT',
'user.id' => $userId,
]);
try {
$pdo = new PDO('mysql:host=localhost;dbname=myapp', 'user', 'pass');
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$userId]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
$tracekit->endSpan($span, [
'db.rows_affected' => $stmt->rowCount(),
]);
return $user;
} catch (PDOException $e) {
$tracekit->recordException($span, $e);
$tracekit->endSpan($span, [], 'ERROR');
throw $e;
}
}External API Calls
<?php
function fetchExternalData($tracekit, $url) {
$span = $tracekit->startSpan('http.client.get', [
'http.url' => $url,
'http.method' => 'GET',
]);
try {
$response = file_get_contents($url);
$data = json_decode($response, true);
$tracekit->endSpan($span, [
'http.status_code' => 200,
'response.size' => strlen($response),
]);
return $data;
} catch (Exception $e) {
$tracekit->recordException($span, $e);
$tracekit->endSpan($span, [], 'ERROR');
throw $e;
}
}Nested Spans
<?php
function processOrder($tracekit, $orderId) {
$orderSpan = $tracekit->startSpan('process-order', [
'order.id' => $orderId,
]);
try {
// Validate order
$validationSpan = $tracekit->startSpan('validate-order', [
'order.id' => $orderId,
]);
$valid = validateOrder($orderId);
$tracekit->endSpan($validationSpan, ['valid' => $valid]);
if (!$valid) {
throw new Exception('Invalid order');
}
// Process payment
$paymentSpan = $tracekit->startSpan('process-payment', [
'order.id' => $orderId,
]);
$paymentResult = processPayment($orderId);
$tracekit->endSpan($paymentSpan, ['payment.status' => $paymentResult]);
$tracekit->endSpan($orderSpan, [
'order.status' => 'completed',
]);
return true;
} catch (Exception $e) {
$tracekit->recordException($orderSpan, $e);
$tracekit->endSpan($orderSpan, [], 'ERROR');
throw $e;
}
}🔐 Environment Variables
Store your API key and configuration in environment variables as shown in the installation section above.
⚙️ Configuration Options
Customize the TracekitClient with these configuration options:
<?php
$tracekit = new TracekitClient([
// Required: Your TraceKit API key
'api_key' => getenv('TRACEKIT_API_KEY'),
// Optional: Service name (default: 'php-app')
'service_name' => 'my-service',
// Optional: TraceKit endpoint (default: 'https://app.tracekit.dev/v1/traces')
'endpoint' => 'https://app.tracekit.dev/v1/traces',
// Optional: Enable/disable tracing (default: true)
'enabled' => getenv('APP_ENV') === 'production',
// Optional: Sample rate 0.0-1.0 (default: 1.0 = 100%)
'sample_rate' => 0.5, // Trace 50% of requests
]);🔧 Troubleshooting
Traces not appearing?
- • Verify your API key is correct and active
- • Check that
enabledis set totrue - • Ensure you're calling
flush()before script termination
Composer dependency issues?
- • Ensure PHP 8.1+ is installed:
php -v - • Update Composer:
composer self-update - • Clear cache:
composer clear-cache
Performance concerns?
- • Use sampling:
'sample_rate' => 0.1(10% of requests) - • Disable in development:
'enabled' => getenv('APP_ENV') === 'production'
✅ Complete Example
See the framework integration examples above for complete working code.