-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement GCP X-Cloud-Trace-Context Propagator (#1132)
- Loading branch information
0 parents
commit 7864488
Showing
6 changed files
with
361 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
[![Releases](https://img.shields.io/badge/releases-purple)](https://github.com/opentelemetry-php/extension-propagator-cloudtrace/releases) | ||
[![Source](https://img.shields.io/badge/source-extension--propagator--xcloudtrace-green)](https://github.com/open-telemetry/opentelemetry-php/tree/main/src/Extension/Propagator/XCloudTrace) | ||
[![Mirror](https://img.shields.io/badge/mirror-opentelemetry--php:extension--propagator--xcloudtrace-blue)](https://github.com/opentelemetry-php/extension-propagator-cloudtrace) | ||
[![Latest Version](http://poser.pugx.org/open-telemetry/extension-propagator-cloudtrace/v/unstable)](https://packagist.org/packages/open-telemetry/extension-propagator-cloudtrace/) | ||
[![Stable](http://poser.pugx.org/open-telemetry/extension-propagator-cloudtrace/v/stable)](https://packagist.org/packages/open-telemetry/extension-propagator-cloudtrace/) | ||
|
||
# OpenTelemetry Extension | ||
### XCloudTrace Propagator | ||
|
||
XCloudTrace is a propagator that supports the specification for the header "x-cloud-trace-context" used for trace context propagation across | ||
service boundaries. (https://cloud.google.com/trace/docs/setup#force-trace). OpenTelemetry PHP XCloudTrace Propagator Extension provides | ||
option to use it bi-directionally or one-way. One-way does not inject the header for downstream consumption, it only processes the incoming headers | ||
and returns the correct span context. It only attaches to existing X-Cloud-Trace-Context traces and does not create downstream ones. | ||
For one-way XCloudTrace: | ||
```text | ||
XCloudTracePropagator::getOneWayInstance() | ||
``` | ||
|
||
For bi-directional XCloudTrace: | ||
```text | ||
XCloudTracePropagator::getInstance() | ||
``` | ||
|
||
## Contributing | ||
|
||
This repository is a read-only git subtree split. | ||
To contribute, please see the main [OpenTelemetry PHP monorepo](https://github.com/open-telemetry/opentelemetry-php). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace OpenTelemetry\Extension\Propagator\XCloudTrace; | ||
|
||
/** | ||
* This class contains utilities that are used by the XCloudTracePropagator. | ||
* This class mostly contains numerical handling functions to work with | ||
* trace and span IDs. | ||
*/ | ||
final class Utils | ||
{ | ||
|
||
/** | ||
* Pads the string with zero string characters on left hand side, to max total string size. | ||
* | ||
* @param string $str The string to pad. | ||
* @param int $amount Total String size, default is 16. | ||
* @return string The padded string | ||
*/ | ||
public static function leftZeroPad(string $str, int $amount = 16) : string | ||
{ | ||
return str_pad($str, $amount, '0', STR_PAD_LEFT); | ||
} | ||
|
||
/** | ||
* Converts a decimal number in string format to a hex number in string format. | ||
* The returned number will not start with 0x. | ||
* | ||
* @param string $num The number to convert. | ||
* @return string The converted number. | ||
*/ | ||
public static function decToHex(string $num) : string | ||
{ | ||
$int = (int) $num; | ||
if (self::isBigNum($int)) { | ||
return self::baseConvert($num, 10, 16); | ||
} | ||
|
||
return dechex($int); | ||
} | ||
|
||
/** | ||
* Converts a hex number in string format to a decimal number in string format. | ||
* The given number does not have to start with 0x. | ||
* | ||
* @param string $num The number to convert. | ||
* @return string The converted number. | ||
*/ | ||
public static function hexToDec(string $num) : string | ||
{ | ||
$dec = hexdec($num); | ||
if (self::isBigNum($dec)) { | ||
return self::baseConvert($num, 16, 10); | ||
} | ||
|
||
return (string) $dec; | ||
} | ||
|
||
/** | ||
* Tests whether the given number is larger than the maximum integer of the installed PHP's build. | ||
* On 32-bit system it's 2147483647 and on 64-bit it's 9223372036854775807. | ||
* We are comparing with >= and no >, because this function is used in context of what method to use | ||
* to convert to some base (in our case hex to octal and vice versa). | ||
* So it's ok if we use >=, because it means that only for MAX_INT we will use the slower baseConvert | ||
* method. | ||
* | ||
* @param int|float $number The number to test. | ||
* @return bool Whether it was bigger or not than the max. | ||
*/ | ||
public static function isBigNum($number) : bool | ||
{ | ||
return $number >= PHP_INT_MAX; | ||
} | ||
|
||
/** | ||
* Custom function to convert a number in string format from one base to another. | ||
* Built-in functions, specifically for hex, do not work well in PHP under | ||
* all versions (32/64-bit) or if the number only fits into an unsigned long. | ||
* PHP does not have unsigned longs, so this function is necessary. | ||
* | ||
* @param string $num The number to convert (in some base). | ||
* @param int $fromBase The base to convert from. | ||
* @param int $toBase The base to convert to. | ||
* @return string Converted number in the new base. | ||
*/ | ||
public static function baseConvert(string $num, int $fromBase, int $toBase) : string | ||
{ | ||
$num = strtolower($num); | ||
$chars = '0123456789abcdefghijklmnopqrstuvwxyz'; | ||
$newstring = substr($chars, 0, $toBase); | ||
|
||
$length = strlen($num); | ||
$result = ''; | ||
|
||
$number = []; | ||
for ($i = 0; $i < $length; $i++) { | ||
$number[$i] = strpos($chars, $num[$i]); | ||
} | ||
|
||
do { | ||
$divide = 0; | ||
$newlen = 0; | ||
for ($i = 0; $i < $length; $i++) { | ||
if (!isset($number[$i]) || $number[$i] === false) { | ||
return ''; | ||
} | ||
$divide = $divide * $fromBase + $number[$i]; | ||
if ($divide >= $toBase) { | ||
$number[$newlen++] = (int) ($divide / $toBase); | ||
$divide %= $toBase; | ||
} elseif ($newlen > 0) { | ||
$number[$newlen++] = 0; | ||
} | ||
} | ||
$length = $newlen; | ||
$result = $newstring[$divide] . $result; | ||
} while ($newlen != 0); | ||
|
||
return $result; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace OpenTelemetry\Extension\Propagator\XCloudTrace; | ||
|
||
use OpenTelemetry\API\Trace\SpanContext; | ||
use OpenTelemetry\API\Trace\SpanContextInterface; | ||
|
||
/** | ||
* This format using a human readable string encoding to propagate SpanContext. | ||
* The current format of the header is `<trace-id>[/<span-id>][;o=<options>]`. | ||
* The options are a bitmask of options. Currently the only option is the | ||
* least significant bit which signals whether the request was traced or not | ||
* (1 = traced, 0 = not traced). | ||
*/ | ||
final class XCloudTraceFormatter | ||
{ | ||
const CONTEXT_HEADER_FORMAT = '/([0-9a-fA-F]{32})(?:\/(\d+))?(?:;o=(\d+))?/'; | ||
|
||
/** | ||
* Generate a SpanContext object from the Trace Context header | ||
* | ||
* @param string $header | ||
* @return SpanContextInterface | ||
*/ | ||
public static function deserialize(string $header) : SpanContextInterface | ||
{ | ||
$matched = preg_match(self::CONTEXT_HEADER_FORMAT, $header, $matches); | ||
|
||
if (!$matched) { | ||
return SpanContext::getInvalid(); | ||
} | ||
if (!array_key_exists(2, $matches) || empty($matches[2])) { | ||
return SpanContext::getInvalid(); | ||
} | ||
if (!array_key_exists(3, $matches)) { | ||
return SpanContext::getInvalid(); | ||
} | ||
|
||
return SpanContext::createFromRemoteParent( | ||
strtolower($matches[1]), | ||
Utils::leftZeroPad(Utils::decToHex($matches[2])), | ||
(int) ($matches[3] == '1') | ||
); | ||
} | ||
|
||
/** | ||
* Convert a SpanContextInterface to header string | ||
* | ||
* @param SpanContextInterface $context | ||
* @return string | ||
*/ | ||
public static function serialize(SpanContextInterface $context) : string | ||
{ | ||
$ret = $context->getTraceId(); | ||
if ($context->getSpanId()) { | ||
$ret .= '/' . Utils::hexToDec($context->getSpanId()); | ||
} | ||
|
||
return $ret . (';o=' . ($context->isSampled() ? '1' : '0')); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace OpenTelemetry\Extension\Propagator\XCloudTrace; | ||
|
||
use OpenTelemetry\API\Trace\Span; | ||
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; | ||
|
||
/** | ||
* XCloudTracePropagator is a propagator that supports the specification for the X-Cloud-Trace-Context | ||
* header used for trace context propagation across service boundaries. | ||
* (https://cloud.google.com/trace/docs/setup#force-trace) | ||
*/ | ||
final class XCloudTracePropagator implements TextMapPropagatorInterface | ||
{ | ||
private static ?TextMapPropagatorInterface $oneWayInstance = null; | ||
private static ?TextMapPropagatorInterface $instance = null; | ||
|
||
public static function getOneWayInstance(): TextMapPropagatorInterface | ||
{ | ||
if (self::$oneWayInstance === null) { | ||
self::$oneWayInstance = new XCloudTracePropagator(true); | ||
} | ||
|
||
return self::$oneWayInstance; | ||
} | ||
|
||
public static function getInstance(): TextMapPropagatorInterface | ||
{ | ||
if (self::$instance === null) { | ||
self::$instance = new XCloudTracePropagator(false); | ||
} | ||
|
||
return self::$instance; | ||
} | ||
|
||
private const XCLOUD = 'x-cloud-trace-context'; | ||
|
||
private const FIELDS = [ | ||
self::XCLOUD, | ||
]; | ||
|
||
private bool $oneWay; | ||
|
||
private function __construct(bool $oneWay) | ||
{ | ||
$this->oneWay = $oneWay; | ||
} | ||
|
||
/** {@inheritdoc} */ | ||
public function fields(): array | ||
{ | ||
return self::FIELDS; | ||
} | ||
|
||
/** {@inheritdoc} */ | ||
public function inject(&$carrier, PropagationSetterInterface $setter = null, ContextInterface $context = null): void | ||
{ | ||
if ($this->oneWay) { | ||
return; | ||
} | ||
|
||
$setter ??= ArrayAccessGetterSetter::getInstance(); | ||
$context ??= Context::getCurrent(); | ||
$spanContext = Span::fromContext($context)->getContext(); | ||
|
||
if (!$spanContext->isValid()) { | ||
return; | ||
} | ||
|
||
$headerValue = XCloudTraceFormatter::serialize($spanContext); | ||
$setter->set($carrier, self::XCLOUD, $headerValue); | ||
} | ||
|
||
/** {@inheritdoc} */ | ||
public function extract($carrier, PropagationGetterInterface $getter = null, ContextInterface $context = null): ContextInterface | ||
{ | ||
$getter ??= ArrayAccessGetterSetter::getInstance(); | ||
$context ??= Context::getCurrent(); | ||
|
||
$headerValue = $getter->get($carrier, self::XCLOUD); | ||
if ($headerValue === null) { | ||
return $context; | ||
} | ||
|
||
$spanContext = XCloudTraceFormatter::deserialize($headerValue); | ||
if (!$spanContext->isValid()) { | ||
return $context; | ||
} | ||
|
||
return $context->withContextValue(Span::wrap($spanContext)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
use OpenTelemetry\Extension\Propagator\XCloudTrace\XCloudTracePropagator; | ||
use OpenTelemetry\SDK\Common\Configuration\KnownValues; | ||
use OpenTelemetry\SDK\Registry; | ||
|
||
Registry::registerTextMapPropagator( | ||
KnownValues::VALUE_XCLOUD_TRACE, | ||
XCloudTracePropagator::getInstance() | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
{ | ||
"name": "open-telemetry/extension-propagator-xcloudtrace", | ||
"description": "XCloudTraceContext propagator extension for OpenTelemetry PHP.", | ||
"keywords": ["opentelemetry", "otel", "tracing", "apm", "extension", "propagator", "xcloudtrace"], | ||
"type": "library", | ||
"support": { | ||
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues", | ||
"source": "https://github.com/open-telemetry/opentelemetry-php", | ||
"docs": "https://opentelemetry.io/docs/php", | ||
"chat": "https://app.slack.com/client/T08PSQ7BQ/C01NFPCV44V" | ||
}, | ||
"license": "Apache-2.0", | ||
"authors": [ | ||
{ | ||
"name": "opentelemetry-php contributors", | ||
"homepage": "https://github.com/open-telemetry/opentelemetry-php/graphs/contributors" | ||
} | ||
], | ||
"require": { | ||
"php": "^7.4 || ^8.0", | ||
"open-telemetry/api": "^1.0", | ||
"open-telemetry/context": "^1.0" | ||
}, | ||
"autoload": { | ||
"psr-4": { | ||
"OpenTelemetry\\Extension\\Propagator\\XCloudTrace\\": "." | ||
}, | ||
"files": [ | ||
"_register.php" | ||
] | ||
}, | ||
"extra": { | ||
"branch-alias": { | ||
"dev-main": "1.0.x-dev" | ||
} | ||
} | ||
} |