opentelemetry-php/sdk/Trace/Span.php

274 lines
7.4 KiB
PHP

<?php
declare(strict_types=1);
namespace OpenTelemetry\Sdk\Trace;
use OpenTelemetry\Sdk\Resource\ResourceInfo;
use OpenTelemetry\Trace as API;
class Span implements API\Span
{
private $name;
private $spanContext;
private $parentSpanContext;
private $spanKind;
private $sampler;
private $startEpochTimestamp;
private $start;
private $end;
private $statusCode;
private $statusDescription;
/**
* @var ResourceInfo
*/
private $resource; // An immutable representation of the entity producing telemetry.
private $attributes;
private $events;
private $links = null;
private $ended = false;
// todo: missing links: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/api-tracing.md#add-links
// -> Need to understand the difference between SpanKind and links. From the documentation:
// SpanKind
// describes the relationship between the Span, its parents, and its children in a Trace. SpanKind describes two independent properties that benefit tracing systems during analysis.
// This was also updated recently -> https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/api-tracing.md#spankind
// Links
// A Span may be linked to zero or more other Spans (defined by SpanContext) that are causally related. Links can point to SpanContexts inside a single Trace
// or across different Traces. Links can be used to represent batched operations where a Span was initiated by multiple initiating Spans,
// each representing a single incoming item being processed in the batch.
// https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/overview.md#links-between-spans
public function __construct(
string $name,
API\SpanContext $spanContext,
?API\SpanContext $parentSpanContext = null,
?Sampler $sampler = null,
?ResourceInfo $resource = null,
int $spanKind = API\SpanKind::KIND_INTERNAL
) {
$this->name = $name;
$this->spanContext = $spanContext;
$this->parentSpanContext = $parentSpanContext;
$this->spanKind = $spanKind;
$this->sampler = $sampler;
$this->resource = $resource ?? ResourceInfo::emptyResource();
$moment = Clock::get()->moment();
$this->startEpochTimestamp = $moment[0];
$this->start = $moment[1];
$this->statusCode = API\SpanStatus::OK;
$this->statusDescription = API\SpanStatus::DESCRIPTION[$this->statusCode];
// todo: set these to null until needed
$this->attributes = new Attributes();
$this->events = new Events();
}
public function getResource(): ResourceInfo
{
return clone $this->resource;
}
public function getContext(): API\SpanContext
{
return clone $this->spanContext;
}
public function getParent(): ?API\SpanContext
{
// todo: Spec says a parent is a Span, SpanContext, or null -> should we implement this here?
return $this->parentSpanContext !== null ? clone $this->parentSpanContext : null;
}
public function setSpanStatus(string $code, ?string $description = null): API\Span
{
if ($this->isRecording()) {
$this->statusCode = $code;
$this->statusDescription = $description ?? self::DESCRIPTION[$code] ?? self::DESCRIPTION[self::UNKNOWN];
}
return $this;
}
public function getStart(): int
{
return $this->start;
}
public function setStart(int $start): Span
{
$this->start = $start;
return $this;
}
public function end(?int $now = null): API\Span
{
if (!isset($this->end)) {
$this->end = $now ?? Clock::get()->now();
$this->ended = true;
}
return $this;
}
public function ended(): bool
{
return $this->ended;
}
public function setStartEpochTimestamp(int $timestamp): Span
{
$this->startEpochTimestamp = $timestamp;
return $this;
}
public function getStartEpochTimestamp(): int
{
return $this->startEpochTimestamp;
}
public function getEnd(): ?int
{
return $this->end;
}
public function getStatus(): API\SpanStatus
{
return SpanStatus::new($this->statusCode, $this->statusDescription);
}
public function isRecording(): bool
{
return null === $this->end;
}
public function getDuration(): ?int
{
if (!$this->end) {
return null;
}
return ($this->end - $this->start);
}
public function getSpanName(): string
{
return $this->name;
}
public function updateName(string $name): API\Span
{
$this->name = $name;
return $this;
}
public function getAttribute(string $key): ?Attribute
{
return $this->attributes->getAttribute($key);
}
public function setAttribute(string $key, $value): API\Span
{
if ($this->isRecording()) {
$this->attributes->setAttribute($key, $value);
}
return $this;
}
public function getAttributes(): API\Attributes
{
return $this->attributes;
}
public function replaceAttributes(API\Attributes $attributes): self
{
if ($this->isRecording()) {
$this->attributes = $attributes;
}
return $this;
}
// todo: is accepting an Iterator enough to satisfy AddLazyEvent? -> Looks like the spec might have been updated here: https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/api-tracing.md#add-events
public function addEvent(string $name, int $timestamp, ?API\Attributes $attributes = null): API\Span
{
if ($this->isRecording()) {
$this->events->addEvent($name, $attributes, $timestamp);
}
return $this;
}
public function getEvents(): API\Events
{
return $this->events;
}
/* A Span is said to have a remote parent if it is the child of a Span
* created in another process. Each propagators' deserialization must
* set IsRemote to true on a parent
* SpanContext so Span creation knows if the parent is remote.
* Returns true if the SpanContext was propagated from a
* remote parent. When creating children
* from remote spans, their IsRemote flag MUST be set to false.
*/
public function isRemote(): bool
{
return $this->spanContext->isRemoteContext();
}
public function isSampled(): bool
{
return $this->spanContext->isSampled();
}
public function setLinks(API\Links $links): Span
{
$this->links = $links;
return $this;
}
public function getLinks(): API\Links
{
// TODO: Implement getLinks() method.
return $this->links;
}
/**
* @inheritDoc
*/
public function addLink(API\SpanContext $context, ?API\Attributes $attributes = null): API\Span
{
return $this;
}
public function getSpanKind(): int
{
return $this->spanKind;
}
public function getCanonicalStatusCode(): string
{
return $this->statusCode;
}
public function getStatusDescription(): string
{
return $this->statusDescription;
}
public function isStatusOk(): bool
{
return $this->statusCode == API\SpanStatus::OK;
}
}