Skip to content

Commit

Permalink
remove global span (#171)
Browse files Browse the repository at this point in the history
* remove global span creation when Tracer is initialized and fix batch exporter not exporting in batches but one by one

* use the monotonic clock instead of realtime clock to compute time elapsed - realtime clocks  can move backwards

* add the NoopSpan. It will be used as the root Span

* Use NoopSpan for new traces

* Generate a new context if the root span is noop

* Remove comments

* Inline variable

* Must set parent as active span so that span ID is propagated

* Revert "Must set parent as active span so that span ID is propagated" as context can't be used as span

This reverts commit 985fa5e.

* Load NoopSpan when required

* Fixed regression: creating a Tracer with an existing context is now supported again.

* Add variable to improve readability

* Rename variable for clarity.

* fix failing psalm checks and add test for root NoopSpan

Co-authored-by: Ben Magee <[email protected]>
  • Loading branch information
beniamin and BnMcG authored Oct 27, 2020
1 parent 89856e5 commit 618a983
Show file tree
Hide file tree
Showing 12 changed files with 357 additions and 51 deletions.
2 changes: 1 addition & 1 deletion Context/Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class Context
* @param mixed|null $value
* @param Context|null $parent Reference to the parent object
*/
public final function __construct(?ContextKey $key=null, $value=null, $parent=null)
final public function __construct(?ContextKey $key=null, $value=null, $parent=null)
{
$this->key = $key;
$this->value = $value;
Expand Down
2 changes: 1 addition & 1 deletion api/Trace/Span.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function getStart(): int;

/**
* Returns system time clock value (MonotonicClock) when the Span was stopped
* @return int
* @return int|null
*/
public function getEnd(): ?int;

Expand Down
16 changes: 10 additions & 6 deletions examples/AlwaysOnJaegerExample.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use OpenTelemetry\Sdk\Trace\Clock;
use OpenTelemetry\Sdk\Trace\Sampler\AlwaysOnSampler;
use OpenTelemetry\Sdk\Trace\SamplingResult;
use OpenTelemetry\Sdk\Trace\SpanProcessor\SimpleSpanProcessor;
use OpenTelemetry\Sdk\Trace\SpanProcessor\BatchSpanProcessor;
use OpenTelemetry\Sdk\Trace\TracerProvider;
use OpenTelemetry\Trace as API;

Expand All @@ -29,16 +29,20 @@
if (SamplingResult::RECORD_AND_SAMPLED === $samplingResult->getDecision()) {
echo 'Starting AlwaysOnJaegerExample';
$tracer = (new TracerProvider())
->addSpanProcessor(new SimpleSpanProcessor($exporter))
->addSpanProcessor(new BatchSpanProcessor($exporter, Clock::get()))
->getTracer('io.opentelemetry.contrib.php');

echo PHP_EOL . sprintf('Trace with id %s started ', $tracer->getActiveSpan()->getContext()->getTraceId());

for ($i = 0; $i < 5; $i++) {
// start a span, register some events
$timestamp = Clock::get()->timestamp();
$span = $tracer->startAndActivateSpan('session.generate.span' . time());
$tracer->setActiveSpan($span);
$span = $tracer->startAndActivateSpan('session.generate.span' . microtime(true));

echo sprintf(
PHP_EOL . 'Exporting Trace: %s, Parent: %s, Span: %s',
$span->getContext()->getTraceId(),
$span->getParent() ? $span->getParent()->getSpanId() : 'None',
$span->getContext()->getSpanId()
);

$span->setAttribute('remote_ip', '1.2.3.4')
->setAttribute('country', 'USA');
Expand Down
12 changes: 8 additions & 4 deletions examples/AlwaysOnZipkinExample.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,17 @@
->addSpanProcessor(new SimpleSpanProcessor($zipkinExporter))
->getTracer('io.opentelemetry.contrib.php');

echo PHP_EOL . sprintf('Trace with id %s started ', $tracer->getActiveSpan()->getContext()->getTraceId());

for ($i = 0; $i < 5; $i++) {
// start a span, register some events
$timestamp = Clock::get()->timestamp();
$span = $tracer->startAndActivateSpan('session.generate.span.' . time());
$tracer->setActiveSpan($span);
$span = $tracer->startAndActivateSpan('session.generate.span.' . microtime(true));

echo sprintf(
PHP_EOL . 'Exporting Trace: %s, Parent: %s, Span: %s',
$span->getContext()->getTraceId(),
$span->getParent() ? $span->getParent()->getSpanId() : 'None',
$span->getContext()->getSpanId()
);

$span->setAttribute('remote_ip', '1.2.3.4')
->setAttribute('country', 'USA');
Expand Down
150 changes: 150 additions & 0 deletions sdk/Trace/NoopSpan.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Sdk\Trace;

use OpenTelemetry\Trace as API;

class NoopSpan implements \OpenTelemetry\Trace\Span
{
/** @var SpanContext */
private $context;

/** @var API\Attributes */
private $attributes;

/** @var API\Links */
private $links;
// @todo when links will be implemented, this attribute should be initialized properly

/** @var API\Events */
private $events;

/** @var API\SpanStatus */
private $status;

public function __construct()
{
$this->context = new SpanContext(
SpanContext::INVALID_TRACE,
SpanContext::INVALID_SPAN,
0,
[]
);

$this->attributes = new Attributes();
$this->events = new Events();
$this->status = SpanStatus::ok();
}

public function getSpanName(): string
{
return '';
}

public function getContext(): API\SpanContext
{
return $this->context;
}

public function getParent(): ?API\SpanContext
{
return null;
}

public function getStartEpochTimestamp(): int
{
return 0;
}

public function getStart(): int
{
return 0;
}

public function getEnd(): ?int
{
return 0;
}

public function getAttributes(): API\Attributes
{
return $this->attributes;
}

public function getLinks(): API\Links
{
return $this->links;
}

public function getEvents(): API\Events
{
return $this->events;
}

public function getStatus(): API\SpanStatus
{
return $this->status;
}

public function setAttribute(string $key, $value): API\Span
{
return $this;
}

public function addEvent(string $name, int $timestamp, ?API\Attributes $attributes = null): API\Span
{
return $this;
}

public function addLink(API\SpanContext $context, ?API\Attributes $attributes = null): API\Span
{
return $this;
}

public function updateName(string $name): API\Span
{
return $this;
}

public function setSpanStatus(string $code, ?string $description = null): API\Span
{
return $this;
}

public function end(int $timestamp = null): API\Span
{
return $this;
}

public function isRecording(): bool
{
return false;
}

public function isSampled(): bool
{
return false;
}

public function getSpanKind(): int
{
return API\SpanKind::KIND_INTERNAL;
}

public function getCanonicalStatusCode(): string
{
return $this->status->getCanonicalStatusCode();
}

public function getStatusDescription(): string
{
return $this->status->getStatusDescription();
}

public function isStatusOk(): bool
{
return $this->status->isStatusOK();
}
}
4 changes: 2 additions & 2 deletions sdk/Trace/SpanContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@

final class SpanContext implements API\SpanContext
{
private const INVALID_TRACE = '00000000000000000000000000000000';
public const INVALID_TRACE = '00000000000000000000000000000000';
private const VALID_TRACE = '/^[0-9a-f]{32}$/';
private const INVALID_SPAN = '0000000000000000';
public const INVALID_SPAN = '0000000000000000';
private const VALID_SPAN = '/^[0-9a-f]{16}$/';
private const SAMPLED_FLAG = 1;

Expand Down
13 changes: 10 additions & 3 deletions sdk/Trace/SpanProcessor/BatchSpanProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class BatchSpanProcessor implements SpanProcessor
/**
* @var int
*/
private $lastExportTimestamp = 0;
private $lastExportTimestamp;
/**
* @var Clock
*/
Expand Down Expand Up @@ -107,7 +107,7 @@ public function onEnd(API\Span $span): void
*/
public function forceFlush(): void
{
if (null !== $this->exporter && $this->running) {
if (null !== $this->exporter) {
$this->exporter->export($this->queue);
$this->queue = [];
$this->lastExportTimestamp = $this->clock->timestamp();
Expand All @@ -126,7 +126,14 @@ protected function queueReachedLimit(): bool

protected function enoughTimeHasPassed(): bool
{
$now = $this->clock->timestamp();
$now = (int) ($this->clock->now() / 1e6);

// if lastExport never occurred let it start from now on
if (null === $this->lastExportTimestamp) {
$this->lastExportTimestamp = $now;

return false;
}

return $this->scheduledDelayMillis < ($now - $this->lastExportTimestamp);
}
Expand Down
33 changes: 16 additions & 17 deletions sdk/Trace/Tracer.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,12 @@ class Tracer implements API\Tracer
private $spans = [];
private $tail = [];

/**
* @var TracerProvider
*/
/** @var TracerProvider */
private $provider;

/**
* @var ResourceInfo
*/
/** @var ResourceInfo */
private $resource;
/** @var API\SpanContext|null */
private $importedContext;

public function __construct(
TracerProvider $provider,
Expand All @@ -30,22 +27,16 @@ public function __construct(
) {
$this->provider = $provider;
$this->resource = $resource;
$context = $context ?: SpanContext::generate();

// todo: hold up, why do we automatically make a root Span?
$this->active = $this->generateSpanInstance('tracer', $context);
$this->importedContext = $context;
}

/**
* @return Span
*/
public function getActiveSpan(): API\Span
{
while (count($this->tail) && $this->active->getEnd()) {
$this->active = array_pop($this->tail);
}

return $this->active;
return $this->active ?? new NoopSpan();
}

public function setActiveSpan(API\Span $span): void
Expand Down Expand Up @@ -129,8 +120,14 @@ public function startAndActivateSpanFromContext(string $name, SpanContext $paren
*/
public function startAndActivateSpan(string $name): API\Span
{
$parent = $this->getActiveSpan()->getContext();
$context = SpanContext::fork($parent->getTraceId(), $parent->isSampled());
$parentContext = $this->getActiveSpan()->getContext();
$parentContextIsNoopSpan = !$parentContext->isValidContext();

if ($parentContextIsNoopSpan) {
$parentContext = $this->importedContext ?? SpanContext::generate(true);
}

$context = SpanContext::fork($parentContext->getTraceId(), $parentContext->isSampled());
$span = $this->generateSpanInstance($name, $context);

if ($span->isRecording()) {
Expand All @@ -141,6 +138,7 @@ public function startAndActivateSpan(string $name): API\Span

return $this->active;
}

public function getSpans(): array
{
return $this->spans;
Expand All @@ -166,6 +164,7 @@ private function generateSpanInstance(string $name, API\SpanContext $context): S
if ($this->active) {
$parent = $this->getActiveSpan()->getContext();
}

$span = new Span($name, $context, $parent);
$this->spans[] = $span;

Expand Down
Loading

0 comments on commit 618a983

Please sign in to comment.