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

Commit e87763b

Browse files
dragoscirjanchingor13
authored andcommitted
fixing hexdec on a 16 char hex number, which could actually exceed INT64 (#8)
* fixing hexdec on a 16 char hex number, which could actually exceed INT64 * Updated method names to camel case * Update SpanConverter.php * Update SpanConverter.php * added pre-install-* script limiter + unit tests for bc and gmp math * fixed typo * lint fixes * fixed integration test * linter fix * linter fix * better way of handling big math * brought back missing big math code * fixed bcmath prezence test * removed un-necessary checks since they were made @ install anyways * no cache * fixed tests * converting SpanConverter to interface & implementations * overzealous replace - const * removed old code * additional tests for bcmath * code clean & lint fix * test fix * test fixes * moved hexdec converter methods into their own classes having their own interface * lint fix * comments and interface removal * added variable declaration for `spanConverter`
1 parent 6b4d2f0 commit e87763b

12 files changed

+358
-41
lines changed

.circleci/config.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
unit-config: &unit-config
22
steps:
33
# library requires sockets extension
4-
- run: sudo docker-php-ext-install sockets
4+
- run: sudo apt-get install -y libgmp-dev re2c libmhash-dev libmcrypt-dev file && sudo ln -s /usr/include/x86_64-linux-gnu/gmp.h /usr/local/include/
5+
- run: sudo docker-php-ext-install bcmath gmp sockets
56

67
- checkout
78

89
# Download and cache dependencies
910
- restore_cache:
1011
keys:
11-
- v1-dependencies-{{ checksum "composer.json" }}
12+
- v1-dependencies-{{ checksum "composer.json" }}-v1
1213
# fallback to using the latest cache if no exact match is found
13-
- v1-dependencies-
14+
- v1-dependencies-v1-
1415

1516
- run: composer install -n --prefer-dist
1617

Dockerfile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ WORKDIR /workspace
1919

2020
# current user is circleci
2121
RUN sudo chown -R $(whoami) /workspace
22-
RUN sudo docker-php-ext-install sockets
22+
RUN sudo apt-get update && sudo apt-get install -y libgmp-dev re2c libmhash-dev libmcrypt-dev file && sudo ln -s /usr/include/x86_64-linux-gnu/gmp.h /usr/local/include/
23+
RUN sudo docker-php-ext-install bcmath gmp sockets
2324

2425
RUN composer install -n --prefer-dist
2526

26-
ENTRYPOINT []
27+
ENTRYPOINT []

composer.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,8 @@
2525
"psr-4": {
2626
"OpenCensus\\Trace\\Exporter\\": "src/"
2727
}
28+
},
29+
"scripts": {
30+
"post-dependencies-solving": "OpenCensus\\Trace\\Exporter\\Installer::checkPhpExtDependency"
2831
}
2932
}

src/Installer.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace OpenCensus\Trace\Exporter;
4+
5+
class Installer
6+
{
7+
public static function checkPhpExtDependency()
8+
{
9+
if (!extension_loaded('bcmath') && !extension_loaded('gmp')) {
10+
throw new \Exception('`opencensus-php-exporter-jaeger` requires one of the two extensions to be '
11+
. 'installed: `php-bcmath` or `php-gmp`');
12+
}
13+
}
14+
}

src/Jaeger/HexdecConverter.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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\Jaeger;
19+
20+
use OpenCensus\Trace\Exporter\Jaeger\HexdecConverterInterface;
21+
22+
/**
23+
* Hexdec converter class. Used for converting hex string values into numbers by using
24+
* `gmp` as large numbers library.
25+
*/
26+
class HexdecConverter implements HexdecConverterInterface
27+
{
28+
29+
const MAX_INT_64S = '9223372036854775807';
30+
31+
/**
32+
* Hexdec convertion method for large numbers with limitation to PhP's signed INT64, using gmp.
33+
* Warning: Method may not work with hex numbers larger than 8 'digits'.
34+
*
35+
* @param str $hex
36+
* @return number
37+
*/
38+
public function convert($hex)
39+
{
40+
$dec = 0;
41+
$len = strlen($hex);
42+
for ($i = 1; $i <= $len; $i++) {
43+
$dec = gmp_add($dec, gmp_mul(strval(hexdec($hex[$i - 1])), gmp_pow('16', strval($len - $i))));
44+
}
45+
if (gmp_cmp($dec, self::MAX_INT_64S) > 0) {
46+
$dec = gmp_sub(gmp_and($dec, self::MAX_INT_64S), gmp_add(self::MAX_INT_64S, '1'));
47+
}
48+
return intval($dec);
49+
}
50+
}

src/Jaeger/HexdecConverterBcMath.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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\Jaeger;
19+
20+
use OpenCensus\Trace\Exporter\Jaeger\HexdecConverterInterface;
21+
22+
/**
23+
* Hexdec converter class. Used for converting hex string values into numbers by using
24+
* `bcmath` as large numbers library.
25+
*/
26+
class HexdecConverterBcMath implements HexdecConverterInterface
27+
{
28+
29+
const MAX_INT_64S = '9223372036854775807';
30+
31+
/**
32+
* Hexdec convertion method for big data with limitation to PhP's signed INT64, using bcmath.
33+
* Warning: Method may not work with hex numbers larger than 8 'digits'.
34+
*
35+
* @param str $hex
36+
* @return number
37+
*/
38+
public function convert($hex)
39+
{
40+
$dec = 0;
41+
$len = strlen($hex);
42+
for ($i = 1; $i <= $len; $i++) {
43+
$dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
44+
}
45+
if (bccomp($dec, self::MAX_INT_64S) > 0) {
46+
$dec = bcsub(bcsub($dec, self::MAX_INT_64S), bcadd(self::MAX_INT_64S, '2'));
47+
}
48+
return intval($dec);
49+
}
50+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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\Jaeger;
19+
20+
/**
21+
* Hexdec converter interface. Used for converting hex string values into numbers by using
22+
* large numbers math libraries like Gmp or BCMath.
23+
*/
24+
interface HexdecConverterInterface
25+
{
26+
/**
27+
* Hexdec convertion method for large numbers with limitation to PhP's signed INT64.
28+
*
29+
* @param str $hex
30+
* @return number
31+
*/
32+
public function convert($hex);
33+
}

src/Jaeger/SpanConverter.php

Lines changed: 57 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,37 @@
3535
*/
3636
class SpanConverter
3737
{
38+
3839
/**
39-
* Convert an OpenCensus Span to its Jaeger Thrift representation.
40+
*@var HexdecConverterInterface
41+
*/
42+
private $hexdec = null;
43+
44+
/**
45+
* Create a new Span Converter.
4046
*
41-
* @access private
47+
* @param array $options [optional] {
48+
* @type HexdecConvertor convertor for hex values
49+
* }
50+
*/
51+
public function __construct(array $options = [])
52+
{
53+
$this->hexdec = empty($options['hexdecConverter']) ? new HexdecConverter() : $options['hexdecConverter'];
54+
}
55+
56+
/**
57+
* Convert an OpenCensus Span to its Jaeger Thrift representation.
4258
*
4359
* @param SpanData $span The span to convert.
4460
* @return Span The Jaeger Thrift Span representation.
4561
*/
46-
public static function convertSpan(SpanData $span)
62+
public function convertSpan(SpanData $span)
4763
{
48-
$startTime = self::convertTimestamp($span->startTime());
49-
$endTime = self::convertTimestamp($span->endTime());
50-
$spanId = hexdec($span->spanId());
51-
$parentSpanId = hexdec($span->parentSpanId());
52-
list($highTraceId, $lowTraceId) = self::convertTraceId($span->traceId());
64+
$startTime = $this->convertTimestamp($span->startTime());
65+
$endTime = $this->convertTimestamp($span->endTime());
66+
$spanId = $this->hexdec->convert($span->spanId());
67+
$parentSpanId = $this->hexdec->convert($span->parentSpanId());
68+
list($highTraceId, $lowTraceId) = $this->convertTraceId($span->traceId());
5369

5470
return new Span([
5571
'traceIdLow' => $lowTraceId,
@@ -61,15 +77,15 @@ public static function convertSpan(SpanData $span)
6177
'flags' => 0,
6278
'startTime' => $startTime,
6379
'duration' => $endTime - $startTime,
64-
'tags' => self::convertTags($span->attributes()),
65-
'logs' => self::convertLogs($span->timeEvents())
80+
'tags' => $this->convertTags($span->attributes()),
81+
'logs' => $this->convertLogs($span->timeEvents())
6682
]);
6783
}
6884

6985
/**
7086
* Convert an associative array of $key => $value to Jaeger Tags.
7187
*/
72-
public static function convertTags(array $attributes)
88+
public function convertTags(array $attributes)
7389
{
7490
$tags = [];
7591
foreach ($attributes as $key => $value) {
@@ -82,33 +98,48 @@ public static function convertTags(array $attributes)
8298
return $tags;
8399
}
84100

85-
private static function convertLogs(array $timeEvents)
101+
/**
102+
*
103+
* @param array $timeEvents
104+
* @return array
105+
*/
106+
private function convertLogs(array $timeEvents)
86107
{
87108
return array_map(function (TimeEvent $timeEvent) {
88109
if ($timeEvent instanceof Annotation) {
89-
return self::convertAnnotation($timeEvent);
110+
return $this->convertAnnotation($timeEvent);
90111
} elseif ($timeEvent instanceof MessageEvent) {
91-
return self::convertMessageEvent($timeEvent);
112+
return $this->convertMessageEvent($timeEvent);
92113
} else {
93114
}
94115
}, $timeEvents);
95116
}
96117

97-
private static function convertAnnotation(Annotation $annotation)
118+
/**
119+
*
120+
* @param Annotation $annotation
121+
* @return Log
122+
*/
123+
private function convertAnnotation(Annotation $annotation)
98124
{
99125
return new Log([
100-
'timestamp' => self::convertTimestamp($annotation->time()),
101-
'fields' => self::convertTags($annotation->attributes() + [
126+
'timestamp' => $this->convertTimestamp($annotation->time()),
127+
'fields' => $this->convertTags($annotation->attributes() + [
102128
'description' => $annotation->description()
103129
])
104130
]);
105131
}
106132

107-
private static function convertMessageEvent(MessageEvent $messageEvent)
133+
/**
134+
*
135+
* @param MessageEvent $messageEvent
136+
* @return Log
137+
*/
138+
private function convertMessageEvent(MessageEvent $messageEvent)
108139
{
109140
return new Log([
110-
'timestamp' => self::convertTimestamp($messageEvent->time()),
111-
'fields' => self::convertTags([
141+
'timestamp' => $this->convertTimestamp($messageEvent->time()),
142+
'fields' => $this->convertTags([
112143
'type' => $messageEvent->type(),
113144
'id' => $messageEvent->id(),
114145
'uncompressedSize' => $messageEvent->uncompressedSize(),
@@ -120,20 +151,23 @@ private static function convertMessageEvent(MessageEvent $messageEvent)
120151
/**
121152
* Return the given timestamp as an int in milliseconds.
122153
*/
123-
private static function convertTimestamp(\DateTimeInterface $dateTime)
154+
private function convertTimestamp(\DateTimeInterface $dateTime)
124155
{
125156
return (int)((float) $dateTime->format('U.u') * 1000 * 1000);
126157
}
127158

128159
/**
129160
* Split the provided hexId into 2 64-bit integers (16 hex chars each).
130161
* Returns array of 2 int values.
162+
*
163+
* @param str $hexId
164+
* @return array
131165
*/
132-
private static function convertTraceId($hexId)
166+
private function convertTraceId($hexId)
133167
{
134168
return array_slice(
135169
array_map(
136-
'hexdec',
170+
[$this->hexdec, 'convert'],
137171
str_split(
138172
substr(
139173
str_pad($hexId, 32, "0", STR_PAD_LEFT),

src/JaegerExporter.php

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,27 @@ class JaegerExporter implements ExporterInterface
3636
/**
3737
* @var string
3838
*/
39-
protected $host;
39+
private $host;
4040

4141
/**
4242
* @var int
4343
*/
44-
protected $port;
44+
private $port;
4545

4646
/**
4747
* @var Process
4848
*/
49-
protected $process;
49+
private $process;
5050

5151
/**
5252
* @var AgentIf
5353
*/
54-
protected $client;
54+
private $client;
55+
56+
/**
57+
* @var SpanConverter
58+
*/
59+
private $spanConverter;
5560

5661
/**
5762
* Create a new Jaeger Exporter.
@@ -76,9 +81,10 @@ public function __construct($serviceName, array $options = [])
7681
];
7782
$this->host = $options['host'];
7883
$this->port = (int) $options['port'];
84+
$this->spanConverter = empty($options['spanConverter']) ? new SpanConverter() : $options['spanConverter'];
7985
$this->process = new Process([
8086
'serviceName' => $serviceName,
81-
'tags' => SpanConverter::convertTags($options['tags'])
87+
'tags' => $this->spanConverter->convertTags($options['tags'])
8288
]);
8389
$this->client = $options['client'];
8490
}
@@ -98,7 +104,7 @@ public function export(array $spans)
98104
$client = $this->client ?: new UDPClient($this->host, $this->port);
99105
$batch = new Batch([
100106
'process' => $this->process,
101-
'spans' => array_map([SpanConverter::class, 'convertSpan'], $spans)
107+
'spans' => array_map([$this->spanConverter, 'convertSpan'], $spans)
102108
]);
103109

104110
$client->emitBatch($batch);

0 commit comments

Comments
 (0)