Skip to content

Commit 618a983

Browse files
beniaminBnMcG
andauthored
remove global span (#171)
* 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]>
1 parent 89856e5 commit 618a983

File tree

12 files changed

+357
-51
lines changed

12 files changed

+357
-51
lines changed

Context/Context.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class Context
4040
* @param mixed|null $value
4141
* @param Context|null $parent Reference to the parent object
4242
*/
43-
public final function __construct(?ContextKey $key=null, $value=null, $parent=null)
43+
final public function __construct(?ContextKey $key=null, $value=null, $parent=null)
4444
{
4545
$this->key = $key;
4646
$this->value = $value;

api/Trace/Span.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public function getStart(): int;
2424

2525
/**
2626
* Returns system time clock value (MonotonicClock) when the Span was stopped
27-
* @return int
27+
* @return int|null
2828
*/
2929
public function getEnd(): ?int;
3030

examples/AlwaysOnJaegerExample.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use OpenTelemetry\Sdk\Trace\Clock;
99
use OpenTelemetry\Sdk\Trace\Sampler\AlwaysOnSampler;
1010
use OpenTelemetry\Sdk\Trace\SamplingResult;
11-
use OpenTelemetry\Sdk\Trace\SpanProcessor\SimpleSpanProcessor;
11+
use OpenTelemetry\Sdk\Trace\SpanProcessor\BatchSpanProcessor;
1212
use OpenTelemetry\Sdk\Trace\TracerProvider;
1313
use OpenTelemetry\Trace as API;
1414

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

35-
echo PHP_EOL . sprintf('Trace with id %s started ', $tracer->getActiveSpan()->getContext()->getTraceId());
36-
3735
for ($i = 0; $i < 5; $i++) {
3836
// start a span, register some events
3937
$timestamp = Clock::get()->timestamp();
40-
$span = $tracer->startAndActivateSpan('session.generate.span' . time());
41-
$tracer->setActiveSpan($span);
38+
$span = $tracer->startAndActivateSpan('session.generate.span' . microtime(true));
39+
40+
echo sprintf(
41+
PHP_EOL . 'Exporting Trace: %s, Parent: %s, Span: %s',
42+
$span->getContext()->getTraceId(),
43+
$span->getParent() ? $span->getParent()->getSpanId() : 'None',
44+
$span->getContext()->getSpanId()
45+
);
4246

4347
$span->setAttribute('remote_ip', '1.2.3.4')
4448
->setAttribute('country', 'USA');

examples/AlwaysOnZipkinExample.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,17 @@
3232
->addSpanProcessor(new SimpleSpanProcessor($zipkinExporter))
3333
->getTracer('io.opentelemetry.contrib.php');
3434

35-
echo PHP_EOL . sprintf('Trace with id %s started ', $tracer->getActiveSpan()->getContext()->getTraceId());
36-
3735
for ($i = 0; $i < 5; $i++) {
3836
// start a span, register some events
3937
$timestamp = Clock::get()->timestamp();
40-
$span = $tracer->startAndActivateSpan('session.generate.span.' . time());
41-
$tracer->setActiveSpan($span);
38+
$span = $tracer->startAndActivateSpan('session.generate.span.' . microtime(true));
39+
40+
echo sprintf(
41+
PHP_EOL . 'Exporting Trace: %s, Parent: %s, Span: %s',
42+
$span->getContext()->getTraceId(),
43+
$span->getParent() ? $span->getParent()->getSpanId() : 'None',
44+
$span->getContext()->getSpanId()
45+
);
4246

4347
$span->setAttribute('remote_ip', '1.2.3.4')
4448
->setAttribute('country', 'USA');

sdk/Trace/NoopSpan.php

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\Sdk\Trace;
6+
7+
use OpenTelemetry\Trace as API;
8+
9+
class NoopSpan implements \OpenTelemetry\Trace\Span
10+
{
11+
/** @var SpanContext */
12+
private $context;
13+
14+
/** @var API\Attributes */
15+
private $attributes;
16+
17+
/** @var API\Links */
18+
private $links;
19+
// @todo when links will be implemented, this attribute should be initialized properly
20+
21+
/** @var API\Events */
22+
private $events;
23+
24+
/** @var API\SpanStatus */
25+
private $status;
26+
27+
public function __construct()
28+
{
29+
$this->context = new SpanContext(
30+
SpanContext::INVALID_TRACE,
31+
SpanContext::INVALID_SPAN,
32+
0,
33+
[]
34+
);
35+
36+
$this->attributes = new Attributes();
37+
$this->events = new Events();
38+
$this->status = SpanStatus::ok();
39+
}
40+
41+
public function getSpanName(): string
42+
{
43+
return '';
44+
}
45+
46+
public function getContext(): API\SpanContext
47+
{
48+
return $this->context;
49+
}
50+
51+
public function getParent(): ?API\SpanContext
52+
{
53+
return null;
54+
}
55+
56+
public function getStartEpochTimestamp(): int
57+
{
58+
return 0;
59+
}
60+
61+
public function getStart(): int
62+
{
63+
return 0;
64+
}
65+
66+
public function getEnd(): ?int
67+
{
68+
return 0;
69+
}
70+
71+
public function getAttributes(): API\Attributes
72+
{
73+
return $this->attributes;
74+
}
75+
76+
public function getLinks(): API\Links
77+
{
78+
return $this->links;
79+
}
80+
81+
public function getEvents(): API\Events
82+
{
83+
return $this->events;
84+
}
85+
86+
public function getStatus(): API\SpanStatus
87+
{
88+
return $this->status;
89+
}
90+
91+
public function setAttribute(string $key, $value): API\Span
92+
{
93+
return $this;
94+
}
95+
96+
public function addEvent(string $name, int $timestamp, ?API\Attributes $attributes = null): API\Span
97+
{
98+
return $this;
99+
}
100+
101+
public function addLink(API\SpanContext $context, ?API\Attributes $attributes = null): API\Span
102+
{
103+
return $this;
104+
}
105+
106+
public function updateName(string $name): API\Span
107+
{
108+
return $this;
109+
}
110+
111+
public function setSpanStatus(string $code, ?string $description = null): API\Span
112+
{
113+
return $this;
114+
}
115+
116+
public function end(int $timestamp = null): API\Span
117+
{
118+
return $this;
119+
}
120+
121+
public function isRecording(): bool
122+
{
123+
return false;
124+
}
125+
126+
public function isSampled(): bool
127+
{
128+
return false;
129+
}
130+
131+
public function getSpanKind(): int
132+
{
133+
return API\SpanKind::KIND_INTERNAL;
134+
}
135+
136+
public function getCanonicalStatusCode(): string
137+
{
138+
return $this->status->getCanonicalStatusCode();
139+
}
140+
141+
public function getStatusDescription(): string
142+
{
143+
return $this->status->getStatusDescription();
144+
}
145+
146+
public function isStatusOk(): bool
147+
{
148+
return $this->status->isStatusOK();
149+
}
150+
}

sdk/Trace/SpanContext.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99

1010
final class SpanContext implements API\SpanContext
1111
{
12-
private const INVALID_TRACE = '00000000000000000000000000000000';
12+
public const INVALID_TRACE = '00000000000000000000000000000000';
1313
private const VALID_TRACE = '/^[0-9a-f]{32}$/';
14-
private const INVALID_SPAN = '0000000000000000';
14+
public const INVALID_SPAN = '0000000000000000';
1515
private const VALID_SPAN = '/^[0-9a-f]{16}$/';
1616
private const SAMPLED_FLAG = 1;
1717

sdk/Trace/SpanProcessor/BatchSpanProcessor.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class BatchSpanProcessor implements SpanProcessor
4141
/**
4242
* @var int
4343
*/
44-
private $lastExportTimestamp = 0;
44+
private $lastExportTimestamp;
4545
/**
4646
* @var Clock
4747
*/
@@ -107,7 +107,7 @@ public function onEnd(API\Span $span): void
107107
*/
108108
public function forceFlush(): void
109109
{
110-
if (null !== $this->exporter && $this->running) {
110+
if (null !== $this->exporter) {
111111
$this->exporter->export($this->queue);
112112
$this->queue = [];
113113
$this->lastExportTimestamp = $this->clock->timestamp();
@@ -126,7 +126,14 @@ protected function queueReachedLimit(): bool
126126

127127
protected function enoughTimeHasPassed(): bool
128128
{
129-
$now = $this->clock->timestamp();
129+
$now = (int) ($this->clock->now() / 1e6);
130+
131+
// if lastExport never occurred let it start from now on
132+
if (null === $this->lastExportTimestamp) {
133+
$this->lastExportTimestamp = $now;
134+
135+
return false;
136+
}
130137

131138
return $this->scheduledDelayMillis < ($now - $this->lastExportTimestamp);
132139
}

sdk/Trace/Tracer.php

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,12 @@ class Tracer implements API\Tracer
1313
private $spans = [];
1414
private $tail = [];
1515

16-
/**
17-
* @var TracerProvider
18-
*/
16+
/** @var TracerProvider */
1917
private $provider;
20-
21-
/**
22-
* @var ResourceInfo
23-
*/
18+
/** @var ResourceInfo */
2419
private $resource;
20+
/** @var API\SpanContext|null */
21+
private $importedContext;
2522

2623
public function __construct(
2724
TracerProvider $provider,
@@ -30,22 +27,16 @@ public function __construct(
3027
) {
3128
$this->provider = $provider;
3229
$this->resource = $resource;
33-
$context = $context ?: SpanContext::generate();
34-
35-
// todo: hold up, why do we automatically make a root Span?
36-
$this->active = $this->generateSpanInstance('tracer', $context);
30+
$this->importedContext = $context;
3731
}
3832

39-
/**
40-
* @return Span
41-
*/
4233
public function getActiveSpan(): API\Span
4334
{
4435
while (count($this->tail) && $this->active->getEnd()) {
4536
$this->active = array_pop($this->tail);
4637
}
4738

48-
return $this->active;
39+
return $this->active ?? new NoopSpan();
4940
}
5041

5142
public function setActiveSpan(API\Span $span): void
@@ -129,8 +120,14 @@ public function startAndActivateSpanFromContext(string $name, SpanContext $paren
129120
*/
130121
public function startAndActivateSpan(string $name): API\Span
131122
{
132-
$parent = $this->getActiveSpan()->getContext();
133-
$context = SpanContext::fork($parent->getTraceId(), $parent->isSampled());
123+
$parentContext = $this->getActiveSpan()->getContext();
124+
$parentContextIsNoopSpan = !$parentContext->isValidContext();
125+
126+
if ($parentContextIsNoopSpan) {
127+
$parentContext = $this->importedContext ?? SpanContext::generate(true);
128+
}
129+
130+
$context = SpanContext::fork($parentContext->getTraceId(), $parentContext->isSampled());
134131
$span = $this->generateSpanInstance($name, $context);
135132

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

142139
return $this->active;
143140
}
141+
144142
public function getSpans(): array
145143
{
146144
return $this->spans;
@@ -166,6 +164,7 @@ private function generateSpanInstance(string $name, API\SpanContext $context): S
166164
if ($this->active) {
167165
$parent = $this->getActiveSpan()->getContext();
168166
}
167+
169168
$span = new Span($name, $context, $parent);
170169
$this->spans[] = $span;
171170

0 commit comments

Comments
 (0)