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

Commit b6be077

Browse files
authored
Change ZipkinReporter to use v2 API (#24)
* Change ZipkinReporterTest to test against the v2 api definition * Update ZipkinReporter to convert spans into the v2 format * Update ZipkinReporter#convertSpans doc block
1 parent 2774ccf commit b6be077

2 files changed

Lines changed: 81 additions & 92 deletions

File tree

src/Trace/Reporter/ZipkinReporter.php

Lines changed: 43 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class ZipkinReporter implements ReporterInterface
5454
* @param int $port The port of the Zipkin server
5555
* @param string $endpoint (optional) The path for the span reporting endpoint. **Defaults to** `/api/v1/spans`
5656
*/
57-
public function __construct($name, $host, $port, $endpoint = '/api/v1/spans')
57+
public function __construct($name, $host, $port, $endpoint = '/api/v2/spans')
5858
{
5959
$this->name = $name;
6060
$this->host = $host;
@@ -95,98 +95,68 @@ public function report(TracerInterface $tracer)
9595
}
9696

9797
/**
98-
* Convert spans into Zipkin's expected JSON output format.
98+
* Convert spans into Zipkin's expected JSON output format. See http://zipkin.io/zipkin-api/#/default/post_spans
99+
* for output format.
99100
*
100-
* @param TracerInterface $tracer
101+
* @param TracerInterface $tracer
102+
* @param array $headers [optional] HTTP headers to parse. **Defaults to** $_SERVER
101103
* @return array Representation of the collected trace spans ready for serialization
102104
*/
103-
public function convertSpans(TracerInterface $tracer)
105+
public function convertSpans(TracerInterface $tracer, $headers = null)
104106
{
107+
$headers = $headers ?: $_SERVER;
105108
$spans = $tracer->spans();
106-
$context = $tracer->context();
107-
$traceId = $context->traceId();
109+
$rootSpan = $spans[0];
110+
$traceId = $tracer->context()->traceId();
111+
112+
$kindMap = [
113+
TraceSpan::SPAN_KIND_CLIENT => 'CLIENT',
114+
TraceSpan::SPAN_KIND_SERVER => 'SERVER',
115+
TraceSpan::SPAN_KIND_PRODUCER => 'PRODUCER',
116+
TraceSpan::SPAN_KIND_CONSUMER => 'CONSUMER'
117+
];
118+
119+
// True is a request to store this span even if it overrides sampling policy.
120+
// This is true when the X-B3-Flags header has a value of 1.
121+
$isDebug = array_key_exists('HTTP_X_B3_FLAGS', $headers) && $headers['HTTP_X_B3_FLAGS'] == '1';
108122

109-
$endpoint = [
123+
// True if we are contributing to a span started by another tracer (ex on a different host).
124+
$isShared = $rootSpan && $rootSpan->parentSpanId() != null;
125+
126+
$localEndpoint = [
127+
'serviceName' => $this->name,
110128
'ipv4' => $this->host,
111-
'port' => $this->port,
112-
'serviceName' => $this->name
129+
'port' => $this->port
113130
];
114131

115-
return array_map(function ($span) use ($traceId, $endpoint) {
132+
$zipkinSpans = [];
133+
foreach ($spans as $span) {
116134
$startTime = (int)((float) $span->startTime()->format('U.u') * 1000 * 1000);
117135
$endTime = (int)((float) $span->endTime()->format('U.u') * 1000 * 1000);
118136
$spanId = str_pad(dechex($span->spanId()), 16, '0', STR_PAD_LEFT);
119137
$parentSpanId = $span->parentSpanId()
120138
? str_pad(dechex($span->parentSpanId()), 16, '0', STR_PAD_LEFT)
121139
: null;
122140

123-
$annotations = [];
124-
switch ($span->kind()) {
125-
case TraceSpan::SPAN_KIND_UNKNOWN:
126-
case TraceSpan::SPAN_KIND_CLIENT:
127-
$annotations = [
128-
[
129-
'endpoint' => $endpoint,
130-
'timestamp' => $startTime,
131-
'value' => 'cs' // client send
132-
],
133-
[
134-
'endpoint' => $endpoint,
135-
'timestamp' => $endTime,
136-
'value' => 'cr' // client receive
137-
]
138-
];
139-
break;
140-
case TraceSpan::SPAN_KIND_SERVER:
141-
$annotations = [
142-
[
143-
'endpoint' => $endpoint,
144-
'timestamp' => $startTime,
145-
'value' => 'sr' // server receive
146-
],
147-
[
148-
'endpoint' => $endpoint,
149-
'timestamp' => $endTime,
150-
'value' => 'ss' // server send
151-
]
152-
];
153-
break;
154-
case TraceSpan::SPAN_KIND_PRODUCER:
155-
$annotations = [
156-
[
157-
'endpoint' => $endpoint,
158-
'timestamp' => $startTime,
159-
'value' => 'ms' // message send
160-
]
161-
];
162-
break;
163-
case TraceSpan::SPAN_KIND_CONSUMER:
164-
$annotations = [
165-
[
166-
'endpoint' => $endpoint,
167-
'timestamp' => $startTime,
168-
'value' => 'mr' // message receive
169-
]
170-
];
171-
break;
172-
}
173-
174-
return [
175-
// 8-byte identifier encoded as 16 lowercase hex characters
176-
'id' => $spanId,
141+
$zipkinSpan = [
177142
'traceId' => $traceId,
178143
'name' => $span->name(),
144+
'parentId' => $parentSpanId,
145+
'id' => $spanId,
179146
'timestamp' => $startTime,
180147
'duration' => $endTime - $startTime,
181-
'annotations' => $annotations,
182-
'binaryAnnotations' => array_map(function ($key, $value) {
183-
return [
184-
'key' => $key,
185-
'value' => $value
186-
];
187-
}, array_keys($span->labels()), $span->labels()),
188-
'parentId' => $parentSpanId
148+
'debug' => $isDebug,
149+
'shared' => $isShared,
150+
'localEndpoint' => $localEndpoint,
151+
'tags' => $span->labels()
189152
];
190-
}, $spans);
153+
if (array_key_exists($span->kind(), $kindMap)) {
154+
$zipkinSpan['kind'] = $kindMap[$span->kind()];
155+
}
156+
157+
$zipkinSpans[] = $zipkinSpan;
158+
}
159+
160+
return $zipkinSpans;
191161
}
192162
}

tests/unit/Trace/Reporter/ZipkinReporterTest.php

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -62,20 +62,14 @@ public function testFormatsTrace()
6262
$this->assertInternalType('string', $span['name']);
6363
$this->assertInternalType('int', $span['timestamp']);
6464
$this->assertInternalType('int', $span['duration']);
65-
$this->assertInternalType('array', $span['annotations']);
66-
foreach ($span['annotations'] as $annotation) {
67-
$this->assertInternalType('array', $annotation['endpoint']);
68-
$this->assertInternalType('string', $annotation['endpoint']['ipv4']);
69-
$this->assertInternalType('int', $annotation['endpoint']['port']);
70-
$this->assertInternalType('string', $annotation['endpoint']['serviceName']);
71-
$this->assertInternalType('int', $annotation['timestamp']);
72-
$this->assertInternalType('string', $annotation['value']);
73-
}
74-
$this->assertInternalType('array', $span['binaryAnnotations']);
75-
foreach ($span['binaryAnnotations'] as $annotation) {
76-
$this->assertInternalType('string', $annotation['key']);
77-
$this->assertInternalType('string', $annotation['value']);
65+
66+
$this->assertInternalType('array', $span['tags']);
67+
foreach ($span['tags'] as $key => $value) {
68+
$this->assertInternalType('string', $key);
69+
$this->assertInternalType('string', $value);
7870
}
71+
$this->assertFalse($span['shared']);
72+
$this->assertFalse($span['debug']);
7973
}
8074
}
8175

@@ -97,11 +91,36 @@ public function testSpanKind()
9791
};
9892

9993
$this->assertCount(5, $spans);
100-
// default unknown spans to client send/receive
101-
$this->assertEquals(['cs', 'cr'], array_map($annotationValue, $spans[0]['annotations']));
102-
$this->assertEquals(['cs', 'cr'], array_map($annotationValue, $spans[1]['annotations']));
103-
$this->assertEquals(['sr', 'ss'], array_map($annotationValue, $spans[2]['annotations']));
104-
$this->assertEquals(['ms'], array_map($annotationValue, $spans[3]['annotations']));
105-
$this->assertEquals(['mr'], array_map($annotationValue, $spans[4]['annotations']));
94+
$this->assertFalse(array_key_exists('kind', $spans[0]));
95+
$this->assertEquals('CLIENT', $spans[1]['kind']);
96+
$this->assertEquals('SERVER', $spans[2]['kind']);
97+
$this->assertEquals('PRODUCER', $spans[3]['kind']);
98+
$this->assertEquals('CONSUMER', $spans[4]['kind']);
99+
}
100+
101+
public function testSpanDebug()
102+
{
103+
$tracer = new ContextTracer(new TraceContext('testtraceid'));
104+
$tracer->inSpan(['name' => 'main'], function () {});
105+
106+
$reporter = new ZipkinReporter('myapp', 'localhost', 9411);
107+
$spans = $reporter->convertSpans($tracer, [
108+
'HTTP_X_B3_FLAGS' => '1'
109+
]);
110+
111+
$this->assertCount(1, $spans);
112+
$this->assertTrue($spans[0]['debug']);
113+
}
114+
115+
public function testSpanShared()
116+
{
117+
$tracer = new ContextTracer(new TraceContext('testtraceid', 12345));
118+
$tracer->inSpan(['name' => 'main'], function () {});
119+
120+
$reporter = new ZipkinReporter('myapp', 'localhost', 9411);
121+
$spans = $reporter->convertSpans($tracer);
122+
123+
$this->assertCount(1, $spans);
124+
$this->assertTrue($spans[0]['shared']);
106125
}
107126
}

0 commit comments

Comments
 (0)