From d504f73a29e9e7aaa04a34fd5885c2945e782a3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Tue, 9 Jan 2018 17:23:55 +0100 Subject: [PATCH 1/5] [#43] Adds in process context propagation. --- README.md | 209 +++++++++++++++------------ src/OpenTracing/ActiveSpanSource.php | 40 +++++ src/OpenTracing/GlobalTracer.php | 4 +- src/OpenTracing/NoopSpan.php | 3 + src/OpenTracing/NoopTracer.php | 38 +++++ src/OpenTracing/Span.php | 13 +- src/OpenTracing/SpanOptions.php | 2 +- src/OpenTracing/Tracer.php | 40 ++++- 8 files changed, 243 insertions(+), 106 deletions(-) create mode 100644 src/OpenTracing/ActiveSpanSource.php diff --git a/README.md b/README.md index 2a80389..d5a829c 100644 --- a/README.md +++ b/README.md @@ -26,134 +26,154 @@ composer require opentracing/opentracing ## Usage When consuming this library one really only need to worry about a couple of key -abstractions: the `Tracer::startSpan` method, the `Span` interface, and binding -a `Tracer` at bootstrap time. Here are code snippets demonstrating some important -use cases: +abstractions: the `Tracer::startActiveSpan` and `Tracer::startManualSpan` method, +the `Span` interface, and binding a `Tracer` at bootstrap time. Here are code snippets +demonstrating some important use cases: ### Singleton initialization The simplest starting point is to set the global tracer. As early as possible, do: ```php - use OpenTracing\GlobalTracer; - use AnOpenTracingImplementation\MyTracer; - - GlobalTracer::set(new MyTracer()); +use OpenTracing\GlobalTracer; + +GlobalTracer::set(new MyTracerImplementation()); ``` ### Creating a Span given an existing Request -To start a new `Span`, you can use the `StartSpanFromContext` method. +To start a new `Span`, you can use the `startActiveSpan` method. ```php - use OpenTracing\Formats; - use OpenTracing\GlobalTracer; +use OpenTracing\Formats; +use OpenTracing\GlobalTracer; - ... +... - $spanContext = GlobalTracer::get()->extract( - Formats\HTTP_HEADERS, - $request->getHeaders() - ); +$spanContext = GlobalTracer::get()->extract( + Formats\HTTP_HEADERS, + getallheaders() +); + +function doSomething(SpanContext $spanContext, ...) { + ... - function doSomething(SpanContext $spanContext, ...) { - ... - - $span = GlobalTracer::get()->startSpan('my_span', [ - 'child_of' => $spanContext, - ]); - - ... - - $span->log([ - 'event' => 'soft error', - 'type' => 'cache timeout', - 'waiter.millis' => 1500, - ]) - - $span->finish(); - } + $span = GlobalTracer::get()->startManualSpan('my_span', ['child_of' => $spanContext]); + + ... + + $span->log([ + 'event' => 'soft error', + 'type' => 'cache timeout', + 'waiter.millis' => 1500, + ]) + + $span->finish(); +} ``` ### Starting an empty trace by creating a "root span" -It's always possible to create a "root" `Span` with no parent or other causal -reference. +It's always possible to create a "root" `Span` with no parent or other causal reference. ```php - $span = $tracer->startSpan('my_span'); - ... - $span->finish(); +$span = $tracer->startActiveSpan('my_first_span'); +... +$span->finish(); ``` -### Creating a (child) Span given an existing (parent) Span +#### Creating a child span assigning parent manually ```php - function xyz(Span $parentSpan, ...) { - ... - $span = GlobalTracer::get()->startSpan( - 'my_span', - [ - 'child_of' => $parentSpan, - ] - ); - - $span->finish(); - ... - } +$parent = GlobalTracer::get()->startManualSpan('parent'); + +$child = GlobalTracer::get()->startManualSpan('child', [ + 'child_of' => $parent +]); + +... + +$child->finish(); + +... + +$parent->finish(); ``` -### Serializing to the wire +#### Creating a child span using automatic active span management + +Every new span will take the active span as parent and it will take its spot. ```php - use OpenTracing\GlobalTracer; - use OpenTracing\Formats; - + $parent = GlobalTracer::get()->startActiveSpan('parent'); + + ... + + // Since the parent span has been created by using startActiveSpan we don't need + // to pass a reference for this child span + $child = GlobalTracer::get()->startActiveSpan('my_second_span'); + + ... + + $child->finish(); + ... + + $parent->finish(); +``` + +### Serializing to the wire + +```php +use GuzzleHttp\Client; +use OpenTracing\Formats; + +... + +$tracer = GlobalTracer::get(); + +$spanContext = $tracer->extract( + Formats\HTTP_HEADERS, + getallheaders() +); + +try { + $span = $tracer->startManualSpan('my_span', ['child_of' => $spanContext]); + + $client = new Client; - $tracer = GlobalTracer::get(); + $headers = []; - $spanContext = $tracer->extract( + $tracer->inject( + $span->getContext(), Formats\HTTP_HEADERS, - $request->getHeaders() + $headers ); - try { - $span = $tracer->startSpan('my_span', ['child_of' => $spanContext]); - - $client = new GuzzleHttp\Client; - - $headers = []; - - $tracer->inject( - $span->getContext(), - Formats\HTTP_HEADERS, - $headers - ); - - $request = new \GuzzleHttp\Psr7\Request('GET', 'http://myservice', $headers); - $client->send($request); - ... - } catch (\Exception $e) { - ... - } - ... + $request = new \GuzzleHttp\Psr7\Request('GET', 'http://myservice', $headers); + $client->send($request); + ... + +} catch (\Exception $e) { + ... +} +... ``` ### Deserializing from the wire -When using http header for context propagation you can use either the `Request` or the `$_SERVER` variable: +When using http header for context propagation you can use either the `Request` or the `$_SERVER` +variable: ```php - use OpenTracing\GlobalTracer; - use OpenTracing\Formats; - - $request = Request::createFromGlobals(); - $tracer = GlobalTracer::get(); - $spanContext = $tracer->extract(Formats\HTTP_HEADERS, $request->getHeaders()); - $tracer->startSpan('my_span', [ - 'child_of' => $spanContext, - ]); +use OpenTracing\GlobalTracer; +use OpenTracing\Formats; + +$tracer = GlobalTracer::get(); +$spanContext = $tracer->extract(Formats\HTTP_HEADERS, getallheaders()); +$tracer->startManualSpan('my_span', [ + 'child_of' => $spanContext, +]); ``` ### Flushing Spans @@ -167,19 +187,18 @@ cause problems for Tracer implementations. This is why the PHP API contains a ```php use OpenTracing\GlobalTracer; -// Do application work, buffer spans in memory $application->run(); -fastcgi_finish_request(); - -$tracer = GlobalTracer::get(); -$tracer->flush(); // release buffer to backend +register_shutdown_function(function() use ($tracer) { + /* Flush the tracer to the backend */ + $tracer = GlobalTracer::get(); + $tracer->flush(); +}); ``` This is optional, tracers can decide to immediately send finished spans to a backend. The flush call can be implemented as a NO-OP for these tracers. - ### Using Span Options Passing options to the pass can be done using either an array or the @@ -191,7 +210,7 @@ SpanOptions wrapper object. The following keys are valid: - `tags` is an array with string keys and scalar values that represent OpenTracing tags. ```php -$span = $tracer->createSpan('my_span', [ +$span = $tracer->startActiveSpan('my_span', [ 'child_of' => $spanContext, 'tags' => ['foo' => 'bar'], 'start_time' => time(), @@ -217,5 +236,5 @@ Tracers will throw an exception if the requested format is not handled by them. ## Coding Style -Opentracing PHP follows the [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) +OpenTracing PHP follows the [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) coding standard and the [PSR-4](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md) autoloading standard. diff --git a/src/OpenTracing/ActiveSpanSource.php b/src/OpenTracing/ActiveSpanSource.php new file mode 100644 index 0000000..2060e3e --- /dev/null +++ b/src/OpenTracing/ActiveSpanSource.php @@ -0,0 +1,40 @@ +tracer->startActiveSpan('request.handler'); + * $user = $this->repository->getUser($userId); + * } + * + * function getUser($userId) + * { + * // `$childSpan` has `$rootSpan` as parent. + * $childSpan = $this->tracer->startActiveSpan('db.query'); + * } + * * @param string $operationName * @param array|SpanOptions $options A set of optional parameters: * - Zero or more references to related SpanContexts, including a shorthand for ChildOf and @@ -21,7 +43,16 @@ interface Tracer * @throws InvalidSpanOption for invalid option * @throws InvalidReferencesSet for invalid references set */ - public function startSpan($operationName, $options = []); + public function startActiveSpan($operationName, $options = []); + + /** + * @param string $operationName + * @param array|SpanOptions $options + * @return Span + * @throws InvalidSpanOption for invalid option + * @throws InvalidReferencesSet for invalid references set + */ + public function startManualSpan($operationName, $options = []); /** * @param SpanContext $spanContext @@ -53,9 +84,8 @@ public function extract($format, $carrier); * * 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. + * As an implementor, a good idea would be to use register_shutdown_function + * or fastcgi_finish_request in order to not to delay the end of the request to the client. * * @see register_shutdown_function() * @see fastcgi_finish_request() From ecde3452522cc50dc2a2f75a5540390cb2cf839b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Thu, 11 Jan 2018 19:43:47 +0100 Subject: [PATCH 2/5] Adds ScopeManager. --- README.md | 36 +++++++++++++------------ src/OpenTracing/ActiveSpanSource.php | 40 ---------------------------- src/OpenTracing/GlobalTracer.php | 2 +- src/OpenTracing/NoopScopeManager.php | 26 ++++++++++++++++++ src/OpenTracing/NoopTracer.php | 40 +++++++++------------------- src/OpenTracing/Scope.php | 30 +++++++++++++++++++++ src/OpenTracing/ScopeManager.php | 35 ++++++++++++++++++++++++ src/OpenTracing/Span.php | 6 ++--- src/OpenTracing/SpanContext.php | 4 +-- src/OpenTracing/{Ext => }/Tags.php | 2 +- src/OpenTracing/Tracer.php | 26 +++++++++++++----- 11 files changed, 149 insertions(+), 98 deletions(-) delete mode 100644 src/OpenTracing/ActiveSpanSource.php create mode 100644 src/OpenTracing/NoopScopeManager.php create mode 100644 src/OpenTracing/Scope.php create mode 100644 src/OpenTracing/ScopeManager.php rename src/OpenTracing/{Ext => }/Tags.php (99%) diff --git a/README.md b/README.md index d5a829c..ee63db4 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ composer require opentracing/opentracing ## Usage When consuming this library one really only need to worry about a couple of key -abstractions: the `Tracer::startActiveSpan` and `Tracer::startManualSpan` method, +abstractions: the `Tracer::startActiveSpan` and `Tracer::startSpan` method, the `Span` interface, and binding a `Tracer` at bootstrap time. Here are code snippets demonstrating some important use cases: @@ -55,10 +55,10 @@ $spanContext = GlobalTracer::get()->extract( getallheaders() ); -function doSomething(SpanContext $spanContext, ...) { +function doSomething() { ... - $span = GlobalTracer::get()->startManualSpan('my_span', ['child_of' => $spanContext]); + $span = GlobalTracer::get()->startActiveSpan('my_span', ['child_of' => $spanContext]); ... @@ -85,9 +85,9 @@ $span->finish(); #### Creating a child span assigning parent manually ```php -$parent = GlobalTracer::get()->startManualSpan('parent'); +$parent = GlobalTracer::get()->startSpan('parent'); -$child = GlobalTracer::get()->startManualSpan('child', [ +$child = GlobalTracer::get()->startSpan('child', [ 'child_of' => $parent ]); @@ -105,21 +105,23 @@ $parent->finish(); Every new span will take the active span as parent and it will take its spot. ```php - $parent = GlobalTracer::get()->startActiveSpan('parent'); - - ... +$parent = GlobalTracer::get()->startActiveSpan('parent'); - // Since the parent span has been created by using startActiveSpan we don't need - // to pass a reference for this child span - $child = GlobalTracer::get()->startActiveSpan('my_second_span'); +... - ... +/* + * Since the parent span has been created by using startActiveSpan we don't need + * to pass a reference for this child span + */ +$child = GlobalTracer::get()->startActiveSpan('my_second_span'); - $child->finish(); +... - ... +$child->finish(); - $parent->finish(); +... + +$parent->finish(); ``` ### Serializing to the wire @@ -138,7 +140,7 @@ $spanContext = $tracer->extract( ); try { - $span = $tracer->startManualSpan('my_span', ['child_of' => $spanContext]); + $span = $tracer->startSpan('my_span', ['child_of' => $spanContext]); $client = new Client; @@ -171,7 +173,7 @@ use OpenTracing\Formats; $tracer = GlobalTracer::get(); $spanContext = $tracer->extract(Formats\HTTP_HEADERS, getallheaders()); -$tracer->startManualSpan('my_span', [ +$tracer->startSpan('my_span', [ 'child_of' => $spanContext, ]); ``` diff --git a/src/OpenTracing/ActiveSpanSource.php b/src/OpenTracing/ActiveSpanSource.php deleted file mode 100644 index 2060e3e..0000000 --- a/src/OpenTracing/ActiveSpanSource.php +++ /dev/null @@ -1,40 +0,0 @@ - + * Many times a {@link Span} will be extant (in that {@link Span#finish()} has not been called) despite being in a + * non-runnable state from a CPU/scheduler standpoint. For instance, a {@link Span} representing the client side of an + * RPC will be unfinished but blocked on IO while the RPC is still outstanding. A {@link Scope} defines when a given + * {@link Span} is scheduled and on the path. + */ +interface Scope +{ + /** + * Mark the end of the active period for the current thread and {@link Scope}, + * updating the {@link ScopeManager#active()} in the process. + * + *

+ * NOTE: Calling {@link #close} more than once on a single {@link Scope} instance leads to undefined + * behavior. + */ + public function close(); + + /** + * @return Span the {@link Span} that's been scoped by this {@link Scope} + */ + public function getSpan(); +} \ No newline at end of file diff --git a/src/OpenTracing/ScopeManager.php b/src/OpenTracing/ScopeManager.php new file mode 100644 index 0000000..b77431d --- /dev/null +++ b/src/OpenTracing/ScopeManager.php @@ -0,0 +1,35 @@ + + * If there is an {@link Scope non-null scope}, its wrapped {@link Span} becomes an implicit parent + * (as {@link References#CHILD_OF} reference) of any + * newly-created {@link Span} at {@link Tracer.SpanBuilder#startActive(boolean)} or {@link SpanBuilder#start()} + * time rather than at {@link Tracer#buildSpan(String)} time. + * + * @return Span the {@link Scope active scope}, or null if none could be found. + */ + public function getActiveSpan(); +} diff --git a/src/OpenTracing/Span.php b/src/OpenTracing/Span.php index 4daeeee..b1dbfc8 100644 --- a/src/OpenTracing/Span.php +++ b/src/OpenTracing/Span.php @@ -45,10 +45,10 @@ public function finish($finishTime = null, array $logRecords = []); public function overwriteOperationName($newOperationName); /** - * Sets tags to the Span in key:value format, key must be a string and tag must be either + * 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} + * As an implementor, consider using "standard tags" listed in {@see \OpenTracing\Tags} * * If the span is already finished, a warning should be logged. * @@ -58,7 +58,7 @@ public function overwriteOperationName($newOperationName); public function setTags(array $tags); /** - * Adds a log record to the span in key:value format, key must be a string and tag must be either + * Adds a log record 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. * * If the span is already finished, a warning should be logged. diff --git a/src/OpenTracing/SpanContext.php b/src/OpenTracing/SpanContext.php index f02069d..a5d3757 100644 --- a/src/OpenTracing/SpanContext.php +++ b/src/OpenTracing/SpanContext.php @@ -8,7 +8,7 @@ * SpanContext must be immutable in order to avoid complicated lifetime * issues around Span finish and references. * - * Baggage items are key:value string pairs that apply to the given Span, + * Baggage items are key => value string pairs that apply to the given Span, * its SpanContext, and all Spans which directly or transitively reference * the local Span. That is, baggage items propagate in-band along with the * trace itself. @@ -25,7 +25,7 @@ interface SpanContext extends IteratorAggregate public function getBaggageItem($key); /** - * Creates a new SpanContext out of the existing one and the new key:value pair. + * Creates a new SpanContext out of the existing one and the new key => value pair. * * @param string $key * @param string $value diff --git a/src/OpenTracing/Ext/Tags.php b/src/OpenTracing/Tags.php similarity index 99% rename from src/OpenTracing/Ext/Tags.php rename to src/OpenTracing/Tags.php index 468c87b..e4702d3 100644 --- a/src/OpenTracing/Ext/Tags.php +++ b/src/OpenTracing/Tags.php @@ -1,6 +1,6 @@ getActive()->getSpan(), + * and null will be returned if {@link Scope#active()} is null. + */ + public function getActiveSpan(); + /** * Starts and returns a new `Span` representing a unit of work. * - * This method differs from `startManualSpan` because it uses in-process + * This method differs from `startSpan` because it uses in-process * context propagation to keep track of the current active `Span` (if * available). * @@ -39,20 +50,21 @@ interface Tracer extends ActiveSpanSource * - An optional explicit start timestamp; if omitted, the current walltime is used by default * The default value should be set by the vendor. * - Zero or more tags - * @return Span - * @throws InvalidSpanOption for invalid option - * @throws InvalidReferencesSet for invalid references set + * @param bool $finishSpanOnClose whether span should automatically be finished when {@link Scope#close()} is called + * @return Scope */ - public function startActiveSpan($operationName, $options = []); + public function startActiveSpan($operationName, $finishSpanOnClose = true, $options = []); /** + * Starts and returns a new `Span` representing a unit of work. + * * @param string $operationName * @param array|SpanOptions $options * @return Span * @throws InvalidSpanOption for invalid option * @throws InvalidReferencesSet for invalid references set */ - public function startManualSpan($operationName, $options = []); + public function startSpan($operationName, $options = []); /** * @param SpanContext $spanContext From 3010347c7329578653546c0f4319ef34ed16aa84 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 2 Feb 2018 14:59:36 +0100 Subject: [PATCH 3/5] Change Pythonic to PHPonic behavior with regard to scopes. The Python In Process propagation is based on the `with` concept in Python that can automatically handle scope closing and therefore its usage is simple. If ported to PHP 100% our API would be bad: $scope = $tracer->startActiveSpan('foo'); try { $span = $scope->getSpan(); $span->setTag('foo', 'bar'); do_some_work(); } finally { $scope->close(); } This is compared to Pythons: with tracer.start_active_span('foo') as scope: scope.span.set_tag('foo', 'bar') do_some_work(); Python is a much more parallel and threaded language than PHP, making the scope a more important concept. But for PHP the default use-case should be shared-nothing single request processing. In this case scopes are of no interest to 99% of the users. In contrast I propose that in PHP we hide scopes from the public API and only abstract them away for implementors convenience: $span = $tracer->startActiveSpan('foo'); try { $span->setTag('foo', 'bar'); do_some_work(); } finally { $span->finish(); } Not as nice as Python, but much better than before. This relies on a new span option called `close_span_on_finish` which is the other way around than in Python. It is `true` by default, again because in PHP we don't usually have threads and active spans can use a simple stack to find the current span, the 99% use case for most library and application developers that will want to use OT. If you set `close_span_on_finish=false` then you can close the span the following way: $span = $tracer->startActiveSpan('foo', ['close_span_on_finish' => false]); try { $span->setTag('foo', 'bar'); do_some_work(); } finally { $span->finish(); $tracer->getScopeManager()->getScope($span)->close(); } This API will usually not be needed by anyone that is using active spans, so I consider the complexity and Law Of Demeter violation to be acceptable compared to code example #1 where a user must use both $span and $scope in the right way. All the examples above will also work for event based PHP scripts if the scope manager is implemented correctly for the Event looped system. By default a scope manager is a simple stack. For an event looped system the scope manager would keep stacks for all active threads and change the stack whenever the thread activity changes. $scopeManager = new MyEventScopeManager(); $scopeManager->activateThread(1); $scopeManager->activateSpan($rootSpanThread1); $scopeManager->activateThread(2); $scopeManager->getActiveScope(); // null Here the `activateThread` method is a public method that is implementation specific based on the event loop. If ReactPHP would implement their scope manager, then all existing library code would keep on working correctly. --- README.md | 64 +++++++++++++++---- composer.json | 2 +- .../Exceptions/InvalidSpanOption.php | 12 ++++ src/OpenTracing/ScopeManager.php | 14 ++-- src/OpenTracing/SpanOptions.php | 23 +++++++ src/OpenTracing/Tracer.php | 6 +- tests/Unit/SpanOptionsTest.php | 25 ++++++++ 7 files changed, 122 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index ee63db4..b90f4bc 100644 --- a/README.md +++ b/README.md @@ -57,17 +57,17 @@ $spanContext = GlobalTracer::get()->extract( function doSomething() { ... - + $span = GlobalTracer::get()->startActiveSpan('my_span', ['child_of' => $spanContext]); - + ... - + $span->log([ 'event' => 'soft error', 'type' => 'cache timeout', 'waiter.millis' => 1500, ]) - + $span->finish(); } ``` @@ -82,6 +82,42 @@ $span = $tracer->startActiveSpan('my_first_span'); $span->finish(); ``` +### Active Spans and Scope Manager + +When using the `Tracer::startActiveSpan` function the underlying tracer uses an +abstraction called scope manager to keep track of the currently active span. + +Starting an active span will always use the currently active span as a parent. +If no parent is available, then the newly created span is considered to be the +root span of the trace. + +Unless you are using asynchronuous code that tracks multiple spans at the same +time, such as when using cURL Multi Exec or MySQLi Polling you are better +of just using `Tracer::startActiveSpan` everywhere in your application. + +The currently active span gets automatically closed and deactivated from the scope +when you call `$span->finish()` as you can see in the previous example. + +If you don't want a span to automatically close when `Span::finish()` is called +then you must pass the option `'close_span_on_finish'=> false,` to the `$options` +argument of `startActiveSpan`. + +An example of a linear, two level deep span tree using active spans looks like +this in PHP code: + +```php +$root = $tracer->startActiveSpan('php'); + + $controller = $tracer->startActiveSpan('controller'); + + $http = $tracer->startActiveSpan('http'); + file_get_contents('http://php.net'); + $http->finish(); + + $controller->finish(); +$root->finish(); +``` + #### Creating a child span assigning parent manually ```php @@ -105,7 +141,7 @@ $parent->finish(); Every new span will take the active span as parent and it will take its spot. ```php -$parent = GlobalTracer::get()->startActiveSpan('parent'); +$parent = GlobalTracer::get()->startActiveSpan('parent'); ... @@ -115,7 +151,7 @@ $parent = GlobalTracer::get()->startActiveSpan('parent'); */ $child = GlobalTracer::get()->startActiveSpan('my_second_span'); -... +... $child->finish(); @@ -132,7 +168,7 @@ use OpenTracing\Formats; ... -$tracer = GlobalTracer::get(); +$tracer = GlobalTracer::get(); $spanContext = $tracer->extract( Formats\HTTP_HEADERS, @@ -143,23 +179,23 @@ try { $span = $tracer->startSpan('my_span', ['child_of' => $spanContext]); $client = new Client; - + $headers = []; - + $tracer->inject( $span->getContext(), Formats\HTTP_HEADERS, $headers ); - + $request = new \GuzzleHttp\Psr7\Request('GET', 'http://myservice', $headers); $client->send($request); ... - + } catch (\Exception $e) { ... } -... +... ``` ### Deserializing from the wire @@ -175,7 +211,7 @@ $tracer = GlobalTracer::get(); $spanContext = $tracer->extract(Formats\HTTP_HEADERS, getallheaders()); $tracer->startSpan('my_span', [ 'child_of' => $spanContext, -]); +]); ``` ### Flushing Spans @@ -208,7 +244,7 @@ SpanOptions wrapper object. The following keys are valid: - `start_time` is a float, int or `\DateTime` representing a timestamp with arbitrary precision. - `child_of` is an object of type `OpenTracing\SpanContext` or `OpenTracing\Span`. -- `references` is an array of `OpenTracing\Reference`. +- `references` is an array of `OpenTracing\Reference`. - `tags` is an array with string keys and scalar values that represent OpenTracing tags. ```php diff --git a/composer.json b/composer.json index 08eeb17..e23b949 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "psr-4": { "OpenTracing\\": "./src/OpenTracing/" }, - "files": ["./src/OpenTracing/Ext/Tags.php", "./src/OpenTracing/Formats.php"] + "files": ["./src/OpenTracing/Tags.php", "./src/OpenTracing/Formats.php"] }, "autoload-dev": { "psr-4": { diff --git a/src/OpenTracing/Exceptions/InvalidSpanOption.php b/src/OpenTracing/Exceptions/InvalidSpanOption.php index bba9d4c..92e0ae1 100644 --- a/src/OpenTracing/Exceptions/InvalidSpanOption.php +++ b/src/OpenTracing/Exceptions/InvalidSpanOption.php @@ -95,4 +95,16 @@ public static function forInvalidReferenceSet($value) is_object($value) ? get_class($value) : gettype($value) )); } + + /** + * @param mixed $value + * @return InvalidSpanOption + */ + public static function forCloseSpanOnFinish($value) + { + return new self(sprintf( + 'Invalid type for close_span_on_finish. Expected bool, got %s', + is_object($value) ? get_class($value) : gettype($value) + )); + } } diff --git a/src/OpenTracing/ScopeManager.php b/src/OpenTracing/ScopeManager.php index b77431d..71738ae 100644 --- a/src/OpenTracing/ScopeManager.php +++ b/src/OpenTracing/ScopeManager.php @@ -13,15 +13,17 @@ interface ScopeManager * that previous spans can be resumed after a deactivation. * * @param Span $span the {@link Span} that should become the {@link #active()} - * @param bool $finishSpanOnClose whether span should automatically be finished when {@link Scope#close()} is called - * @return callable instance to control the end of the active period for the {@link Span}. It is a - * programming error to neglect to call it on the returned instance. + * + * Weather the span automatically closes when finish is called depends on + * the span options set during the creation of the span. See {@link SpanOptions#closeSpanOnFinish} + * + * @return Scope */ - public function activate(Span $span, $finishSpanOnClose); + public function activate(Span $span); /** * Return the currently active {@link Scope} which can be used to access the currently active - * {@link Scope#span()}. + * {@link Scope#getSpan()}. * *

* If there is an {@link Scope non-null scope}, its wrapped {@link Span} becomes an implicit parent @@ -29,7 +31,7 @@ public function activate(Span $span, $finishSpanOnClose); * newly-created {@link Span} at {@link Tracer.SpanBuilder#startActive(boolean)} or {@link SpanBuilder#start()} * time rather than at {@link Tracer#buildSpan(String)} time. * - * @return Span the {@link Scope active scope}, or null if none could be found. + * @return \OpenTracing\Scope */ public function getActiveSpan(); } diff --git a/src/OpenTracing/SpanOptions.php b/src/OpenTracing/SpanOptions.php index 85a33f5..b069676 100644 --- a/src/OpenTracing/SpanOptions.php +++ b/src/OpenTracing/SpanOptions.php @@ -22,6 +22,13 @@ final class SpanOptions */ private $startTime; + /** + * Only used for spans that are actively managed by scope manager. + * + * @var bool + */ + private $closeSpanOnFinish = true; + /** * @param array $options * @throws InvalidSpanOption when one of the options is invalid @@ -79,6 +86,14 @@ public static function create(array $options) $spanOptions->startTime = $value; break; + case 'close_span_on_finish': + if (!is_bool($value)) { + throw InvalidSpanOption::forCloseSpanOnFinish($value); + } + + $spanOptions->closeSpanOnFinish = $value; + break; + default: throw InvalidSpanOption::forUnknownOption($key); break; @@ -113,6 +128,14 @@ public function getStartTime() return $this->startTime; } + /** + * @return bool + */ + public function getCloseSpanOnFinish() + { + return $this->closeSpanOnFinish; + } + private static function buildChildOf($value) { if ($value instanceof Span) { diff --git a/src/OpenTracing/Tracer.php b/src/OpenTracing/Tracer.php index ed91a67..de96143 100644 --- a/src/OpenTracing/Tracer.php +++ b/src/OpenTracing/Tracer.php @@ -50,10 +50,10 @@ public function getActiveSpan(); * - An optional explicit start timestamp; if omitted, the current walltime is used by default * The default value should be set by the vendor. * - Zero or more tags - * @param bool $finishSpanOnClose whether span should automatically be finished when {@link Scope#close()} is called - * @return Scope + * - CloseSpanOnFinish option which defaults to true. + * @return Span */ - public function startActiveSpan($operationName, $finishSpanOnClose = true, $options = []); + public function startActiveSpan($operationName, $options = []); /** * Starts and returns a new `Span` representing a unit of work. diff --git a/tests/Unit/SpanOptionsTest.php b/tests/Unit/SpanOptionsTest.php index 8e46abb..3ae6057 100644 --- a/tests/Unit/SpanOptionsTest.php +++ b/tests/Unit/SpanOptionsTest.php @@ -24,6 +24,15 @@ public function testSpanOptionsCanNotBeCreatedDueToInvalidOption() ]); } + public function testSpanOptionsWithInvalidCloseOnFinishOption() + { + $this->expectException(InvalidSpanOption::class); + + SpanOptions::create([ + 'close_span_on_finish' => 'value' + ]); + } + public function testSpanOptionsCanNotBeCreatedBecauseInvalidStartTime() { $this->expectException(InvalidSpanOption::class); @@ -67,4 +76,20 @@ public function testSpanOptionsCanBeCreatedWithValidReference() $this->assertTrue($references->isType(self::REFERENCE_TYPE)); $this->assertSame($context, $references->getContext()); } + + public function testSpanOptionsDefaultCloseOnFinishValue() + { + $options = SpanOptions::create([]); + + $this->assertTrue($options->getCloseSpanOnFinish()); + } + + public function testSpanOptionsWithValidCloseOnFinishValue() + { + $options = SpanOptions::create([ + 'close_span_on_finish' => false, + ]); + + $this->assertFalse($options->getCloseSpanOnFinish()); + } } From 7bf5caa01bfbe9418bf5d7336c948cc1d5e2c2cf Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 2 Feb 2018 15:20:08 +0100 Subject: [PATCH 4/5] Fix CS --- src/OpenTracing/Scope.php | 3 ++- src/OpenTracing/ScopeManager.php | 11 ++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/OpenTracing/Scope.php b/src/OpenTracing/Scope.php index 7dc9c14..136313a 100644 --- a/src/OpenTracing/Scope.php +++ b/src/OpenTracing/Scope.php @@ -27,4 +27,5 @@ public function close(); * @return Span the {@link Span} that's been scoped by this {@link Scope} */ public function getSpan(); -} \ No newline at end of file +} + diff --git a/src/OpenTracing/ScopeManager.php b/src/OpenTracing/ScopeManager.php index 71738ae..586e212 100644 --- a/src/OpenTracing/ScopeManager.php +++ b/src/OpenTracing/ScopeManager.php @@ -33,5 +33,14 @@ public function activate(Span $span); * * @return \OpenTracing\Scope */ - public function getActiveSpan(); + public function getActiveScope(); + + /** + * Access the scope of a given Span if available. + * + * @param Span $span + * + * @return \OpenTracing\Scope + */ + public function getScope(Span $span); } From d1160e1574b896ab1661d7179ad1fec3e6622f67 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Fri, 2 Feb 2018 15:24:18 +0100 Subject: [PATCH 5/5] Fix CS --- src/OpenTracing/Scope.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/OpenTracing/Scope.php b/src/OpenTracing/Scope.php index 136313a..08ca173 100644 --- a/src/OpenTracing/Scope.php +++ b/src/OpenTracing/Scope.php @@ -28,4 +28,3 @@ public function close(); */ public function getSpan(); } -