Created
December 18, 2017 17:25
-
-
Save tylerchr/67f5ff8621f697903d189f71d4ea6d74 to your computer and use it in GitHub Desktop.
Stack management example using opentracing-php API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Tracing; | |
final class Span implements OTSpan | |
{ | |
private $span; | |
function __construct($operationName, $tags = null) | |
{ | |
$this->span = Tracing::push($operationName, $tags); | |
} | |
function __destruct() | |
{ | |
Tracing::pop($this->span); | |
} | |
// all these just call directly through to the underlying span | |
public function getOperationName() { ... } | |
public function getContext() { ... } | |
public function finish($finishTime = null, array $logRecords = []) { ... } | |
public function overwriteOperationName($newOperationName) { ... } | |
public function setTags(array $tags) { ... } | |
public function log(array $fields = [], $timestamp = null) { ... } | |
public function addBaggageItem($key, $value) { ... } | |
public function getBaggageItem($key) { ... } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Tracing; | |
use OpenTracing\GlobalTracer; | |
use OpenTracing\Reference; | |
use OpenTracing\SpanOptions; | |
final class Tracing | |
{ | |
static $spanStack = []; | |
static $rootSpan = null; | |
... | |
/** | |
* Start a new span with optional tags, which will be automatically finished | |
* when that span goes out of scope. | |
* | |
* If a currently-active span already exists, then the new span will be | |
* automatically created as a child of that span. If no previous spans | |
* exist, the new span will be a root span. | |
* | |
* Note that `createAutofinishing()` differs from `push()` in the way the | |
* span is expected to be closed. Spans obtained via `createAutofinishing()` | |
* are finished when the value returned from `createAutofinishing()` goes | |
* out of scope and its destructor runs, while those obtained via `push()` | |
* must be explicitly finished with `pop()`. | |
* | |
* @param string $operationName | |
* @param mixed[] $tags | |
*/ | |
public static function createAutofinishing($operationName, $tags = null) | |
{ | |
return new Span($operationName, $tags); | |
} | |
/** | |
* Start a new span with optional tags. | |
* | |
* If a currently-active span already exists, then the new span will be | |
* automatically created as a child of that span. If no previous spans | |
* exist, the new span will be a root span. | |
* | |
* @param string $operationName | |
* @param mixed[] $tags | |
*/ | |
public static function push($operationName, $tags = null) | |
{ | |
// error_log("@GREEN: Creating span: " . $operationName); | |
$options = []; | |
if (is_array($tags)) | |
{ | |
$options["tags"] = $tags; | |
} | |
if (count(self::$spanStack) > 0) | |
{ | |
$options[Reference::CHILD_OF] = self::peek(); | |
} | |
$span = GlobalTracer::get()->startSpan($operationName, SpanOptions::create($options)); | |
array_push(self::$spanStack, $span); | |
return $span; | |
} | |
/** | |
* Finish the currently-active span | |
*/ | |
public static function pop($intendedSpan = null) | |
{ | |
// Sanity-check: no point in attempting any post-teardown pops. | |
if (self::$alreadyTornDown) | |
{ | |
return; | |
} | |
// Sanity-check: make sure there's something to pop. | |
if (count(self::$spanStack) < 1) | |
{ | |
throw new \LogicException("Unexpected Tracing::pop() detected where no stack exists"); | |
} | |
// Sanity-check: validate that the span we intend to pop is the one we're actually popping. | |
if (!is_null($intendedSpan) && $intendedSpan !== self::peek()) | |
{ | |
// error_log("Warning: Tracing: Unbalanced usage of Tracing::push() and Tracing::pop() detected; attempting recovery"); | |
// If we can find the $intendedSpan further down the stack, we'll assume | |
// that the in-between spans are finished and pop them automatically. | |
// | |
// There are legitimate ways for this to occur, for example if die() or | |
// exit() is called after writing some downloadable file to the response | |
// and all pops are skipped until the final one in the shutdown handler. | |
// Locate $intendedSpan in the self::$spanStack, or determine that it isn't there. | |
$indexOfIntendedSpan = -1; | |
for ($i = count(self::$spanStack) - 1; $i >= 0; $i--) | |
{ | |
if ($intendedSpan == self::$spanStack[$i]) | |
{ | |
$indexOfIntendedSpan = $i; | |
break; | |
} | |
} | |
// If the $intendedSpan DOES exist somewhere on the self::$spanStack, | |
// pop off each of intermediate spans. | |
if ($indexOfIntendedSpan >= 0) | |
{ | |
for ($i=count(self::$spanStack) - 1; $i > $indexOfIntendedSpan; $i--) | |
{ | |
// error_log("Warning: Tracing: Auto-popping Span#" . $i . " (" . self::$spanStack[$i]->getOperationName() . ")"); | |
Tracing::pop(self::$spanStack[$i]); | |
} | |
} | |
else | |
{ | |
throw new \LogicException("Unrecoverable invalid usage of Tracing::push() and Tracing::pop() detected"); | |
} | |
} | |
$span = array_pop(self::$spanStack); | |
$span->finish(); | |
} | |
/** | |
* Retrieve the current span | |
* | |
* @return Span | |
*/ | |
private static function peek() | |
{ | |
return self::$spanStack[count(self::$spanStack)-1]; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment