SigNoz
Docs
PricingCustomers
Get Started - Free
Docs
IntroductionContributingMigrate from DatadogSigNoz API
OpenTelemetry
What is OpenTelemetryOpenTelemetry Collector GuideOpenTelemetry Demo
Community
Support
Slack
X
Launch Week
Changelog
Dashboard Templates
DevOps Wordle
Newsletter
KubeCon, Atlanta 2025
More
SigNoz vs DatadogSigNoz vs New RelicSigNoz vs GrafanaSigNoz vs Dynatrace
Careers
AboutTermsPrivacySecurity & Compliance
SigNoz Logo
SigNoz
All systems operational
HIPAASOC-2
SigNoz Cloud - This page applies to SigNoz Cloud editions.
Self-Host - This page applies to self-hosted SigNoz editions.

How to add manual instrumentation in PHP

Manual instrumentation gives you fine-grained control when automatic instrumentation alone cannot express important business operations. Use it to capture steps that matter for debugging, attach business-specific attributes, or ensure failures surface with the right context in SigNoz.

Most steps are identical. To adapt this guide, update the endpoint and remove the ingestion key header as shown in Cloud → Self-Hosted.

Prerequisites

  • PHP 8.1+.
  • OpenTelemetry SDK installed via Composer:
    composer require open-telemetry/sdk open-telemetry/exporter-otlp
    
  • Environment variables configured to export traces to SigNoz. See the PHP instrumentation guide for setup instructions.

Step 1. Get a tracer

Acquire a tracer from the global tracer provider to create spans:

tracer.php
<?php

use OpenTelemetry\API\Globals;

$tracerProvider = Globals::tracerProvider();
$tracer = $tracerProvider->getTracer(
    'my-app',      // instrumentation scope name
    '1.0.0'        // version (optional)
);

Reuse the tracer instance throughout your application instead of creating new ones for each request.

Step 2. Create manual spans

Wrap important operations inside custom spans:

create-span.php
<?php

use OpenTelemetry\API\Globals;

function processOrder(string $orderId): void
{
    $tracer = Globals::tracerProvider()->getTracer('order-service');

    $span = $tracer->spanBuilder('process-order')->startSpan();

    try {
        // Your business logic here
        validateOrder($orderId);
        chargePayment($orderId);
        fulfillOrder($orderId);
    } finally {
        $span->end();
    }
}

Tips:

  • Use descriptive span names that match business operations (checkout, validate-user, send-email).
  • Always call end() on spans—they won't export otherwise.
  • Use try/finally to ensure spans end even when exceptions occur.

Step 3. Create nested spans

Track hierarchical operations by activating parent spans:

nested-spans.php
<?php

use OpenTelemetry\API\Globals;
use OpenTelemetry\API\Trace\Span;

function processCheckout(string $cartId): void
{
    $tracer = Globals::tracerProvider()->getTracer('checkout-service');

    // Parent span
    $parentSpan = $tracer->spanBuilder('checkout')->startSpan();
    $scope = $parentSpan->activate();

    try {
        // Child span - automatically linked to parent
        $childSpan = $tracer->spanBuilder('validate-cart')->startSpan();
        try {
            validateCart($cartId);
        } finally {
            $childSpan->end();
        }

        // Another child span
        $paymentSpan = $tracer->spanBuilder('process-payment')->startSpan();
        try {
            processPayment($cartId);
        } finally {
            $paymentSpan->end();
        }
    } finally {
        $scope->detach();
        $parentSpan->end();
    }
}

Always detach scopes to prevent context leaks.

Step 4. Add attributes

Attributes show up as key-value pairs in SigNoz for filtering and aggregation:

attributes.php
<?php

use OpenTelemetry\API\Globals;

function handlePayment(float $amount, string $currency): void
{
    $tracer = Globals::tracerProvider()->getTracer('payment-service');

    $span = $tracer->spanBuilder('payment')->startSpan();

    try {
        $span->setAttribute('payment.amount', $amount);
        $span->setAttribute('payment.currency', $currency);
        $span->setAttribute('payment.method', 'credit_card');
        $span->setAttribute('user.id', getCurrentUserId());

        // Process payment...
    } finally {
        $span->end();
    }
}

Use semantic conventions for common attributes when possible.

Step 5. Add events

Events capture notable moments within a span:

events.php
<?php

use OpenTelemetry\API\Globals;
use OpenTelemetry\SDK\Common\Attribute\Attributes;

function processJob(string $jobId): void
{
    $tracer = Globals::tracerProvider()->getTracer('job-service');

    $span = $tracer->spanBuilder('process-job')->startSpan();

    try {
        $span->addEvent('job.started');

        // Do work...

        $span->addEvent('job.completed', Attributes::create([
            'job.id' => $jobId,
            'job.result' => 'success',
            'job.duration_ms' => 1234,
        ]));
    } finally {
        $span->end();
    }
}

Use events to mark retries, cache hits/misses, queue waits, and similar milestones.

Step 6. Record errors

Flag failures so they're easy to query in SigNoz:

error-handling.php
<?php

use OpenTelemetry\API\Globals;
use OpenTelemetry\API\Trace\StatusCode;

function riskyOperation(): void
{
    $tracer = Globals::tracerProvider()->getTracer('my-service');

    $span = $tracer->spanBuilder('risky-operation')->startSpan();

    try {
        doSomethingRisky();
        $span->setStatus(StatusCode::STATUS_OK);
    } catch (Throwable $e) {
        $span->recordException($e, [
            'exception.escaped' => true,
        ]);
        $span->setStatus(StatusCode::STATUS_ERROR, $e->getMessage());
        throw $e;
    } finally {
        $span->end();
    }
}
  • recordException() attaches exception details including stack trace.
  • Setting status to STATUS_ERROR surfaces the span in SigNoz error views and alerts.

Step 7. Add span links

Link spans representing causally-related but not parent-child operations:

span-links.php
<?php

use OpenTelemetry\API\Globals;

function processBatch(array $itemSpanContexts): void
{
    $tracer = Globals::tracerProvider()->getTracer('batch-service');

    $spanBuilder = $tracer->spanBuilder('batch-process');

    // Link to all items being processed in this batch
    foreach ($itemSpanContexts as $context) {
        $spanBuilder->addLink($context);
    }

    $span = $spanBuilder->startSpan();

    try {
        // Process batch...
    } finally {
        $span->end();
    }
}

Validate

  1. Trigger the code paths that emit manual spans.
  2. In SigNoz Traces, filter by service.name or your span name.
  3. Open a trace and verify attributes, events, and error status.
  4. Check the Errors view to confirm failures show up with recorded exceptions.

Troubleshooting

Why don't I see my custom spans in SigNoz?

  • Ensure the OpenTelemetry extension is loaded: php --ri opentelemetry
  • Verify environment variables are set correctly (OTEL_PHP_AUTOLOAD_ENABLED=true, OTEL_EXPORTER_OTLP_ENDPOINT, etc.).
  • Check that span->end() is being called—spans without end() won't export.
  • Confirm the SDK is properly initialized before your application code runs.

Why are child spans not linked to parent spans?

  • Make sure you call activate() on the parent span before creating children.
  • Always detach() the scope when done to prevent context leaks.
  • Verify you're using the same tracer provider instance throughout.

Why don't attributes or events appear on the span?

  • Call setAttribute() or addEvent() before span->end(). Post-end mutations are ignored.
  • Attribute values must be scalar types (string, int, float, bool) or arrays of scalars.
  • Avoid reusing finished spans—always create a new span for each invocation.

Next steps

  • Set up alerts for your PHP application
  • Create dashboards to visualize your custom spans
  • Explore supported PHP instrumentation libraries for automatic instrumentation of frameworks and libraries

Last updated: May 18, 2026

Edit on GitHub

Was this page helpful?

Your response helps us improve this page.

Prev
PHP
Next
Rust
On this page
Prerequisites
Step 1. Get a tracer
Step 2. Create manual spans
Step 3. Create nested spans
Step 4. Add attributes
Step 5. Add events
Step 6. Record errors
Step 7. Add span links
Validate
Troubleshooting
Why don't I see my custom spans in SigNoz?
Why are child spans not linked to parent spans?
Why don't attributes or events appear on the span?
Next steps

Is this page helpful?

Your response helps us improve this page.