» /rd/lib.framework/Monitoring/Metric/Clients/StatsdClient.php

<?php

declare(strict_types=1);

namespace 
Monitoring\Metric\Clients;

use 
Config\Env;
use 
InvalidArgumentException;
use 
ProxyManager\Exception\DisabledMethodException;
use 
RuntimeException;

class 
StatsdClient implements ClientInterface
{
    public const 
DEFAULT_EVENT_METRIC 'event';
    public const 
METRIC_NAME_REGEXP '|^[a-z]+[a-z0-9_.]{3,}$|iu';

    private 
Connection\ConnectionInterface $connection;
    private 
string $namespace;
    private 
string $eventMetric;

    public function 
__construct(array $config)
    {
        if (empty(
$config['dvp-enabled']) && Env::isDvp()) {
            throw new 
DisabledMethodException('Disabled on DVP');
        }

        if (empty(
$config['statsd'])) {
            throw new 
DisabledMethodException('No statsd section in config');
        }

        if (!
extension_loaded('sockets')) {
            throw new 
RuntimeException('PHP sockets extension is required');
        }

        
$config $config['statsd'];
        if (empty(
$config['host'])) {
            throw new 
InvalidArgumentException('Host is required in config');
        }

        
$this->connection = (($config['mode'] ?? null) === 'tcp')
            ? new 
Connection\TcpSocket($config['host'], $config['port'] ?? 8125$config['timeout'] ?? null, !empty($config['persistent']))
            : new 
Connection\UdpSocket($config['host'], $config['port'] ?? 8126$config['timeout'] ?? null, !empty($config['persistent']));
        
$this->namespace $config['namespace'] ?? '';
        
$this->eventMetric $config['event_metric'] ?? self::DEFAULT_EVENT_METRIC;
    }

    public function 
increment(string $metricint $value, array $tags null): void
    
{

        $this->count($metric$value$tags);

    }

    public function 
gauge(string $metricfloat $value, array $tags = []): void
    
{
        
$this->send($metric$value'g'$tags);
    }

    public function 
timing(string $metricfloat $value, array $tags = []): void
    
{
        
$this->send($metric$value'ms'$tags);
    }

    public function 
set(string $metricint $value, array $tags = []): void
    
{
        
$this->send($metric$value's'$tags);
    }

    public function 
histogram(string $metricfloat $value, array $tags = []): void
    
{
        
// Statsd cannot send histogram, we will send using gauge and process it on Grafana side
        
$this->gauge($metric$value$tags);
    }

    public function 
event(string $titlestring $event, array $tags = []): void
    
{
        
// Prometheus cannot send events, emulate via increment with additional tags
        
$tags['title'] = $title;
        
$tags['event'] = $event;
        
$this->count($this->eventMetric1$tags);
    }

    public function 
count(string $metricfloat $value, array $tags = []): void
    
{
        
$this->send($metric$value'c'$tags);
    }

    private function 
send(string $metric$valuestring $type, array $tags = []): void
    
{
        
$this->validateMetricName($metric);
        if (!empty(
$this->namespace)) {
            
$metric $this->namespace '.' $metric;
        }

        
$message $metric ':' $value '|' $type;
        if (!empty(
$tags)) {
            
$tagArray = [];
            foreach (
$tags as $tagName => $tagValue) {
                
$tagArray[] = ($tagName ':' $tagValue);
            }
            
$message .= '|#' implode(','$tagArray);
        }

        
$this->connection->send($message);
    }

    private function 
validateMetricName(string $metric): void
    
{
        if (
substr($metric, -1) === '.') {
            return;
        }

        if  (
preg_match(self::METRIC_NAME_REGEXP$metric)) {
            return;
        }

        throw new 
ValidateException("Invalid metric name: $metric");
    }
}