Skip to content

Commit

Permalink
Add league/statsd implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
cosmastech authored Jul 2, 2024
2 parents 047d0b6 + 69a0b89 commit 35f8e2f
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 3 deletions.
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
"require-dev": {
"phpunit/phpunit": "^11.2.5",
"nesbot/carbon": "^3.6.0",
"friendsofphp/php-cs-fixer": "^3.59"
"friendsofphp/php-cs-fixer": "^3.59",
"league/statsd": "^2.0.0"
},
"suggest": {
"datadog/php-datadogstatsd": "For DataDog stats",
"nesbot/carbon": "For using Carbon as psr/clock-implementation"
"nesbot/carbon": "For using Carbon as psr/clock-implementation",
"league/statsd": "For generic statsd clients"
},
"autoload": {
"psr-4": {
Expand Down
5 changes: 5 additions & 0 deletions src/Clients/Datadog/DatadogStatsDClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,9 @@ public function updateStats(array|string $stats, int $delta = 1, $sampleRate = 1
{
$this->datadogClient->updateStats($stats, $delta, $sampleRate, $this->normalizeTags($tags));
}

public function getClient(): DogStatsd
{
return $this->datadogClient;
}
}
13 changes: 12 additions & 1 deletion src/Clients/InMemory/InMemoryClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,13 @@ public function updateStats(array|string $stats, int $delta = 1, $sampleRate = 1
$now = $this->clock->now();

foreach ($stats as $stat) {
$this->stats->count[] = new InMemoryCountRecord($stat, $delta, $sampleRate, $this->normalizeTags($tags), $now);
$this->stats->count[] = new InMemoryCountRecord(
$stat,
$delta,
$sampleRate,
$this->normalizeTags($tags),
$now
);
}
}

Expand All @@ -118,4 +124,9 @@ public function reset(): void
{
$this->stats = new InMemoryStatsRecord();
}

public function getClient(): null
{
return null;
}
}
113 changes: 113 additions & 0 deletions src/Clients/League/LeagueStatsDClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php

namespace Cosmastech\StatsDClient\Clients\League;

use Cosmastech\StatsDClient\Clients\Concerns\TagNormalizerAwareTrait;
use Cosmastech\StatsDClient\Clients\Contracts\TagNormalizerAware;
use Cosmastech\StatsDClient\Clients\StatsDClient;
use Cosmastech\StatsDClient\Utility\SampleRateDecider\Contracts\SampleRateSendDecider as SampleRateSendDeciderInterface;
use Cosmastech\StatsDClient\Utility\SampleRateDecider\SampleRateSendDecider;
use League\StatsD\Client;
use League\StatsD\Exception\ConfigurationException;
use League\StatsD\Exception\ConnectionException;
use League\StatsD\StatsDClient as LeagueStatsDClientInterface;

class LeagueStatsDClient implements StatsDClient, TagNormalizerAware
{
use TagNormalizerAwareTrait;

public function __construct(
protected readonly LeagueStatsDClientInterface $leagueStatsDClient,
protected readonly SampleRateSendDeciderInterface $sampleRateSendDecider
) {
}

/**
* @throws ConfigurationException
*/
public static function fromConfig(
array $config,
string $instanceName = 'default',
?SampleRateSendDeciderInterface $sampleRateSendDecider = null
): static {
$instance = Client::instance($instanceName);
$instance->configure($config);

return new static($instance, $sampleRateSendDecider ?? new SampleRateSendDecider());
}

/**
* @throws ConnectionException
*/
public function timing(string $stat, float $durationMs, float $sampleRate = 1.0, array $tags = []): void
{
if (! $this->sampleRateSendDecider->decide($sampleRate)) {
return;
}

$this->leagueStatsDClient->timing($stat, $durationMs, $this->normalizeTags($tags));
}

/**
* @throws ConnectionException
*/
public function gauge(string $stat, float $value, float $sampleRate = 1.0, array $tags = []): void
{
if (! $this->sampleRateSendDecider->decide($sampleRate)) {
return;
}

$this->leagueStatsDClient->gauge($stat, $value, $tags);
}

public function histogram(string $stat, float $value, float $sampleRate = 1.0, array $tags = []): void
{
trigger_error("histogram is not implemented for this client");
}

public function distribution(string $stat, float $value, float $sampleRate = 1.0, array $tags = []): void
{
trigger_error("distribution is not implemented for this client");
}

/**
* @throws ConnectionException
*/
public function set(string $stat, float|string $value, float $sampleRate = 1.0, array $tags = []): void
{
if (! $this->sampleRateSendDecider->decide($sampleRate)) {
return;
}

$this->leagueStatsDClient->set($stat, $value, $tags);
}

/**
* @throws ConnectionException
*/
public function increment(array|string $stats, float $sampleRate = 1.0, array $tags = [], int $value = 1): void
{
$this->leagueStatsDClient->increment($stats, $value, $sampleRate, $tags);
}

/**
* @throws ConnectionException
*/
public function decrement(array|string $stats, float $sampleRate = 1.0, array $tags = [], int $value = 1): void
{
$this->leagueStatsDClient->decrement($stats, $value, $sampleRate, $tags);
}

/**
* @throws ConnectionException
*/
public function updateStats(array|string $stats, int $delta = 1, $sampleRate = 1.0, $tags = null): void
{
$this->increment($stats, $sampleRate, $tags, $delta);
}

public function getClient(): LeagueStatsDClientInterface
{
return $this->leagueStatsDClient;
}
}
2 changes: 2 additions & 0 deletions src/Clients/StatsDClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ public function increment(array|string $stats, float $sampleRate = 1.0, array $t
public function decrement(array|string $stats, float $sampleRate = 1.0, array $tags = [], int $value = 1): void;

public function updateStats(array|string $stats, int $delta = 1, $sampleRate = 1.0, $tags = null): void;

public function getClient(): mixed;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Cosmastech\StatsDClient\Utility\SampleRateDecider\Contracts;

interface SampleRateSendDecider
{
public function decide(float $sampleRate): bool;
}
21 changes: 21 additions & 0 deletions src/Utility/SampleRateDecider/SampleRateSendDecider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Cosmastech\StatsDClient\Utility\SampleRateDecider;

use Cosmastech\StatsDClient\Utility\SampleRateDecider\Contracts\SampleRateSendDecider as SampleRateSendDeciderInterface;

class SampleRateSendDecider implements SampleRateSendDeciderInterface
{
public function decide(float $sampleRate): bool
{
if ($sampleRate >= 1) {
return true;
}

if ((mt_rand() / mt_getrandmax()) <= $sampleRate) {
return true;
}

return false;
}
}
13 changes: 13 additions & 0 deletions tests/Clients/InMemory/InMemoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ public function setTagNormalizer()
$this->assertEqualsCanonicalizing([["my-tags" => "are-here"]], $tagNormalizerSpy->getNormalizeCalls());
}

#[Test]
public function getClient_returnsNull()
{
// Given
$inMemoryClient = new InMemoryClient(new ClockStub(new DateTimeImmutable()));

// When
$client = $inMemoryClient->getClient();

// Then
self::assertNull($client);
}

private static function assertEachRecordWithinStatsRecordIsEmpty(InMemoryStatsRecord $record): void
{
self::assertEmpty($record->distribution);
Expand Down
24 changes: 24 additions & 0 deletions tests/Clients/League/LeagueStatsDClientTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Cosmastech\StatsDClient\Tests\Clients\League;

use Cosmastech\StatsDClient\Clients\League\LeagueStatsDClient;
use Cosmastech\StatsDClient\Tests\BaseTestCase;
use League\StatsD\StatsDClient;
use PHPUnit\Framework\Attributes\Test;

class LeagueStatsDClientTest extends BaseTestCase
{
#[Test]
public function getClient_returnsLeagueStatsDClient()
{
// Given
$leagueStatsDClient = LeagueStatsDClient::fromConfig([]);

// When
$client = $leagueStatsDClient->getClient();

// Then
self::assertInstanceOf(StatsDClient::class, $client);
}
}
62 changes: 62 additions & 0 deletions tests/Utility/SampleRateDecider/SampleRateSendDeciderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace Cosmastech\StatsDClient\Tests\Utility\SampleRateDecider;

use Cosmastech\StatsDClient\Tests\BaseTestCase;
use Cosmastech\StatsDClient\Utility\SampleRateDecider\SampleRateSendDecider;
use PHPUnit\Framework\Attributes\Test;

class SampleRateSendDeciderTest extends BaseTestCase
{
#[Test]
public function decide_sampleRateEquals1_returnsTrue()
{
// Given
$sampleRate = 1;

// When
$actual = (new SampleRateSendDecider())->decide($sampleRate);

// Then
self::assertTrue($actual);
}

#[Test]
public function decide_sampleRateAbove1_returnsTrue()
{
// Given
$sampleRate = 1.01;

// When
$actual = (new SampleRateSendDecider())->decide($sampleRate);

// Then
self::assertTrue($actual);
}

#[Test]
public function decide_sampleRateEquals0_returnsFalse()
{
// Given
$sampleRate = 0.0;

// When
$actual = (new SampleRateSendDecider())->decide($sampleRate);

// Then
self::assertFalse($actual);
}

#[Test]
public function decide_sampleRateBelow0_returnsFalse()
{
// Given
$sampleRate = -110.0;

// When
$actual = (new SampleRateSendDecider())->decide($sampleRate);

// Then
self::assertFalse($actual);
}
}

0 comments on commit 35f8e2f

Please sign in to comment.