360 lines
9.5 KiB
PHP
360 lines
9.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace OpenTelemetry\SDK\Trace;
|
|
|
|
use function get_class;
|
|
use OpenTelemetry\API\Trace as API;
|
|
use OpenTelemetry\Context\ContextInterface;
|
|
use OpenTelemetry\SDK\Common\Attribute\AttributesBuilderInterface;
|
|
use OpenTelemetry\SDK\Common\Dev\Compatibility\Util as BcUtil;
|
|
use OpenTelemetry\SDK\Common\Exception\StackTraceFormatter;
|
|
use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScopeInterface;
|
|
use OpenTelemetry\SDK\Common\Time\ClockFactory;
|
|
use OpenTelemetry\SDK\Resource\ResourceInfo;
|
|
use Throwable;
|
|
|
|
final class Span extends API\Span implements ReadWriteSpanInterface
|
|
{
|
|
|
|
/** @readonly */
|
|
private API\SpanContextInterface $context;
|
|
|
|
/** @readonly */
|
|
private API\SpanContextInterface $parentSpanContext;
|
|
|
|
/** @readonly */
|
|
private SpanLimits $spanLimits;
|
|
|
|
/** @readonly */
|
|
private SpanProcessorInterface $spanProcessor;
|
|
|
|
/**
|
|
* @readonly
|
|
*
|
|
* @var list<LinkInterface>
|
|
*/
|
|
private array $links;
|
|
|
|
/** @readonly */
|
|
private int $totalRecordedLinks;
|
|
|
|
/** @readonly */
|
|
private int $kind;
|
|
|
|
/** @readonly */
|
|
private ResourceInfo $resource;
|
|
|
|
/** @readonly */
|
|
private InstrumentationScopeInterface $instrumentationScope;
|
|
|
|
/** @readonly */
|
|
private int $startEpochNanos;
|
|
|
|
/** @var non-empty-string */
|
|
private string $name;
|
|
|
|
/** @var list<EventInterface> */
|
|
private array $events = [];
|
|
|
|
private AttributesBuilderInterface $attributesBuilder;
|
|
private int $totalRecordedEvents = 0;
|
|
private StatusDataInterface $status;
|
|
private int $endEpochNanos = 0;
|
|
private bool $hasEnded = false;
|
|
|
|
/**
|
|
* @param non-empty-string $name
|
|
* @param list<LinkInterface> $links
|
|
*/
|
|
private function __construct(
|
|
string $name,
|
|
API\SpanContextInterface $context,
|
|
InstrumentationScopeInterface $instrumentationScope,
|
|
int $kind,
|
|
API\SpanContextInterface $parentSpanContext,
|
|
SpanLimits $spanLimits,
|
|
SpanProcessorInterface $spanProcessor,
|
|
ResourceInfo $resource,
|
|
AttributesBuilderInterface $attributesBuilder,
|
|
array $links,
|
|
int $totalRecordedLinks,
|
|
int $startEpochNanos
|
|
) {
|
|
$this->context = $context;
|
|
$this->instrumentationScope = $instrumentationScope;
|
|
$this->parentSpanContext = $parentSpanContext;
|
|
$this->links = $links;
|
|
$this->totalRecordedLinks = $totalRecordedLinks;
|
|
$this->name = $name;
|
|
$this->kind = $kind;
|
|
$this->spanProcessor = $spanProcessor;
|
|
$this->resource = $resource;
|
|
$this->startEpochNanos = $startEpochNanos;
|
|
$this->attributesBuilder = $attributesBuilder;
|
|
$this->status = StatusData::unset();
|
|
$this->spanLimits = $spanLimits;
|
|
}
|
|
|
|
/**
|
|
* This method _MUST_ not be used directly.
|
|
* End users should use a {@see API\TracerInterface} in order to create spans.
|
|
*
|
|
* @param non-empty-string $name
|
|
* @psalm-param API\SpanKind::KIND_* $kind
|
|
* @param list<LinkInterface> $links
|
|
*
|
|
* @internal
|
|
* @psalm-internal OpenTelemetry
|
|
*/
|
|
public static function startSpan(
|
|
string $name,
|
|
API\SpanContextInterface $context,
|
|
InstrumentationScopeInterface $instrumentationScope,
|
|
int $kind,
|
|
API\SpanInterface $parentSpan,
|
|
ContextInterface $parentContext,
|
|
SpanLimits $spanLimits,
|
|
SpanProcessorInterface $spanProcessor,
|
|
ResourceInfo $resource,
|
|
AttributesBuilderInterface $attributesBuilder,
|
|
array $links,
|
|
int $totalRecordedLinks,
|
|
int $startEpochNanos
|
|
): self {
|
|
$span = new self(
|
|
$name,
|
|
$context,
|
|
$instrumentationScope,
|
|
$kind,
|
|
$parentSpan->getContext(),
|
|
$spanLimits,
|
|
$spanProcessor,
|
|
$resource,
|
|
$attributesBuilder,
|
|
$links,
|
|
$totalRecordedLinks,
|
|
$startEpochNanos !== 0 ? $startEpochNanos : ClockFactory::getDefault()->now()
|
|
);
|
|
|
|
// Call onStart here to ensure the span is fully initialized.
|
|
$spanProcessor->onStart($span, $parentContext);
|
|
|
|
return $span;
|
|
}
|
|
|
|
/**
|
|
* Backward compatibility methods
|
|
*
|
|
* @codeCoverageIgnore
|
|
*/
|
|
public static function formatStackTrace(Throwable $e, array &$seen = null): string
|
|
{
|
|
BcUtil::triggerMethodDeprecationNotice(
|
|
__METHOD__,
|
|
'format',
|
|
StackTraceFormatter::class
|
|
);
|
|
|
|
return StackTraceFormatter::format($e);
|
|
}
|
|
|
|
/** @inheritDoc */
|
|
public function getContext(): API\SpanContextInterface
|
|
{
|
|
return $this->context;
|
|
}
|
|
|
|
/** @inheritDoc */
|
|
public function isRecording(): bool
|
|
{
|
|
return !$this->hasEnded;
|
|
}
|
|
|
|
/** @inheritDoc */
|
|
public function setAttribute(string $key, $value): self
|
|
{
|
|
if ($this->hasEnded) {
|
|
return $this;
|
|
}
|
|
|
|
$this->attributesBuilder[$key] = $value;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/** @inheritDoc */
|
|
public function setAttributes(iterable $attributes): self
|
|
{
|
|
foreach ($attributes as $key => $value) {
|
|
$this->attributesBuilder[$key] = $value;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/** @inheritDoc */
|
|
public function addEvent(string $name, iterable $attributes = [], ?int $timestamp = null): self
|
|
{
|
|
if ($this->hasEnded) {
|
|
return $this;
|
|
}
|
|
if (++$this->totalRecordedEvents > $this->spanLimits->getEventCountLimit()) {
|
|
return $this;
|
|
}
|
|
|
|
$timestamp ??= ClockFactory::getDefault()->now();
|
|
$eventAttributesBuilder = $this->spanLimits->getEventAttributesFactory()->builder($attributes);
|
|
|
|
$this->events[] = new Event($name, $timestamp, $eventAttributesBuilder->build());
|
|
|
|
return $this;
|
|
}
|
|
|
|
/** @inheritDoc */
|
|
public function recordException(Throwable $exception, iterable $attributes = [], ?int $timestamp = null): self
|
|
{
|
|
if ($this->hasEnded) {
|
|
return $this;
|
|
}
|
|
if (++$this->totalRecordedEvents > $this->spanLimits->getEventCountLimit()) {
|
|
return $this;
|
|
}
|
|
|
|
$timestamp ??= ClockFactory::getDefault()->now();
|
|
$eventAttributesBuilder = $this->spanLimits->getEventAttributesFactory()->builder([
|
|
'exception.type' => get_class($exception),
|
|
'exception.message' => $exception->getMessage(),
|
|
'exception.stacktrace' => StackTraceFormatter::format($exception),
|
|
]);
|
|
|
|
foreach ($attributes as $key => $value) {
|
|
$eventAttributesBuilder[$key] = $value;
|
|
}
|
|
|
|
$this->events[] = new Event('exception', $timestamp, $eventAttributesBuilder->build());
|
|
|
|
return $this;
|
|
}
|
|
|
|
/** @inheritDoc */
|
|
public function updateName(string $name): self
|
|
{
|
|
if ($this->hasEnded) {
|
|
return $this;
|
|
}
|
|
$this->name = $name;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/** @inheritDoc */
|
|
public function setStatus(string $code, string $description = null): self
|
|
{
|
|
if ($this->hasEnded) {
|
|
return $this;
|
|
}
|
|
|
|
// An attempt to set value Unset SHOULD be ignored.
|
|
if ($code === API\StatusCode::STATUS_UNSET) {
|
|
return $this;
|
|
}
|
|
|
|
// When span status is set to Ok it SHOULD be considered final and any further attempts to change it SHOULD be ignored.
|
|
if ($this->status->getCode() === API\StatusCode::STATUS_OK) {
|
|
return $this;
|
|
}
|
|
$this->status = StatusData::create($code, $description);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/** @inheritDoc */
|
|
public function end(int $endEpochNanos = null): void
|
|
{
|
|
if ($this->hasEnded) {
|
|
return;
|
|
}
|
|
|
|
$this->endEpochNanos = $endEpochNanos ?? ClockFactory::getDefault()->now();
|
|
$this->hasEnded = true;
|
|
|
|
$this->spanProcessor->onEnd($this);
|
|
}
|
|
|
|
/** @inheritDoc */
|
|
public function getName(): string
|
|
{
|
|
return $this->name;
|
|
}
|
|
|
|
public function getParentContext(): API\SpanContextInterface
|
|
{
|
|
return $this->parentSpanContext;
|
|
}
|
|
|
|
public function getInstrumentationScope(): InstrumentationScopeInterface
|
|
{
|
|
return $this->instrumentationScope;
|
|
}
|
|
|
|
public function hasEnded(): bool
|
|
{
|
|
return $this->hasEnded;
|
|
}
|
|
|
|
public function toSpanData(): SpanDataInterface
|
|
{
|
|
return new ImmutableSpan(
|
|
$this,
|
|
$this->name,
|
|
$this->links,
|
|
$this->events,
|
|
$this->attributesBuilder->build(),
|
|
$this->totalRecordedEvents,
|
|
$this->status,
|
|
$this->endEpochNanos,
|
|
$this->hasEnded
|
|
);
|
|
}
|
|
|
|
/** @inheritDoc */
|
|
public function getDuration(): int
|
|
{
|
|
return ($this->hasEnded ? $this->endEpochNanos : ClockFactory::getDefault()->now()) - $this->startEpochNanos;
|
|
}
|
|
|
|
/** @inheritDoc */
|
|
public function getKind(): int
|
|
{
|
|
return $this->kind;
|
|
}
|
|
|
|
/** @inheritDoc */
|
|
public function getAttribute(string $key)
|
|
{
|
|
return $this->attributesBuilder[$key];
|
|
}
|
|
|
|
public function getStartEpochNanos(): int
|
|
{
|
|
return $this->startEpochNanos;
|
|
}
|
|
|
|
public function getTotalRecordedLinks(): int
|
|
{
|
|
return $this->totalRecordedLinks;
|
|
}
|
|
|
|
public function getTotalRecordedEvents(): int
|
|
{
|
|
return $this->totalRecordedEvents;
|
|
}
|
|
|
|
public function getResource(): ResourceInfo
|
|
{
|
|
return $this->resource;
|
|
}
|
|
}
|