summaryrefslogtreecommitdiff
path: root/vendor/open-telemetry/api/Trace/Propagation/TraceContextPropagator.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/open-telemetry/api/Trace/Propagation/TraceContextPropagator.php')
-rw-r--r--vendor/open-telemetry/api/Trace/Propagation/TraceContextPropagator.php157
1 files changed, 157 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
+ );
+ }
+}