A simple, memory-safe span tracing library for PHP with Swoole coroutine support.
composer require utopia-php/spanuse Utopia\Span\Span;
use Utopia\Span\Storage;
use Utopia\Span\Exporter;
// Bootstrap once at startup
Span::setStorage(new Storage\Auto());
Span::addExporter(new Exporter\Stdout());
// Create a span
$span = Span::init('http.request');
$span->set('user.id', '123');
$span->finish();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();Spans automatically include these attributes:
| Attribute | Description |
|---|---|
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) |
Use static methods anywhere in your codebase without passing the span around:
// Set attribute on current span
Span::add('db.query_count', 5);
// Capture an exception
Span::error($exception);The setError() method captures the exception for exporters to process:
try {
// ...
} catch (Throwable $e) {
$span->setError($e);
throw $e;
}Exporters access the exception via $span->getError() and extract what they need (message, trace, etc.).
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'));Add a sampler to control which spans get exported:
Span::addExporter(
new Exporter\Sentry('https://[email protected]/123'),
sampler: fn(Span $s) =>
$s->getError() !== null || // errors
$s->get('span.duration') > 5.0 || // slow requests (>5s)
$s->get('plan') === 'enterprise' // enterprise customers
);| Backend | Use Case |
|---|---|
Storage\Auto |
Auto-detects best storage (recommended) |
Storage\Memory |
Plain PHP (FPM, CLI) |
Storage\Coroutine |
Swoole coroutine contexts |
| Exporter | Description |
|---|---|
Exporter\Stdout |
JSON to stdout/stderr |
Exporter\Sentry |
Sentry events (Issues) |
Exporter\None |
Discard (for testing) |
Span::addExporter(new Exporter\Stdout(
maxTraceFrames: 3 // default, limits error stacktrace length
));Outputs JSON to stdout (info) or stderr (errors).
Span::addExporter(new Exporter\Sentry(
dsn: 'https://[email protected]/123',
environment: 'production' // optional
));Only exports error spans with full stacktraces. Non-error spans are skipped.
use Utopia\Span\Exporter\Exporter;
use Utopia\Span\Span;
class MyExporter implements Exporter
{
public function export(Span $span): void
{
$data = $span->getAttributes();
$error = $span->getError();
// Send to your backend
}
}Disable or capture spans in tests:
// Option 1: Discard all spans
Span::resetExporters();
Span::addExporter(new Exporter\None());
// Option 2: Capture for assertions
$spans = [];
Span::addExporter(new class($spans) implements Exporter {
public function __construct(private array &$spans) {}
public function export(Span $span): void {
$this->spans[] = $span;
}
});
// Run code...
$this->assertCount(1, $spans);
$this->assertEquals('http.request', $spans[0]->get('action'));| Method | Description |
|---|---|
setStorage(Storage $storage) |
Set the storage backend |
addExporter(Exporter $exporter, ?Closure $sampler) |
Add an exporter with optional sampler |
resetExporters() |
Remove 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 |
error(Throwable $e) |
Capture exception on current span |
traceparent(): ?string |
Get traceparent header from current span |
| Method | Description |
|---|---|
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(): void |
End span and export |
| Prefix | Description |
|---|---|
span.* |
Built-in span metadata |
* |
User-defined |
MIT