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?

ComponentAutomaticDetails
HTTP RequestsManualAdd tracing in your request handler
Database QueriesManualInstrument your database calls
External APIsManualWrap HTTP client calls with spans
ExceptionsBuilt-inUse recordException() method

📦 Installation

1. Install via Composer

composer require tracekit/php-apm

2. 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 enabled is set to true
  • 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.