Skip to content

Commit

Permalink
Refactor span context (#801)
Browse files Browse the repository at this point in the history
* Otel-php:632 Move stack trace formatting out of Span class

* added function usages

* fix linting errors

* Added documentation for adding Span Attributes

* removing TracingUtl class

* Refactor TraceState's __toString method

* Added path with @Covers to remove warnings

* Added @coversDefaultClass annotation

* fix to check if iterable is empty

* fixed for faling test case

* unit test

* Resolved review Comments

* reverting changes

* removing check for empty iterable span list

* review changes for SpanProcessor incorrect calls to SpanExporter::forceFlush()

* updated test case

* resolved review comments

* reverted changes for batch span processors

* reverting changes for batch span proccessor

* merge upstream and changes

* updating changes

* Refactoring SpanContext

* updating changes

* updated test cases

* updated changes suggested after review

* Revert "Reverting back to previous commit"

This reverts commit 941f368.

* updating changes after review

* unused use statement

* added unit test and resolved review comments

* Removed Double underscores from constant variables

* Removed constants from SpanContext.php to remove duplicacy

* updated failing test

* Moving Factory Methods back into SpanContext.php

* modified constructor of SpanContext

* updated SpanContext

* Modified use of additional if statement

* corrected parsing error
  • Loading branch information
amber0612 authored Sep 21, 2022
1 parent be3f88f commit c0a6279
Show file tree
Hide file tree
Showing 18 changed files with 238 additions and 133 deletions.
11 changes: 6 additions & 5 deletions src/API/Trace/Propagation/TraceContextPropagator.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use OpenTelemetry\API\Trace\Span;
use OpenTelemetry\API\Trace\SpanContext;
use OpenTelemetry\API\Trace\SpanContextInterface;
use OpenTelemetry\API\Trace\SpanContextValidator;
use OpenTelemetry\API\Trace\TraceState;
use OpenTelemetry\Context\Context;
use OpenTelemetry\Context\ContextInterface;
Expand Down Expand Up @@ -114,10 +115,10 @@ private static function extractImpl($carrier, PropagationGetterInterface $getter
* - Version is invalid (not 2 char hex or 'ff')
* - Trace version, trace ID, span ID or trace flag are invalid
*/
if (!SpanContext::isValidTraceVersion($version)
|| !SpanContext::isValidTraceId($traceId)
|| !SpanContext::isValidSpanId($spanId)
|| !SpanContext::isValidTraceFlag($traceFlags)
if (!TraceContextValidator::isValidTraceVersion($version)
|| !SpanContextValidator::isValidTraceId($traceId)
|| !SpanContextValidator::isValidSpanId($spanId)
|| !TraceContextValidator::isValidTraceFlag($traceFlags)
) {
return SpanContext::getInvalid();
}
Expand All @@ -130,7 +131,7 @@ private static function extractImpl($carrier, PropagationGetterInterface $getter

// Only the sampled flag is extracted from the traceFlags (00000001)
$convertedTraceFlags = hexdec($traceFlags);
$isSampled = ($convertedTraceFlags & SpanContext::SAMPLED_FLAG) === SpanContext::SAMPLED_FLAG;
$isSampled = ($convertedTraceFlags & SpanContextInterface::TRACE_FLAG_SAMPLED) === SpanContextInterface::TRACE_FLAG_SAMPLED;

// Tracestate = 'Vendor1=Value1,...,VendorN=ValueN'
$rawTracestate = $getter->get($carrier, self::TRACESTATE);
Expand Down
31 changes: 31 additions & 0 deletions src/API/Trace/Propagation/TraceContextValidator.php
Original file line number Diff line number Diff line change
@@ -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($traceFlag): bool
{
return ctype_xdigit($traceFlag) && strlen($traceFlag) === self::TRACE_FLAG_LENGTH;
}
}
139 changes: 45 additions & 94 deletions src/API/Trace/SpanContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,92 +4,9 @@

namespace OpenTelemetry\API\Trace;

use OpenTelemetry\API\Trace as API;
use function strlen;
use function strtolower;

final class SpanContext implements API\SpanContextInterface
final class SpanContext implements SpanContextInterface
{
public const INVALID_TRACE = '00000000000000000000000000000000';
public const TRACE_VERSION_REGEX = '/^(?!ff)[\da-f]{2}$/';
public const VALID_TRACE = '/^[0-9a-f]{32}$/';
public const TRACE_LENGTH = 32;
public const INVALID_SPAN = '0000000000000000';
public const VALID_SPAN = '/^[0-9a-f]{16}$/';
public const SPAN_LENGTH = 16;
public const SPAN_LENGTH_BYTES = 8;
public const SAMPLED_FLAG = 1;
public const TRACE_FLAG_LENGTH = 2;

private static ?API\SpanContextInterface $invalidContext = null;

/** @inheritDoc */
public static function createFromRemoteParent(string $traceId, string $spanId, int $traceFlags = self::TRACE_FLAG_DEFAULT, ?API\TraceStateInterface $traceState = null): API\SpanContextInterface
{
return new self(
$traceId,
$spanId,
$traceFlags,
true,
$traceState,
);
}

/** @inheritDoc */
public static function create(string $traceId, string $spanId, int $traceFlags = self::TRACE_FLAG_DEFAULT, ?API\TraceStateInterface $traceState = null): API\SpanContextInterface
{
return new self(
$traceId,
$spanId,
$traceFlags,
false,
$traceState,
);
}

/** @inheritDoc */
public static function getInvalid(): API\SpanContextInterface
{
if (null === self::$invalidContext) {
self::$invalidContext = self::create(self::INVALID_TRACE, self::INVALID_SPAN, 0);
}

return self::$invalidContext;
}

/**
* @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 a trace id is valid
*/
public static function isValidTraceId($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($spanId): bool
{
return ctype_xdigit($spanId) && strlen($spanId) === self::SPAN_LENGTH && $spanId !== self::INVALID_SPAN && $spanId === strtolower($spanId);
}

/**
* @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($traceFlag): bool
{
return ctype_xdigit($traceFlag) && strlen($traceFlag) === self::TRACE_FLAG_LENGTH;
}
private static ?SpanContextInterface $invalidContext = null;

/**
* @see https://www.w3.org/TR/trace-context/#trace-flags
Expand All @@ -99,8 +16,8 @@ public static function isValidTraceFlag($traceFlag): bool

private string $traceId;
private string $spanId;
private ?API\TraceStateInterface $traceState;
private bool $isValid;
private ?TraceStateInterface $traceState;
private bool $isValid = true;
private bool $isRemote;
private int $traceFlags;

Expand All @@ -109,22 +26,22 @@ private function __construct(
string $spanId,
int $traceFlags,
bool $isRemote,
API\TraceStateInterface $traceState = null
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 (!self::isValidTraceId($traceId) || !self::isValidSpanId($spanId)) {
$traceId = self::INVALID_TRACE;
$spanId = self::INVALID_SPAN;
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 & self::SAMPLED_FLAG) === self::SAMPLED_FLAG;
$this->isSampled = ($traceFlags & self::TRACE_FLAG_SAMPLED) === self::TRACE_FLAG_SAMPLED;
$this->traceFlags = $traceFlags;
$this->isValid = self::isValidTraceId($this->traceId) && self::isValidSpanId($this->spanId);
}

public function getTraceId(): string
Expand All @@ -137,7 +54,7 @@ public function getSpanId(): string
return $this->spanId;
}

public function getTraceState(): ?API\TraceStateInterface
public function getTraceState(): ?TraceStateInterface
{
return $this->traceState;
}
Expand All @@ -161,4 +78,38 @@ public function getTraceFlags(): int
{
return $this->traceFlags;
}

/** @inheritDoc */
public static function createFromRemoteParent(string $traceId, string $spanId, int $traceFlags = self::TRACE_FLAG_DEFAULT, ?TraceStateInterface $traceState = null): SpanContextInterface
{
return new self(
$traceId,
$spanId,
$traceFlags,
true,
$traceState,
);
}

/** @inheritDoc */
public static function create(string $traceId, string $spanId, int $traceFlags = self::TRACE_FLAG_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;
}
}
35 changes: 35 additions & 0 deletions src/API/Trace/SpanContextValidator.php
Original file line number Diff line number Diff line change
@@ -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($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($spanId): bool
{
return ctype_xdigit($spanId) && strlen($spanId) === self::SPAN_LENGTH && $spanId !== self::INVALID_SPAN && $spanId === strtolower($spanId);
}
}
8 changes: 5 additions & 3 deletions src/Extension/Propagator/B3/B3MultiPropagator.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use OpenTelemetry\API\Trace\Span;
use OpenTelemetry\API\Trace\SpanContext;
use OpenTelemetry\API\Trace\SpanContextInterface;
use OpenTelemetry\API\Trace\SpanContextValidator;
use OpenTelemetry\Context\Context;
use OpenTelemetry\Context\ContextInterface;
use OpenTelemetry\Context\Propagation\ArrayAccessGetterSetter;
Expand Down Expand Up @@ -167,17 +168,18 @@ private static function extractImpl($carrier, PropagationGetterInterface $getter

// Validates the traceId and spanId
// Returns an invalid spanContext if any of the checks fail
if (!SpanContext::isValidTraceId($traceId) || !SpanContext::isValidSpanId($spanId)) {
if (!SpanContextValidator::isValidTraceId($traceId) || !SpanContextValidator::isValidSpanId($spanId)) {
return SpanContext::getInvalid();
}

if ($debug && $debug === self::IS_SAMPLED) {
$context = $context->with(B3DebugFlagContextKey::instance(), self::IS_SAMPLED);
$isSampled = SpanContext::SAMPLED_FLAG;
$isSampled = SpanContextInterface::TRACE_FLAG_SAMPLED;
} else {
$isSampled = ($sampled === SpanContext::SAMPLED_FLAG);
$isSampled = ($sampled === SpanContextInterface::TRACE_FLAG_SAMPLED);
}

// Only traceparent header is extracted. No tracestate.
return SpanContext::createFromRemoteParent(
$traceId,
$spanId,
Expand Down
5 changes: 3 additions & 2 deletions src/Extension/Propagator/B3/B3SinglePropagator.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use OpenTelemetry\API\Trace\Span;
use OpenTelemetry\API\Trace\SpanContext;
use OpenTelemetry\API\Trace\SpanContextInterface;
use OpenTelemetry\API\Trace\SpanContextValidator;
use OpenTelemetry\Context\Context;
use OpenTelemetry\Context\ContextInterface;
use OpenTelemetry\Context\Propagation\ArrayAccessGetterSetter;
Expand Down Expand Up @@ -156,7 +157,7 @@ private static function extractImpl($carrier, PropagationGetterInterface $getter

// Validates the traceId and spanId
// Returns an invalid spanContext if any of the checks fail
if (!SpanContext::isValidTraceId($traceId) || !SpanContext::isValidSpanId($spanId)) {
if (!SpanContextValidator::isValidTraceId($traceId) || !SpanContextValidator::isValidSpanId($spanId)) {
return SpanContext::getInvalid();
}

Expand All @@ -165,7 +166,7 @@ private static function extractImpl($carrier, PropagationGetterInterface $getter
}

$sampled = self::processSampledValue($samplingState);
$isSampled = ($sampled === SpanContext::SAMPLED_FLAG);
$isSampled = ($sampled === SpanContextInterface::TRACE_FLAG_SAMPLED);

return SpanContext::createFromRemoteParent(
$traceId,
Expand Down
6 changes: 3 additions & 3 deletions src/SDK/Trace/RandomIdGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace OpenTelemetry\SDK\Trace;

use OpenTelemetry\API\Trace\SpanContext;
use OpenTelemetry\API\Trace\SpanContextValidator;
use Throwable;

class RandomIdGenerator implements IdGeneratorInterface
Expand All @@ -16,7 +16,7 @@ public function generateTraceId(): string
{
do {
$traceId = $this->randomHex(self::TRACE_ID_HEX_LENGTH);
} while (!SpanContext::isValidTraceId($traceId));
} while (!SpanContextValidator::isValidTraceId($traceId));

return $traceId;
}
Expand All @@ -25,7 +25,7 @@ public function generateSpanId(): string
{
do {
$spanId = $this->randomHex(self::SPAN_ID_HEX_LENGTH);
} while (!SpanContext::isValidSpanId($spanId));
} while (!SpanContextValidator::isValidSpanId($spanId));

return $spanId;
}
Expand Down
5 changes: 3 additions & 2 deletions tests/Integration/SDK/Context/SpanContextTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use OpenTelemetry\API\Trace as API;
use OpenTelemetry\API\Trace\SpanContext;
use OpenTelemetry\API\Trace\SpanContextValidator;
use OpenTelemetry\API\Trace\TraceState;
use OpenTelemetry\SDK\Trace\RandomIdGenerator;
use PHPUnit\Framework\TestCase;
Expand All @@ -21,8 +22,8 @@ class SpanContextTest extends TestCase
public function test_invalid_span(string $traceId, string $spanId): void
{
$spanContext = SpanContext::create($traceId, $spanId);
$this->assertSame(SpanContext::INVALID_TRACE, $spanContext->getTraceId());
$this->assertSame(SpanContext::INVALID_SPAN, $spanContext->getSpanId());
$this->assertSame(SpanContextValidator::INVALID_TRACE, $spanContext->getTraceId());
$this->assertSame(SpanContextValidator::INVALID_SPAN, $spanContext->getSpanId());
}

public function invalidSpanData(): array
Expand Down
Loading

0 comments on commit c0a6279

Please sign in to comment.