Skip to content
This repository was archived by the owner on Oct 3, 2023. It is now read-only.

Commit fd401f3

Browse files
authored
Add detached spans API (#62)
* Add generic Context class * Context can fetch single values * ContextTracer now uses generic Context * Scope/context should now play nicely with the extension * Remove Context::setCurrent * Add @internal annotation for Context::reset() * Add ability to set multiple context values at once * Remove default span name * Add ScopeTest * Delegate fetching of the current SpanContext to the tracer impl. Each implementation handles SpanContext differently. * php cs fixes * Update README documentation * Fix static function call in documentation * Adding withSpan tests * Extension can accept spanId as option when starting a span * Remove unused 'use' statements
1 parent 0a0ffb8 commit fd401f3

23 files changed

Lines changed: 453 additions & 219 deletions

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,16 @@ $pi = RequestTracer::inSpan(['name' => 'expensive-operation'], 'calculatePi', [1
124124
### Explicit Span Management
125125

126126
```php
127-
RequestTracer::startSpan(['name' => 'expensive-operation']);
127+
// Creates a detached span
128+
$span = RequestTracer::startSpan(['name' => 'expensive-operation']);
129+
130+
// Opens a scope that attaches the span to the current context
131+
$scope = RequestTracer::withSpan($span);
128132
try {
129133
$pi = calculatePi(1000);
130134
} finally {
131-
// Make sure we close the span to avoid mismatched span boundaries
132-
RequestTracer::endSpan();
135+
// Closes the scope (ends the span)
136+
$scope->close();
133137
}
134138
```
135139

ext/opencensus_trace_span.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,9 @@ int opencensus_trace_span_apply_span_options(opencensus_trace_span_t *span, zval
356356
span->name = zend_string_copy(Z_STR_P(v));
357357
} else if (strcmp(ZSTR_VAL(k), "kind") == 0) {
358358
span->kind = Z_LVAL_P(v);
359-
}
359+
} else if (strcmp(ZSTR_VAL(k), "spanId") == 0) {
360+
span->span_id = zend_string_copy(Z_STR_P(v));
361+
}
360362
} ZEND_HASH_FOREACH_END();
361363
return SUCCESS;
362364
}

ext/tests/context_span_id.phpt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
OpenCensus Trace: Trace Context from provided spanId
3+
--FILE--
4+
<?php
5+
6+
opencensus_trace_set_context("traceid", '12345678');
7+
opencensus_trace_begin('foo', ['spanId' => 'abcd1234']);
8+
$context = opencensus_trace_context();
9+
opencensus_trace_finish();
10+
echo "Context trace id: " . $context->traceId() . PHP_EOL;
11+
echo "Context span id: " . $context->spanId() . PHP_EOL;
12+
13+
$spans = opencensus_trace_list();
14+
if (count($spans) == 1) {
15+
$span = $spans[0];
16+
echo "Span id: " . $span->spanId() . PHP_EOL;
17+
echo "Parent span id: " . $span->parentSpanId() . PHP_EOL;
18+
} else {
19+
echo "Wrong number of spans. Expected 1, got " . count($spans) . PHP_EOL;
20+
}
21+
?>
22+
--EXPECT--
23+
Context trace id: traceid
24+
Context span id: abcd1234
25+
Span id: abcd1234
26+
Parent span id: 12345678

src/Core/Context.php

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ public function value($key, $default = null)
100100
: $default;
101101
}
102102

103+
/**
104+
* Returns all the contained data.
105+
*
106+
* @return array
107+
*/
108+
public function values()
109+
{
110+
return $this->values;
111+
}
112+
103113
/**
104114
* Attaches this context, thus entering a new scope within which this
105115
* context is current(). The previously current context is returned.
@@ -128,16 +138,6 @@ public function detach(Context $toAttach)
128138
self::$current = $toAttach;
129139
}
130140

131-
/**
132-
* Returns all the contained data.
133-
*
134-
* @return array
135-
*/
136-
public function values()
137-
{
138-
return $this->values;
139-
}
140-
141141
/**
142142
* Returns the context associated with the current scope, will never return
143143
* null.

src/Core/Scope.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
/**
3+
* Copyright 2017 OpenCensus Authors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace OpenCensus\Core;
19+
20+
/**
21+
* This class in an implementation of a generic scope that has something to
22+
* execute when the scope finishes.
23+
*
24+
* Example:
25+
* ```
26+
* $scope = RequestTracer::withSpan($span);
27+
* try {
28+
* return do_something();
29+
* } finally {
30+
* $scope->close();
31+
* }
32+
* ```
33+
*/
34+
class Scope
35+
{
36+
/**
37+
* @var callable
38+
*/
39+
private $callback;
40+
41+
/**
42+
* @var array
43+
*/
44+
private $args;
45+
46+
/**
47+
* Creates a new Scope
48+
*
49+
* @param callable $callback
50+
*/
51+
public function __construct(callable $callback, $args = [])
52+
{
53+
$this->callback = $callback;
54+
$this->args = $args;
55+
}
56+
57+
/**
58+
* Close and clean up the scope. Runs the initial callback provided.
59+
*/
60+
public function close()
61+
{
62+
call_user_func_array($this->callback, $this->args);
63+
}
64+
}

src/Trace/Exporter/StackdriverExporter.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
use Google\Cloud\Trace\TraceClient;
2323
use Google\Cloud\Trace\TraceSpan;
2424
use OpenCensus\Trace\Tracer\TracerInterface;
25-
use OpenCensus\Trace\Span as OpenCensusSpan;
25+
use OpenCensus\Trace\Span;
2626

2727
/**
2828
* This implementation of the ExporterInterface use the BatchRunner to provide
@@ -166,7 +166,7 @@ public function report(TracerInterface $tracer)
166166

167167
// build a Trace object and assign Spans
168168
$trace = self::$client->trace(
169-
$tracer->context()->traceId()
169+
$tracer->spanContext()->traceId()
170170
);
171171
$trace->setSpans($spans);
172172

@@ -203,8 +203,8 @@ public function processSpans(TracerInterface $tracer, $headers = null)
203203
public function convertSpans(TracerInterface $tracer)
204204
{
205205
$spanKindMap = [
206-
OpenCensusSpan::SPAN_KIND_CLIENT => TraceSpan::SPAN_KIND_RPC_CLIENT,
207-
OpenCensusSpan::SPAN_KIND_SERVER => TraceSpan::SPAN_KIND_RPC_SERVER
206+
Span::SPAN_KIND_CLIENT => TraceSpan::SPAN_KIND_RPC_CLIENT,
207+
Span::SPAN_KIND_SERVER => TraceSpan::SPAN_KIND_RPC_SERVER
208208
];
209209

210210
// transform OpenCensus Spans to Google\Cloud\Spans

src/Trace/Exporter/ZipkinExporter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public function convertSpans(TracerInterface $tracer, $headers = null)
107107
$headers = $headers ?: $_SERVER;
108108
$spans = $tracer->spans();
109109
$rootSpan = $spans[0];
110-
$traceId = $tracer->context()->traceId();
110+
$traceId = $tracer->spanContext()->traceId();
111111

112112
$kindMap = [
113113
Span::SPAN_KIND_CLIENT => 'CLIENT',

src/Trace/Integrations/Guzzle/EventSubscriber.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
namespace OpenCensus\Trace\Integrations\Guzzle;
1919

20+
use OpenCensus\Core\Scope;
2021
use OpenCensus\Trace\RequestTracer;
2122
use OpenCensus\Trace\Propagator\HttpHeaderPropagator;
2223
use OpenCensus\Trace\Propagator\PropagatorInterface;
@@ -46,6 +47,11 @@ class EventSubscriber implements SubscriberInterface
4647
*/
4748
private $propagator;
4849

50+
/**
51+
* @var Scope
52+
*/
53+
private $scope;
54+
4955
/**
5056
* Create a new Guzzle event listener that creates trace spans and propagates the current
5157
* trace context to the downstream request.
@@ -82,13 +88,14 @@ public function onBefore(BeforeEvent $event)
8288
if ($context = RequestTracer::context()) {
8389
$request->setHeader($this->propagator->key(), $this->propagator->formatter->serialize($context));
8490
}
85-
RequestTracer::startSpan([
91+
$span = RequestTracer::startSpan([
8692
'name' => 'GuzzleHttp::request',
8793
'labels' => [
8894
'method' => $request->getMethod(),
8995
'uri' => $request->getUrl()
9096
]
9197
]);
98+
$this->scope = RequestTracer::withSpan($span);
9299
}
93100

94101
/**
@@ -99,6 +106,6 @@ public function onBefore(BeforeEvent $event)
99106
*/
100107
public function onEnd(EndEvent $event)
101108
{
102-
RequestTracer::endSpan();
109+
$this->scope->close();
103110
}
104111
}

src/Trace/RequestHandler.php

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
namespace OpenCensus\Trace;
1919

20+
use OpenCensus\Core\Context;
21+
use OpenCensus\Core\Scope;
2022
use OpenCensus\Trace\Exporter\ExporterInterface;
2123
use OpenCensus\Trace\Sampler\SamplerInterface;
2224
use OpenCensus\Trace\Span;
@@ -47,6 +49,11 @@ class RequestHandler
4749
*/
4850
private $tracer;
4951

52+
/**
53+
* @var Scope
54+
*/
55+
private $scope;
56+
5057
/**
5158
* Create a new RequestHandler.
5259
*
@@ -71,33 +78,34 @@ public function __construct(
7178
? $options['headers']
7279
: $_SERVER;
7380

74-
$context = $propagator->extract($headers);
81+
$spanContext = $propagator->extract($headers);
7582

7683
// If the context force disables tracing, don't consult the $sampler.
77-
if ($context->enabled() !== false) {
78-
$context->setEnabled($context->enabled() || $sampler->shouldSample());
84+
if ($spanContext->enabled() !== false) {
85+
$spanContext->setEnabled($spanContext->enabled() || $sampler->shouldSample());
7986
}
8087

8188
// If the request was provided with a trace context header, we need to send it back with the response
8289
// including whether the request was sampled or not.
83-
if ($context->fromHeader()) {
90+
if ($spanContext->fromHeader()) {
8491
if (!headers_sent()) {
85-
foreach ($propagator->inject($context, $headers) as $header => $value) {
92+
foreach ($propagator->inject($spanContext, $headers) as $header => $value) {
8693
header("$header: $value");
8794
}
8895
}
8996
}
9097

91-
$this->tracer = $context->enabled()
92-
? extension_loaded('opencensus') ? new ExtensionTracer($context) : new ContextTracer($context)
98+
$this->tracer = $spanContext->enabled()
99+
? extension_loaded('opencensus') ? new ExtensionTracer($spanContext) : new ContextTracer($spanContext)
93100
: new NullTracer();
94101

95102
$spanOptions = $options + [
96103
'startTime' => $this->startTimeFromHeaders($headers),
97104
'name' => $this->nameFromHeaders($headers),
98105
'labels' => []
99106
];
100-
$this->tracer->startSpan($spanOptions);
107+
$span = $this->tracer->startSpan($spanOptions);
108+
$this->scope = $this->tracer->withSpan($span);
101109

102110
register_shutdown_function([$this, 'onExit']);
103111
}
@@ -109,10 +117,7 @@ public function __construct(
109117
*/
110118
public function onExit()
111119
{
112-
// close all open spans
113-
do {
114-
$span = $this->tracer->endSpan();
115-
} while ($span);
120+
$this->scope->close();
116121
$this->reporter->report($this->tracer);
117122
}
118123

@@ -156,23 +161,15 @@ public function startSpan(array $spanOptions = [])
156161
}
157162

158163
/**
159-
* Explicitly finish the current context (Span).
160-
*
161-
* @return Span
162-
*/
163-
public function endSpan()
164-
{
165-
return $this->tracer->endSpan();
166-
}
167-
168-
/**
169-
* Return the current context (Span)
164+
* Attaches the provided span as the current span and returns a Scope
165+
* object which must be closed.
170166
*
171-
* @return SpanContext
167+
* @param Span $span
168+
* @return Scope
172169
*/
173-
public function context()
170+
public function withSpan(Span $span)
174171
{
175-
return $this->tracer->context();
172+
return $this->tracer->withSpan($span);
176173
}
177174

178175
private function startTimeFromHeaders(array $headers)

0 commit comments

Comments
 (0)