*/ 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 */ 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 $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 $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; } }