diff --git a/.travis.yml b/.travis.yml index 4f77895..f9f1ce7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,11 @@ language: php php: - '7.0' - '7.1' + - '7.2' + - nightly -install: +install: - composer install -script: +script: - vendor/bin/phpunit diff --git a/LICENSE b/LICENSE index 85c1ce9..8e37af0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Jonah George +Copyright (c) 2018 Jonah George, José Carlos Chávez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index e0c491d..250b3a8 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Please see [CONTRIBUTING.md](./CONTRIBUTING.md). ```sh { "require": { - "jonahgeorge/jaeger-client-php": "0.2.0" + "jonahgeorge/jaeger-client-php": "0.3.0" } } ``` @@ -30,7 +30,10 @@ use OpenTracing\GlobalTracer; $config = new Config( [ - 'sampler' => ['type' => 'const', 'param' => true], + 'sampler' => [ + 'type' => 'const', + 'param' => true, + ], 'logging' => true, ], 'your-app-name' @@ -39,8 +42,8 @@ $config->initializeTracer(); $tracer = GlobalTracer::get(); -$span = $tracer->startSpan('TestSpan', []); -$span->finish(); +$scope = $tracer->startActiveSpan('TestSpan', []); +$scope->close(); $tracer->flush(); ``` diff --git a/composer.json b/composer.json index 83bfcb4..f950369 100644 --- a/composer.json +++ b/composer.json @@ -6,9 +6,12 @@ "require": { "php": ">=7.0", "packaged/thrift": "0.10.0", - "opentracing/opentracing" : "1.0.0-beta2", - "monolog/monolog": "^1.0", - "ext-gmp": "*" + "opentracing/opentracing" : "1.0.0-beta5", + "phlib/base_convert": "^1.0", + "psr/log": "1.0.2" + }, + "provide": { + "opentracing/opentracing": "1.0.0-beta5" }, "require-dev": { "phpunit/phpunit": "^6.4" diff --git a/src/Jaeger/Codec/B3Codec.php b/src/Jaeger/Codec/B3Codec.php deleted file mode 100644 index be2367d..0000000 --- a/src/Jaeger/Codec/B3Codec.php +++ /dev/null @@ -1,72 +0,0 @@ -getTraceId()); - $carrier[self::SPAN_ID_NAME] = Utils::dechex($spanContext->getSpanId()); - if ($spanContext->getParentId() != null) { - $carrier[self::PARENT_ID_NAME] = Utils::dechex($spanContext->getParentId()); - } - $carrier[self::FLAGS_NAME] = (int) $spanContext->getFlags(); - } - - /** - * @param array $carrier - * @return SpanContext|null - */ - public function extract($carrier) - { - $traceId = null; - $spanId = null; - $parentId = 0; - $flags = 0; - - if (isset($carrier[strtolower(self::SAMPLED_NAME)])) { - if ($carrier[strtolower(self::SAMPLED_NAME)] === "1" || strtolower($carrier[strtolower(self::SAMPLED_NAME)] === "true")) { - $flags = $flags | SAMPLED_FLAG; - } - } - - if (isset($carrier[strtolower(self::TRACE_ID_NAME)])) { - $traceId = Utils::hexdec($carrier[strtolower(self::TRACE_ID_NAME)]); - } - - if (isset($carrier[strtolower(self::PARENT_ID_NAME)])) { - $parentId = Utils::hexdec($carrier[strtolower(self::PARENT_ID_NAME)]); - } - - if (isset($carrier[strtolower(self::SPAN_ID_NAME)])) { - $spanId = Utils::hexdec($carrier[strtolower(self::SPAN_ID_NAME)]); - } - - if (isset($carrier[strtolower(self::FLAGS_NAME)])) { - if ($carrier[strtolower(self::FLAGS_NAME)] === "1") { - $flags = $flags | DEBUG_FLAG; - } - } - - if ($traceId !== null && $spanId !== null) { - return new SpanContext($traceId, $spanId, $parentId, $flags); - } - - return null; - } -} diff --git a/src/Jaeger/Codec/BinaryCodec.php b/src/Jaeger/Codec/BinaryCodec.php index fe362fb..05f61ca 100644 --- a/src/Jaeger/Codec/BinaryCodec.php +++ b/src/Jaeger/Codec/BinaryCodec.php @@ -12,7 +12,9 @@ public function inject(SpanContext $spanContext, &$carrier) throw new UnsupportedFormat('Binary encoding not implemented'); } - /** @return SpanContext|null */ + /** + * @return SpanContext|null + */ public function extract($carrier) { throw new UnsupportedFormat('Binary encoding not implemented'); diff --git a/src/Jaeger/Codec/CodecInterface.php b/src/Jaeger/Codec/CodecInterface.php index 1a02696..06cdbb9 100644 --- a/src/Jaeger/Codec/CodecInterface.php +++ b/src/Jaeger/Codec/CodecInterface.php @@ -8,6 +8,8 @@ interface CodecInterface { public function inject(SpanContext $spanContext, &$carrier); - /** @return SpanContext|null */ + /** + * @return SpanContext|null + */ public function extract($carrier); } \ No newline at end of file diff --git a/src/Jaeger/Codec/TextCodec.php b/src/Jaeger/Codec/TextCodec.php index f26b820..f04e5ed 100644 --- a/src/Jaeger/Codec/TextCodec.php +++ b/src/Jaeger/Codec/TextCodec.php @@ -3,10 +3,13 @@ namespace Jaeger\Codec; use Exception; -use const Jaeger\BAGGAGE_HEADER_PREFIX; -use const Jaeger\DEBUG_ID_HEADER_KEY; use Jaeger\SpanContext; + use const Jaeger\TRACE_ID_HEADER; +use const Jaeger\BAGGAGE_HEADER_PREFIX; +use const Jaeger\DEBUG_ID_HEADER_KEY; + +use function Phlib\base_convert; class TextCodec implements CodecInterface { @@ -16,8 +19,14 @@ class TextCodec implements CodecInterface private $debugIdHeader; private $prefixLength; + /** + * @param bool $urlEncoding + * @param string $traceIdHeader + * @param string $baggageHeaderPrefix + * @param string $debugIdHeader + */ public function __construct( - bool $urlEncoding = False, + bool $urlEncoding = false, string $traceIdHeader = TRACE_ID_HEADER, string $baggageHeaderPrefix = BAGGAGE_HEADER_PREFIX, string $debugIdHeader = DEBUG_ID_HEADER_KEY @@ -52,7 +61,9 @@ public function inject(SpanContext $spanContext, &$carrier) } } - /** @return SpanContext|null */ + /** + * @return SpanContext|null + */ public function extract($carrier) { $traceId = null; @@ -109,67 +120,30 @@ private function spanContextToString($traceId, $spanId, $parentId, $flags) return sprintf('%x:%x:%x:%x', $traceId, $spanId, $parentId, $flags); } + /** + * @return array + */ private function spanContextFromString($value): array { $parts = explode(':', $value); if (count($parts) != 4) { - throw new \Exception('Malformed tracer state string'); + throw new Exception('Malformed tracer state string'); } return [ - $this->baseConvert($parts[0]), - $this->baseConvert($parts[1]), - $this->baseConvert($parts[2]), + base_convert($parts[0], 16, 10), + base_convert($parts[1], 16, 10), + base_convert($parts[2], 16, 10), $parts[3], ]; } /** - * Converts any string of any base to any other base without PHP native - * method base_convert's double and float limitations. - * - * @url https://github.com/credomane/php_baseconvert - * - * @param string $numstring - * @param int $frombase - * @param int $tobase - * - * @return string + * @param string $haystack + * @param string $needle + * @return bool */ - private function baseConvert($numstring, $frombase = 16, $tobase = 10) - { - $chars = "0123456789abcdefghijklmnopqrstuvwxyz"; - $tostring = substr($chars, 0, $tobase); - $length = strlen($numstring); - $number = ''; - $result = ''; - - for ($i = 0; $i < $length; $i++) { - $number[$i] = strpos($chars, $numstring{$i}); - } - - do { - $divide = 0; - $newlen = 0; - - for ($i = 0; $i < $length; $i++) { - $divide = $divide * $frombase + $number[$i]; - - if ($divide >= $tobase) { - $number[$newlen++] = (int)($divide / $tobase); - $divide = $divide % $tobase; - } elseif ($newlen > 0) { - $number[$newlen++] = 0; - } - } - $length = $newlen; - $result = $tostring{$divide} . $result; - } while ($newlen != 0); - - return $result; - } - private function startsWith(string $haystack, string $needle): bool { return substr($haystack, 0, strlen($needle)) == $needle; diff --git a/src/Jaeger/Codec/Utils.php b/src/Jaeger/Codec/Utils.php deleted file mode 100644 index 3926351..0000000 --- a/src/Jaeger/Codec/Utils.php +++ /dev/null @@ -1,48 +0,0 @@ -getTraceId(); - $carrier['span_id'] = $spanContext->getSpanId(); - $carrier['parent_id'] = $spanContext->getParentId(); - $carrier['traceflags'] = $spanContext->getFlags(); + $carrier[self::TRACE_ID_NAME] = base_convert($spanContext->getTraceId(), 10, 16); + $carrier[self::SPAN_ID_NAME] = base_convert($spanContext->getSpanId(), 10, 16); + if ($spanContext->getParentId() != null) { + $carrier[self::PARENT_ID_NAME] = base_convert($spanContext->getParentId(), 10, 16); + } + $carrier[self::FLAGS_NAME] = (int) $spanContext->getFlags(); } - /** @return SpanContext|null */ + /** + * @param array $carrier + * @return SpanContext|null + */ public function extract($carrier) { - $traceId = $carrier['trace_id']; - $spanId = $carrier['span_id']; - $parentId = $carrier['parent_id']; - $flags = $carrier['traceflags']; + $traceId = "0"; + $spanId = "0"; + $parentId = "0"; + $flags = 0; + + if (isset($carrier[strtolower(self::SAMPLED_NAME)])) { + if ($carrier[strtolower(self::SAMPLED_NAME)] === "1" || strtolower($carrier[strtolower(self::SAMPLED_NAME)] === "true")) { + $flags = $flags | SAMPLED_FLAG; + } + } + + if (isset($carrier[strtolower(self::TRACE_ID_NAME)])) { + $traceId = base_convert($carrier[strtolower(self::TRACE_ID_NAME)], 16, 10); + } + + if (isset($carrier[strtolower(self::PARENT_ID_NAME)])) { + $parentId = base_convert($carrier[strtolower(self::PARENT_ID_NAME)], 16, 10); + } + + if (isset($carrier[strtolower(self::SPAN_ID_NAME)])) { + $spanId = base_convert($carrier[strtolower(self::SPAN_ID_NAME)], 16, 10); + } + + if (isset($carrier[strtolower(self::FLAGS_NAME)])) { + if ($carrier[strtolower(self::FLAGS_NAME)] === "1") { + $flags = $flags | DEBUG_FLAG; + } + } - if ($traceId === null) { - return null; + if ($traceId !== "0" && $spanId !== "0") { + return new SpanContext($traceId, $spanId, $parentId, $flags); } - return new SpanContext($traceId, $spanId, $parentId, $flags); + return null; } -} \ No newline at end of file +} diff --git a/src/Jaeger/Config.php b/src/Jaeger/Config.php index 609e817..99bdbfb 100644 --- a/src/Jaeger/Config.php +++ b/src/Jaeger/Config.php @@ -11,35 +11,48 @@ use Jaeger\Sampler\ProbabilisticSampler; use Jaeger\Sampler\SamplerInterface; use Psr\Log\LoggerInterface; -use OpenTracing\GlobalTracer; use Psr\Log\NullLogger; +use OpenTracing\GlobalTracer; +use Thrift\Exception\TTransportException; +use Thrift\Transport\TTransport; +use Thrift\Protocol\TCompactProtocol; +use Thrift\Transport\TBufferedTransport; +use Jaeger\ThriftGen\AgentClient; +use Jaeger\Sender\UdpSender; class Config { - /** @var array */ + /** + * @var array + */ private $config; - /** @var string */ + /** + * @var string + */ private $serviceName; private $errorReporter; - /** @var bool */ + /** + * @var bool + */ private $initialized = false; - /** @var LoggerInterface */ + /** + * @var LoggerInterface + */ private $logger; /** * Config constructor. - * - * @param $config + * @param array $config * @param string|null $serviceName * @param LoggerInterface|null $logger * @throws Exception */ public function __construct( - $config, + array $config, string $serviceName = null, LoggerInterface $logger = null // $metricsFactory = null @@ -77,7 +90,6 @@ public function initializeTracer() if ($sampler === null) { $sampler = new ConstSampler(true); } - $this->logger->info('Using sampler ' . $sampler); $reporter = new RemoteReporter( $channel, @@ -118,13 +130,7 @@ public function createTracer(ReporterInterface $reporter, SamplerInterface $samp private function initializeGlobalTracer(Tracer $tracer) { GlobalTracer::set($tracer); - $this->logger->info( - sprintf( - 'OpenTracing\GlobalTracer initialized to %s[app_name=%s]', - $tracer, - $this->serviceName - ) - ); + $this->logger->info('OpenTracing\GlobalTracer initialized to ' . $tracer->getServiceName()); } /** @@ -171,22 +177,40 @@ private function getBatchSize(): int } /** - * @return LocalAgentSender + * @return UdpSender */ - private function getLocalAgentSender(): LocalAgentSender + private function getLocalAgentSender(): UdpSender { - $this->logger->info('Initializing Jaeger Tracer with UDP reporter'); - return new LocalAgentSender( + $udp = new ThriftUdpTransport( $this->getLocalAgentReportingHost(), $this->getLocalAgentReportingPort(), + $this->logger + ); + + $transport = new TBufferedTransport($udp, 4096, 4096); + try { + $transport->open(); + } catch (TTransportException $e) { + $this->logger->warning($e->getMessage()); + } + + $protocol = new TCompactProtocol($transport); + $client = new AgentClient($protocol); + + $this->logger->info('Initializing Jaeger Tracer with UDP reporter'); + return new UdpSender( + $client, $this->getBatchSize(), $this->logger ); } - private function getLocalAgentGroup() + /** + * @return array + */ + private function getLocalAgentGroup(): array { - return $this->config['local_agent'] ?? null; + return $this->config['local_agent'] ?? []; } /** diff --git a/src/Jaeger/Reporter/CompositeReporter.php b/src/Jaeger/Reporter/CompositeReporter.php index a3d994c..ae831fa 100644 --- a/src/Jaeger/Reporter/CompositeReporter.php +++ b/src/Jaeger/Reporter/CompositeReporter.php @@ -9,14 +9,23 @@ */ class CompositeReporter implements ReporterInterface { - /** @var ReporterInterface[] */ + /** + * @var ReporterInterface[] + */ private $reporters; + /** + * CompositeReporter constructor. + * @param ReporterInterface ...$reporters + */ public function __construct(ReporterInterface ...$reporters) { $this->reporters = $reporters; } + /** + * @param Span $span + */ public function reportSpan(Span $span) { foreach ($this->reporters as $reporter) { @@ -30,4 +39,4 @@ public function close() $reporter->close(); } } -} \ No newline at end of file +} diff --git a/src/Jaeger/Reporter/InMemoryReporter.php b/src/Jaeger/Reporter/InMemoryReporter.php index dd16e0f..e04f4a0 100644 --- a/src/Jaeger/Reporter/InMemoryReporter.php +++ b/src/Jaeger/Reporter/InMemoryReporter.php @@ -9,13 +9,22 @@ */ class InMemoryReporter implements ReporterInterface { + /** + * @var array + */ private $spans = []; + /** + * @param Span $span + */ public function reportSpan(Span $span) { $this->spans[] = $span; } + /** + * @return array + */ public function getSpans(): array { return $this->spans; @@ -24,4 +33,4 @@ public function getSpans(): array public function close() { } -} \ No newline at end of file +} diff --git a/src/Jaeger/Reporter/LoggingReporter.php b/src/Jaeger/Reporter/LoggingReporter.php index 9c9efee..108587d 100644 --- a/src/Jaeger/Reporter/LoggingReporter.php +++ b/src/Jaeger/Reporter/LoggingReporter.php @@ -3,27 +3,37 @@ namespace Jaeger\Reporter; use Jaeger\Span; -use Monolog\Logger; use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; /** * LoggingReporter logs all spans. */ class LoggingReporter implements ReporterInterface { + /** + * @var LoggerInterface + */ private $logger; + /** + * LoggingReporter constructor. + * @param LoggerInterface|null $logger + */ public function __construct(LoggerInterface $logger = null) { - $this->logger = $logger ?? new Logger('jaeger_tracing'); + $this->logger = $logger ?? new NullLogger(); } + /** + * @param Span $span + */ public function reportSpan(Span $span) { - $this->logger->info('Reporting span ' . $span); + $this->logger->info('Reporting span ' . $span->getOperationName()); } public function close() { } -} \ No newline at end of file +} diff --git a/src/Jaeger/Reporter/NullReporter.php b/src/Jaeger/Reporter/NullReporter.php index bb8781c..7dc9f8d 100644 --- a/src/Jaeger/Reporter/NullReporter.php +++ b/src/Jaeger/Reporter/NullReporter.php @@ -9,6 +9,9 @@ */ class NullReporter implements ReporterInterface { + /** + * @param Span $span + */ public function reportSpan(Span $span) { } @@ -16,4 +19,4 @@ public function reportSpan(Span $span) public function close() { } -} \ No newline at end of file +} diff --git a/src/Jaeger/Reporter/RemoteReporter.php b/src/Jaeger/Reporter/RemoteReporter.php index 35cb14f..6ca9946 100644 --- a/src/Jaeger/Reporter/RemoteReporter.php +++ b/src/Jaeger/Reporter/RemoteReporter.php @@ -2,27 +2,42 @@ namespace Jaeger\Reporter; -use Jaeger\LocalAgentSender; +use Jaeger\Sender\UdpSender; use Jaeger\Span; -use Monolog\Logger; use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; class RemoteReporter implements ReporterInterface { - /** @var LocalAgentSender */ + /** + * @var UdpSender + */ private $transport; - /** @var LoggerInterface */ + /** + * @var LoggerInterface + */ private $logger; - /** @var string */ + /** + * @var string + */ private $serviceName; - /** @var int */ + /** + * @var int + */ private $batchSize; + /** + * RemoteReporter constructor. + * @param UdpSender $transport + * @param string $serviceName + * @param int $batchSize + * @param LoggerInterface|null $logger + */ public function __construct( - $transport, + UdpSender $transport, string $serviceName, int $batchSize = 10, LoggerInterface $logger = null @@ -31,9 +46,12 @@ public function __construct( $this->transport = $transport; $this->serviceName = $serviceName; $this->batchSize = $batchSize; - $this->logger = $logger ?? new Logger('jaeger_tracing'); + $this->logger = $logger ?? new NullLogger(); } + /** + * @param Span $span + */ public function reportSpan(Span $span) { $this->transport->append($span); @@ -44,4 +62,4 @@ public function close() $this->transport->flush(); $this->transport->close(); } -} \ No newline at end of file +} diff --git a/src/Jaeger/Reporter/ReporterInterface.php b/src/Jaeger/Reporter/ReporterInterface.php index c961afd..0aa66de 100644 --- a/src/Jaeger/Reporter/ReporterInterface.php +++ b/src/Jaeger/Reporter/ReporterInterface.php @@ -6,6 +6,11 @@ interface ReporterInterface { + /** + * @param Span $span + * @return mixed + */ public function reportSpan(Span $span); + public function close(); } diff --git a/src/Jaeger/Sampler/ConstSampler.php b/src/Jaeger/Sampler/ConstSampler.php index 4e36a32..89d9eb3 100644 --- a/src/Jaeger/Sampler/ConstSampler.php +++ b/src/Jaeger/Sampler/ConstSampler.php @@ -10,10 +10,21 @@ */ class ConstSampler implements SamplerInterface { + /** + * @var bool + */ private $decision; + + /** + * @var array + */ private $tags = []; - public function __construct($decision = true) + /** + * ConstSampler constructor. + * @param bool $decision + */ + public function __construct(bool $decision = true) { $this->tags = [ SAMPLER_TYPE_TAG_KEY => SAMPLER_TYPE_CONST, @@ -22,7 +33,12 @@ public function __construct($decision = true) $this->decision = $decision; } - public function isSampled($traceId, $operation = '') + /** + * @param string $traceId + * @param string $operation + * @return array + */ + public function isSampled(string $traceId, string $operation = ''): array { return array($this->decision, $this->tags); } @@ -30,9 +46,4 @@ public function isSampled($traceId, $operation = '') public function close() { } - - public function __toString(): string - { - return sprintf('ConstSampler(%s)', $this->decision ? 'True' : 'False'); - } } diff --git a/src/Jaeger/Sampler/ProbabilisticSampler.php b/src/Jaeger/Sampler/ProbabilisticSampler.php index a695a36..1b0697f 100644 --- a/src/Jaeger/Sampler/ProbabilisticSampler.php +++ b/src/Jaeger/Sampler/ProbabilisticSampler.php @@ -13,11 +13,26 @@ */ class ProbabilisticSampler implements SamplerInterface { - /** @var float */ + /** + * @var float + */ private $rate; + + /** + * @var array + */ private $tags = []; + + /** + * @var float|int + */ private $boundary; + /** + * ProbabilisticSampler constructor. + * @param float $rate + * @throws Exception + */ public function __construct(float $rate) { $this->tags = [ @@ -33,7 +48,12 @@ public function __construct(float $rate) $this->boundary = $rate * PHP_INT_MAX; } - public function isSampled($traceId, $operation = '') + /** + * @param string $traceId + * @param string $operation + * @return array + */ + public function isSampled(string $traceId, string $operation = ''): array { return [($traceId < $this->boundary), $this->tags]; } @@ -41,9 +61,4 @@ public function isSampled($traceId, $operation = '') public function close() { } - - public function __toString(): string - { - return "ProbabilisticSampler($this->rate)"; - } -} \ No newline at end of file +} diff --git a/src/Jaeger/Sampler/SamplerInterface.php b/src/Jaeger/Sampler/SamplerInterface.php index d23b222..8308da7 100644 --- a/src/Jaeger/Sampler/SamplerInterface.php +++ b/src/Jaeger/Sampler/SamplerInterface.php @@ -4,7 +4,6 @@ interface SamplerInterface { - public function isSampled($traceId, $operation); + public function isSampled(string $traceId, string $operation); public function close(); - public function __toString(): string; -} \ No newline at end of file +} diff --git a/src/Jaeger/Scope.php b/src/Jaeger/Scope.php new file mode 100644 index 0000000..57d7163 --- /dev/null +++ b/src/Jaeger/Scope.php @@ -0,0 +1,71 @@ +scopeManager = $scopeManager; + $this->wrapped = $wrapped; + $this->finishSpanOnClose = $finishSpanOnClose; + $this->toRestore = $scopeManager->getActive(); + } + + /** + * {@inheritdoc} + */ + public function close() + { + if ($this->scopeManager->getActive() !== $this) { + // This shouldn't happen if users call methods in expected order + return; + } + + if ($this->finishSpanOnClose) { + $this->wrapped->finish(); + } + + $this->scopeManager->setActive($this->toRestore); + } + + /** + * {@inheritdoc} + */ + public function getSpan() + { + return $this->wrapped; + } +} diff --git a/src/Jaeger/ScopeManager.php b/src/Jaeger/ScopeManager.php new file mode 100644 index 0000000..956c39b --- /dev/null +++ b/src/Jaeger/ScopeManager.php @@ -0,0 +1,45 @@ +active = new Scope($this, $span, $finishSpanOnClose); + + return $this->active; + } + + /** + * {@inheritdoc} + */ + public function getActive() + { + return $this->active; + } + + /** + * Sets the scope as active. + * @param OTScope|null $scope + */ + public function setActive(OTScope $scope = null) + { + $this->active = $scope; + } +} diff --git a/src/Jaeger/LocalAgentSender.php b/src/Jaeger/Sender/UdpSender.php similarity index 75% rename from src/Jaeger/LocalAgentSender.php rename to src/Jaeger/Sender/UdpSender.php index f98a5d3..c5c267a 100644 --- a/src/Jaeger/LocalAgentSender.php +++ b/src/Jaeger/Sender/UdpSender.php @@ -1,69 +1,69 @@ host = $host; - $this->port = $port; + /** + * @var LoggerInterface + */ + private $logger; + + /** + * UdpSender constructor. + * @param AgentClient $client + * @param int $batchSize + * @param LoggerInterface $logger + */ + public function __construct( + AgentClient $client, + int $batchSize = 10, + LoggerInterface $logger = null + ) { + $this->client = $client; $this->batchSize = $batchSize; $this->logger = $logger ?? new NullLogger(); - - $udp = new TUDPTransport($this->host, $this->port, $this->logger); - - $transport = new TBufferedTransport($udp, 4096, 4096); - try { - $transport->open(); - } catch (TTransportException $e) { - $this->logger->warning($e->getMessage()); - } - - $protocol = new TCompactProtocol($transport); - - // Create client - $this->client = new AgentClient($protocol); } /** - * @param \Jaeger\Span $span + * @param JaegerSpan $span * * @return int the number of flushed spans */ - public function append(\Jaeger\Span $span): int + public function append(JaegerSpan $span): int { $this->spans[] = $span; @@ -74,7 +74,9 @@ public function append(\Jaeger\Span $span): int return 0; } - /** @return int the number of flushed spans */ + /** + * @return int the number of flushed spans + */ public function flush(): int { $count = count($this->spans); @@ -105,16 +107,16 @@ private function send(array $spans) } /** - * @param \Jaeger\Span[] $spans - * @return \Jaeger\ThriftGen\Span[] + * @param JaegerSpan[] $spans + * @return ThriftSpan[] */ private function makeZipkinBatch(array $spans): array { - /** @var \Jaeger\ThriftGen\Span[] */ + /** @var ThriftSpan[] */ $zipkinSpans = []; foreach ($spans as $span) { - /** @var \Jaeger\Span $span */ + /** @var JaegerSpan $span */ $endpoint = $this->makeEndpoint( $span->getTracer()->getIpAddress(), @@ -131,7 +133,7 @@ private function makeZipkinBatch(array $spans): array $this->addZipkinAnnotations($span, $endpoint); - $zipkinSpan = new Span([ + $zipkinSpan = new ThriftSpan([ 'name' => $span->getOperationName(), 'id' => $span->getContext()->getSpanId(), 'parent_id' => $span->getContext()->getParentId() ?? null, @@ -149,7 +151,7 @@ private function makeZipkinBatch(array $spans): array return $zipkinSpans; } - private function addZipkinAnnotations(\Jaeger\Span $span, $endpoint) + private function addZipkinAnnotations(JaegerSpan $span, Endpoint $endpoint) { if ($span->isRpc()) { $isClient = $span->isRpcClient(); @@ -175,7 +177,7 @@ private function addZipkinAnnotations(\Jaeger\Span $span, $endpoint) } } - private function makeLocalComponentTag(string $componentName, $endpoint): BinaryAnnotation + private function makeLocalComponentTag(string $componentName, Endpoint $endpoint): BinaryAnnotation { return new BinaryAnnotation([ 'key' => "lc", @@ -196,7 +198,7 @@ private function makeEndpoint(string $ipv4, int $port, string $serviceName): End ]); } - private function ipv4ToInt($ipv4): int + private function ipv4ToInt(string $ipv4): int { if ($ipv4 == 'localhost') { $ipv4 = '127.0.0.1'; @@ -209,13 +211,13 @@ private function ipv4ToInt($ipv4): int // Used for Zipkin binary annotations like CA/SA (client/server address). // They are modeled as Boolean type with '0x01' as the value. - private function makePeerAddressTag($key, $host) + private function makePeerAddressTag(string $key, Endpoint $host): BinaryAnnotation { return new BinaryAnnotation([ "key" => $key, "value" => '0x01', "annotation_type" => AnnotationType::BOOL, - "host" => $host + "host" => $host, ]); } } diff --git a/src/Jaeger/Span.php b/src/Jaeger/Span.php index f486cb6..3da95f2 100644 --- a/src/Jaeger/Span.php +++ b/src/Jaeger/Span.php @@ -4,30 +4,43 @@ use Jaeger\ThriftGen\AnnotationType; use Jaeger\ThriftGen\BinaryAnnotation; -use OpenTracing; -use const OpenTracing\Ext\Tags\COMPONENT; -use const OpenTracing\Ext\Tags\PEER_HOST_IPV4; -use const OpenTracing\Ext\Tags\PEER_PORT; -use const OpenTracing\Ext\Tags\PEER_SERVICE; -use const OpenTracing\Ext\Tags\SPAN_KIND; -use const OpenTracing\Ext\Tags\SPAN_KIND_RPC_CLIENT; -use const OpenTracing\Ext\Tags\SPAN_KIND_RPC_SERVER; - -class Span implements OpenTracing\Span +use OpenTracing\Span as OTSpan; +use DateTime; +use DateTimeInterface; + +use const OpenTracing\Tags\COMPONENT; +use const OpenTracing\Tags\PEER_HOST_IPV4; +use const OpenTracing\Tags\PEER_PORT; +use const OpenTracing\Tags\PEER_SERVICE; +use const OpenTracing\Tags\SPAN_KIND; +use const OpenTracing\Tags\SPAN_KIND_RPC_CLIENT; +use const OpenTracing\Tags\SPAN_KIND_RPC_SERVER; + +class Span implements OTSpan { - /** @var Tracer */ + /** + * @var Tracer + */ private $tracer; - /** @var SpanContext */ + /** + * @var SpanContext + */ private $context; - /** @var string */ + /** + * @var string + */ private $operationName; - /** @var float */ + /** + * @var int|float|DateTime|null + */ private $startTime; - /** @var float */ + /** + * @var int|float|DateTime|null + */ private $endTime; /** @@ -36,25 +49,39 @@ class Span implements OpenTracing\Span */ private $kind; - /** @var array|null */ + /** + * @var array|null + */ public $peer; private $component; private $logs; - /** @var BinaryAnnotation[] */ + /** + * @var BinaryAnnotation[] + */ public $tags = []; - /** @var bool */ + /** + * @var bool + */ private $debug = false; + /** + * Span constructor. + * @param SpanContext $context + * @param Tracer $tracer + * @param string $operationName + * @param array $tags + * @param int|float|DateTime|null $startTime + */ public function __construct( SpanContext $context, Tracer $tracer, string $operationName, array $tags = [], - float $startTime = null + $startTime = null ) { $this->context = $context; @@ -73,34 +100,49 @@ public function __construct( } } + /** + * @return Tracer + */ public function getTracer(): Tracer { return $this->tracer; } + /** + * @return bool + */ public function isDebug(): bool { return $this->debug; } - /** @return float|null */ + /** + * @return DateTime|float|int|null + */ public function getStartTime() { return $this->startTime; } - /** @return float|null */ + /** + * @return DateTime|float|int|null + */ public function getEndTime() { return $this->endTime; } + /** + * @return string + */ public function getOperationName(): string { return $this->operationName; } - /** @return mixed */ + /** + * @return mixed + */ public function getComponent() { // TODO @@ -108,29 +150,15 @@ public function getComponent() } /** - * Yields the SpanContext for this Span. Note that the return value of - * Span::getContext() is still valid after a call to Span::finish(), as is - * a call to Span::getContext() after a call to Span::finish(). - * - * @return OpenTracing\SpanContext + * {@inheritdoc} */ - public function getContext(): SpanContext + public function getContext() { return $this->context; } /** - * Sets the end timestamp and finalizes Span state. - * - * With the exception of calls to Context() (which are always allowed), - * finish() must be the last call made to any span instance, and to do - * otherwise leads to undefined behavior - * - * If the span is already finished, a warning should be logged. - * - * @param float|int|\DateTimeInterface|null $finishTime if passing float or int - * it should represent the timestamp (including as many decimal places as you need) - * @param array $logRecords + * {@inheritdoc} */ public function finish($finishTime = null, array $logRecords = []) { @@ -146,15 +174,21 @@ public function finish($finishTime = null, array $logRecords = []) $this->tracer->reportSpan($this); } + /** + * @return bool + */ public function isSampled(): bool { - return $this->getContext()->getFlags() & SAMPLED_FLAG == SAMPLED_FLAG; + $context = $this->getContext(); + if ($context instanceof SpanContext) { + return $context->getFlags() & SAMPLED_FLAG == SAMPLED_FLAG; + } + + return false; } /** - * If the span is already finished, a warning should be logged. - * - * @param string $newOperationName + * {@inheritdoc} */ public function overwriteOperationName($newOperationName) { @@ -163,23 +197,19 @@ public function overwriteOperationName($newOperationName) } /** - * Sets tags to the Span in key:value format, key must be a string and tag must be either - * a string, a boolean value, or a numeric type. - * - * As an implementor, consider using "standard tags" listed in {@see \OpenTracing\Ext\Tags} - * - * If the span is already finished, a warning should be logged. - * - * @param array $tags + * {@inheritdoc} */ - public function setTags(array $tags) + public function setTags($tags) { foreach ($tags as $key => $value) { $this->setTag($key, $value); } } - public function setTag($key, $value): Span + /** + * {@inheritdoc} + */ + public function setTag($key, $value) { // if ($key == SAMPLING_PRIORITY) { // } @@ -209,12 +239,18 @@ public function setTag($key, $value): Span COMPONENT => 'setComponent', ]; + /** + * @return bool + */ private function setComponent($value): bool { $this->component = $value; return true; } + /** + * @return bool + */ private function setSpanKind($value): bool { if ($value === null || $value === SPAN_KIND_RPC_CLIENT || $value === SPAN_KIND_RPC_SERVER) { @@ -224,6 +260,9 @@ private function setSpanKind($value): bool return false; } + /** + * @return bool + */ private function setPeerPort($value): bool { if ($this->peer === null) { @@ -234,6 +273,9 @@ private function setPeerPort($value): bool return true; } + /** + * @return bool + */ private function setPeerHostIpv4($value): bool { if ($this->peer === null) { @@ -244,6 +286,9 @@ private function setPeerHostIpv4($value): bool return true; } + /** + * @return bool + */ private function setPeerService($value): bool { if ($this->peer === null) { @@ -254,23 +299,24 @@ private function setPeerService($value): bool return true; } + /** + * @return bool + */ public function isRpc(): bool { return $this->kind == SPAN_KIND_RPC_CLIENT || $this->kind == SPAN_KIND_RPC_SERVER; } + /** + * @return bool + */ public function isRpcClient(): bool { return $this->kind == SPAN_KIND_RPC_CLIENT; } /** - * Adds a log record to the span - * - * If the span is already finished, a warning should be logged. - * - * @param array $fields - * @param int|float|\DateTimeInterface $timestamp + * {@inheritdoc} */ public function log(array $fields = [], $timestamp = null) { @@ -278,48 +324,42 @@ public function log(array $fields = [], $timestamp = null) } /** - * Adds a baggage item to the SpanContext which is immutable so it is required to use SpanContext::withBaggageItem - * to get a new one. - * - * If the span is already finished, a warning should be logged. - * - * @param string $key - * @param string $value + * {@inheritdoc} */ public function addBaggageItem($key, $value) { - // TODO: Implement addBaggageItem() method. + $this->context = $this->context->withBaggageItem($key, $value); } /** - * @param string $key - * @return string + * {@inheritdoc} */ public function getBaggageItem($key) { - // TODO: Implement getBaggageItem() method. - } - - public function __toString(): string - { - return sprintf( - 'Span(operationName=%s startTime=%s endTime=%s)', - $this->operationName, - $this->startTime, - $this->endTime - ); + return $this->context->getBaggageItem($key); } + /** + * @return array + */ public function getTags(): array { return $this->tags; } + /** + * @return int + */ private function timestampMicro(): int { return round(microtime(true) * 1000000); } + /** + * @param string $key + * @param string $value + * @return BinaryAnnotation + */ private function makeStringTag(string $key, string $value): BinaryAnnotation { if (strlen($value) > 256) { diff --git a/src/Jaeger/SpanContext.php b/src/Jaeger/SpanContext.php index 4847e15..0ec9b23 100644 --- a/src/Jaeger/SpanContext.php +++ b/src/Jaeger/SpanContext.php @@ -3,17 +3,33 @@ namespace Jaeger; use ArrayIterator; -use OpenTracing; +use OpenTracing\SpanContext as OTSpanContext; -class SpanContext implements OpenTracing\SpanContext +class SpanContext implements OTSpanContext { private $traceId; + private $spanId; + private $parentId; + private $flags; + + /** + * @var array + */ private $baggage; + private $debugId; + /** + * SpanContext constructor. + * @param string $traceId + * @param string $spanId + * @param string $parentId + * @param $flags + * @param array $baggage + */ public function __construct($traceId, $spanId, $parentId, $flags, $baggage = []) { $this->traceId = $traceId; @@ -24,6 +40,12 @@ public function __construct($traceId, $spanId, $parentId, $flags, $baggage = []) $this->debugId = null; } + /** + * TODO + * @deprecated + * @param $debugId + * @return SpanContext + */ public static function withDebugId($debugId) { $ctx = new SpanContext(null, null, null, null); @@ -32,54 +54,40 @@ public static function withDebugId($debugId) return $ctx; } + /** + * {@inheritdoc} + */ public function getIterator() { return new ArrayIterator($this->baggage); } /** - * @param string $key - * @return string + * {@inheritdoc} */ - public function getBaggageItem($key): string + public function getBaggageItem($key) { - return $this->baggage[$key]; + return array_key_exists($key, $this->baggage) ? $this->baggage[$key] : null; } /** - * Creates a new SpanContext out of the existing one and the new key:value pair. - * - * @param string $key - * @param string $value - * @return \OpenTracing\SpanContext + * {@inheritdoc} */ public function withBaggageItem($key, $value) { - $baggage = $this->baggage; - $baggage[$key] = $value; - - return new SpanContext( - $this->traceId, - $this->spanId, - $this->parentId, - $this->flags, - $baggage - ); + return new self($this->traceId, $this->spanId, $this->parentId, $this->flags, [$key => $value] + $this->baggage); } - /** @return int */ public function getTraceId() { return $this->traceId; } - /** @return int|null */ public function getParentId() { return $this->parentId; } - /** @return int */ public function getSpanId() { return $this->spanId; @@ -104,4 +112,4 @@ public function isDebugIdContainerOnly(): bool { return ($this->traceId === null) && ($this->debugId !== null); } -} \ No newline at end of file +} diff --git a/src/Jaeger/TUDPTransport.php b/src/Jaeger/ThriftUdpTransport.php similarity index 82% rename from src/Jaeger/TUDPTransport.php rename to src/Jaeger/ThriftUdpTransport.php index 5c54d69..d680042 100644 --- a/src/Jaeger/TUDPTransport.php +++ b/src/Jaeger/ThriftUdpTransport.php @@ -7,20 +7,32 @@ use Thrift\Exception\TTransportException; use Thrift\Transport\TTransport; -class TUDPTransport extends TTransport +class ThriftUdpTransport extends TTransport { private $socket; - /** @var string */ + /** + * @var string + */ private $host; - /** @var int */ + /** + * @var int + */ private $port; - /** @var LoggerInterface */ + /** + * @var LoggerInterface + */ private $logger; - public function __construct($host, $port, LoggerInterface $logger = null) + /** + * ThriftUdpTransport constructor. + * @param string $host + * @param int $port + * @param LoggerInterface $logger + */ + public function __construct(string $host, int $port, LoggerInterface $logger = null) { $this->host = $host; $this->port = $port; diff --git a/src/Jaeger/Tracer.php b/src/Jaeger/Tracer.php index f12b261..6a7c49e 100644 --- a/src/Jaeger/Tracer.php +++ b/src/Jaeger/Tracer.php @@ -2,35 +2,43 @@ namespace Jaeger; +use Exception; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; use Jaeger\Codec\BinaryCodec; use Jaeger\Codec\CodecInterface; use Jaeger\Codec\TextCodec; use Jaeger\Codec\ZipkinCodec; use Jaeger\Reporter\ReporterInterface; use Jaeger\Sampler\SamplerInterface; -use Monolog\Logger; -use OpenTracing\Exceptions\InvalidSpanOption; -use OpenTracing\Exceptions\SpanContextNotFound; +use OpenTracing\Tracer as OTTracer; +use OpenTracing\SpanContext as OTSpanContext; +use OpenTracing\Reference; +use OpenTracing\StartSpanOptions; +use InvalidArgumentException; use OpenTracing\Exceptions\UnsupportedFormat; -use OpenTracing; -use const OpenTracing\Ext\Tags\SPAN_KIND; -use const OpenTracing\Ext\Tags\SPAN_KIND_RPC_SERVER; + use const OpenTracing\Formats\BINARY; use const OpenTracing\Formats\HTTP_HEADERS; use const OpenTracing\Formats\TEXT_MAP; -use OpenTracing\Propagation\Reader; -use OpenTracing\Propagation\Writer; -use Psr\Log\LoggerInterface; +use const OpenTracing\Tags\SPAN_KIND; +use const OpenTracing\Tags\SPAN_KIND_RPC_SERVER; -class Tracer implements OpenTracing\Tracer +class Tracer implements OTTracer { - /** @var string */ + /** + * @var string + */ private $serviceName; - /** @var ReporterInterface */ + /** + * @var ReporterInterface + */ private $reporter; - /** @var SamplerInterface */ + /** + * @var SamplerInterface + */ private $sampler; private $ipAddress; @@ -39,39 +47,68 @@ class Tracer implements OpenTracing\Tracer private $metrics; - /** @var string */ + /** + * @var string + */ private $debugIdHeader; - /** @var CodecInterface[] */ + /** + * @var CodecInterface[] + */ private $codecs; - /** @var LoggerInterface */ + /** + * @var LoggerInterface + */ private $logger; - /** @var bool */ + /** + * @var bool + */ private $oneSpanPerRpc; private $tags; + /** + * @var ScopeManager + */ + private $scopeManager; + + /** + * Tracer constructor. + * @param string $serviceName + * @param ReporterInterface $reporter + * @param SamplerInterface $sampler + * @param bool $oneSpanPerRpc + * @param LoggerInterface|null $logger + * @param ScopeManager|null $scopeManager + * @param string $traceIdHeader + * @param string $baggageHeaderPrefix + * @param string $debugIdHeader + * @param array|null $tags + */ public function __construct( - string $serviceName, + $serviceName, ReporterInterface $reporter, SamplerInterface $sampler, - bool $oneSpanPerRpc = True, + $oneSpanPerRpc = True, LoggerInterface $logger = null, - string $traceIdHeader = TRACE_ID_HEADER, - string $baggageHeaderPrefix = BAGGAGE_HEADER_PREFIX, - string $debugIdHeader = DEBUG_ID_HEADER_KEY, - array $tags = null + ScopeManager $scopeManager = null, + $traceIdHeader = TRACE_ID_HEADER, + $baggageHeaderPrefix = BAGGAGE_HEADER_PREFIX, + $debugIdHeader = DEBUG_ID_HEADER_KEY, + $tags = null ) { $this->serviceName = $serviceName; $this->reporter = $reporter; $this->sampler = $sampler; $this->oneSpanPerRpc = $oneSpanPerRpc; - $this->logger = $logger ?? new Logger('jaeger_tracing'); - $this->ipAddress = getHostByName(getHostName()); + $this->logger = $logger ?? new NullLogger(); + $this->scopeManager = $scopeManager ?? new ScopeManager(); + + $this->ipAddress = gethostbyname(gethostname()); $this->debugIdHeader = $debugIdHeader; @@ -107,57 +144,38 @@ public function __construct( } } - public function setSampler(SamplerInterface $sampler) - { - $this->sampler = $sampler; - - return $this; - } - - public function getServiceName(): string - { - return $this->serviceName; - } - - public function getIpAddress() - { - return $this->ipAddress; - } - /** - * @param string $operationName - * @param array|OpenTracing\SpanOptions $options - * @return Span - * @throws InvalidSpanOption for invalid option + * {@inheritdoc} */ public function startSpan($operationName, $options = []) { - $parent = $options['child_of'] ?? null; - $tags = $options['tags'] ?? null; - $startTime = $options['startTime'] ?? null; - -// if ($options['references']) { -// if (is_array($options['references'])) { -// $references = $options['references'][0]; -// } -// $parent = $references->referenced_context; -// } - - if ($parent instanceof Span) { - /** @var SpanContext $parent */ - $parent = $parent->getContext(); + if (!($options instanceof StartSpanOptions)) { + $options = StartSpanOptions::create($options); } - $rpcServer = ($tags !== null) && - ($tags[SPAN_KIND] ?? null) == SPAN_KIND_RPC_SERVER; + // TODO abstract into private method + /** @var SpanContext $parent */ + $parent = (function ($references) { + foreach ($references as $reference) { + /** @var Reference $reference */ + if ($reference->isType(Reference::CHILD_OF)) { + return $reference->getContext(); + } + } + return null; + })($options->getReferences()); + + $tags = $options->getTags(); + + $rpcServer = ($tags[SPAN_KIND] ?? null) == SPAN_KIND_RPC_SERVER; - if ($parent === null || $parent->isDebugIdContainerOnly()) { + if ($parent == null || $parent->isDebugIdContainerOnly()) { $traceId = $this->randomId(); $spanId = $traceId; $parentId = null; $flags = 0; $baggage = null; - if ($parent === null) { + if ($parent == null) { list($sampled, $samplerTags) = $this->sampler->isSampled($traceId, $operationName); if ($sampled) { $flags = SAMPLED_FLAG; @@ -199,7 +217,7 @@ public function startSpan($operationName, $options = []) $this, $operationName, $tags ?? [], - $startTime + $options->getStartTime() ); if (($rpcServer || $parentId === null) && ($flags & SAMPLED_FLAG)) { @@ -211,56 +229,42 @@ public function startSpan($operationName, $options = []) } /** - * @param OpenTracing\SpanContext $spanContext - * @param int $format - * @param Writer $carrier - * @throws UnsupportedFormat when the format is not recognized by the tracer - * implementation + * {@inheritdoc} + * @throws InvalidArgumentException */ - public function inject(OpenTracing\SpanContext $spanContext, $format, &$carrier) + public function inject(OTSpanContext $spanContext, $format, &$carrier) { - $codec = $this->codecs[$format] ?? null; - if ($codec === null) { - throw new UnsupportedFormat($format); + if ($spanContext instanceof SpanContext) { + $codec = $this->codecs[$format] ?? null; + if ($codec == null) { + throw new UnsupportedFormat('Unsupported format: %s', $format); + } + + $codec->inject($spanContext, $carrier); + return; } - return $codec->inject($spanContext, $carrier); + throw new InvalidArgumentException(sprintf( + 'Invalid span context. Expected Jaeger\SpanContext, got %s.', + is_object($spanContext) ? get_class($spanContext) : gettype($spanContext) + )); } /** - * @param int $format - * @param Reader $carrier - * @return OpenTracing\SpanContext - * @throws SpanContextNotFound when a context could not be extracted from Reader - * @throws UnsupportedFormat when the format is not recognized by the tracer - * implementation + * {@inheritdoc} */ public function extract($format, $carrier) { $codec = $this->codecs[$format] ?? null; - if ($codec === null) { - throw new UnsupportedFormat($format); - } - - $context = $codec->extract($carrier); - if ($context === null) { - throw new SpanContextNotFound('Failed to find span context'); + if ($codec == null) { + throw new UnsupportedFormat('Unsupported format: ' . $format); } - return $context; + return $codec->extract($carrier); } /** - * Allow tracer to send span data to be instrumented. - * - * This method might not be needed depending on the tracing implementation - * but one should make sure this method is called after the request is finished. - * As an implementor, a good idea would be to use an asynchronous message bus - * or use the call to fastcgi_finish_request in order to not to delay the end - * of the request to the client. - * - * @see fastcgi_finish_request() - * @see https://www.google.com/search?q=message+bus+php + * {@inheritdoc} */ public function flush() { @@ -272,13 +276,96 @@ public function reportSpan(Span $span) $this->reporter->reportSpan($span); } - public function __toString(): string + /** + * {@inheritdoc} + */ + public function getScopeManager() { - return 'Tracer'; + return $this->scopeManager; } - private function randomId(): int + /** + * {@inheritdoc} + */ + public function getActiveSpan() { - return random_int(0, PHP_INT_MAX); + $activeScope = $this->getScopeManager()->getActive(); + if ($activeScope === null) { + return null; + } + + return $activeScope->getSpan(); + } + + /** + * {@inheritdoc} + */ + public function startActiveSpan($operationName, $options = []) + { + if (!$options instanceof StartSpanOptions) { + $options = StartSpanOptions::create($options); + } + + if (!$this->hasParentInOptions($options) && $this->getActiveSpan() !== null) { + $parent = $this->getActiveSpan()->getContext(); + $options = $options->withParent($parent); + } + + $span = $this->startSpan($operationName, $options); + $scope = $this->scopeManager->activate($span, $options->shouldFinishSpanOnClose()); + + return $scope; + } + + /** + * @param StartSpanOptions $options + * @return null|OTSpanContext + */ + private function hasParentInOptions(StartSpanOptions $options) + { + $references = $options->getReferences(); + foreach ($references as $ref) { + if ($ref->isType(Reference::CHILD_OF)) { + return $ref->getContext(); + } + } + + return null; + } + + /** + * @return string + * @throws Exception + */ + private function randomId(): string + { + return (string) random_int(0, PHP_INT_MAX); + } + + /** + * @param SamplerInterface $sampler + * @return $this + */ + public function setSampler(SamplerInterface $sampler) + { + $this->sampler = $sampler; + + return $this; + } + + /** + * @return string + */ + public function getServiceName() + { + return $this->serviceName; + } + + /** + * @return string + */ + public function getIpAddress() + { + return $this->ipAddress; } } diff --git a/tests/Jaeger/Codec/B3CodecTest.php b/tests/Jaeger/Codec/B3CodecTest.php deleted file mode 100644 index ecc7573..0000000 --- a/tests/Jaeger/Codec/B3CodecTest.php +++ /dev/null @@ -1,103 +0,0 @@ -codec = new B3Codec; - } - - function testInject() - { - // Given - $traceId = 123; - $spanId = 456; - $parentId = 789; - - $spanContext = new SpanContext( - $traceId, - $spanId, - $parentId, - SAMPLED_FLAG - ); - $carrier = array(); - - // When - $this->codec->inject($spanContext, $carrier); - - // Then - $this->assertEquals('7b', $carrier['X-B3-TraceId']); - $this->assertEquals('1c8', $carrier['X-B3-SpanId']); - $this->assertEquals('315', $carrier['X-B3-ParentSpanId']); - $this->assertEquals('1', $carrier['X-B3-Flags']); - } - - function testExtract() - { - // Given - $carrier = array( - 'x-b3-traceid' => '463ac35c9f6413ad48485a3953bb6124', - 'x-b3-spanid' => '463ac35c9f6413ad48485a3953bb6124', - 'x-b3-parentspanid' => '463ac35c9f6413ad48485a3953bb6124', - 'x-b3-flags' => '1', - ); - - // When - $spanContext = $this->codec->extract($carrier); - - // Then - $this->assertEquals(new SpanContext( - '93351075330931896558786731617803788580', - '93351075330931896558786731617803788580', - '93351075330931896558786731617803788580', - DEBUG_FLAG - ), $spanContext); - } - - function testExtractWithoutParentSpanId() - { - // Given - $carrier = array( - 'x-b3-traceid' => '463ac35c9f6413ad48485a3953bb6124', - 'x-b3-spanid' => '463ac35c9f6413ad48485a3953bb6124', - 'x-b3-flags' => '1', - ); - - // When - $spanContext = $this->codec->extract($carrier); - - // Then - $this->assertEquals(new SpanContext( - '93351075330931896558786731617803788580', - '93351075330931896558786731617803788580', - '0', - DEBUG_FLAG - ), $spanContext); - } - - function testExtractInvalidHeader() - { - // Given - $carrier = array( - 'x-b3-traceid' => 'zzzz', - 'x-b3-spanid' => '463ac35c9f6413ad48485a3953bb6124', - 'x-b3-flags' => '1', - ); - - // When - $spanContext = $this->codec->extract($carrier); - - // Then - $this->assertEquals(null, $spanContext); - } -} diff --git a/tests/Jaeger/Codec/TextCodecTest.php b/tests/Jaeger/Codec/TextCodecTest.php index c886d10..5583a23 100644 --- a/tests/Jaeger/Codec/TextCodecTest.php +++ b/tests/Jaeger/Codec/TextCodecTest.php @@ -34,9 +34,9 @@ public function testSpanContextParsingFromHeader() $spanContext = $this->textCodec->extract($carrier); - self::assertEquals("1717370599544170", $spanContext->getTraceId()); - self::assertEquals("641546560935337", $spanContext->getSpanId()); - self::assertEquals("1717370599544170", $spanContext->getParentId()); + self::assertEquals("3639838965278119798", $spanContext->getTraceId()); + self::assertEquals("1114643325879075213", $spanContext->getSpanId()); + self::assertEquals("3639838965278119798", $spanContext->getParentId()); self::assertEquals(1, $spanContext->getFlags()); } diff --git a/tests/Jaeger/Codec/UtilsTest.php b/tests/Jaeger/Codec/UtilsTest.php deleted file mode 100644 index 83c6558..0000000 --- a/tests/Jaeger/Codec/UtilsTest.php +++ /dev/null @@ -1,44 +0,0 @@ -assertEquals('463ac35c9f6413ad48485a3953bb6124', $traceIdHeader); - } - - function testHexToHeader() - { - // Given - $traceIdHeader = '463ac35c9f6413ad48485a3953bb6124'; - - // When - $traceId = Utils::hexdec($traceIdHeader); - - // Then - $this->assertEquals('93351075330931896558786731617803788580', $traceId); - } - - function testInvalidHexToHeader() - { - // Given - $traceIdHeader = 'zzzz'; - - // When - $traceId = Utils::hexdec($traceIdHeader); - - // Then - $this->assertEquals(null, $traceId); - } -} diff --git a/tests/Jaeger/Codec/ZipkinCodecTest.php b/tests/Jaeger/Codec/ZipkinCodecTest.php index b2249c9..87c1034 100644 --- a/tests/Jaeger/Codec/ZipkinCodecTest.php +++ b/tests/Jaeger/Codec/ZipkinCodecTest.php @@ -5,26 +5,100 @@ use Jaeger\SpanContext; use PHPUnit\Framework\TestCase; +use const Jaeger\DEBUG_FLAG; +use const Jaeger\SAMPLED_FLAG; + class ZipkinCodecTest extends TestCase { - /** @var TextCodec */ - private $textCodec; + /** @var ZipkinCodec */ + private $codec; + + function setUp() + { + $this->codec = new ZipkinCodec; + } + + function testInject() + { + // Given + $traceId = 123; + $spanId = 456; + $parentId = 789; + + $spanContext = new SpanContext( + $traceId, + $spanId, + $parentId, + SAMPLED_FLAG + ); + $carrier = array(); + + // When + $this->codec->inject($spanContext, $carrier); - /** @var SpanContext */ - private $ctx; + // Then + $this->assertEquals('7b', $carrier['X-B3-TraceId']); + $this->assertEquals('1c8', $carrier['X-B3-SpanId']); + $this->assertEquals('315', $carrier['X-B3-ParentSpanId']); + $this->assertEquals('1', $carrier['X-B3-Flags']); + } + + function testExtract() + { + // Given + $carrier = array( + 'x-b3-traceid' => '463ac35c9f6413ad48485a3953bb6124', + 'x-b3-spanid' => '463ac35c9f6413ad48485a3953bb6124', + 'x-b3-parentspanid' => '463ac35c9f6413ad48485a3953bb6124', + 'x-b3-flags' => '1', + ); + + // When + $spanContext = $this->codec->extract($carrier); - public function setUp() + // Then + $this->assertEquals(new SpanContext( + '93351075330931896558786731617803788580', + '93351075330931896558786731617803788580', + '93351075330931896558786731617803788580', + DEBUG_FLAG + ), $spanContext); + } + + function testExtractWithoutParentSpanId() { - $this->ctx = new SpanContext('trace-id', 'span-id', null, null); - $this->textCodec = new ZipkinCodec(); + // Given + $carrier = array( + 'x-b3-traceid' => '463ac35c9f6413ad48485a3953bb6124', + 'x-b3-spanid' => '463ac35c9f6413ad48485a3953bb6124', + 'x-b3-flags' => '1', + ); + + // When + $spanContext = $this->codec->extract($carrier); + + // Then + $this->assertEquals(new SpanContext( + '93351075330931896558786731617803788580', + '93351075330931896558786731617803788580', + '0', + DEBUG_FLAG + ), $spanContext); } - public function testCanInjectContextInCarrier() + function testExtractInvalidHeader() { - $carrier = []; + // Given + $carrier = array( + 'x-b3-traceid' => 'zzzz', + 'x-b3-spanid' => '463ac35c9f6413ad48485a3953bb6124', + 'x-b3-flags' => '1', + ); - $this->textCodec->inject($this->ctx, $carrier); + // When + $spanContext = $this->codec->extract($carrier); - $this->assertFalse(empty($carrier)); + // Then + $this->assertEquals(null, $spanContext); } } diff --git a/tests/Jaeger/ConfigTest.php b/tests/Jaeger/ConfigTest.php new file mode 100644 index 0000000..33dd8e5 --- /dev/null +++ b/tests/Jaeger/ConfigTest.php @@ -0,0 +1,42 @@ +config = new Config([], $this->serviceName); + $this->reporter = $this->createMock(ReporterInterface::class); + $this->sampler = $this->createmock(SamplerInterface::class); + } + + function testCreateTracer() + { + $tracer = $this->config->createTracer($this->reporter, $this->sampler); + + $this->assertEquals(Tracer::class, get_class($tracer)); + $this->assertEquals($this->serviceName, $tracer->getServiceName()); + } +} \ No newline at end of file diff --git a/tests/Jaeger/Reporter/CompositeReporterTest.php b/tests/Jaeger/Reporter/CompositeReporterTest.php new file mode 100644 index 0000000..9095528 --- /dev/null +++ b/tests/Jaeger/Reporter/CompositeReporterTest.php @@ -0,0 +1,50 @@ +childReporter1 = $this->createMock(ReporterInterface::class); + $this->childReporter2 = $this->createMock(ReporterInterface::class); + + $this->reporter = new CompositeReporter($this->childReporter1, $this->childReporter2); + } + + function testReportSpan() + { + $span = $this->createMock(Span::class); + + $this->childReporter1->expects($this->once())->method('reportSpan')->with($span); + $this->childReporter2->expects($this->once())->method('reportSpan')->with($span); + + $this->reporter->reportSpan($span); + } + + function testClose() + { + $this->childReporter1->expects($this->once())->method('close'); + $this->childReporter2->expects($this->once())->method('close'); + + $this->reporter->close(); + } +} \ No newline at end of file diff --git a/tests/Jaeger/Reporter/RemoteReporterTest.php b/tests/Jaeger/Reporter/RemoteReporterTest.php new file mode 100644 index 0000000..8db8419 --- /dev/null +++ b/tests/Jaeger/Reporter/RemoteReporterTest.php @@ -0,0 +1,43 @@ +transport = $this->createMock(UdpSender::class); + $this->reporter = new RemoteReporter($this->transport, $this->serviceName); + } + + function testReportSpan() + { + $span = $this->createMock(Span::class); + + $this->transport->expects($this->once())->method('append')->with($span); + + $this->reporter->reportSpan($span); + } + + function testClose() + { + $this->transport->expects($this->once())->method('flush'); + $this->transport->expects($this->once())->method('close'); + + $this->reporter->close(); + } +} \ No newline at end of file diff --git a/tests/Jaeger/Sampler/ConstSamplerTest.php b/tests/Jaeger/Sampler/ConstSamplerTest.php index 468497d..10dd2b2 100644 --- a/tests/Jaeger/Sampler/ConstSamplerTest.php +++ b/tests/Jaeger/Sampler/ConstSamplerTest.php @@ -30,6 +30,5 @@ public function testConstSampler() list($sampled, $tags) = $sampler->isSampled(PHP_INT_MAX); $this->assertFalse($sampled); $this->assertEquals($tags, $this->getTags('const', False)); - $this->assertEquals('ConstSampler(False)', $sampler->__toString()); } } diff --git a/tests/Jaeger/Sampler/ProbablisticSamplerTest.php b/tests/Jaeger/Sampler/ProbablisticSamplerTest.php index 29838c1..a6e1f5f 100644 --- a/tests/Jaeger/Sampler/ProbablisticSamplerTest.php +++ b/tests/Jaeger/Sampler/ProbablisticSamplerTest.php @@ -25,6 +25,5 @@ public function testProbabilisticSampler() list($sampled, $tags) = $sampler->isSampled(PHP_INT_MAX - 10); $this->assertFalse($sampled); $sampler->close(); - $this->assertEquals($sampler->__toString(), 'ProbabilisticSampler(0.5)'); } } diff --git a/tests/Jaeger/ScopeManagerTest.php b/tests/Jaeger/ScopeManagerTest.php new file mode 100644 index 0000000..bc2c63b --- /dev/null +++ b/tests/Jaeger/ScopeManagerTest.php @@ -0,0 +1,47 @@ +scopeManager = new ScopeManager(); + } + + function testActivate() + { + $span = $this->createMock(Span::class); + + $scope = $this->scopeManager->activate($span, true); + + $this->assertEquals($scope->getSpan(), $span); + } + + function testAbleGetActiveScope() + { + $span = $this->createMock(Span::class); + + $this->assertNull($this->scopeManager->getActive()); + $scope = $this->scopeManager->activate($span, false); + + $this->assertEquals($scope, $this->scopeManager->getActive()); + } + + function testScopeClosingDeactivates() + { + $span = $this->createMock(Span::class); + + $scope = $this->scopeManager->activate($span, false); + $scope->close(); + + $this->assertNull($this->scopeManager->getActive()); + } +} diff --git a/tests/Jaeger/ScopeTest.php b/tests/Jaeger/ScopeTest.php new file mode 100644 index 0000000..443326b --- /dev/null +++ b/tests/Jaeger/ScopeTest.php @@ -0,0 +1,55 @@ +scopeManager = $this->createMock(ScopeManager::class); + $this->span = $this->createMock(Span::class); + } + + function testCloseDoNotFinishSpanOnClose() + { + $scope = new Scope($this->scopeManager, $this->span, false); + + $this->scopeManager->method('getActive')->willReturn($scope); + $this->scopeManager->expects($this->once())->method('getActive'); + $this->span->expects($this->never())->method('finish'); + $this->scopeManager->expects($this->once())->method('setActive'); + + $scope->close(); + } + + function testCloseFinishSpanOnClose() + { + $scope = new Scope($this->scopeManager, $this->span, true); + + $this->scopeManager->method('getActive')->willReturn($scope); + $this->scopeManager->expects($this->once())->method('getActive'); + $this->span->expects($this->once())->method('finish'); + $this->scopeManager->expects($this->once())->method('setActive'); + + $scope->close(); + } + + function testGetSpan() + { + $scope = new Scope($this->scopeManager, $this->span, false); + + $this->assertEquals($this->span, $scope->getSpan()); + } +} diff --git a/tests/Jaeger/Sender/UdpSenderTest.php b/tests/Jaeger/Sender/UdpSenderTest.php new file mode 100644 index 0000000..3e44d78 --- /dev/null +++ b/tests/Jaeger/Sender/UdpSenderTest.php @@ -0,0 +1,78 @@ +client = $this->createMock(AgentClient::class); + $this->sender = new UdpSender($this->client); + } + + function testAppendUnderBatchSize() + { + $tracer = $this->createMock(Tracer::class); + $tracer->method('getIpAddress')->willReturn(''); + $tracer->method('getServiceName')->willReturn(''); + + $context = $this->createMock(SpanContext::class); + + $span = $this->createMock(Span::class); + $span->method('getTracer')->willReturn($tracer); + $span->method('getContext')->willReturn($context); + + $sender = new UdpSender($this->client); + + $this->assertEquals(0, $sender->append($span)); + } + + function testAppendAboveBatchSize() + { + $tracer = $this->createMock(Tracer::class); + $tracer->method('getIpAddress')->willReturn(''); + $tracer->method('getServiceName')->willReturn(''); + $context = $this->createMock(SpanContext::class); + $span = $this->createMock(Span::class); + $span->method('getTracer')->willReturn($tracer); + $span->method('getContext')->willReturn($context); + + $sender = new UdpSender($this->client, 0); + + $this->client->expects($this->once())->method('emitZipkinBatch'); + + $this->assertEquals(1, $sender->append($span)); + } + + function testFlush() + { + $this->assertEquals(0, $this->sender->flush()); + + $tracer = $this->createMock(Tracer::class); + $tracer->method('getIpAddress')->willReturn(''); + $tracer->method('getServiceName')->willReturn(''); + $context = $this->createMock(SpanContext::class); + $span = $this->createMock(Span::class); + $span->method('getTracer')->willReturn($tracer); + $span->method('getContext')->willReturn($context); + + $this->sender->append($span); + $this->assertEquals(1, $this->sender->flush()); + } +} \ No newline at end of file diff --git a/tests/Jaeger/SpanTest.php b/tests/Jaeger/SpanTest.php index 88a6459..b44df25 100644 --- a/tests/Jaeger/SpanTest.php +++ b/tests/Jaeger/SpanTest.php @@ -8,10 +8,14 @@ class SpanTest extends TestCase { - /** @var Tracer */ + /** + * @var Tracer + */ private $tracer; - /** @var SpanContext */ + /** + * @var SpanContext + */ private $context; function setUp() diff --git a/tests/Jaeger/TUDPTransportTest.php b/tests/Jaeger/ThriftUdpTransportTest.php similarity index 80% rename from tests/Jaeger/TUDPTransportTest.php rename to tests/Jaeger/ThriftUdpTransportTest.php index 86ea00e..30b6080 100644 --- a/tests/Jaeger/TUDPTransportTest.php +++ b/tests/Jaeger/ThriftUdpTransportTest.php @@ -5,14 +5,16 @@ use Thrift\Exception\TTransportException; use PHPUnit\Framework\TestCase; -class TUDPTransportTest extends TestCase +class ThriftUdpTransportTest extends TestCase { - /** @var TUDPTransport */ + /** + * @var ThriftUdpTransport + */ private $transport; public function setUp() { - $this->transport = new TUDPTransport('127.0.0.1', 12345); + $this->transport = new ThriftUdpTransport('127.0.0.1', 12345); } public function testisOpenWhenOpen() diff --git a/tests/Jaeger/TracerTest.php b/tests/Jaeger/TracerTest.php new file mode 100644 index 0000000..b81a3fe --- /dev/null +++ b/tests/Jaeger/TracerTest.php @@ -0,0 +1,129 @@ +scopeManager = $this->createMock(ScopeManager::class); + $this->sampler = $this->createMock(SamplerInterface::class); + $this->reporter = $this->createMock(ReporterInterface::class); + $this->logger = new NullLogger(); + + $this->tracer = new Tracer($this->serviceName, $this->reporter, $this->sampler, true, $this->logger, $this->scopeManager); + } + + function testStartSpan() + { + $span = $this->tracer->startSpan($this->operationName); + + $this->assertEquals($this->operationName, $span->getOperationName()); + } + + function testStartActiveSpan() + { + $tracer = new Tracer($this->serviceName, $this->reporter, $this->sampler); + + $tracer->startActiveSpan('test-operation1'); + $this->assertEquals('test-operation1', $tracer->getActiveSpan()->getOperationName()); + + $scope = $tracer->startActiveSpan('test-operation2'); + $this->assertEquals('test-operation2', $tracer->getActiveSpan()->getOperationName()); + $scope->close(); + + $this->assertEquals('test-operation1', $tracer->getActiveSpan()->getOperationName()); + } + + function testInjectInvalidContext() + { + $spanContext = new NoopSpanContext(); + $carrier = []; + + $this->expectException(InvalidArgumentException::class); + + $this->tracer->inject($spanContext, ZIPKIN_SPAN_FORMAT, $carrier); + } + + function testExtractInvalidFormat() + { + $this->expectException(UnsupportedFormat::class); + + $spanContext = $this->tracer->extract("bad-format", []); + } + + function testGetScopeManager() + { + $this->assertEquals($this->scopeManager, $this->tracer->getScopeManager()); + } + + function testGetActiveSpan() + { + $span = $this->createMock(Span::class); + $scope = $this->createMock(Scope::class); + $scope->expects($this->once())->method('getSpan')->willReturn($span); + + $this->scopeManager->expects($this->once())->method('getActive')->willReturn($scope); + + $this->assertEquals($span, $this->tracer->getActiveSpan()); + } + + function testGetActiveSpanNull() + { + $this->scopeManager->expects($this->once())->method('getActive')->willReturn(null); + + $this->assertEquals(null, $this->tracer->getActiveSpan()); + } + + function testFlush() + { + $this->reporter->expects($this->once())->method('close'); + + $this->tracer->flush(); + } +}