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

Commit 4c1844e

Browse files
authored
Move StackdriverExporter implementation from OpenCensus core (#2)
* Move StackdriverExporter. Split conversion into SpanConverter * Fix copyright years * Convert Links, TimeEvents, Status * Make timestamp regexp a constant
1 parent 93ef550 commit 4c1844e

6 files changed

Lines changed: 555 additions & 9 deletions

File tree

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"type": "library",
55
"require": {
66
"php": ">=5.6",
7-
"opencensus/opencensus": "^0.2",
7+
"opencensus/opencensus": "^0.4",
88
"google/cloud-trace": "^0.4"
99
},
1010
"require-dev": {
@@ -21,7 +21,7 @@
2121
"minimum-stability": "stable",
2222
"autoload": {
2323
"psr-4": {
24-
"OpenCensus\\Trace\\": "src/Trace/"
24+
"OpenCensus\\Trace\\Exporter\\": "src/"
2525
}
2626
}
2727
}

src/Stackdriver/SpanConverter.php

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<?php
2+
/**
3+
* Copyright 2018 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\Trace\Exporter\Stackdriver;
19+
20+
use Google\Cloud\Trace\Annotation;
21+
use Google\Cloud\Trace\Link;
22+
use Google\Cloud\Trace\MessageEvent;
23+
use Google\Cloud\Trace\Span;
24+
use Google\Cloud\Trace\Status;
25+
use OpenCensus\Trace\Annotation as OCAnnotation;
26+
use OpenCensus\Trace\Link as OCLink;
27+
use OpenCensus\Trace\MessageEvent as OCMessageEvent;
28+
use OpenCensus\Trace\Span as OCSpan;
29+
use OpenCensus\Trace\Status as OCStatus;
30+
use OpenCensus\Trace\SpanData;
31+
32+
/**
33+
* This class handles converting from the OpenCensus data model into its
34+
* Stackdriver Trace representation.
35+
*/
36+
class SpanConverter
37+
{
38+
const ATTRIBUTE_MAP = [
39+
OCSpan::ATTRIBUTE_HOST => '/http/host',
40+
OCSpan::ATTRIBUTE_PORT => '/http/port',
41+
OCSpan::ATTRIBUTE_METHOD => '/http/method',
42+
OCSpan::ATTRIBUTE_PATH => '/http/url',
43+
OCSpan::ATTRIBUTE_USER_AGENT => '/http/user_agent',
44+
OCSpan::ATTRIBUTE_STATUS_CODE => '/http/status_code'
45+
];
46+
const LINK_TYPE_MAP = [
47+
OCLink::TYPE_UNSPECIFIED => Link::TYPE_UNSPECIFIED,
48+
OCLink::TYPE_CHILD_LINKED_SPAN => Link::TYPE_CHILD_LINKED_SPAN,
49+
OCLink::TYPE_PARENT_LINKED_SPAN => Link::TYPE_PARENT_LINKED_SPAN
50+
];
51+
const MESSAGE_TYPE_MAP = [
52+
OCMessageEvent::TYPE_UNSPECIFIED => MessageEvent::TYPE_UNSPECIFIED,
53+
OCMessageEvent::TYPE_SENT => MessageEvent::TYPE_SENT,
54+
OCMessageEvent::TYPE_RECEIVED => MessageEvent::TYPE_RECEIVED
55+
];
56+
57+
/**
58+
* Convert an OpenCensus SpanData to its Stackdriver Trace representation.
59+
*
60+
* @access private
61+
*
62+
* @param SpanData $span The span to convert.
63+
* @return Span
64+
*/
65+
public static function convertSpan(SpanData $span)
66+
{
67+
$spanOptions = [
68+
'name' => $span->name(),
69+
'startTime' => $span->startTime(),
70+
'endTime' => $span->endTime(),
71+
'spanId' => $span->spanId(),
72+
'attributes' => self::convertAttributes($span->attributes()),
73+
'stackTrace' => $span->stackTrace(),
74+
'links' => self::convertLinks($span->links()),
75+
'timeEvents' => self::convertTimeEvents($span->timeEvents()),
76+
'status' => self::convertStatus($span->status())
77+
];
78+
if ($span->parentSpanId()) {
79+
$spanOptions['parentSpanId'] = $span->parentSpanId();
80+
}
81+
return new Span($span->traceId(), $spanOptions);
82+
}
83+
84+
private static function convertAttributes(array $attributes)
85+
{
86+
$newAttributes = [];
87+
foreach ($attributes as $key => $value) {
88+
if (array_key_exists($key, self::ATTRIBUTE_MAP)) {
89+
$newAttributes[self::ATTRIBUTE_MAP[$key]] = $value;
90+
} else {
91+
$newAttributes[$key] = $value;
92+
}
93+
}
94+
return $newAttributes;
95+
}
96+
97+
private static function convertLinks(array $links)
98+
{
99+
return array_map(function (OCLink $link) {
100+
return new Link($link->traceId(), $link->spanId(), [
101+
'type' => self::LINK_TYPE_MAP[$link->type()],
102+
'attributes' => $link->attributes()
103+
]);
104+
}, $links);
105+
}
106+
107+
private static function convertTimeEvents(array $events)
108+
{
109+
$newEvents = [];
110+
foreach ($events as $event) {
111+
if ($event instanceof OCAnnotation) {
112+
$newEvents[] = self::convertAnnotation($event);
113+
} elseif ($event instanceof OCMessageEvent) {
114+
$newEvents[] = self::convertMessageEvent($event);
115+
}
116+
}
117+
return $newEvents;
118+
}
119+
120+
private static function convertAnnotation(OCAnnotation $annotation)
121+
{
122+
return new Annotation($annotation->description(), [
123+
'attributes' => $annotation->attributes()
124+
]);
125+
}
126+
127+
private static function convertMessageEvent(OCMessageEvent $messageEvent)
128+
{
129+
return new MessageEvent($messageEvent->id(), [
130+
'type' => self::MESSAGE_TYPE_MAP[$messageEvent->type()],
131+
'uncompressedSizeBytes' => $messageEvent->uncompressedSize(),
132+
'compressedSizeBytes' => $messageEvent->compressedSize()
133+
]);
134+
}
135+
136+
private static function convertStatus(OCStatus $status = null)
137+
{
138+
if ($status) {
139+
return new Status($status->code(), $status->message());
140+
} else {
141+
return null;
142+
}
143+
}
144+
}

src/StackdriverExporter.php

Lines changed: 106 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,13 @@
1717

1818
namespace OpenCensus\Trace\Exporter;
1919

20-
use OpenCensus\Trace\Tracer\TracerInterface;
20+
use Google\Cloud\Core\Batch\BatchRunner;
21+
use Google\Cloud\Core\Batch\BatchTrait;
22+
use Google\Cloud\Trace\TraceClient;
23+
use Google\Cloud\Trace\Span;
24+
use Google\Cloud\Trace\Trace;
25+
use OpenCensus\Trace\SpanData;
26+
use OpenCensus\Trace\Exporter\Stackdriver\SpanConverter;
2127

2228
/**
2329
* This implementation of the ExporterInterface use the BatchRunner to provide
@@ -65,15 +71,110 @@
6571
*/
6672
class StackdriverExporter implements ExporterInterface
6773
{
74+
use BatchTrait;
75+
76+
/**
77+
* @var TraceClient
78+
*/
79+
private static $client;
80+
81+
/**
82+
* @var bool
83+
*/
84+
private $async;
85+
86+
/**
87+
* @var array
88+
*/
89+
private $clientConfig;
90+
91+
/**
92+
* Create a TraceExporter that utilizes background batching.
93+
*
94+
* @param array $options [optional] Configuration options.
95+
*
96+
* @type TraceClient $client A trace client used to instantiate traces
97+
* to be delivered to the batch queue.
98+
* @type bool $debugOutput Whether or not to output debug information.
99+
* Please note debug output currently only applies in CLI based
100+
* applications. **Defaults to** `false`.
101+
* @type array $batchOptions A set of options for a BatchJob. See
102+
* <a href="https://github.com/GoogleCloudPlatform/google-cloud-php/blob/master/src/Core/Batch/BatchJob.php">\Google\Cloud\Core\Batch\BatchJob::__construct()</a>
103+
* for more details.
104+
* **Defaults to** ['batchSize' => 1000,
105+
* 'callPeriod' => 2.0,
106+
* 'workerNum' => 2].
107+
* @type array $clientConfig Configuration options for the Trace client
108+
* used to handle processing of batch items.
109+
* For valid options please see
110+
* <a href="https://github.com/GoogleCloudPlatform/google-cloud-php/blob/master/src/Trace/TraceClient.php">\Google\Cloud\Trace\TraceClient::__construct()</a>.
111+
* @type BatchRunner $batchRunner A BatchRunner object. Mainly used for
112+
* the tests to inject a mock. **Defaults to** a newly created
113+
* BatchRunner.
114+
* @type string $identifier An identifier for the batch job.
115+
* **Defaults to** `stackdriver-trace`.
116+
* @type bool $async Whether we should try to use the batch runner.
117+
* **Defaults to** `false`.
118+
*/
119+
public function __construct(array $options = [])
120+
{
121+
$options += [
122+
'async' => false,
123+
'client' => null
124+
];
125+
$this->async = $options['async'];
126+
$this->setCommonBatchProperties($options + [
127+
'identifier' => 'stackdriver-trace',
128+
'batchMethod' => 'insertBatch'
129+
]);
130+
self::$client = $options['client'] ?: new TraceClient($this->clientConfig);
131+
}
132+
68133
/**
69134
* Report the provided Trace to a backend.
70135
*
71-
* @param TracerInterface $tracer
136+
* @param SpanData[] $spans
72137
* @return bool
73138
*/
74-
public function report(TracerInterface $tracer)
139+
public function export(array $spans)
75140
{
76-
// TODO: Implement this
77-
return false;
141+
if (empty($spans)) {
142+
return false;
143+
}
144+
145+
// Pull the traceId from the first span
146+
$spans = array_map([SpanConverter::class, 'convertSpan'], $spans);
147+
$trace = self::$client->trace(
148+
$spans[0]->traceId()
149+
);
150+
151+
// build a Trace object and assign Spans
152+
$trace->setSpans($spans);
153+
154+
try {
155+
if ($this->async) {
156+
return $this->batchRunner->submitItem($this->identifier, $trace);
157+
} else {
158+
return self::$client->insert($trace);
159+
}
160+
} catch (\Exception $e) {
161+
error_log('Reporting the Trace data failed: ' . $e->getMessage());
162+
return false;
163+
}
164+
}
165+
166+
/**
167+
* Returns an array representation of a callback which will be used to write
168+
* batch items.
169+
*
170+
* @return array
171+
*/
172+
protected function getCallback()
173+
{
174+
if (!isset(self::$client)) {
175+
self::$client = new TraceClient($this->clientConfig);
176+
}
177+
178+
return [self::$client, $this->batchMethod];
78179
}
79180
}

0 commit comments

Comments
 (0)