TraceState.php
Namespace
OpenTelemetry\API\TraceFile
-
vendor/
open-telemetry/ api/ Trace/ TraceState.php
View source
<?php
declare (strict_types=1);
namespace OpenTelemetry\API\Trace;
use function count;
use function end;
use function explode;
use function key;
use OpenTelemetry\API\Behavior\LogsMessagesTrait;
use function prev;
use function sprintf;
use function strlen;
use function trim;
class TraceState implements TraceStateInterface {
use LogsMessagesTrait;
public const MAX_LIST_MEMBERS = 32;
//@see https://www.w3.org/TR/trace-context/#tracestate-header-field-values
/** @deprecated will be removed */
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 array<string, string> */
private array $traceState;
public function __construct(?string $rawTracestate = null) {
$this->traceState = self::parse($rawTracestate ?? '');
}
public function with(string $key, string $value) : TraceStateInterface {
if (!self::validateMember($this->traceState, $key, $value)) {
self::logWarning('Invalid tracestate key/value for: ' . $key);
return $this;
}
$clone = clone $this;
$clone->traceState = [
$key => $value,
] + $this->traceState;
return $clone;
}
public function without(string $key) : TraceStateInterface {
if (!isset($this->traceState[$key])) {
return $this;
}
$clone = clone $this;
unset($clone->traceState[$key]);
return $clone;
}
public function get(string $key) : ?string {
return $this->traceState[$key] ?? null;
}
public function getListMemberCount() : int {
return count($this->traceState);
}
public function toString(?int $limit = null) : string {
$traceState = $this->traceState;
if ($limit !== null) {
$length = 0;
foreach ($traceState as $key => $value) {
$length && ($length += 1);
$length += strlen($key) + 1 + strlen($value);
}
if ($length > $limit) {
// Entries larger than 128 characters long SHOULD be removed first.
foreach ([
128,
0,
] as $threshold) {
// Then entries SHOULD be removed starting from the end of tracestate.
for ($value = end($traceState); $key = key($traceState);) {
$entry = strlen($key) + 1 + strlen($value);
$value = prev($traceState);
if ($entry <= $threshold) {
continue;
}
unset($traceState[$key]);
if (($length -= $entry + 1) <= $limit) {
break 2;
}
}
}
}
}
$s = '';
foreach ($traceState as $key => $value) {
$s && ($s .= ',');
$s .= $key;
$s .= '=';
$s .= $value;
}
return $s;
}
public function __toString() : string {
return $this->toString();
}
private static function parse(string $rawTracestate) : array {
$traceState = [];
$members = explode(',', $rawTracestate);
foreach ($members as $member) {
if (($member = trim($member, " \t")) === '') {
continue;
}
$member = explode('=', $member, 2);
if (count($member) !== 2) {
self::logWarning(sprintf('Incomplete list member in tracestate "%s"', $rawTracestate));
return [];
}
[
$key,
$value,
] = $member;
if (!self::validateMember($traceState, $key, $value)) {
self::logWarning(sprintf('Invalid list member "%s=%s" in tracestate "%s"', $key, $value, $rawTracestate));
return [];
}
$traceState[$key] ??= $value;
}
return $traceState;
}
private static function validateMember(array $traceState, string $key, string $value) : bool {
return (isset($traceState[$key]) || self::validateKey($key)) && self::validateValue($value) && (count($traceState) < self::MAX_LIST_MEMBERS || isset($traceState[$key]));
}
/**
* 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 static 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 static 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;
}
}
Classes
Title | Deprecated | Summary |
---|---|---|
TraceState |