Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add opentsdb container #3

Merged
merged 4 commits into from
Sep 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/.docker_data_hbase
/.idea
/vendor
.DS_STORE
Expand Down
33 changes: 29 additions & 4 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
php80:
container_name: php-opentsdb-client-lib-80
container_name: php-opentsdb-client-lib-php-80
image: php-opentsdb-client-lib-80
build:
context: ./
Expand All @@ -9,9 +9,11 @@ services:
working_dir: /app
volumes:
- ./:/app
networks:
- php-opentsdb-client-lib

php81:
container_name: php-opentsdb-client-lib-81
container_name: php-opentsdb-client-lib-php-81
image: php-opentsdb-client-lib-81
build:
context: ./
Expand All @@ -20,9 +22,11 @@ services:
working_dir: /app
volumes:
- ./:/app
networks:
- php-opentsdb-client-lib

php82:
container_name: php-opentsdb-client-lib-82
container_name: php-opentsdb-client-lib-php-82
image: php-opentsdb-client-lib-82
build:
context: ./
Expand All @@ -31,9 +35,11 @@ services:
working_dir: /app
volumes:
- ./:/app
networks:
- php-opentsdb-client-lib

php83:
container_name: php-opentsdb-client-lib-83
container_name: php-opentsdb-client-lib-php-83
image: php-opentsdb-client-lib-83
build:
context: ./
Expand All @@ -42,3 +48,22 @@ services:
working_dir: /app
volumes:
- ./:/app
networks:
- php-opentsdb-client-lib

opentsdb:
container_name: php-opentsdb-client-lib-opentsdb
image: petergrace/opentsdb-docker:latest
environment:
- WAITSECS=30 # Give time for hbase to shutdown, otherwise data corruption may result
ports:
- 4242:4242
- 60030:60030
networks:
- php-opentsdb-client-lib
volumes:
- "./.docker_data_hbase:/data/hbase"

networks:
php-opentsdb-client-lib:
driver: bridge
7 changes: 5 additions & 2 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
stopOnFailure="true"
stopOnFailure="false"
>
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./test/Unit</directory>
<directory>./test/Unit</directory>
</testsuite>
<testsuite name="Integration">
<directory>./test/Integration</directory>
</testsuite>
</testsuites>
<php>
Expand Down
47 changes: 43 additions & 4 deletions src/DataPoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ final class DataPoint implements
{
/**
* @param int $timestamp A Unix epoch style timestamp in seconds or milliseconds.
* @param int | float | string $value The value to record for this data point.
* @param int | float | string $value The number value to record for this data point.
* @param array<string, string> $tags A map of tag name/tag value pairs. At least one pair must be supplied.
*/
public function __construct(
Expand All @@ -32,9 +32,48 @@ public function __construct(
private int | float | string $value,
private array $tags,
) {
// TODO: Assert metric != ''
// TODO: Assert timestamp > 0
// TODO: Assert at least one tag value pair supplied
if ($this->metric === '') {
throw new \AssertionError(
'Metric name is empty',
);
}
if ($this->timestamp <= 0) {
throw new \AssertionError(
"Invalid timestamp, must be greater than 0, got `$this->timestamp`",
);
}
if ($this->value === '') {
throw new \AssertionError(
'Metric value is empty',
);
}
if (!\is_numeric($this->value)) {
throw new \AssertionError(
"Metric value is not numeric, got `$this->value`",
);
}
if ($this->tags === []) {
throw new \AssertionError(
'At least one tag value pair must be supplied',
);
}
foreach ($this->tags as $tagName => $tagValue) {
if (empty($tagName) || empty($tagValue)) {
throw new \AssertionError(
"Tag name and value are required, got `$tagName=$tagValue`",
);
}
if (!\is_string($tagName)) {
throw new \AssertionError(
"Tag name must be string, got `" . get_debug_type($tagName) . "`",
);
}
if (!\is_string($tagValue)) {
throw new \AssertionError(
"Tag value must be string, got `" . get_debug_type($tagValue) . "`",
);
}
}
}

public function jsonSerialize(): array
Expand Down
19 changes: 19 additions & 0 deletions src/Exception/OpenTsdbConnectionException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

/*
* This file is part of OpenTSDB PHP Client.
*
* (c) Anton Komarev <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Cog\OpenTsdbClient\Exception;

final class OpenTsdbConnectionException extends \RuntimeException implements
OpenTsdbExceptionInterface
{
}
19 changes: 19 additions & 0 deletions src/Exception/OpenTsdbException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

/*
* This file is part of OpenTSDB PHP Client.
*
* (c) Anton Komarev <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Cog\OpenTsdbClient\Exception;

final class OpenTsdbException extends \RuntimeException implements
OpenTsdbExceptionInterface
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

declare(strict_types=1);

namespace Cog\OpenTsdbClient;
namespace Cog\OpenTsdbClient\Exception;

final class OpenTsdbException extends \RuntimeException
interface OpenTsdbExceptionInterface
{
}
89 changes: 62 additions & 27 deletions src/OpenTsdbClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@

namespace Cog\OpenTsdbClient;

use Cog\OpenTsdbClient\Exception\OpenTsdbConnectionException;
use Cog\OpenTsdbClient\Exception\OpenTsdbException;
use GuzzleHttp\Psr7\Request;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\ResponseInterface;

final class OpenTsdbClient
{
Expand All @@ -27,28 +30,14 @@ public function __construct(
/**
* @param list<DataPoint> $dataPointList
*/
public function sendDataPointList(
public function sendDataPointListSilently(
array $dataPointList,
): void {
$request = new Request(
'POST',
$result = $this->sendRequest(
$this->buildUrl('/api/put'),
[
'Content-Type' => 'application/json',
],
json_encode($dataPointList),
);

try {
$result = $this->httpClient->sendRequest($request);
} catch (ClientExceptionInterface $exception) {
throw new OpenTsdbException(
'Failed to send data points to OpenTSDB.',
0,
$exception,
);
}

$statusCode = $result->getStatusCode();

switch ($statusCode) {
Expand All @@ -62,61 +51,107 @@ public function sendDataPointList(
case 301:
throw new OpenTsdbException(
'This may be used in the event that an API call has migrated or should be forwarded to another server.',
11,
1,
);
case 400:
throw new OpenTsdbException(
'Information provided by the API user, via a query string or content data, was in error or missing. This will usually include information in the error body about what parameter caused the issue. Correct the data and try again.',
1,
2,
);
case 404:
throw new OpenTsdbException(
'The requested endpoint or file was not found. This is usually related to the static file endpoint.',
2,
3,
);
case 405:
throw new OpenTsdbException(
'The requested verb or method was not allowed. Please see the documentation for the endpoint you are attempting to access.',
3,
4,
);
case 406:
throw new OpenTsdbException(
'The request could not generate a response in the format specified. For example, if you ask for a PNG file of the logs endpoing, you will get a 406 response since log entries cannot be converted to a PNG image (easily).',
4,
5,
);
case 408:
throw new OpenTsdbException(
'The request has timed out. This may be due to a timeout fetching data from the underlying storage system or other issues.',
5,
6,
);
case 413:
throw new OpenTsdbException(
'The results returned from a query may be too large for the server’s buffers to handle. This can happen if you request a lot of raw data from OpenTSDB. In such cases break your query up into smaller queries and run each individually.',
6,
7,
);
case 500:
throw new OpenTsdbException(
'An internal error occured within OpenTSDB. Make sure all of the systems OpenTSDB depends on are accessible and check the bug list for issues.',
7,
8,
);
case 501:
throw new OpenTsdbException(
'The requested feature has not been implemented yet. This may appear with formatters or when calling methods that depend on plugins.',
8,
9,
);
case 503:
throw new OpenTsdbException(
'A temporary overload has occurred. Check with other users/applications that are interacting with OpenTSDB and determine if you need to reduce requests or scale your system.',
9,
10,
);
default:
throw new OpenTsdbException(
"Failed to send metric to OpenTSDB. Unknown error. HTTP status code `$statusCode`.",
10,
11,
);
}
}

/**
* @param list<DataPoint> $dataPointList
*/
public function sendDataPointList(
array $dataPointList,
): SendDataPointListResponse {
$result = $this->sendRequest(
$this->buildUrl('/api/put?details&summary'),
json_encode($dataPointList),
);

$statusCode = $result->getStatusCode();
$decodedResult = json_decode($result->getBody()->getContents(), true);

return new SendDataPointListResponse(
$statusCode,
$decodedResult['success'],
$decodedResult['failed'],
$decodedResult['errors'],
);
}

private function sendRequest(
string $url,
string $body,
): ResponseInterface {
$request = new Request(
'POST',
$url,
[
'Content-Type' => 'application/json',
],
$body,
);

try {
return $this->httpClient->sendRequest($request);
} catch (ClientExceptionInterface $exception) {
throw new OpenTsdbConnectionException(
'Failed to send data points to OpenTSDB.',
0,
$exception,
);
}
}

private function buildUrl(
string $uri,
): string {
Expand Down
Loading