diff options
Diffstat (limited to 'vendor/open-telemetry/api/Trace')
20 files changed, 1136 insertions, 0 deletions
diff --git a/vendor/open-telemetry/api/Trace/NonRecordingSpan.php b/vendor/open-telemetry/api/Trace/NonRecordingSpan.php new file mode 100644 index 000000000..67d74d39b --- /dev/null +++ b/vendor/open-telemetry/api/Trace/NonRecordingSpan.php @@ -0,0 +1,76 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +use Throwable; + +/** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#wrapping-a-spancontext-in-a-span + * + * @psalm-internal OpenTelemetry + */ +final class NonRecordingSpan extends Span +{ + private SpanContextInterface $context; + + public function __construct( + SpanContextInterface $context + ) { + $this->context = $context; + } + + /** @inheritDoc */ + public function getContext(): SpanContextInterface + { + return $this->context; + } + + /** @inheritDoc */ + public function isRecording(): bool + { + return false; + } + + /** @inheritDoc */ + public function setAttribute(string $key, $value): SpanInterface + { + return $this; + } + + /** @inheritDoc */ + public function setAttributes(iterable $attributes): SpanInterface + { + return $this; + } + + /** @inheritDoc */ + public function addEvent(string $name, iterable $attributes = [], int $timestamp = null): SpanInterface + { + return $this; + } + + /** @inheritDoc */ + public function recordException(Throwable $exception, iterable $attributes = []): SpanInterface + { + return $this; + } + + /** @inheritDoc */ + public function updateName(string $name): SpanInterface + { + return $this; + } + + /** @inheritDoc */ + public function setStatus(string $code, string $description = null): SpanInterface + { + return $this; + } + + /** @inheritDoc */ + public function end(int $endEpochNanos = null): void + { + } +} diff --git a/vendor/open-telemetry/api/Trace/NoopSpanBuilder.php b/vendor/open-telemetry/api/Trace/NoopSpanBuilder.php new file mode 100644 index 000000000..6f971e525 --- /dev/null +++ b/vendor/open-telemetry/api/Trace/NoopSpanBuilder.php @@ -0,0 +1,65 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +use OpenTelemetry\Context\Context; +use OpenTelemetry\Context\ContextInterface; +use OpenTelemetry\Context\ContextStorageInterface; + +final class NoopSpanBuilder implements SpanBuilderInterface +{ + private ContextStorageInterface $contextStorage; + + /** @var ContextInterface|false|null */ + private $parentContext = null; + + public function __construct(ContextStorageInterface $contextStorage) + { + $this->contextStorage = $contextStorage; + } + + public function setParent($context): SpanBuilderInterface + { + $this->parentContext = $context; + + return $this; + } + + public function addLink(SpanContextInterface $context, iterable $attributes = []): SpanBuilderInterface + { + return $this; + } + + public function setAttribute(string $key, $value): SpanBuilderInterface + { + return $this; + } + + public function setAttributes(iterable $attributes): SpanBuilderInterface + { + return $this; + } + + public function setStartTimestamp(int $timestampNanos): SpanBuilderInterface + { + return $this; + } + + public function setSpanKind(int $spanKind): SpanBuilderInterface + { + return $this; + } + + public function startSpan(): SpanInterface + { + $parentContext = Context::resolve($this->parentContext, $this->contextStorage); + $span = Span::fromContext($parentContext); + if ($span->isRecording()) { + $span = Span::wrap($span->getContext()); + } + + return $span; + } +} diff --git a/vendor/open-telemetry/api/Trace/NoopTracer.php b/vendor/open-telemetry/api/Trace/NoopTracer.php new file mode 100644 index 000000000..bc50248bd --- /dev/null +++ b/vendor/open-telemetry/api/Trace/NoopTracer.php @@ -0,0 +1,26 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +use OpenTelemetry\Context\Context; + +final class NoopTracer implements TracerInterface +{ + private static ?self $instance = null; + + public static function getInstance(): self + { + if (null === self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + public function spanBuilder(string $spanName): SpanBuilderInterface + { + return new NoopSpanBuilder(Context::storage()); + } +} diff --git a/vendor/open-telemetry/api/Trace/NoopTracerProvider.php b/vendor/open-telemetry/api/Trace/NoopTracerProvider.php new file mode 100644 index 000000000..e186a6fd9 --- /dev/null +++ b/vendor/open-telemetry/api/Trace/NoopTracerProvider.php @@ -0,0 +1,17 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +class NoopTracerProvider implements TracerProviderInterface +{ + public function getTracer( + string $name, + ?string $version = null, + ?string $schemaUrl = null, + iterable $attributes = [] + ): TracerInterface { + return NoopTracer::getInstance(); + } +} diff --git a/vendor/open-telemetry/api/Trace/Propagation/TraceContextPropagator.php b/vendor/open-telemetry/api/Trace/Propagation/TraceContextPropagator.php new file mode 100644 index 000000000..b70a15647 --- /dev/null +++ b/vendor/open-telemetry/api/Trace/Propagation/TraceContextPropagator.php @@ -0,0 +1,157 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace\Propagation; + +use function count; +use function explode; +use function hexdec; +use OpenTelemetry\API\Trace\Span; +use OpenTelemetry\API\Trace\SpanContext; +use OpenTelemetry\API\Trace\SpanContextInterface; +use OpenTelemetry\API\Trace\SpanContextValidator; +use OpenTelemetry\API\Trace\TraceFlags; +use OpenTelemetry\API\Trace\TraceState; +use OpenTelemetry\Context\Context; +use OpenTelemetry\Context\ContextInterface; +use OpenTelemetry\Context\Propagation\ArrayAccessGetterSetter; +use OpenTelemetry\Context\Propagation\PropagationGetterInterface; +use OpenTelemetry\Context\Propagation\PropagationSetterInterface; +use OpenTelemetry\Context\Propagation\TextMapPropagatorInterface; + +/** + * TraceContext is a propagator that supports the W3C Trace Context format + * (https://www.w3.org/TR/trace-context/) + * + * This propagator will propagate the traceparent and tracestate headers to + * guarantee traces are not broken. It is up to the users of this propagator + * to choose if they want to participate in a trace by modifying the + * traceparent header and relevant parts of the tracestate header containing + * their proprietary information. + */ +final class TraceContextPropagator implements TextMapPropagatorInterface +{ + public const TRACEPARENT = 'traceparent'; + public const TRACESTATE = 'tracestate'; + private const VERSION = '00'; // Currently, only '00' is supported + + public const FIELDS = [ + self::TRACEPARENT, + self::TRACESTATE, + ]; + + private static ?self $instance = null; + + public static function getInstance(): self + { + if (null === self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** {@inheritdoc} */ + public function fields(): array + { + return self::FIELDS; + } + + /** {@inheritdoc} */ + public function inject(&$carrier, PropagationSetterInterface $setter = null, ContextInterface $context = null): void + { + $setter ??= ArrayAccessGetterSetter::getInstance(); + $context ??= Context::getCurrent(); + $spanContext = Span::fromContext($context)->getContext(); + + if (!$spanContext->isValid()) { + return; + } + + // Build and inject the traceparent header + $traceparent = self::VERSION . '-' . $spanContext->getTraceId() . '-' . $spanContext->getSpanId() . '-' . ($spanContext->isSampled() ? '01' : '00'); + $setter->set($carrier, self::TRACEPARENT, $traceparent); + + // Build and inject the tracestate header + // Spec says to avoid sending empty tracestate headers + if (($tracestate = (string) $spanContext->getTraceState()) !== '') { + $setter->set($carrier, self::TRACESTATE, $tracestate); + } + } + + /** {@inheritdoc} */ + public function extract($carrier, PropagationGetterInterface $getter = null, ContextInterface $context = null): ContextInterface + { + $getter ??= ArrayAccessGetterSetter::getInstance(); + $context ??= Context::getCurrent(); + + $spanContext = self::extractImpl($carrier, $getter); + if (!$spanContext->isValid()) { + return $context; + } + + return $context->withContextValue(Span::wrap($spanContext)); + } + + private static function extractImpl($carrier, PropagationGetterInterface $getter): SpanContextInterface + { + $traceparent = $getter->get($carrier, self::TRACEPARENT); + if ($traceparent === null) { + return SpanContext::getInvalid(); + } + + // traceParent = {version}-{trace-id}-{parent-id}-{trace-flags} + $pieces = explode('-', $traceparent); + + // If the header does not have at least 4 pieces, it is invalid -- restart the trace. + if (count($pieces) < 4) { + return SpanContext::getInvalid(); + } + + [$version, $traceId, $spanId, $traceFlags] = $pieces; + + /** + * Return invalid if: + * - Version is invalid (not 2 char hex or 'ff') + * - Trace version, trace ID, span ID or trace flag are invalid + */ + if (!TraceContextValidator::isValidTraceVersion($version) + || !SpanContextValidator::isValidTraceId($traceId) + || !SpanContextValidator::isValidSpanId($spanId) + || !TraceContextValidator::isValidTraceFlag($traceFlags) + ) { + return SpanContext::getInvalid(); + } + + // Return invalid if the trace version is not a future version but still has > 4 pieces. + $versionIsFuture = hexdec($version) > hexdec(self::VERSION); + if (count($pieces) > 4 && !$versionIsFuture) { + return SpanContext::getInvalid(); + } + + // Only the sampled flag is extracted from the traceFlags (00000001) + $convertedTraceFlags = hexdec($traceFlags); + $isSampled = ($convertedTraceFlags & TraceFlags::SAMPLED) === TraceFlags::SAMPLED; + + // Tracestate = 'Vendor1=Value1,...,VendorN=ValueN' + $rawTracestate = $getter->get($carrier, self::TRACESTATE); + if ($rawTracestate !== null) { + $tracestate = new TraceState($rawTracestate); + + return SpanContext::createFromRemoteParent( + $traceId, + $spanId, + $isSampled ? TraceFlags::SAMPLED : TraceFlags::DEFAULT, + $tracestate + ); + } + + // Only traceparent header is extracted. No tracestate. + return SpanContext::createFromRemoteParent( + $traceId, + $spanId, + $isSampled ? TraceFlags::SAMPLED : TraceFlags::DEFAULT + ); + } +} diff --git a/vendor/open-telemetry/api/Trace/Propagation/TraceContextValidator.php b/vendor/open-telemetry/api/Trace/Propagation/TraceContextValidator.php new file mode 100644 index 000000000..5fb3f12c7 --- /dev/null +++ b/vendor/open-telemetry/api/Trace/Propagation/TraceContextValidator.php @@ -0,0 +1,31 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace\Propagation; + +use function strlen; + +class TraceContextValidator +{ + public const TRACE_FLAG_LENGTH = 2; + public const TRACE_VERSION_REGEX = '/^(?!ff)[\da-f]{2}$/'; + + /** + * @param string $traceVersion + * @return bool Returns a value that indicates whether a trace version is valid. + */ + public static function isValidTraceVersion(string $traceVersion): bool + { + return 1 === preg_match(self::TRACE_VERSION_REGEX, $traceVersion); + } + + /** + * @return bool Returns a value that indicates whether trace flag is valid + * TraceFlags must be exactly 1 bytes (1 char) representing a bit field + */ + public static function isValidTraceFlag(string $traceFlag): bool + { + return ctype_xdigit($traceFlag) && strlen($traceFlag) === self::TRACE_FLAG_LENGTH; + } +} diff --git a/vendor/open-telemetry/api/Trace/Span.php b/vendor/open-telemetry/api/Trace/Span.php new file mode 100644 index 000000000..88360e6cd --- /dev/null +++ b/vendor/open-telemetry/api/Trace/Span.php @@ -0,0 +1,59 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +use OpenTelemetry\Context\Context; +use OpenTelemetry\Context\ContextInterface; +use OpenTelemetry\Context\ContextKeys; +use OpenTelemetry\Context\ScopeInterface; + +abstract class Span implements SpanInterface +{ + private static ?self $invalidSpan = null; + + /** @inheritDoc */ + final public static function fromContext(ContextInterface $context): SpanInterface + { + return $context->get(ContextKeys::span()) ?? self::getInvalid(); + } + + /** @inheritDoc */ + final public static function getCurrent(): SpanInterface + { + return self::fromContext(Context::getCurrent()); + } + + /** @inheritDoc */ + final public static function getInvalid(): SpanInterface + { + if (null === self::$invalidSpan) { + self::$invalidSpan = new NonRecordingSpan(SpanContext::getInvalid()); + } + + return self::$invalidSpan; + } + + /** @inheritDoc */ + final public static function wrap(SpanContextInterface $spanContext): SpanInterface + { + if (!$spanContext->isValid()) { + return self::getInvalid(); + } + + return new NonRecordingSpan($spanContext); + } + + /** @inheritDoc */ + final public function activate(): ScopeInterface + { + return Context::getCurrent()->withContextValue($this)->activate(); + } + + /** @inheritDoc */ + final public function storeInContext(ContextInterface $context): ContextInterface + { + return $context->with(ContextKeys::span(), $this); + } +} diff --git a/vendor/open-telemetry/api/Trace/SpanBuilderInterface.php b/vendor/open-telemetry/api/Trace/SpanBuilderInterface.php new file mode 100644 index 000000000..52070933a --- /dev/null +++ b/vendor/open-telemetry/api/Trace/SpanBuilderInterface.php @@ -0,0 +1,51 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +use OpenTelemetry\Context\ContextInterface; + +/** + * Obtained from a {@see TracerInterface} and used to construct a {@see SpanInterface}. + */ +interface SpanBuilderInterface +{ + /** + * Sets the parent `Context`. + * + * @param ContextInterface|false|null $context the parent context, null to use the + * current context, false to set no parent + * @return SpanBuilderInterface this span builder + * + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#span-creation + */ + public function setParent($context): SpanBuilderInterface; + + public function addLink(SpanContextInterface $context, iterable $attributes = []): SpanBuilderInterface; + public function setAttribute(string $key, $value): SpanBuilderInterface; + public function setAttributes(iterable $attributes): SpanBuilderInterface; + + /** + * Sets an explicit start timestamp for the newly created {@see SpanInterface}. + * The provided *$timestamp* is assumed to be in nanoseconds. + * + * Defaults to the timestamp when {@see SpanBuilderInterface::startSpan} was called if not explicitly set. + */ + public function setStartTimestamp(int $timestampNanos): SpanBuilderInterface; + + /** + * @psalm-param SpanKind::KIND_* $spanKind + */ + public function setSpanKind(int $spanKind): SpanBuilderInterface; + + /** + * Starts and returns a new {@see SpanInterface}. + * + * The user _MUST_ manually end the span by calling {@see SpanInterface::end}. + * + * This method does _NOT_ automatically install the span into the current context. + * The user is responsible for calling {@see SpanInterface::activate} when they wish to do so. + */ + public function startSpan(): SpanInterface; +} diff --git a/vendor/open-telemetry/api/Trace/SpanContext.php b/vendor/open-telemetry/api/Trace/SpanContext.php new file mode 100644 index 000000000..7da7c0701 --- /dev/null +++ b/vendor/open-telemetry/api/Trace/SpanContext.php @@ -0,0 +1,127 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +use function hex2bin; + +final class SpanContext implements SpanContextInterface +{ + private static ?SpanContextInterface $invalidContext = null; + + /** + * @see https://www.w3.org/TR/trace-context/#trace-flags + * @see https://www.w3.org/TR/trace-context/#sampled-flag + */ + private bool $isSampled; + + private string $traceId; + private string $spanId; + private ?TraceStateInterface $traceState; + private bool $isValid = true; + private bool $isRemote; + private int $traceFlags; + + private function __construct( + string $traceId, + string $spanId, + int $traceFlags, + bool $isRemote, + TraceStateInterface $traceState = null + ) { + // TraceId must be exactly 16 bytes (32 chars) and at least one non-zero byte + // SpanId must be exactly 8 bytes (16 chars) and at least one non-zero byte + if (!SpanContextValidator::isValidTraceId($traceId) || !SpanContextValidator::isValidSpanId($spanId)) { + $traceId = SpanContextValidator::INVALID_TRACE; + $spanId = SpanContextValidator::INVALID_SPAN; + $this->isValid=false; + } + + $this->traceId = $traceId; + $this->spanId = $spanId; + $this->traceState = $traceState; + $this->isRemote = $isRemote; + $this->isSampled = ($traceFlags & TraceFlags::SAMPLED) === TraceFlags::SAMPLED; + $this->traceFlags = $traceFlags; + } + + public function getTraceId(): string + { + return $this->traceId; + } + + public function getTraceIdBinary(): string + { + return hex2bin($this->traceId); + } + + public function getSpanId(): string + { + return $this->spanId; + } + + public function getSpanIdBinary(): string + { + return hex2bin($this->spanId); + } + + public function getTraceState(): ?TraceStateInterface + { + return $this->traceState; + } + + public function isSampled(): bool + { + return $this->isSampled; + } + + public function isValid(): bool + { + return $this->isValid; + } + + public function isRemote(): bool + { + return $this->isRemote; + } + + public function getTraceFlags(): int + { + return $this->traceFlags; + } + + /** @inheritDoc */ + public static function createFromRemoteParent(string $traceId, string $spanId, int $traceFlags = TraceFlags::DEFAULT, ?TraceStateInterface $traceState = null): SpanContextInterface + { + return new self( + $traceId, + $spanId, + $traceFlags, + true, + $traceState, + ); + } + + /** @inheritDoc */ + public static function create(string $traceId, string $spanId, int $traceFlags = TraceFlags::DEFAULT, ?TraceStateInterface $traceState = null): SpanContextInterface + { + return new self( + $traceId, + $spanId, + $traceFlags, + false, + $traceState, + ); + } + + /** @inheritDoc */ + public static function getInvalid(): SpanContextInterface + { + if (null === self::$invalidContext) { + self::$invalidContext = self::create(SpanContextValidator::INVALID_TRACE, SpanContextValidator::INVALID_SPAN, 0); + } + + return self::$invalidContext; + } +} diff --git a/vendor/open-telemetry/api/Trace/SpanContextInterface.php b/vendor/open-telemetry/api/Trace/SpanContextInterface.php new file mode 100644 index 000000000..d15bc5987 --- /dev/null +++ b/vendor/open-telemetry/api/Trace/SpanContextInterface.php @@ -0,0 +1,28 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +/** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#spancontext + */ +interface SpanContextInterface +{ + public static function createFromRemoteParent(string $traceId, string $spanId, int $traceFlags = TraceFlags::DEFAULT, ?TraceStateInterface $traceState = null): SpanContextInterface; + public static function getInvalid(): SpanContextInterface; + public static function create(string $traceId, string $spanId, int $traceFlags = TraceFlags::DEFAULT, ?TraceStateInterface $traceState = null): SpanContextInterface; + + /** @psalm-mutation-free */ + public function getTraceId(): string; + public function getTraceIdBinary(): string; + + /** @psalm-mutation-free */ + public function getSpanId(): string; + public function getSpanIdBinary(): string; + public function getTraceFlags(): int; + public function getTraceState(): ?TraceStateInterface; + public function isValid(): bool; + public function isRemote(): bool; + public function isSampled(): bool; +} diff --git a/vendor/open-telemetry/api/Trace/SpanContextValidator.php b/vendor/open-telemetry/api/Trace/SpanContextValidator.php new file mode 100644 index 000000000..09b39f6d8 --- /dev/null +++ b/vendor/open-telemetry/api/Trace/SpanContextValidator.php @@ -0,0 +1,35 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +use function strlen; +use function strtolower; + +class SpanContextValidator +{ + public const VALID_SPAN = '/^[0-9a-f]{16}$/'; + public const VALID_TRACE = '/^[0-9a-f]{32}$/'; + public const INVALID_SPAN = '0000000000000000'; + public const INVALID_TRACE = '00000000000000000000000000000000'; + public const SPAN_LENGTH = 16; + public const TRACE_LENGTH = 32; + public const SPAN_LENGTH_BYTES = 8; + + /** + * @return bool Returns a value that indicates whether a trace id is valid + */ + public static function isValidTraceId(string $traceId): bool + { + return ctype_xdigit($traceId) && strlen($traceId) === self::TRACE_LENGTH && $traceId !== self::INVALID_TRACE && $traceId === strtolower($traceId); + } + + /** + * @return bool Returns a value that indicates whether a span id is valid + */ + public static function isValidSpanId(string $spanId): bool + { + return ctype_xdigit($spanId) && strlen($spanId) === self::SPAN_LENGTH && $spanId !== self::INVALID_SPAN && $spanId === strtolower($spanId); + } +} diff --git a/vendor/open-telemetry/api/Trace/SpanInterface.php b/vendor/open-telemetry/api/Trace/SpanInterface.php new file mode 100644 index 000000000..274a257ea --- /dev/null +++ b/vendor/open-telemetry/api/Trace/SpanInterface.php @@ -0,0 +1,96 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +use OpenTelemetry\Context\ContextInterface; +use OpenTelemetry\Context\ImplicitContextKeyedInterface; +use Throwable; + +/** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#span-operations + */ +interface SpanInterface extends ImplicitContextKeyedInterface +{ + /** + * Returns the {@see SpanInterface} from the provided *$context*, + * falling back on {@see SpanInterface::getInvalid()} if there is no span in the provided context. + */ + public static function fromContext(ContextInterface $context): SpanInterface; + + /** + * Returns the current {@see SpanInterface} from the current {@see ContextInterface}, + * falling back on {@see SpanInterface::getEmpty()} if there is no span in the current context. + */ + public static function getCurrent(): SpanInterface; + + /** + * Returns an invalid {@see SpanInterface} that is used when tracing is disabled, such s when there is no available SDK. + */ + public static function getInvalid(): SpanInterface; + + /** + * Returns a non-recording {@see SpanInterface} that hold the provided *$spanContext* but has no functionality. + * It will not be exported and al tracing operations are no-op, but can be used to propagate a valid {@see SpanContext} downstream. + * + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#wrapping-a-spancontext-in-a-span + */ + public static function wrap(SpanContextInterface $spanContext): SpanInterface; + + /** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#get-context + */ + public function getContext(): SpanContextInterface; + + /** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#isrecording + */ + public function isRecording(): bool; + + /** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#set-attributes + * Adding attributes at span creation is preferred to calling setAttribute later, as samplers can only consider information + * already present during span creation + * @param non-empty-string $key + * @param bool|int|float|string|array|null $value Note: arrays MUST be homogeneous, i.e. it MUST NOT contain values of different types. + */ + public function setAttribute(string $key, $value): SpanInterface; + + /** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#set-attributes + * An attribute with a null key will be dropped, and an attribute with a null value will be dropped but also remove any existing + * attribute with the same key. + * @param iterable<non-empty-string, bool|int|float|string|array|null> $attributes + */ + public function setAttributes(iterable $attributes): SpanInterface; + + /** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#add-events + */ + public function addEvent(string $name, iterable $attributes = [], int $timestamp = null): SpanInterface; + + /** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#record-exception + */ + public function recordException(Throwable $exception, iterable $attributes = []): SpanInterface; + + /** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#updatename + * + * @param non-empty-string $name + */ + public function updateName(string $name): SpanInterface; + + /** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#set-status + * + * @psalm-param StatusCode::STATUS_* $code + */ + public function setStatus(string $code, string $description = null): SpanInterface; + + /** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#end + */ + public function end(int $endEpochNanos = null): void; +} diff --git a/vendor/open-telemetry/api/Trace/SpanKind.php b/vendor/open-telemetry/api/Trace/SpanKind.php new file mode 100644 index 000000000..f44339e00 --- /dev/null +++ b/vendor/open-telemetry/api/Trace/SpanKind.php @@ -0,0 +1,17 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +/** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#spankind + */ +interface SpanKind +{ + public const KIND_INTERNAL = 0; + public const KIND_CLIENT = 1; + public const KIND_SERVER = 2; + public const KIND_PRODUCER = 3; + public const KIND_CONSUMER = 4; +} diff --git a/vendor/open-telemetry/api/Trace/StatusCode.php b/vendor/open-telemetry/api/Trace/StatusCode.php new file mode 100644 index 000000000..0d95e96a5 --- /dev/null +++ b/vendor/open-telemetry/api/Trace/StatusCode.php @@ -0,0 +1,15 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +/** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.6.1/specification/trace/api.md#set-status + */ +interface StatusCode +{ + public const STATUS_UNSET = 'Unset'; + public const STATUS_OK = 'Ok'; + public const STATUS_ERROR = 'Error'; +} diff --git a/vendor/open-telemetry/api/Trace/TraceFlags.php b/vendor/open-telemetry/api/Trace/TraceFlags.php new file mode 100644 index 000000000..6adf72b1a --- /dev/null +++ b/vendor/open-telemetry/api/Trace/TraceFlags.php @@ -0,0 +1,11 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +interface TraceFlags +{ + public const SAMPLED = 0x01; + public const DEFAULT = 0x00; +} diff --git a/vendor/open-telemetry/api/Trace/TraceState.php b/vendor/open-telemetry/api/Trace/TraceState.php new file mode 100644 index 000000000..306a63322 --- /dev/null +++ b/vendor/open-telemetry/api/Trace/TraceState.php @@ -0,0 +1,190 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +use function array_reverse; +use OpenTelemetry\API\Behavior\LogsMessagesTrait; +use function strlen; + +class TraceState implements TraceStateInterface +{ + use LogsMessagesTrait; + + public const MAX_LIST_MEMBERS = 32; //@see https://www.w3.org/TR/trace-context/#tracestate-header-field-values + public const MAX_COMBINED_LENGTH = 512; //@see https://www.w3.org/TR/trace-context/#tracestate-limits + public const LIST_MEMBERS_SEPARATOR = ','; + public const LIST_MEMBER_KEY_VALUE_SPLITTER = '='; + private const VALID_KEY_CHAR_RANGE = '[_0-9a-z-*\/]'; + private const VALID_KEY = '[a-z]' . self::VALID_KEY_CHAR_RANGE . '{0,255}'; + private const VALID_VENDOR_KEY = '[a-z0-9]' . self::VALID_KEY_CHAR_RANGE . '{0,240}@[a-z]' . self::VALID_KEY_CHAR_RANGE . '{0,13}'; + private const VALID_KEY_REGEX = '/^(?:' . self::VALID_KEY . '|' . self::VALID_VENDOR_KEY . ')$/'; + private const VALID_VALUE_BASE_REGEX = '/^[ -~]{0,255}[!-~]$/'; + private const INVALID_VALUE_COMMA_EQUAL_REGEX = '/,|=/'; + + /** + * @var string[] + */ + private array $traceState = []; + + public function __construct(string $rawTracestate = null) + { + if ($rawTracestate === null || trim($rawTracestate) === '') { + return; + } + $this->traceState = $this->parse($rawTracestate); + } + + /** + * {@inheritdoc} + */ + public function with(string $key, string $value): TraceStateInterface + { + $clonedTracestate = clone $this; + + if ($this->validateKey($key) && $this->validateValue($value)) { + + /* + * Only one entry per key is allowed. In this case we need to overwrite the vendor entry + * upon reentry to the tracing system and ensure the updated entry is at the beginning of + * the list. This means we place it the back for now and it will be at the beginning once + * we reverse the order back during __toString(). + */ + if (array_key_exists($key, $clonedTracestate->traceState)) { + unset($clonedTracestate->traceState[$key]); + } + + // Add new or updated entry to the back of the list. + $clonedTracestate->traceState[$key] = $value; + } else { + self::logWarning('Invalid tracetrace key/value for: ' . $key); + } + + return $clonedTracestate; + } + + /** + * {@inheritdoc} + */ + public function without(string $key): TraceStateInterface + { + $clonedTracestate = clone $this; + + if ($key !== '') { + unset($clonedTracestate->traceState[$key]); + } + + return $clonedTracestate; + } + + /** + * {@inheritdoc} + */ + public function get(string $key): ?string + { + return $this->traceState[$key] ?? null; + } + + /** + * {@inheritdoc} + */ + public function getListMemberCount(): int + { + return count($this->traceState); + } + + /** + * {@inheritdoc} + */ + public function __toString(): string + { + if ($this->traceState === []) { + return ''; + } + $traceStateString=''; + foreach (array_reverse($this->traceState) as $k => $v) { + $traceStateString .=$k . self::LIST_MEMBER_KEY_VALUE_SPLITTER . $v . self::LIST_MEMBERS_SEPARATOR; + } + + return rtrim($traceStateString, ','); + } + + /** + * Parse the raw tracestate header into the TraceState object. Since new or updated entries must + * be added to the beginning of the list, the key-value pairs in the TraceState object will be + * stored in reverse order. This ensures new entries added to the TraceState object are at the + * beginning when we reverse the order back again while building the final tracestate header. + * + * Ex: + * tracestate = 'vendor1=value1,vendor2=value2' + * + * || + * \/ + * + * $this->tracestate = ['vendor2' => 'value2' ,'vendor1' => 'value1'] + * + */ + private function parse(string $rawTracestate): array + { + if (strlen($rawTracestate) > self::MAX_COMBINED_LENGTH) { + self::logWarning('tracestate discarded, exceeds max combined length: ' . self::MAX_COMBINED_LENGTH); + + return []; + } + $parsedTracestate = []; + $listMembers = explode(self::LIST_MEMBERS_SEPARATOR, $rawTracestate); + + if (count($listMembers) > self::MAX_LIST_MEMBERS) { + self::logWarning('tracestate discarded, too many members'); + + return []; + } + + foreach ($listMembers as $listMember) { + $vendor = explode(self::LIST_MEMBER_KEY_VALUE_SPLITTER, trim($listMember)); + + // There should only be one list-member per vendor separated by '=' + if (count($vendor) !== 2 || !$this->validateKey($vendor[0]) || !$this->validateValue($vendor[1])) { + self::logWarning('tracestate discarded, invalid member: ' . $listMember); + + return []; + } + $parsedTracestate[$vendor[0]] = $vendor[1]; + } + + /* + * Reversing the tracestate ensures the new entries added to the TraceState object are at + * the beginning when we reverse it back during __toString(). + */ + return array_reverse($parsedTracestate); + } + + /** + * The Key is opaque string that is an identifier for a vendor. It can be up + * to 256 characters and MUST begin with a lowercase letter or a digit, and can + * only contain lowercase letters (a-z), digits (0-9), underscores (_), dashes (-), + * asterisks (*), and forward slashes (/). For multi-tenant vendor scenarios, an at + * sign (@) can be used to prefix the vendor name. Vendors SHOULD set the tenant ID + * at the beginning of the key. + * + * @see https://www.w3.org/TR/trace-context/#key + */ + private function validateKey(string $key): bool + { + return preg_match(self::VALID_KEY_REGEX, $key) !== 0; + } + + /** + * The value is an opaque string containing up to 256 printable ASCII [RFC0020] + * characters (i.e., the range 0x20 to 0x7E) except comma (,) and (=). Note that + * this also excludes tabs, newlines, carriage returns, etc. + * + * @see https://www.w3.org/TR/trace-context/#value + */ + private function validateValue(string $key): bool + { + return (preg_match(self::VALID_VALUE_BASE_REGEX, $key) !== 0) + && (preg_match(self::INVALID_VALUE_COMMA_EQUAL_REGEX, $key) === 0); + } +} diff --git a/vendor/open-telemetry/api/Trace/TraceStateInterface.php b/vendor/open-telemetry/api/Trace/TraceStateInterface.php new file mode 100644 index 000000000..79d4e0299 --- /dev/null +++ b/vendor/open-telemetry/api/Trace/TraceStateInterface.php @@ -0,0 +1,61 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +/** + * TraceState parses and stores the tracestate header as an immutable list of string + * key/value pairs. It provides the following operations following the rules described + * in the W3C Trace Context specification: + * - Get value for a given key + * - Add a new key/value pair + * - Update an existing value for a given key + * - Delete a key/value pair + * + * All mutating operations return a new TraceState with the modifications applied. + * + * @see https://www.w3.org/TR/trace-context/#tracestate-header + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md#tracestate + */ +interface TraceStateInterface +{ + /** + * Return a new TraceState object that inherits from this TraceState + * and contains the given key value pair. + * + * @param string $key + * @param string $value + * @return TraceStateInterface + */ + public function with(string $key, string $value): TraceStateInterface; + + /** + * Return a new TraceState object that inherits from this TraceState + * without the given key value pair. + * + * @param string $key + * @return TraceStateInterface + */ + public function without(string $key): TraceStateInterface; + + /** + * Return the value of a given key from this TraceState if it exists + * + * @param string $key + * @return string|null + */ + public function get(string $key): ?string; + + /** + * Get the list-member count in this TraceState + * + * @return int + */ + public function getListMemberCount(): int; + + /** + * Returns a string representation of this TraceSate + */ + public function __toString(): string; +} diff --git a/vendor/open-telemetry/api/Trace/TracerInterface.php b/vendor/open-telemetry/api/Trace/TracerInterface.php new file mode 100644 index 000000000..1293a6642 --- /dev/null +++ b/vendor/open-telemetry/api/Trace/TracerInterface.php @@ -0,0 +1,11 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +interface TracerInterface +{ + /** @param non-empty-string $spanName */ + public function spanBuilder(string $spanName): SpanBuilderInterface; +} diff --git a/vendor/open-telemetry/api/Trace/TracerProviderInterface.php b/vendor/open-telemetry/api/Trace/TracerProviderInterface.php new file mode 100644 index 000000000..9f5d20759 --- /dev/null +++ b/vendor/open-telemetry/api/Trace/TracerProviderInterface.php @@ -0,0 +1,19 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +/** @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/specification/trace/api.md#tracerprovider */ +interface TracerProviderInterface +{ + /** + * @see https://github.com/open-telemetry/opentelemetry-specification/blob/v1.7.0/specification/trace/api.md#get-a-tracer + */ + public function getTracer( + string $name, + ?string $version = null, + ?string $schemaUrl = null, + iterable $attributes = [] + ): TracerInterface; +} diff --git a/vendor/open-telemetry/api/Trace/functions.php b/vendor/open-telemetry/api/Trace/functions.php new file mode 100644 index 000000000..79f730717 --- /dev/null +++ b/vendor/open-telemetry/api/Trace/functions.php @@ -0,0 +1,44 @@ +<?php + +declare(strict_types=1); + +namespace OpenTelemetry\API\Trace; + +use Closure; +use Throwable; + +/** + * Executes the given closure within the provided span. + * + * The span will be ended. + * + * @template R + * @param SpanInterface $span span to enclose the closure with + * @param Closure(...): R $closure closure to invoke + * @param iterable<int|string, mixed> $args arguments to provide to the closure + * @return R result of the closure invocation + * + * @phpstan-ignore-next-line + */ +function trace(SpanInterface $span, Closure $closure, iterable $args = []) +{ + $s = $span; + $c = $closure; + $a = $args; + unset($span, $closure, $args); + + $scope = $s->activate(); + + try { + /** @psalm-suppress InvalidArgument */ + return $c(...$a, ...($a = [])); + } catch (Throwable $e) { + $s->setStatus(StatusCode::STATUS_ERROR, $e->getMessage()); + $s->recordException($e, ['exception.escaped' => true]); + + throw $e; + } finally { + $scope->detach(); + $s->end(); + } +} |