Skip to content

Instantly share code, notes, and snippets.

@tylerchr
Created December 18, 2017 17:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tylerchr/67f5ff8621f697903d189f71d4ea6d74 to your computer and use it in GitHub Desktop.
Save tylerchr/67f5ff8621f697903d189f71d4ea6d74 to your computer and use it in GitHub Desktop.
Stack management example using opentracing-php API
<?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) { ... }
}
<?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