Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Spans automatically include these attributes:
| `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

Expand Down Expand Up @@ -78,6 +79,13 @@ try {

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

The `level` attribute is automatically set to `error` when an error is captured. You can override it:

```php
$span->setError($e);
$span->set('level', 'warning'); // override auto-detected level
```

### Distributed Tracing

Propagate trace context across services using W3C Trace Context headers:
Expand Down
9 changes: 5 additions & 4 deletions src/Span/Exporter/Sentry.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ public function export(Span $span): void
}
}

curl_close($ch);
}

private function buildEnvelope(Span $span): ?string
Expand Down Expand Up @@ -205,7 +204,7 @@ private function buildEnvelope(Span $span): ?string
}

$payloadData = [
'level' => 'error',
'level' => (string) $attributes['level'],
'platform' => 'php',
'sdk' => [
'name' => 'utopia-php/span',
Expand Down Expand Up @@ -278,11 +277,13 @@ private function classifyAttributes(array $attributes): array
$extra = [];

foreach ($attributes as $key => $value) {
// Skip internal span attributes
// Skip internal span attributes and level (handled in payload)
if (str_starts_with((string) $key, 'span.')) {
continue;
}

if ($key === 'level') {
continue;
}
// Skip only the HTTP attributes we handle in buildRequest/buildResponse
if (\in_array($key, self::HANDLED_HTTP_KEYS, true)) {
continue;
Expand Down
4 changes: 4 additions & 0 deletions src/Span/Span.php
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,10 @@ public function finish(): void
$this->attributes['span.finished_at'] = $finishedAt;
$this->attributes['span.duration'] = $finishedAt - $startedAt;

if (!isset($this->attributes['level'])) {
$this->attributes['level'] = $this->error instanceof \Throwable ? 'error' : 'info';
}

foreach (self::$exporters as $config) {
try {
$exporter = $config['exporter'];
Expand Down
34 changes: 34 additions & 0 deletions tests/SpanTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,40 @@ public function testInitWithInvalidTraceparentCreatesNewTrace(): void
$this->assertNull($span->get('span.parent_id'));
}

public function testFinishSetsLevelInfoByDefault(): void
{
$span = new Span();
$span->finish();

$this->assertSame('info', $span->get('level'));
}

public function testFinishSetsLevelErrorWhenErrorSet(): void
{
$span = new Span();
$span->setError(new RuntimeException('Test'));
$span->finish();

$this->assertSame('error', $span->get('level'));
}

public function testFinishDoesNotOverrideExplicitLevel(): void
{
$span = new Span();
$span->set('level', 'warning');
$span->setError(new RuntimeException('Test'));
$span->finish();

$this->assertSame('warning', $span->get('level'));
}

public function testLevelNotSetBeforeFinish(): void
{
$span = new Span();

$this->assertNull($span->get('level'));
}

/**
* @param array<Span> $exported
*/
Expand Down