diff options
Diffstat (limited to 'vendor/open-telemetry/api/Trace/Propagation')
-rw-r--r-- | vendor/open-telemetry/api/Trace/Propagation/TraceContextPropagator.php | 157 | ||||
-rw-r--r-- | vendor/open-telemetry/api/Trace/Propagation/TraceContextValidator.php | 31 |
2 files changed, 188 insertions, 0 deletions
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; + } +} |