-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
25 changed files
with
1,034 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Luzrain\PHPStreamServer\BundledPlugin\Metrics; | ||
|
||
use Luzrain\PHPStreamServer\BundledPlugin\Metrics\Exception\LabelsNotMatchException; | ||
use Luzrain\PHPStreamServer\BundledPlugin\Metrics\Internal\Message\IncreaseCounterMessage; | ||
use Luzrain\PHPStreamServer\BundledPlugin\Metrics\Internal\Metric; | ||
use Revolt\EventLoop; | ||
|
||
final class Counter extends Metric | ||
{ | ||
private array $buffer = []; | ||
|
||
/** | ||
* @param array<string, string> $labels | ||
* @throws LabelsNotMatchException | ||
*/ | ||
public function inc(array $labels = []): void | ||
{ | ||
$this->add(1, $labels); | ||
} | ||
|
||
/** | ||
* @param array<string, string> $labels | ||
* @throws LabelsNotMatchException | ||
*/ | ||
public function add(int $value, array $labels = []): void | ||
{ | ||
$this->checkLabels($labels); | ||
|
||
$key = \hash('xxh128', \json_encode($labels)); | ||
$this->buffer[$key] ??= [0, '']; | ||
$buffer = &$this->buffer[$key][0]; | ||
$callbackId = &$this->buffer[$key][1]; | ||
$buffer += $value; | ||
|
||
if ($callbackId !== '') { | ||
return; | ||
} | ||
|
||
$callbackId = EventLoop::delay(self::FLUSH_TIMEOUT, function() use($labels, &$buffer, $key) { | ||
$value = $buffer; | ||
unset($this->buffer[$key]); | ||
$this->messageBus->dispatch(new IncreaseCounterMessage($this->namespace, $this->name, $labels, $value)); | ||
}); | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
src/BundledPlugin/Metrics/Exception/LabelsNotMatchException.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Luzrain\PHPStreamServer\BundledPlugin\Metrics\Exception; | ||
|
||
final class LabelsNotMatchException extends \InvalidArgumentException | ||
{ | ||
public function __construct(array $labels, array $givenLabels) | ||
{ | ||
if ($labels === [] && $givenLabels !== []) { | ||
$text = \sprintf('Labels do not match. Should not contain labels, %s assigned', \json_encode($givenLabels)); | ||
} else if($labels !== [] && $givenLabels === []) { | ||
$text = \sprintf('Labels do not match. Should contain %s labels, no labels assigned', \json_encode($labels)); | ||
} else { | ||
$text = \sprintf('Labels do not match. Should contain %s labels, %s assigned', \json_encode($labels), \json_encode($givenLabels)); | ||
} | ||
|
||
parent::__construct($text); | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
src/BundledPlugin/Metrics/Exception/MetricNotFoundException.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Luzrain\PHPStreamServer\BundledPlugin\Metrics\Exception; | ||
|
||
final class MetricNotFoundException extends \InvalidArgumentException | ||
{ | ||
public function __construct(string $type, string $namespace, string $name) | ||
{ | ||
parent::__construct(\sprintf('%s metric "%s_%s" not found', \ucfirst($type), $namespace, $name)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Luzrain\PHPStreamServer\BundledPlugin\Metrics; | ||
|
||
use Luzrain\PHPStreamServer\BundledPlugin\Metrics\Exception\LabelsNotMatchException; | ||
use Luzrain\PHPStreamServer\BundledPlugin\Metrics\Internal\Message\SetGaugeMessage; | ||
use Luzrain\PHPStreamServer\BundledPlugin\Metrics\Internal\Metric; | ||
use Revolt\EventLoop; | ||
|
||
final class Gauge extends Metric | ||
{ | ||
private array $buffer = []; | ||
|
||
/** | ||
* @param array<string, string> $labels | ||
* @throws LabelsNotMatchException | ||
*/ | ||
public function set(float $value, array $labels = []): void | ||
{ | ||
$this->checkLabels($labels); | ||
|
||
$key = \hash('xxh128', \json_encode($labels).'set'); | ||
$this->buffer[$key] ??= [0, '']; | ||
$buffer = &$this->buffer[$key][0]; | ||
$callbackId = &$this->buffer[$key][1]; | ||
$buffer = $value; | ||
|
||
if ($callbackId !== '') { | ||
return; | ||
} | ||
|
||
$callbackId = EventLoop::delay(self::FLUSH_TIMEOUT, function() use($labels, &$buffer, $key) { | ||
$value = $buffer; | ||
unset($this->buffer[$key]); | ||
$this->messageBus->dispatch(new SetGaugeMessage($this->namespace, $this->name, $labels, $value, false)); | ||
}); | ||
} | ||
|
||
/** | ||
* @param array<string, string> $labels | ||
* @throws LabelsNotMatchException | ||
*/ | ||
public function inc(array $labels = []): void | ||
{ | ||
$this->add(1, $labels); | ||
} | ||
|
||
/** | ||
* @param array<string, string> $labels | ||
* @throws LabelsNotMatchException | ||
*/ | ||
public function dec(array $labels = []): void | ||
{ | ||
$this->add(-1, $labels); | ||
} | ||
|
||
/** | ||
* @param array<string, string> $labels | ||
* @throws LabelsNotMatchException | ||
*/ | ||
public function add(float $value, array $labels = []): void | ||
{ | ||
$this->checkLabels($labels); | ||
|
||
$key = \hash('xxh128', \json_encode($labels).'add'); | ||
$this->buffer[$key] ??= [0, '']; | ||
$buffer = &$this->buffer[$key][0]; | ||
$callbackId = &$this->buffer[$key][1]; | ||
$buffer += $value; | ||
|
||
if ($callbackId !== '') { | ||
return; | ||
} | ||
|
||
$callbackId = EventLoop::delay(self::FLUSH_TIMEOUT, function() use($labels, &$buffer, $key) { | ||
$value = $buffer; | ||
unset($this->buffer[$key]); | ||
$this->messageBus->dispatch(new SetGaugeMessage($this->namespace, $this->name, $labels, $value, true)); | ||
}); | ||
} | ||
|
||
/** | ||
* @param array<string, string> $labels | ||
* @throws LabelsNotMatchException | ||
*/ | ||
public function sub(float $value, array $labels = []): void | ||
{ | ||
$this->add(-$value, $labels); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Luzrain\PHPStreamServer\BundledPlugin\Metrics; | ||
|
||
use Luzrain\PHPStreamServer\BundledPlugin\Metrics\Exception\LabelsNotMatchException; | ||
use Luzrain\PHPStreamServer\BundledPlugin\Metrics\Internal\Message\ObserveHistorgamMessage; | ||
use Luzrain\PHPStreamServer\BundledPlugin\Metrics\Internal\Metric; | ||
use Revolt\EventLoop; | ||
|
||
final class Histogram extends Metric | ||
{ | ||
private array $buffer = []; | ||
|
||
/** | ||
* @param array<string, string> $labels | ||
* @throws LabelsNotMatchException | ||
*/ | ||
public function observe(float $value, array $labels = []): void | ||
{ | ||
$this->checkLabels($labels); | ||
|
||
$key = \hash('xxh128', \json_encode($labels)); | ||
$this->buffer[$key] ??= [[], '']; | ||
$buffer = &$this->buffer[$key][0]; | ||
$callbackId = &$this->buffer[$key][1]; | ||
$buffer[] = $value; | ||
|
||
if ($callbackId !== '') { | ||
return; | ||
} | ||
|
||
$callbackId = EventLoop::delay(self::FLUSH_TIMEOUT, function() use($labels, &$buffer, $key) { | ||
$values = $buffer; | ||
unset($this->buffer[$key]); | ||
$this->messageBus->dispatch(new ObserveHistorgamMessage($this->namespace, $this->name, $labels, $values)); | ||
}); | ||
} | ||
|
||
/** | ||
* Creates count buckets, where the lowest bucket has an upper bound of start and each following bucket's upper | ||
* bound is factor times the previous bucket's upper bound. | ||
* The returned slice is meant to be used for the Buckets field of HistogramOpts. | ||
* | ||
* @return list<float> | ||
*/ | ||
public static function exponentialBuckets(float $start, float $factor, int $count): array | ||
{ | ||
$start > 0 ?: throw new \InvalidArgumentException('$start must be a positive integer'); | ||
$factor > 0 ?: throw new \InvalidArgumentException('$factor must greater than 1'); | ||
$count >= 1 ?: throw new \InvalidArgumentException('$count must be a positive integer'); | ||
|
||
$buckets = []; | ||
for ($i = 0; $i < $count; $i++) { | ||
$buckets[] = $start; | ||
$start *= $factor; | ||
} | ||
|
||
return $buckets; | ||
} | ||
|
||
/** | ||
* Creates count regular buckets, each width wide, where the lowest bucket has an upper bound of start. | ||
* The returned slice is meant to be used for the Buckets field of HistogramOpts. | ||
* | ||
* @return list<float> | ||
*/ | ||
public static function linearBuckets(float $start, float $width, int $count): array | ||
{ | ||
$width > 0 ?: throw new \InvalidArgumentException('$width must greater than 1'); | ||
$count >= 1 ?: throw new \InvalidArgumentException('$count must be a positive integer'); | ||
|
||
$buckets = []; | ||
for ($i = 0; $i < $count; $i++) { | ||
$buckets[] = $start; | ||
$start += $width; | ||
} | ||
|
||
return $buckets; | ||
} | ||
|
||
/** | ||
* Creates default buckets. | ||
* | ||
* @return list<float> | ||
*/ | ||
public static function defaultBuckets(): array | ||
{ | ||
return [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]; | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
src/BundledPlugin/Metrics/Internal/Message/GetMetricMessage.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Luzrain\PHPStreamServer\BundledPlugin\Metrics\Internal\Message; | ||
|
||
use Luzrain\PHPStreamServer\MessageBus\MessageInterface; | ||
|
||
/** | ||
* @implements MessageInterface<GetMetricResponse|false> | ||
*/ | ||
final readonly class GetMetricMessage implements MessageInterface | ||
{ | ||
public const TYPE_COUNTER = 'counter'; | ||
public const TYPE_GAUGE = 'gauge'; | ||
public const TYPE_HISTOGRAM = 'histogram'; | ||
public const TYPE_SUMMARY = 'summary'; | ||
|
||
private function __construct( | ||
public string $type, | ||
public string $namespace, | ||
public string $name, | ||
) { | ||
} | ||
|
||
public static function counter(string $namespace, string $name): self | ||
{ | ||
return new self(self::TYPE_COUNTER, $namespace, $name); | ||
} | ||
|
||
public static function gauge(string $namespace, string $name): self | ||
{ | ||
return new self(self::TYPE_GAUGE, $namespace, $name); | ||
} | ||
|
||
public static function histogram(string $namespace, string $name): self | ||
{ | ||
return new self(self::TYPE_HISTOGRAM, $namespace, $name); | ||
} | ||
|
||
public static function summary(string $namespace, string $name): self | ||
{ | ||
return new self(self::TYPE_SUMMARY, $namespace, $name); | ||
} | ||
} |
Oops, something went wrong.