PHPackages                             utopia-php/span - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Logging &amp; Monitoring](/categories/logging)
4. /
5. utopia-php/span

ActiveLibrary[Logging &amp; Monitoring](/categories/logging)

utopia-php/span
===============

Simple span tracing library for PHP with coroutine support

4.0.1(2w ago)0118.9k—7.2%13MITPHPPHP &gt;=8.2CI passing

Since Jan 12Pushed 2w agoCompare

[ Source](https://github.com/utopia-php/span)[ Packagist](https://packagist.org/packages/utopia-php/span)[ RSS](/packages/utopia-php-span/feed)WikiDiscussions main Synced 2d ago

READMEChangelog (10)Dependencies (11)Versions (18)Used By (3)

Utopia Span
===========

[](#utopia-span)

Important

This repository is a read-only mirror of the [utopia-php monorepo](https://github.com/utopia-php/monorepo). Development happens in [`packages/span`](https://github.com/utopia-php/monorepo/tree/main/packages/span) — please open issues and pull requests there.

A simple, memory-safe span tracing library for PHP with Swoole coroutine support.

Installation
------------

[](#installation)

```
composer require utopia-php/span
```

Quick Start
-----------

[](#quick-start)

```
use Utopia\Span\Span;
use Utopia\Span\Storage;
use Utopia\Span\Exporter;

// Bootstrap once at startup
Span::setStorage(new Storage\Auto());
Span::setExporters(new Exporter\Stdout());

// Create a span
$span = Span::init('http.request');
$span->set('user.id', '123');
$span->finish();
```

Usage
-----

[](#usage)

### Setting Attributes

[](#setting-attributes)

Everything is a flat key-value attribute. Only scalar types are allowed (string, int, float, bool, null):

```
$span = Span::init('api.request');
$span->set('service.name', 'api');
$span->set('request.duration_ms', 42.5);
$span->set('request.cached', true);
$span->finish();
```

### Built-in Attributes

[](#built-in-attributes)

Spans automatically include these attributes:

AttributeDescription`span.trace_id`Unique trace identifier (32 hex chars)`span.id`Unique span identifier (16 hex chars)`span.started_at`Start timestamp in seconds (float)`span.finished_at`End timestamp in seconds (float)`span.duration`Duration in seconds (float)`level``error` if error set, `info` otherwise### Static Helpers

[](#static-helpers)

Use static methods anywhere in your codebase without passing the span around:

```
// Set attribute on current span
Span::add('db.query_count', 5);

// Add warning details as attributes
Span::add('warning', 'retry scheduled');
```

### Error Handling

[](#error-handling)

Pass the exception to `finish()` when the span ends because of an error:

```
$span = Span::init('api.request');

try {
    // ...
} catch (Throwable $e) {
    $span->finish(error: $e);
    throw $e;
}
```

Exporters access the exception via `$span->getError()` and extract what they need (message, trace, etc.).

Use `setError()` when you need to record the error before the span ends, such as before cleanup work that should still be included in the same span.

The `level` attribute is set when the span finishes. It defaults to `Level::Error` when an error is captured and `Level::Info` otherwise. Pass a `Level` to `finish()` to override it:

```
use Utopia\Span\Level;

$span->finish(level: Level::Warn, error: $e);
```

Level names follow Grafana Loki's `detected_level` vocabulary (note `warn`). The Sentry exporter only sends spans at `Level::Warn` or above — translating to Sentry's own terms (`warn` → `warning`) — and reports each at its own level; lower levels (`Info`, `Debug`) are skipped.

Use attributes for warning details that do not end the span:

```
Span::add('warning', 'retry scheduled');
Span::add('warning.message', $message);
```

### Distributed Tracing

[](#distributed-tracing)

Propagate trace context across services using W3C Trace Context headers:

```
// Service A: outgoing request
$client->post('/api/downstream', $payload, [
    'traceparent' => Span::traceparent(),
]);

// Service B: incoming request
$span = Span::init('http.request', $request->getHeader('traceparent'));
```

### Sampling

[](#sampling)

Each exporter decides which spans it accepts via its `sample()` method. Most built-in exporters accept a `sampler` closure as their first constructor argument:

```
Span::setExporters(
    new Exporter\Stdout(
        sampler: fn(Span $s) =>
            $s->getError() !== null ||          // errors
            $s->get('span.duration') > 5.0 ||   // slow requests (>5s)
            $s->get('plan') === 'enterprise'    // enterprise customers
    ),
);
```

The Sentry exporter is hard-wired to error spans only; a custom sampler is composed with that filter and can further restrict — but not broaden — what is sent.

Storage Backends
----------------

[](#storage-backends)

BackendUse Case`Storage\Auto`Auto-detects best storage (recommended)`Storage\Memory`Plain PHP (FPM, CLI)`Storage\Coroutine`Swoole coroutine contextsExporters
---------

[](#exporters)

ExporterDescription`Exporter\Stdout`JSON to stdout/stderr`Exporter\Pretty`Colourful human-readable output`Exporter\Sentry`Sentry events (Issues)`Exporter\None`Discard (for testing)### Stdout Exporter

[](#stdout-exporter)

```
Span::setExporters(new Exporter\Stdout(
    maxTraceFrames: 3  // default, limits error stacktrace length
));
```

Outputs JSON to stdout (info) or stderr (errors). Exports every span by default; pass `sampler:` to filter.

### Pretty Exporter

[](#pretty-exporter)

```
Span::setExporters(new Exporter\Pretty(
    maxTraceFrames: 3,  // default, limits error stacktrace length
    width: 60           // default, separator line width
));
```

Colourful, multi-line output for local development. Attributes are displayed with aligned values, duration is colour-coded (green &lt; 100ms, yellow &lt; 1s, red &gt;= 1s), and errors are highlighted in red. Writes to stdout (info) or stderr (errors).

```
http.request · 12.3ms · abc12345

  http.method GET
  http.url    /api/users
  user.id     42

────────────────────────────────────────────────────────────

```

### Sentry Exporter

[](#sentry-exporter)

```
Span::setExporters(new Exporter\Sentry(
    dsn: 'https://key@sentry.io/123',
    environment: 'production'  // optional
));
```

Only exports error spans with full stacktraces. Non-error spans are skipped, even if you pass a custom `sampler`.

### Custom Exporter

[](#custom-exporter)

```
use Utopia\Span\Exporter\Exporter;
use Utopia\Span\Span;

class MyExporter implements Exporter
{
    public function sample(Span $span): bool
    {
        return true; // export every span
    }

    public function export(Span $span): void
    {
        $data = $span->getAttributes();
        $error = $span->getError();
        // Send to your backend
    }
}
```

Testing
-------

[](#testing)

Disable or capture spans in tests:

```
// Option 1: Discard all spans
Span::setExporters(new Exporter\None());

// Option 2: Capture for assertions
$spans = [];
Span::setExporters(new class($spans) implements Exporter {
    public function __construct(private array &$spans) {}
    public function sample(Span $span): bool { return true; }
    public function export(Span $span): void {
        $this->spans[] = $span;
    }
});

// Run code...

$this->assertCount(1, $spans);
$this->assertEquals('http.request', $spans[0]->get('action'));
```

API Reference
-------------

[](#api-reference)

### Span (static)

[](#span-static)

MethodDescription`setStorage(?Storage $storage)`Set the storage backend (null clears)`setExporters(Exporter ...$exporters)`Replace all exporters`init(string $action, ?string $traceparent): Span`Create and store a new span`current(): ?Span`Get the current span`add(string $key, scalar $value)`Set attribute on current span`traceparent(): ?string`Get traceparent header from current span### Span (instance)

[](#span-instance)

MethodDescription`set(string $key, scalar $value): self`Set an attribute`get(string $key): scalar`Get an attribute`getAttributes(): array`Get all attributes`getAction(): string`Get the span action`setError(Throwable $e): self`Capture exception`getError(): ?Throwable`Get captured exception`getTraceparent(): string`Get W3C traceparent header value`finish(?Level $level = null, ?Throwable $error = null): void`End span and export### Attribute Conventions

[](#attribute-conventions)

PrefixDescription`span.*`Built-in span metadata`*`User-definedLicense
-------

[](#license)

MIT

###  Health Score

54

—

FairBetter than 96% of packages

Maintenance98

Actively maintained with recent releases

Popularity34

Limited adoption so far

Community16

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 86% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~13 days

Total

13

Last Release

14d ago

Major Versions

1.1.5 → 2.0.02026-04-27

2.0.0 → 3.0.02026-05-13

3.0.2 → 4.0.02026-06-16

PHP version history (2 changes)1.0.0PHP &gt;=8.1

1.1.0PHP &gt;=8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/023f08a9df59f81cc4a04b1cebd20f45ede5db53ef2f9e9ad3d75f4c69be66b8?d=identicon)[eldadfux](/maintainers/eldadfux)

---

Top Contributors

[![loks0n](https://avatars.githubusercontent.com/u/22452787?v=4)](https://github.com/loks0n "loks0n (37 commits)")[![ChiragAgg5k](https://avatars.githubusercontent.com/u/110609663?v=4)](https://github.com/ChiragAgg5k "ChiragAgg5k (6 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/utopia-php-span/health.svg)

```
[![Health](https://phpackages.com/badges/utopia-php-span/health.svg)](https://phpackages.com/packages/utopia-php-span)
```

###  Alternatives

[psr/log

Common interface for logging libraries

10.4k1.2B11.5k](/packages/psr-log)[open-telemetry/api

API for OpenTelemetry PHP.

1941.5M276](/packages/open-telemetry-api)[open-telemetry/sdk

SDK for OpenTelemetry PHP.

2328.5M343](/packages/open-telemetry-sdk)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
