ttrss/vendor/open-telemetry/api/Trace/Propagation/TraceContextPropagator.php

158 lines
5.5 KiB
PHP
Raw Normal View History

2023-10-20 14:12:29 +00:00
<?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
);
}
}