From c97a2403cb60ddee8c80ac72055e6f3e0d33b657 Mon Sep 17 00:00:00 2001 From: Serhii Nochevnyi <94836648+serhii-nochevnyi@users.noreply.github.com> Date: Mon, 1 May 2023 20:42:50 +0300 Subject: [PATCH] Socket madness problem fix (#1) * Lazy creation of sockets * Resend on socket errors. Default max attempts is 2. Can be configured with `max_attempts_to_send` option. * Closing the socket in the destructor * Fix potential loss of last buffer in BatchedDogStatsd * php version >=7.4 * ver 1.6.0 candidate * README: php version changed * composer.json: repo changed --- .github/workflows/test.yaml | 56 +- CHANGELOG.md | 7 + README.md | 4 +- composer.json | 11 +- examples/example.php | 4 +- examples/expandedExample.php | 27 +- src/BatchedDogStatsd.php | 24 +- src/DogStatsd.php | 543 +++++++++++------- tests/TestHelpers/SocketSpy.php | 40 +- tests/TestHelpers/SocketSpyTestCase.php | 72 +-- tests/UnitTests/BatchedDogStatsdTest.php | 32 +- tests/UnitTests/DogStatsd/SocketsTest.php | 500 +++++++++------- .../DogStatsd/TagSerializationTest.php | 58 +- tests/socket_function_stubs.php | 15 +- 14 files changed, 813 insertions(+), 580 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 8b6b6b9..92b63ce 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -15,33 +15,33 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-versions: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.2', '8.3'] + php-versions: [ '7.4', '8.0', '8.2', '8.3' ] steps: - - uses: actions/checkout@v3 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-versions }} - extensions: sockets - - - name: Validate composer.json and composer.lock - run: composer validate --strict - - - name: Cache Composer packages - id: composer-cache - uses: actions/cache@v3 - with: - path: vendor - key: ${{ runner.os }}-php-${{ matrix.php-versions }}-${{ hashFiles('**/composer.lock') }} - restore-keys: | - ${{ runner.os }}-php- - - - name: Install dependencies - run: composer install --prefer-dist --no-progress - - - name: Run Tests - run: | - composer lint - composer test + - uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: sockets + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-php-${{ matrix.php-versions }}-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run Tests + run: | + composer lint + composer test diff --git a/CHANGELOG.md b/CHANGELOG.md index 8314554..e63e261 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ CHANGELOG [//]: # (comment: Don't forget to update src/DogStatsd.php:DogStatsd::version when releasing a new version) +# 1.6.0 / 2023-05-1 +* Lazy creation of sockets +* Resend on socket errors. Default max attempts is 2. Can be configured with `max_attempts_to_send` option. +* Closing the socket in the destructor +* Fix potential loss of last buffer in BatchedDogStatsd +* php version >=7.4 + # 1.5.6 / 2023-01-3 * Fix warnings to support PHP 8.2 diff --git a/README.md b/README.md index 249f7a7..8de9447 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This is an extremely simple PHP [DogStatsD](https://docs.datadoghq.com/developers/dogstatsd/?code-lang=php) client. -**Requires PHP >= 5.6.0.** +**Requires PHP >= 7.4.0.** See [CHANGELOG.md](CHANGELOG.md) for changes. @@ -18,7 +18,7 @@ See [CHANGELOG.md](CHANGELOG.md) for changes. Add the following to your `composer.json`: ``` -"datadog/php-datadogstatsd": "1.5.*" +"datadog/php-datadogstatsd": "1.6.*" ``` The first version shipped in composer is *0.0.3* diff --git a/composer.json b/composer.json index ef929fc..47bf285 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "datadog/php-datadogstatsd", + "name": "pdffiller/php-datadogstatsd", "type": "library", "description": "An extremely simple PHP datadogstatsd client", "keywords": ["datadog", "monitoring", "logging", "statsd", "error-reporting", "check", "health"], @@ -18,15 +18,12 @@ } ], "require": { - "php": ">=5.6.0", + "php": ">=7.4.0", "ext-sockets": "*" }, "support": { - "email": "package@datadoghq.com", - "irc": "irc://irc.freenode.net/datadog", - "issues": "https://github.com/DataDog/php-datadogstatsd/issues", - "source": "https://github.com/DataDog/php-datadogstatsd", - "chat": "https://chat.datadoghq.com/" + "issues": "https://github.com/pdffiller/php-datadogstatsd/issues", + "source": "https://github.com/pdffiller/php-datadogstatsd" }, "autoload": { "psr-4": { diff --git a/examples/example.php b/examples/example.php index 28aa083..187a666 100644 --- a/examples/example.php +++ b/examples/example.php @@ -2,8 +2,8 @@ require '../vendor/autoload.php'; -use DataDog\DogStatsd; use DataDog\BatchedDogStatsd; +use DataDog\DogStatsd; $statsd = new DogStatsd(); $statsd->increment('web.page_views'); @@ -11,7 +11,7 @@ $statsd->distribution('web.render_time', 15); $statsd->set('web.uniques', 3 /* a unique user id */); $statsd->serviceCheck('my.service.check', DogStatsd::CRITICAL); -$statsd->event("Event title", array("text" => "Event text")); +$statsd->event("Event title", ["text" => "Event text"]); //All the following metrics will be sent in a single UDP packet to the statsd server $batchedStatsd = new BatchedDogStatsd(); diff --git a/examples/expandedExample.php b/examples/expandedExample.php index cdc398b..2854db8 100644 --- a/examples/expandedExample.php +++ b/examples/expandedExample.php @@ -20,34 +20,37 @@ $statsd->set('web.uniques', 3); // A unique user id runFunction($statsd); - $statsd->timing('test.data.point', microtime(true) - $startTime1, 1, array('tagname' => 'php_example_tag_1')); + $statsd->timing('test.data.point', microtime(true) - $startTime1, 1, ['tagname' => 'php_example_tag_1']); sleep(1); // Sleep for one second } echo "Script has completed.\n"; +/** + * @throws Exception + */ function runFunction($statsd) { $startTime = microtime(true); - $testArray = array(); - for ($i = 0; $i < rand(1, 1000000000); $i++) { + $testArray = []; + for ($i = 0; $i < random_int(1, 1000000000); $i++) { $testArray[$i] = $i; // Simulate an event at every 1000000th element if ($i % 1000000 == 0) { echo "Event simulated.\n"; - $statsd->event('A thing broke!', array( - 'alert_type' => 'error', - 'aggregation_key' => 'test_aggr' - )); - $statsd->event('Now it is fixed.', array( - 'alert_type' => 'success', - 'aggregation_key' => 'test_aggr' - )); + $statsd->event('A thing broke!', [ + 'alert_type' => 'error', + 'aggregation_key' => 'test_aggr', + ]); + $statsd->event('Now it is fixed.', [ + 'alert_type' => 'success', + 'aggregation_key' => 'test_aggr', + ]); } } unset($testArray); - $statsd->timing('test.data.point', microtime(true) - $startTime, 1, array('tagname' => 'php_example_tag_2')); + $statsd->timing('test.data.point', microtime(true) - $startTime, 1, ['tagname' => 'php_example_tag_2']); } diff --git a/src/BatchedDogStatsd.php b/src/BatchedDogStatsd.php index ffb7822..4da6673 100644 --- a/src/BatchedDogStatsd.php +++ b/src/BatchedDogStatsd.php @@ -14,12 +14,11 @@ */ class BatchedDogStatsd extends DogStatsd { - private static $buffer = array(); - private static $bufferLength = 0; public static $maxBufferLength = 50; + private static $buffer = []; + private static $bufferLength = 0; - - public function __construct(array $config = array()) + public function __construct(array $config = []) { // by default the telemetry is enabled for BatchedDogStatsd if (!isset($config["disable_telemetry"])) { @@ -28,14 +27,23 @@ public function __construct(array $config = array()) parent::__construct($config); } + public function __destruct() + { + if (static::$bufferLength) { + $this->flushBuffer(); + } + + parent::__destruct(); + } + /** * @param string $message */ public function report($message) { static::$buffer[] = $message; - static::$bufferLength++; - if (static::$bufferLength > static::$maxBufferLength) { + + if (++static::$bufferLength > static::$maxBufferLength) { $this->flushBuffer(); } } @@ -51,8 +59,8 @@ public function flush_buffer() // phpcs:ignore public function flushBuffer() { - $this->flush(join("\n", static::$buffer)); - static::$buffer = array(); + $this->flush(implode("\n", static::$buffer)); + static::$buffer = []; static::$bufferLength = 0; } } diff --git a/src/DogStatsd.php b/src/DogStatsd.php index 84081ac..fda6af4 100644 --- a/src/DogStatsd.php +++ b/src/DogStatsd.php @@ -5,17 +5,46 @@ /** * Datadog implementation of StatsD **/ - class DogStatsd { // phpcs:disable - const OK = 0; - const WARNING = 1; - const CRITICAL = 2; - const UNKNOWN = 3; - // phpcs:enable - + const OK = 0; + const WARNING = 1; + const CRITICAL = 2; + const UNKNOWN = 3; + const DEFAULT_MAX_ATTEMPTS_TO_SEND = 1; + // phpcs:enable + public static $version = '1.6.0'; + private static $eventUrl = '/api/v1/events'; + /** + * @var bool|resource|\Socket + */ + protected $socket = false; + /** + * @var int + */ + protected $maxAttemptsToSend = self::DEFAULT_MAX_ATTEMPTS_TO_SEND; + protected $retryStatusCodes = [ + 10053 => true, + 10054 => true, + 10058 => true, + 10060 => true, + 10061 => true, + 100 => true, + 101 => true, + 102 => true, + 103 => true, + 104 => true, + 105 => true, + 107 => true, + 108 => true, + 110 => true, + 111 => true, + 112 => true, + 121 => true, + 125 => true, + ]; /** * @var string */ @@ -24,6 +53,8 @@ class DogStatsd * @var int */ private $port; + + // Telemetry /** * @var string */ @@ -44,8 +75,6 @@ class DogStatsd * @var string The prefix to apply to all metrics */ private $metricPrefix; - - // Telemetry private $disable_telemetry; private $telemetry_tags; private $metrics_sent; @@ -54,13 +83,9 @@ class DogStatsd private $bytes_sent; private $bytes_dropped; private $packets_sent; - private $packets_dropped; - - - private static $eventUrl = '/api/v1/events'; // Used for the telemetry tags - public static $version = '1.5.6'; + private $packets_dropped; /** * DogStatsd constructor, takes a configuration array. The configuration can take any of the following values: @@ -73,23 +98,25 @@ class DogStatsd * * @param array $config */ - public function __construct(array $config = array()) + public function __construct(array $config = []) { - $this->host = isset($config['host']) - ? $config['host'] : (getenv('DD_AGENT_HOST') - ? getenv('DD_AGENT_HOST') : 'localhost'); + $agentHost = getenv('DD_AGENT_HOST') ?: 'localhost'; + $this->host = isset($config['host']) ? $config['host'] : $agentHost; - $this->port = isset($config['port']) - ? $config['port'] : (getenv('DD_DOGSTATSD_PORT') - ? (int)getenv('DD_DOGSTATSD_PORT') : 8125); + $dogStatsdPort = getenv('DD_DOGSTATSD_PORT') ?: 8125; + $this->port = (int) (isset($config['port']) ? $config['port'] : $dogStatsdPort); $this->socketPath = isset($config['socket_path']) ? $config['socket_path'] : null; + $this->maxAttemptsToSend = isset($config['max_attempts_to_send']) + ? $config['max_attempts_to_send'] + : self::DEFAULT_MAX_ATTEMPTS_TO_SEND; + $this->datadogHost = isset($config['datadog_host']) ? $config['datadog_host'] : 'https://app.datadoghq.com'; $this->decimalPrecision = isset($config['decimal_precision']) ? $config['decimal_precision'] : 2; - $this->globalTags = isset($config['global_tags']) ? $config['global_tags'] : array(); + $this->globalTags = isset($config['global_tags']) ? $config['global_tags'] : []; if (getenv('DD_ENTITY_ID')) { $this->globalTags['dd.internal.entity_id'] = getenv('DD_ENTITY_ID'); } @@ -100,68 +127,45 @@ public function __construct(array $config = array()) $this->disable_telemetry = isset($config["disable_telemetry"]) ? $config["disable_telemetry"] : true; $transport_type = !is_null($this->socketPath) ? "uds" : "udp"; $this->telemetry_tags = $this->serializeTags( - array( - "client" => "php", - "client_version" => self::$version, - "client_transport" => $transport_type) + [ + "client" => "php", + "client_version" => self::$version, + "client_transport" => $transport_type, + ] ); $this->resetTelemetry(); } - /** - * Reset the telemetry value to zero - */ - private function resetTelemetry() + public function __destruct() { - $this->metrics_sent = 0; - $this->events_sent = 0; - $this->service_checks_sent = 0; - $this->bytes_sent = 0; - $this->bytes_dropped = 0; - $this->packets_sent = 0; - $this->packets_dropped = 0; - } - /** - * Reset the telemetry value to zero - */ - private function flushTelemetry() - { - if ($this->disable_telemetry == true) { - return ""; - } - - return "\ndatadog.dogstatsd.client.metrics:{$this->metrics_sent}|c{$this->telemetry_tags}" - . "\ndatadog.dogstatsd.client.events:{$this->events_sent}|c{$this->telemetry_tags}" - . "\ndatadog.dogstatsd.client.service_checks:{$this->service_checks_sent}|c{$this->telemetry_tags}" - . "\ndatadog.dogstatsd.client.bytes_sent:{$this->bytes_sent}|c{$this->telemetry_tags}" - . "\ndatadog.dogstatsd.client.bytes_dropped:{$this->bytes_dropped}|c{$this->telemetry_tags}" - . "\ndatadog.dogstatsd.client.packets_sent:{$this->packets_sent}|c{$this->telemetry_tags}" - . "\ndatadog.dogstatsd.client.packets_dropped:{$this->packets_dropped}|c{$this->telemetry_tags}"; + $this->closeSocket(); } /** * Log timing information * - * @param string $stat The metric to in log timing info for. - * @param float $time The elapsed time (ms) to log - * @param float $sampleRate the rate (0-1) for sampling. - * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * @param string $stat The metric to in log timing info for. + * @param float $time The elapsed time (ms) to log + * @param float $sampleRate the rate (0-1) for sampling. + * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * * @return void */ public function timing($stat, $time, $sampleRate = 1.0, $tags = null) { - $time = $this->normalizeValue($time); - $this->send(array($stat => "$time|ms"), $sampleRate, $tags); + $normalizedValue = $this->normalizeValue($time); + $this->send([$stat => "$normalizedValue|ms"], $sampleRate, $tags); } /** * A convenient alias for the timing function when used with micro-timing * - * @param string $stat The metric name - * @param float $time The elapsed time to log, IN SECONDS - * @param float $sampleRate the rate (0-1) for sampling. - * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * @param string $stat The metric name + * @param float $time The elapsed time to log, IN SECONDS + * @param float $sampleRate the rate (0-1) for sampling. + * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * * @return void **/ public function microtiming($stat, $time, $sampleRate = 1.0, $tags = null) @@ -172,74 +176,80 @@ public function microtiming($stat, $time, $sampleRate = 1.0, $tags = null) /** * Gauge * - * @param string $stat The metric - * @param float $value The value - * @param float $sampleRate the rate (0-1) for sampling. - * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * @param string $stat The metric + * @param float $value The value + * @param float $sampleRate the rate (0-1) for sampling. + * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * * @return void **/ public function gauge($stat, $value, $sampleRate = 1.0, $tags = null) { - $value = $this->normalizeValue($value); - $this->send(array($stat => "$value|g"), $sampleRate, $tags); + $normalizedValue = $this->normalizeValue($value); + $this->send([$stat => "$normalizedValue|g"], $sampleRate, $tags); } /** * Histogram * - * @param string $stat The metric - * @param float $value The value - * @param float $sampleRate the rate (0-1) for sampling. - * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * @param string $stat The metric + * @param float $value The value + * @param float $sampleRate the rate (0-1) for sampling. + * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * * @return void **/ public function histogram($stat, $value, $sampleRate = 1.0, $tags = null) { - $value = $this->normalizeValue($value); - $this->send(array($stat => "$value|h"), $sampleRate, $tags); + $normalizedValue = $this->normalizeValue($value); + $this->send([$stat => "$normalizedValue|h"], $sampleRate, $tags); } /** * Distribution * - * @param string $stat The metric - * @param float $value The value - * @param float $sampleRate the rate (0-1) for sampling. - * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * @param string $stat The metric + * @param float $value The value + * @param float $sampleRate the rate (0-1) for sampling. + * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * * @return void **/ public function distribution($stat, $value, $sampleRate = 1.0, $tags = null) { - $value = $this->normalizeValue($value); - $this->send(array($stat => "$value|d"), $sampleRate, $tags); + $normalizedValue = $this->normalizeValue($value); + $this->send([$stat => "$normalizedValue|d"], $sampleRate, $tags); } /** * Set * - * @param string $stat The metric - * @param string|float $value The value - * @param float $sampleRate the rate (0-1) for sampling. - * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * @param string $stat The metric + * @param string|float $value The value + * @param float $sampleRate the rate (0-1) for sampling. + * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * * @return void **/ public function set($stat, $value, $sampleRate = 1.0, $tags = null) { if (!is_string($value)) { - $value = $this->normalizeValue($value); + $normalizedValue = $this->normalizeValue($value); + } else { + $normalizedValue = $value; } - $this->send(array($stat => "$value|s"), $sampleRate, $tags); + $this->send([$stat => "$normalizedValue|s"], $sampleRate, $tags); } - /** * Increments one or more stats counters * - * @param string|array $stats The metric(s) to increment. - * @param float $sampleRate the rate (0-1) for sampling. - * @param array|string $tags Key Value array of Tag => Value, or single tag as string - * @param int $value the amount to increment by (default 1) + * @param string|array $stats The metric(s) to increment. + * @param float $sampleRate the rate (0-1) for sampling. + * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * @param int $value the amount to increment by (default 1) + * * @return void **/ public function increment($stats, $sampleRate = 1.0, $tags = null, $value = 1) @@ -250,10 +260,11 @@ public function increment($stats, $sampleRate = 1.0, $tags = null, $value = 1) /** * Decrements one or more stats counters. * - * @param string|array $stats The metric(s) to decrement. - * @param float $sampleRate the rate (0-1) for sampling. - * @param array|string $tags Key Value array of Tag => Value, or single tag as string - * @param int $value the amount to decrement by (default -1) + * @param string|array $stats The metric(s) to decrement. + * @param float $sampleRate the rate (0-1) for sampling. + * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * @param int $value the amount to decrement by (default -1) + * * @return void **/ public function decrement($stats, $sampleRate = 1.0, $tags = null, $value = -1) @@ -267,108 +278,45 @@ public function decrement($stats, $sampleRate = 1.0, $tags = null, $value = -1) /** * Updates one or more stats counters by arbitrary amounts. * - * @param string|array $stats The metric(s) to update. Should be either a string or array of metrics. - * @param int $delta The amount to increment/decrement each metric by. - * @param float $sampleRate the rate (0-1) for sampling. - * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * @param string|array $stats The metric(s) to update. Should be either a string or array of metrics. + * @param int $delta The amount to increment/decrement each metric by. + * @param float $sampleRate the rate (0-1) for sampling. + * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * * @return void **/ public function updateStats($stats, $delta = 1, $sampleRate = 1.0, $tags = null) { - $delta = $this->normalizeValue($delta); + $normalizedValue = $this->normalizeValue($delta); if (!is_array($stats)) { - $stats = array($stats); + $stats = [$stats]; } - $data = array(); + $data = []; foreach ($stats as $stat) { - $data[$stat] = "$delta|c"; + $data[$stat] = "$normalizedValue|c"; } $this->send($data, $sampleRate, $tags); } - /** - * Serialize tags to StatsD protocol - * - * @param string|array $tags The tags to be serialize - * @return string - **/ - private function serializeTags($tags) - { - $all_tags = array_merge( - $this->normalizeTags($this->globalTags), - $this->normalizeTags($tags) - ); - - if (count($all_tags) === 0) { - return ''; - } - $tag_strings = array(); - foreach ($all_tags as $tag => $value) { - if ($value === null) { - $tag_strings[] = $tag; - } elseif (is_bool($value)) { - $tag_strings[] = $tag . ':' . ($value === true ? 'true' : 'false'); - } else { - $tag_strings[] = $tag . ':' . $value; - } - } - return '|#' . implode(',', $tag_strings); - } - - /** - * Turns tags in any format into an array of tags - * - * @param mixed $tags The tags to normalize - * @return array - */ - private function normalizeTags($tags) - { - if ($tags === null) { - return array(); - } - if (is_array($tags)) { - $data = array(); - foreach ($tags as $tag_key => $tag_val) { - if (isset($tag_val)) { - $data[$tag_key] = $tag_val; - } else { - $data[$tag_key] = null; - } - } - return $data; - } else { - $tags = explode(',', $tags); - $data = array(); - foreach ($tags as $tag_string) { - if (false === strpos($tag_string, ':')) { - $data[$tag_string] = null; - } else { - list($key, $value) = explode(':', $tag_string, 2); - $data[$key] = $value; - } - } - return $data; - } - } - /** * Squirt the metrics over UDP * - * @param array $data Incoming Data - * @param float $sampleRate the rate (0-1) for sampling. - * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * @param array $data Incoming Data + * @param float $sampleRate the rate (0-1) for sampling. + * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * * @return void **/ public function send($data, $sampleRate = 1.0, $tags = null) { - $sampleRate = $this->normalizeValue($sampleRate); + $normalizedValue = $this->normalizeValue($sampleRate); $this->metrics_sent += count($data); // sampling - $sampledData = array(); - if ($sampleRate < 1) { + $sampledData = []; + if ($normalizedValue < 1) { foreach ($data as $stat => $value) { - if ((mt_rand() / mt_getrandmax()) <= $sampleRate) { - $sampledData[$stat] = "$value|@$sampleRate"; + if ((mt_rand() / mt_getrandmax()) <= $normalizedValue) { + $sampledData[$stat] = "$value|@$normalizedValue"; } } } else { @@ -386,17 +334,18 @@ public function send($data, $sampleRate = 1.0, $tags = null) } /** - * @deprecated service_check will be removed in future versions in favor of serviceCheck + * @param string $name service check name + * @param int $status service check status code (see OK, WARNING,...) + * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * @param string $hostname hostname to associate with this service check status + * @param string $message message to associate with this service check status + * @param int $timestamp timestamp for the service check status (defaults to now) * - * Send a custom service check status over UDP - * @param string $name service check name - * @param int $status service check status code (see OK, WARNING,...) - * @param array|string $tags Key Value array of Tag => Value, or single tag as string - * @param string $hostname hostname to associate with this service check status - * @param string $message message to associate with this service check status - * @param int $timestamp timestamp for the service check status (defaults to now) * @return void - **/ + **@deprecated service_check will be removed in future versions in favor of serviceCheck + * + * Send a custom service check status over UDP + */ public function service_check( // phpcs:ignore $name, $status, @@ -411,12 +360,13 @@ public function service_check( // phpcs:ignore /** * Send a custom service check status over UDP * - * @param string $name service check name - * @param int $status service check status code (see OK, WARNING,...) - * @param array|string $tags Key Value array of Tag => Value, or single tag as string - * @param string $hostname hostname to associate with this service check status - * @param string $message message to associate with this service check status - * @param int $timestamp timestamp for the service check status (defaults to now) + * @param string $name service check name + * @param int $status service check status code (see OK, WARNING,...) + * @param array|string $tags Key Value array of Tag => Value, or single tag as string + * @param string $hostname hostname to associate with this service check status + * @param string $message message to associate with this service check status + * @param int $timestamp timestamp for the service check status (defaults to now) + * * @return void **/ public function serviceCheck( @@ -440,15 +390,10 @@ public function serviceCheck( $msg .= sprintf('|m:%s', $this->escapeScMessage($message)); } - $this->service_checks_sent += 1; + ++$this->service_checks_sent; $this->report($msg); } - private function escapeScMessage($msg) - { - return str_replace("m:", "m\:", str_replace("\n", "\\n", $msg)); - } - public function report($message) { $this->flush($message); @@ -456,39 +401,29 @@ public function report($message) public function flush($message) { - $message .= $this->flushTelemetry(); + $message .= $this->flushTelemetry() . PHP_EOL; - // Non - Blocking UDP I/O - Use IP Addresses! - $socket = is_null($this->socketPath) ? socket_create(AF_INET, SOCK_DGRAM, SOL_UDP) - : socket_create(AF_UNIX, SOCK_DGRAM, 0); - socket_set_nonblock($socket); + $res = $this->sendMessage($message); - if (!is_null($this->socketPath)) { - $res = socket_sendto($socket, $message, strlen($message), 0, $this->socketPath); - } else { - $res = socket_sendto($socket, $message, strlen($message), 0, $this->host, $this->port); - } - - if ($res !== false) { + if (!empty($res)) { $this->resetTelemetry(); $this->bytes_sent += strlen($message); - $this->packets_sent += 1; + ++$this->packets_sent; } else { $this->bytes_dropped += strlen($message); - $this->packets_dropped += 1; + ++$this->packets_dropped; } - - socket_close($socket); } - /** + /** * Formats $vals array into event for submission to Datadog via UDP * - * @param array $vals Optional values of the event. See + * @param array $vals Optional values of the event. See * https://docs.datadoghq.com/api/?lang=bash#post-an-event for the valid keys + * * @return bool */ - public function event($title, $vals = array()) + public function event($title, $vals = []) { // Format required values title and text $text = isset($vals['text']) ? (string) $vals['text'] : ''; @@ -510,12 +445,182 @@ public function event($title, $vals = array()) $title_length = strlen($title); $text_length = strlen($textField) - 1; - $this->events_sent += 1; + $this->events_sent++; $this->report('_e{' . $title_length . ',' . $text_length . '}:' . $fields); return true; } + /** + * @return bool|resource|\Socket + */ + protected function getSocket() + { + if ($this->socket) { + return $this->socket; + } + + $domain = is_null($this->socketPath) ? AF_INET : AF_UNIX; + $protocol = is_null($this->socketPath) ? SOL_UDP : 0; + + // Non - Blocking UDP I/O - Use IP Addresses! + if ($this->socket = @socket_create($domain, SOCK_DGRAM, $protocol)) { + socket_set_nonblock($this->socket); + } + + return $this->socket; + } + + protected function closeSocket() + { + if (!$this->socket) { + return; + } + + @socket_close($this->socket); + + $this->socket = false; + } + + /** + * @param string $message + * @param int $attempt + * + * @return false|int|mixed + */ + protected function sendMessage($message, $attempt = 0) + { + $res = false; + + if ($socket = $this->getSocket()) { + if (!is_null($this->socketPath)) { + $res = socket_sendto($socket, $message, strlen($message), 0, $this->socketPath); + } else { + $res = socket_sendto($socket, $message, strlen($message), 0, $this->host, $this->port); + } + } + + if ($res === false) { + $socketError = $socket ? socket_last_error($socket) : socket_last_error(); + if (isset($this->retryStatusCodes[$socketError])) { + $this->closeSocket(); + if ($attempt < $this->maxAttemptsToSend) { + return $this->sendMessage($message, ++$attempt); + } + } + } + + return $res; + } + + /** + * Reset the telemetry value to zero + */ + private function resetTelemetry() + { + $this->metrics_sent = 0; + $this->events_sent = 0; + $this->service_checks_sent = 0; + $this->bytes_sent = 0; + $this->bytes_dropped = 0; + $this->packets_sent = 0; + $this->packets_dropped = 0; + } + + /** + * Reset the telemetry value to zero + */ + private function flushTelemetry() + { + if ($this->disable_telemetry) { + return ""; + } + + return "\ndatadog.dogstatsd.client.metrics:{$this->metrics_sent}|c{$this->telemetry_tags}" + . "\ndatadog.dogstatsd.client.events:{$this->events_sent}|c{$this->telemetry_tags}" + . "\ndatadog.dogstatsd.client.service_checks:{$this->service_checks_sent}|c{$this->telemetry_tags}" + . "\ndatadog.dogstatsd.client.bytes_sent:{$this->bytes_sent}|c{$this->telemetry_tags}" + . "\ndatadog.dogstatsd.client.bytes_dropped:{$this->bytes_dropped}|c{$this->telemetry_tags}" + . "\ndatadog.dogstatsd.client.packets_sent:{$this->packets_sent}|c{$this->telemetry_tags}" + . "\ndatadog.dogstatsd.client.packets_dropped:{$this->packets_dropped}|c{$this->telemetry_tags}"; + } + + /** + * Serialize tags to StatsD protocol + * + * @param string|array $tags The tags to be serialize + * + * @return string + **/ + private function serializeTags($tags) + { + $all_tags = array_merge( + $this->normalizeTags($this->globalTags), + $this->normalizeTags($tags) + ); + + if (count($all_tags) === 0) { + return ''; + } + $tag_strings = []; + foreach ($all_tags as $tag => $value) { + if ($value === null) { + $tag_strings[] = $tag; + } elseif (is_bool($value)) { + $tag_strings[] = $tag . ':' . ($value === true ? 'true' : 'false'); + } else { + $tag_strings[] = $tag . ':' . $value; + } + } + + return '|#' . implode(',', $tag_strings); + } + + /** + * Turns tags in any format into an array of tags + * + * @param mixed $tags The tags to normalize + * + * @return array + */ + private function normalizeTags($tags) + { + if ($tags === null) { + return []; + } + + if (is_array($tags)) { + $data = []; + foreach ($tags as $tag_key => $tag_val) { + if (isset($tag_val)) { + $data[$tag_key] = $tag_val; + } else { + $data[$tag_key] = null; + } + } + + return $data; + } + + $tags = explode(',', $tags); + $data = []; + foreach ($tags as $tag_string) { + if (false === strpos($tag_string, ':')) { + $data[$tag_string] = null; + } else { + [$key, $value] = explode(':', $tag_string, 2); + $data[$key] = $value; + } + } + + return $data; + } + + private function escapeScMessage($msg) + { + return str_replace(["\n", "m:"], ["\\n", "m\:"], $msg); + } + /** * Normalize the value witout locale consideration before queuing the metric for sending * diff --git a/tests/TestHelpers/SocketSpy.php b/tests/TestHelpers/SocketSpy.php index a3f1c33..edb2d80 100644 --- a/tests/TestHelpers/SocketSpy.php +++ b/tests/TestHelpers/SocketSpy.php @@ -7,7 +7,7 @@ * * Useful for recording calls to stubbed global socket functions built into PHP * - * @see \DataDog\SocketSpyTestCase + * @see \DataDog\SocketSpyTestCase * @package DataDog */ class SocketSpy @@ -15,27 +15,32 @@ class SocketSpy /** * @var array */ - public $argsFromSocketCreateCalls = array(); + public $argsFromSocketCreateCalls = []; /** * @var array */ - public $socketCreateReturnValues = array(); + public $socketCreateReturnValues = []; /** * @var array */ - public $argsFromSocketSetNonblockCalls = array(); + public $argsFromSocketSetNonblockCalls = []; /** * @var array */ - public $argsFromSocketSendtoCalls = array(); + public $argsFromSocketSendtoCalls = []; /** * @var array */ - public $argsFromSocketCloseCalls = array(); + public $argsFromSocketCloseCalls = []; + + /** + * @var array + */ + public $argsFromSocketLastErrorCalls = []; /** * var boolean @@ -49,11 +54,11 @@ class SocketSpy */ public function socketCreateWasCalledWithArgs($domain, $type, $protocol) { - $this->argsFromSocketCreateCalls[] = array( + $this->argsFromSocketCreateCalls[] = [ $domain, $type, - $protocol - ); + $protocol, + ]; } /** @@ -89,17 +94,18 @@ public function socketSendtoWasCalledWithArgs( $port ) { if ($this->returnErrorOnSend === true) { - return false; + return false; } - $this->argsFromSocketSendtoCalls[] = array( + $this->argsFromSocketSendtoCalls[] = [ $socket, $buf, $len, $flags, $addr, - $port - ); + $port, + ]; + return $len; } @@ -110,4 +116,12 @@ public function socketCloseWasCalled($socket) { $this->argsFromSocketCloseCalls[] = $socket; } + + /** + * @param resource $socket + */ + public function socketLastErrorWasCalled($socket) + { + $this->argsFromSocketLastErrorCalls[] = $socket; + } } diff --git a/tests/TestHelpers/SocketSpyTestCase.php b/tests/TestHelpers/SocketSpyTestCase.php index 7b85dda..347d4bc 100644 --- a/tests/TestHelpers/SocketSpyTestCase.php +++ b/tests/TestHelpers/SocketSpyTestCase.php @@ -21,6 +21,39 @@ */ class SocketSpyTestCase extends TestCase { + public function assertSameTelemetry($expected, $actual, $message = "", $params = []) + { + $metrics_sent = $this->get_default($params["metrics"], 1); + $events_sent = $this->get_default($params["events"], 0); + $service_checks_sent = $this->get_default($params["service_checks"], 0); + $bytes_sent = $this->get_default($params["bytes_sent"], 0); + $bytes_dropped = $this->get_default($params["bytes_dropped"], 0); + $packets_sent = $this->get_default($params["packets_sent"], 0); + $packets_dropped = $this->get_default($params["packets_dropped"], 0); + $transport_type = $this->get_default($params["transport"], "udp"); + + $version = DogStatsd::$version; + $tags = "client:php,client_version:{$version},client_transport:{$transport_type}"; + $extra_tags = $this->get_default($params["tags"], ""); + if ($extra_tags != "") { + $tags = $extra_tags . "," . $tags; + } + + $telemetry = "\ndatadog.dogstatsd.client.metrics:{$metrics_sent}|c|#{$tags}" + . "\ndatadog.dogstatsd.client.events:{$events_sent}|c|#{$tags}" + . "\ndatadog.dogstatsd.client.service_checks:{$service_checks_sent}|c|#{$tags}" + . "\ndatadog.dogstatsd.client.bytes_sent:{$bytes_sent}|c|#{$tags}" + . "\ndatadog.dogstatsd.client.bytes_dropped:{$bytes_dropped}|c|#{$tags}" + . "\ndatadog.dogstatsd.client.packets_sent:{$packets_sent}|c|#{$tags}" + . "\ndatadog.dogstatsd.client.packets_dropped:{$packets_dropped}|c|#{$tags}" . PHP_EOL; + + $this->assertSame( + $expected . $telemetry, + $actual, + $message + ); + } + /** * Set up a spy object to capture calls to global built in socket functions */ @@ -34,7 +67,7 @@ protected function set_up() } /** - * @return \DataDog\TestHelpers\SocketSpy + * @return SocketSpy */ protected function getSocketSpy() { @@ -43,42 +76,9 @@ protected function getSocketSpy() return $socketSpy; } - private function get_default(&$var, $default=null) { - return isset($var) ? $var : $default; - } - - public function assertSameTelemetry($expected, $actual, $message="", $params = array()) + private function get_default(&$var, $default = null) { - $metrics_sent = $this->get_default($params["metrics"], 1); - $events_sent = $this->get_default($params["events"], 0); - $service_checks_sent = $this->get_default($params["service_checks"], 0); - $bytes_sent = $this->get_default($params["bytes_sent"], 0); - $bytes_dropped = $this->get_default($params["bytes_dropped"], 0); - $packets_sent = $this->get_default($params["packets_sent"], 0); - $packets_dropped = $this->get_default($params["packets_dropped"], 0); - $transport_type = $this->get_default($params["transport"], "udp"); - - $version = DogStatsd::$version; - $tags = "client:php,client_version:{$version},client_transport:{$transport_type}"; - $extra_tags = $this->get_default($params["tags"], ""); - if ($extra_tags != "") - { - $tags = $extra_tags.",".$tags; - } - - $telemetry = "\ndatadog.dogstatsd.client.metrics:{$metrics_sent}|c|#{$tags}" - . "\ndatadog.dogstatsd.client.events:{$events_sent}|c|#{$tags}" - . "\ndatadog.dogstatsd.client.service_checks:{$service_checks_sent}|c|#{$tags}" - . "\ndatadog.dogstatsd.client.bytes_sent:{$bytes_sent}|c|#{$tags}" - . "\ndatadog.dogstatsd.client.bytes_dropped:{$bytes_dropped}|c|#{$tags}" - . "\ndatadog.dogstatsd.client.packets_sent:{$packets_sent}|c|#{$tags}" - . "\ndatadog.dogstatsd.client.packets_dropped:{$packets_dropped}|c|#{$tags}"; - - $this->assertSame( - $expected.$telemetry, - $actual, - $message - ); + return isset($var) ? $var : $default; } } diff --git a/tests/UnitTests/BatchedDogStatsdTest.php b/tests/UnitTests/BatchedDogStatsdTest.php index 7594d91..66c30a8 100644 --- a/tests/UnitTests/BatchedDogStatsdTest.php +++ b/tests/UnitTests/BatchedDogStatsdTest.php @@ -8,21 +8,6 @@ class BatchedDogStatsdTest extends SocketSpyTestCase { - protected function set_up() - { - parent::set_up(); - - // Flush the buffer to reset state for next test - BatchedDogStatsd::$maxBufferLength = 50; - $batchedDog = new BatchedDogStatsd(); - $batchedDog->flushBuffer(); - - // Reset the SocketSpy state to get clean assertions. - // @see \DataDog\SocketSpy - global $socketSpy; - $socketSpy = new SocketSpy(); - } - public function testReportDoesNotSendIfBufferNotFilled() { $batchedDog = new BatchedDogStatsd(); @@ -71,7 +56,22 @@ public function testReportSendsOnceBufferIsFilled() $expectedUdpMessageOnceSent, $spy->argsFromSocketSendtoCalls[0][1], 'Should concatenate UDP messages with newlines', - array("metrics" => 3) + ["metrics" => 3] ); } + + protected function set_up() + { + parent::set_up(); + + // Flush the buffer to reset state for next test + BatchedDogStatsd::$maxBufferLength = 50; + $batchedDog = new BatchedDogStatsd(); + $batchedDog->flushBuffer(); + + // Reset the SocketSpy state to get clean assertions. + // @see \DataDog\SocketSpy + global $socketSpy; + $socketSpy = new SocketSpy(); + } } diff --git a/tests/UnitTests/DogStatsd/SocketsTest.php b/tests/UnitTests/DogStatsd/SocketsTest.php index 27ecbb7..5b71539 100644 --- a/tests/UnitTests/DogStatsd/SocketsTest.php +++ b/tests/UnitTests/DogStatsd/SocketsTest.php @@ -2,13 +2,21 @@ namespace DataDog\UnitTests\DogStatsd; -use DateTime; -use ReflectionProperty; use DataDog\DogStatsd; use DataDog\TestHelpers\SocketSpyTestCase; +use DateTime; +use ReflectionProperty; class SocketsTest extends SocketSpyTestCase { + static function getPrivate($object, $property) + { + $reflector = new ReflectionProperty(get_class($object), $property); + $reflector->setAccessible(true); + + return $reflector->getValue($object); + } + public function set_up() { parent::set_up(); @@ -20,17 +28,7 @@ public function set_up() $mt_getrandmax_stub_return_value = null; } - static function getPrivate($object, $property) { - $reflector = new ReflectionProperty(get_class($object), $property); - $reflector->setAccessible(true); - return $reflector->getValue($object); - } - - private function get_default(&$var, $default=null) { - return isset($var) ? $var : $default; - } - - public function assertSameWithTelemetry($expected, $actual, $message="", $params = array()) + public function assertSameWithTelemetry($expected, $actual, $message = "", $params = []) { $metrics_sent = $this->get_default($params["metrics"], 1); $events_sent = $this->get_default($params["events"], 0); @@ -44,23 +42,22 @@ public function assertSameWithTelemetry($expected, $actual, $message="", $params $version = DogStatsd::$version; $tags = "client:php,client_version:{$version},client_transport:{$transport_type}"; $extra_tags = $this->get_default($params["tags"], ""); - if ($extra_tags != "") - { - $tags = $extra_tags.",".$tags; + if ($extra_tags != "") { + $tags = $extra_tags . "," . $tags; } $telemetry = "\ndatadog.dogstatsd.client.metrics:{$metrics_sent}|c|#{$tags}" - . "\ndatadog.dogstatsd.client.events:{$events_sent}|c|#{$tags}" - . "\ndatadog.dogstatsd.client.service_checks:{$service_checks_sent}|c|#{$tags}" - . "\ndatadog.dogstatsd.client.bytes_sent:{$bytes_sent}|c|#{$tags}" - . "\ndatadog.dogstatsd.client.bytes_dropped:{$bytes_dropped}|c|#{$tags}" - . "\ndatadog.dogstatsd.client.packets_sent:{$packets_sent}|c|#{$tags}" - . "\ndatadog.dogstatsd.client.packets_dropped:{$packets_dropped}|c|#{$tags}"; + . "\ndatadog.dogstatsd.client.events:{$events_sent}|c|#{$tags}" + . "\ndatadog.dogstatsd.client.service_checks:{$service_checks_sent}|c|#{$tags}" + . "\ndatadog.dogstatsd.client.bytes_sent:{$bytes_sent}|c|#{$tags}" + . "\ndatadog.dogstatsd.client.bytes_dropped:{$bytes_dropped}|c|#{$tags}" + . "\ndatadog.dogstatsd.client.packets_sent:{$packets_sent}|c|#{$tags}" + . "\ndatadog.dogstatsd.client.packets_dropped:{$packets_dropped}|c|#{$tags}"; $this->assertSame( - $expected.$telemetry, - $actual, - $message + $expected . $telemetry . PHP_EOL, + $actual, + $message ); } @@ -87,10 +84,10 @@ public function testHostAndPortFromArgs() { putenv("DD_AGENT_HOST=myenvvarhost"); putenv("DD_DOGSTATSD_PORT=1234"); - $dog = new DogStatsd(array( + $dog = new DogStatsd([ 'host' => 'myhost', - 'port' => 4321 - )); + 'port' => 4321, + ]); $this->assertSame( 'myhost', self::getPrivate($dog, 'host'), @@ -125,10 +122,10 @@ public function testTiming() $stat = 'some.timing_metric'; $time = 43; $sampleRate = 1.0; - $tags = array('horse' => 'cart'); + $tags = ['horse' => 'cart']; $expectedUdpMessage = 'some.timing_metric:43|ms|#horse:cart'; - $dog = new DogStatsd(array('disable_telemetry' => false)); + $dog = new DogStatsd(['disable_telemetry' => false]); $dog->timing( $stat, @@ -158,10 +155,10 @@ public function testMicrotiming() $stat = 'some.microtiming_metric'; $time = 26; $sampleRate = 1.0; - $tags = array('tuba' => 'solo'); + $tags = ['tuba' => 'solo']; $expectedUdpMessage = 'some.microtiming_metric:26000|ms|#tuba:solo'; - $dog = new DogStatsd(array('disable_telemetry' => false)); + $dog = new DogStatsd(['disable_telemetry' => false]); $dog->microtiming( $stat, @@ -191,10 +188,10 @@ public function testGauge() $stat = 'some.gauge_metric'; $value = 5; $sampleRate = 1.0; - $tags = array('baseball' => 'cap'); + $tags = ['baseball' => 'cap']; $expectedUdpMessage = 'some.gauge_metric:5|g|#baseball:cap'; - $dog = new DogStatsd(array('disable_telemetry' => false)); + $dog = new DogStatsd(['disable_telemetry' => false]); $dog->gauge( $stat, @@ -224,10 +221,10 @@ public function testGaugeZero() $stat = 'some.gauge_metric'; $value = 0; $sampleRate = 1.0; - $tags = array('baseball' => 'cap'); + $tags = ['baseball' => 'cap']; $expectedUdpMessage = 'some.gauge_metric:0|g|#baseball:cap'; - $dog = new DogStatsd(array('disable_telemetry' => false)); + $dog = new DogStatsd(['disable_telemetry' => false]); $dog->gauge( $stat, @@ -257,10 +254,10 @@ public function testHistogram() $stat = 'some.histogram_metric'; $value = 109; $sampleRate = 1.0; - $tags = array('happy' => 'days'); + $tags = ['happy' => 'days']; $expectedUdpMessage = 'some.histogram_metric:109|h|#happy:days'; - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->histogram( $stat, @@ -290,10 +287,10 @@ public function testDistribution() $stat = 'some.distribution_metric'; $value = 7; $sampleRate = 1.0; - $tags = array('floppy' => 'hat'); + $tags = ['floppy' => 'hat']; $expectedUdpMessage = 'some.distribution_metric:7|d|#floppy:hat'; - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->distribution( $stat, @@ -320,6 +317,7 @@ public function testDistribution() /** * @dataProvider setProvider + * * @param $stat * @param $value * @param $sampleRate @@ -328,7 +326,7 @@ public function testDistribution() */ public function testSet($stat, $value, $sampleRate, $tags, $expectedUdpMessage) { - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->set( $stat, @@ -355,33 +353,34 @@ public function testSet($stat, $value, $sampleRate, $tags, $expectedUdpMessage) public function setProvider() { - return array( - 'integer' => array( + return [ + 'integer' => [ 'some.int_metric', 1, 1.0, - array('little' => 'bit'), - 'some.int_metric:1|s|#little:bit' - ), - 'float' => array( + ['little' => 'bit'], + 'some.int_metric:1|s|#little:bit', + ], + 'float' => [ 'some.float_metric', 3.1415926535898, 1.0, - array('little' => 'bit'), - 'some.float_metric:3.14|s|#little:bit' - ), - 'string' => array( + ['little' => 'bit'], + 'some.float_metric:3.14|s|#little:bit', + ], + 'string' => [ 'some.string_metric', 'uniqueval', 1.0, - array('little' => 'bit'), - 'some.string_metric:uniqueval|s|#little:bit' - ), - ); + ['little' => 'bit'], + 'some.string_metric:uniqueval|s|#little:bit', + ], + ]; } /** * @dataProvider serviceCheckProvider + * * @param $name * @param $status * @param $tags @@ -399,7 +398,7 @@ public function testServiceCheck( $timestamp, $expectedUdpMessage ) { - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->service_check( $name, @@ -418,7 +417,7 @@ public function testServiceCheck( $expectedUdpMessage, $argsPassedToSocketSendto[1], '', - array('metrics' => 0, 'service_checks' => 1) + ['metrics' => 0, 'service_checks' => 1] ); } @@ -426,13 +425,13 @@ public function serviceCheckProvider() { $name = 'neat-service'; $status = DogStatsd::CRITICAL; - $tags = array('red' => 'balloon', 'green' => 'ham'); + $tags = ['red' => 'balloon', 'green' => 'ham']; $hostname = 'some-host.com'; $message = 'Important message'; $timestamp = $this->getDeterministicTimestamp(); - return array( - 'all arguments provided' => array( + return [ + 'all arguments provided' => [ $name, $status, $tags, @@ -440,8 +439,8 @@ public function serviceCheckProvider() $message, $timestamp, '_sc|neat-service|2|d:1535776860|h:some-host.com|#red:balloon,green:ham|m:Important message', - ), - 'without tags' => array( + ], + 'without tags' => [ $name, $status, null, @@ -449,8 +448,8 @@ public function serviceCheckProvider() $message, $timestamp, '_sc|neat-service|2|d:1535776860|h:some-host.com|m:Important message', - ), - 'without hostname' => array( + ], + 'without hostname' => [ $name, $status, $tags, @@ -458,8 +457,8 @@ public function serviceCheckProvider() $message, $timestamp, '_sc|neat-service|2|d:1535776860|#red:balloon,green:ham|m:Important message', - ), - 'without message' => array( + ], + 'without message' => [ $name, $status, $tags, @@ -467,8 +466,8 @@ public function serviceCheckProvider() null, $timestamp, '_sc|neat-service|2|d:1535776860|h:some-host.com|#red:balloon,green:ham', - ), - 'without timestamp' => array( + ], + 'without timestamp' => [ $name, $status, $tags, @@ -476,21 +475,21 @@ public function serviceCheckProvider() $message, null, '_sc|neat-service|2|h:some-host.com|#red:balloon,green:ham|m:Important message', - ), - ); + ], + ]; } public function testSend() { $sampleRate = 1.0; - $tags = array( - 'cowboy' => 'hat' - ); + $tags = [ + 'cowboy' => 'hat', + ]; $expectedUdpMessage1 = 'foo.metric:893|s|#cowboy:hat'; $expectedUdpMessage2 = 'bar.metric:4|s|#cowboy:hat'; - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->set("foo.metric", 893, $sampleRate, $tags); $dog->set("bar.metric", 4, $sampleRate, $tags); @@ -516,21 +515,21 @@ public function testSend() $expectedUdpMessage2, $argsPassedToSocketSendtoCall2[1], 'Second UDP message should be correct', - array("bytes_sent" => 693, "packets_sent" => 1) + ["bytes_sent" => 694, "packets_sent" => 1] ); } public function testSendSerializesTagAsString() { - $data = array( + $data = [ 'foo.metric' => '82|s', - ); + ]; $sampleRate = 1.0; $tag = 'string:tag'; $expectedUdpMessage = 'foo.metric:82|s|#string:tag'; - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->send($data, $sampleRate, $tag); @@ -545,15 +544,15 @@ public function testSendSerializesTagAsString() public function testSendSerializesMessageWithoutTags() { - $data = array( + $data = [ 'foo.metric' => '19872|h', - ); + ]; $sampleRate = 1.0; $tag = null; $expectedUdpMessage = 'foo.metric:19872|h'; - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->send($data, $sampleRate, $tag); @@ -568,9 +567,9 @@ public function testSendSerializesMessageWithoutTags() public function testSendReturnsEarlyWhenPassedEmptyData() { - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); - $dog->send(array()); + $dog->send([]); $spy = $this->getSocketSpy(); @@ -590,12 +589,12 @@ public function testSendSendsWhenRandCalculationLessThanSampleRate() $mt_rand_stub_return_value = 1; $mt_getrandmax_stub_return_value = 3; - $data = array( - 'foo.metric' => '469|s' - ); + $data = [ + 'foo.metric' => '469|s', + ]; $sampleRate = 0.5; - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->send($data, $sampleRate); @@ -617,12 +616,12 @@ public function testSendSendsWhenRandCalculationEqualToSampleRate() $mt_rand_stub_return_value = 1; $mt_getrandmax_stub_return_value = 2; - $data = array( - 'foo.metric' => '23|g' - ); + $data = [ + 'foo.metric' => '23|g', + ]; $sampleRate = 0.5; - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->send($data, $sampleRate); @@ -644,12 +643,12 @@ public function testSendDoesNotSendWhenRandCalculationGreaterThanSampleRate() $mt_rand_stub_return_value = 1; $mt_getrandmax_stub_return_value = 1; - $data = array( - 'foo.metric' => '23|g' - ); + $data = [ + 'foo.metric' => '23|g', + ]; $sampleRate = 0.5; - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->send($data, $sampleRate); @@ -664,13 +663,13 @@ public function testSendDoesNotSendWhenRandCalculationGreaterThanSampleRate() public function testIncrement() { - $stats = array( + $stats = [ 'foo.metric', - ); + ]; $expectedUdpMessage = 'foo.metric:1|c'; - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->increment($stats); @@ -693,13 +692,13 @@ public function testIncrement() public function testDecrement() { - $stats = array( + $stats = [ 'foo.metric', - ); + ]; $expectedUdpMessage = 'foo.metric:-1|c'; - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->decrement($stats); @@ -722,13 +721,13 @@ public function testDecrement() public function testDecrementWithValueGreaterThanOne() { - $stats = array( + $stats = [ 'foo.metric', - ); + ]; $expectedUdpMessage = 'foo.metric:-9|c'; - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->decrement($stats, 1.0, null, 9); @@ -751,13 +750,13 @@ public function testDecrementWithValueGreaterThanOne() public function testDecrementWithValueLessThanOne() { - $stats = array( + $stats = [ 'foo.metric', - ); + ]; $expectedUdpMessage = 'foo.metric:-47|c'; - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->decrement($stats, 1.0, null, -47); @@ -780,20 +779,20 @@ public function testDecrementWithValueLessThanOne() public function testUpdateStats() { - $stats = array( + $stats = [ 'foo.metric', 'bar.metric', - ); + ]; $delta = 3; $sampleRate = 1.0; - $tags = array( + $tags = [ 'every' => 'day', - ); + ]; $expectedUdpMessage1 = 'foo.metric:3|c|#every:day'; $expectedUdpMessage2 = 'bar.metric:3|c|#every:day'; - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->updateStats($stats, $delta, $sampleRate, $tags); @@ -816,14 +815,14 @@ public function testUpdateStats() $expectedUdpMessage1, $argsPassedToSocketSendto[0][1], 'Should send the expected message for the first call', - array("metrics" => 2) + ["metrics" => 2] ); $this->assertSameWithTelemetry( $expectedUdpMessage2, $argsPassedToSocketSendto[1][1], 'Should send the expected message for the first call', - array("metrics" => 0, "bytes_sent" => 690, "packets_sent" => 1) + ["metrics" => 0, "bytes_sent" => 691, "packets_sent" => 1] ); } @@ -832,13 +831,13 @@ public function testUpdateStatsWithStringMetric() $stats = 'foo.metric'; $delta = -45; $sampleRate = 1.0; - $tags = array( + $tags = [ 'long' => 'walk', - ); + ]; $expectedUdpMessage = 'foo.metric:-45|c|#long:walk'; - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->updateStats($stats, $delta, $sampleRate, $tags); @@ -863,7 +862,7 @@ public function testReport() { $expectedUdpMessage = 'some fake UDP message'; - $dog = new DogStatsd(array("disable_telemetry" => true)); + $dog = new DogStatsd(["disable_telemetry" => true]); $dog->report($expectedUdpMessage); @@ -872,12 +871,12 @@ public function testReport() $argsPassedToSocketSendto = $spy->argsFromSocketSendtoCalls[0]; $this->assertSame( - $expectedUdpMessage, + $expectedUdpMessage . PHP_EOL, $argsPassedToSocketSendto[1] ); $this->assertSame( - strlen($expectedUdpMessage), + strlen($expectedUdpMessage) + 1, $argsPassedToSocketSendto[2] ); } @@ -886,7 +885,7 @@ public function testFlushUdp() { $expectedUdpMessage = 'foo'; - $dog = new DogStatsd(array("disable_telemetry" => true)); + $dog = new DogStatsd(["disable_telemetry" => true]); $dog->flush($expectedUdpMessage); @@ -901,7 +900,7 @@ public function testFlushUdp() ); $this->assertSame( - array(AF_INET, SOCK_DGRAM, SOL_UDP), + [AF_INET, SOCK_DGRAM, SOL_UDP], $spy->argsFromSocketCreateCalls[0], 'Should create a UDP socket to send datagrams over IPv4' ); @@ -925,20 +924,20 @@ public function testFlushUdp() ); $this->assertSame( - array( + [ $socketCreateReturnValue, - $expectedUdpMessage, - strlen($expectedUdpMessage), + $expectedUdpMessage . PHP_EOL, + strlen($expectedUdpMessage) + 1, 0, 'localhost', - 8125 - ), + 8125, + ], $spy->argsFromSocketSendtoCalls[0], 'Should send the expected message to localhost:8125' ); $this->assertCount( - 1, + 0, $spy->argsFromSocketCloseCalls, 'Should call socket_close once' ); @@ -955,7 +954,7 @@ public function testFlushUds() $expectedUdsMessage = 'foo'; $expectedUdsSocketPath = '/path/to/some.socket'; - $dog = new Dogstatsd(array("socket_path" => $expectedUdsSocketPath, "disable_telemetry" => true)); + $dog = new Dogstatsd(["socket_path" => $expectedUdsSocketPath, "disable_telemetry" => true]); $dog->flush($expectedUdsMessage); @@ -970,7 +969,7 @@ public function testFlushUds() ); $this->assertSame( - array(AF_UNIX, SOCK_DGRAM, 0), + [AF_UNIX, SOCK_DGRAM, 0], $spy->argsFromSocketCreateCalls[0], 'Should create a UDS socket to send datagrams over UDS' ); @@ -994,22 +993,22 @@ public function testFlushUds() ); $this->assertSame( - array( + [ $socketCreateReturnValue, - $expectedUdsMessage, - strlen($expectedUdsMessage), + $expectedUdsMessage . PHP_EOL, + strlen($expectedUdsMessage . PHP_EOL), 0, $expectedUdsSocketPath, - null - ), + null, + ], $spy->argsFromSocketSendtoCalls[0], 'Should send the expected message to /path/to/some.socket' ); $this->assertCount( - 1, + 0, $spy->argsFromSocketCloseCalls, - 'Should call socket_close once' + 'Should no call socket_close' ); $this->assertSame( @@ -1022,22 +1021,23 @@ public function testFlushUds() public function testEventUdp() { $eventTitle = 'Some event title'; - $eventVals = array( - 'text' => "Some event text\nthat spans 2 lines", - 'date_happened' => $this->getDeterministicTimestamp(), - 'hostname' => 'some.host.com', - 'aggregation_key' => '83e2cf', - 'priority' => 'normal', + $eventVals = [ + 'text' => "Some event text\nthat spans 2 lines", + 'date_happened' => $this->getDeterministicTimestamp(), + 'hostname' => 'some.host.com', + 'aggregation_key' => '83e2cf', + 'priority' => 'normal', 'source_type_name' => 'jenkins', - 'alert_type' => 'warning', - 'tags' => array( + 'alert_type' => 'warning', + 'tags' => [ 'chicken' => 'nachos', - ), - ); + ], + ]; - $expectedUdpMessage = "_e{16,35}:Some event title|Some event text\\nthat spans 2 lines|d:1535776860|h:some.host.com|k:83e2cf|p:normal|s:jenkins|t:warning|#chicken:nachos"; + $expectedUdpMessage = + "_e{16,35}:Some event title|Some event text\\nthat spans 2 lines|d:1535776860|h:some.host.com|k:83e2cf|p:normal|s:jenkins|t:warning|#chicken:nachos"; - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->event($eventTitle, $eventVals); @@ -1055,7 +1055,7 @@ public function testEventUdp() $expectedUdpMessage, $argsPassedToSocketSendto[1], "", - array("events" => 1, "metrics" => 0) + ["events" => 1, "metrics" => 0] ); } @@ -1071,7 +1071,7 @@ public function testEventUdpWithEmptyValues() $expectedUdpMessage = "_e{0,0}:|"; - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->event($eventTitle); @@ -1089,18 +1089,18 @@ public function testEventUdpWithEmptyValues() $expectedUdpMessage, $argsPassedToSocketSendto[1], "", - array("events" => 1, "metrics" => 0) + ["events" => 1, "metrics" => 0] ); } public function testGlobalTags() { - $dog = new DogStatsd(array( - 'global_tags' => array( + $dog = new DogStatsd([ + 'global_tags' => [ 'my_tag' => 'tag_value', - ), - 'disable_telemetry' => false - )); + ], + 'disable_telemetry' => false, + ]); $dog->timing('metric', 42, 1.0); $spy = $this->getSocketSpy(); $this->assertSame( @@ -1115,19 +1115,19 @@ public function testGlobalTags() $expectedUdpMessage, $argsPassedToSocketSendTo[1], "", - array("tags" => "my_tag:tag_value") + ["tags" => "my_tag:tag_value"] ); } public function testGlobalTagsWithEntityIdFromEnvVar() { putenv("DD_ENTITY_ID=04652bb7-19b7-11e9-9cc6-42010a9c016d"); - $dog = new DogStatsd(array( - 'global_tags' => array( + $dog = new DogStatsd([ + 'global_tags' => [ 'my_tag' => 'tag_value', - ), - 'disable_telemetry' => false - )); + ], + 'disable_telemetry' => false, + ]); $dog->timing('metric', 42, 1.0); $spy = $this->getSocketSpy(); $this->assertSame( @@ -1135,27 +1135,28 @@ public function testGlobalTagsWithEntityIdFromEnvVar() count($spy->argsFromSocketSendtoCalls), 'Should send 1 UDP message' ); - $expectedUdpMessage = 'metric:42|ms|#my_tag:tag_value,dd.internal.entity_id:04652bb7-19b7-11e9-9cc6-42010a9c016d'; + $expectedUdpMessage = + 'metric:42|ms|#my_tag:tag_value,dd.internal.entity_id:04652bb7-19b7-11e9-9cc6-42010a9c016d'; $argsPassedToSocketSendTo = $spy->argsFromSocketSendtoCalls[0]; $this->assertSameWithTelemetry( $expectedUdpMessage, $argsPassedToSocketSendTo[1], "", - array("tags" => "my_tag:tag_value,dd.internal.entity_id:04652bb7-19b7-11e9-9cc6-42010a9c016d") + ["tags" => "my_tag:tag_value,dd.internal.entity_id:04652bb7-19b7-11e9-9cc6-42010a9c016d"] ); putenv("DD_ENTITY_ID"); } public function testGlobalTagsAreSupplementedWithLocalTags() { - $dog = new DogStatsd(array( - 'global_tags' => array( + $dog = new DogStatsd([ + 'global_tags' => [ 'my_tag' => 'tag_value', - ), - 'disable_telemetry' => false - )); - $dog->timing('metric', 42, 1.0, array('other_tag' => 'other_value')); + ], + 'disable_telemetry' => false, + ]); + $dog->timing('metric', 42, 1.0, ['other_tag' => 'other_value']); $spy = $this->getSocketSpy(); $this->assertSame( 1, @@ -1169,21 +1170,20 @@ public function testGlobalTagsAreSupplementedWithLocalTags() $expectedUdpMessage, $argsPassedToSocketSendTo[1], "", - array("tags" => "my_tag:tag_value") + ["tags" => "my_tag:tag_value"] ); } - public function testGlobalTagsAreReplacedWithConflictingLocalTags() { - $dog = new DogStatsd(array( - 'global_tags' => array( + $dog = new DogStatsd([ + 'global_tags' => [ 'my_tag' => 'tag_value', - ), - 'disable_telemetry' => false - )); + ], + 'disable_telemetry' => false, + ]); - $dog->timing('metric', 42, 1.0, array('my_tag' => 'other_value')); + $dog->timing('metric', 42, 1.0, ['my_tag' => 'other_value']); $spy = $this->getSocketSpy(); $this->assertSame( 1, @@ -1197,7 +1197,7 @@ public function testGlobalTagsAreReplacedWithConflictingLocalTags() $expectedUdpMessage, $argsPassedToSocketSendTo[1], "", - array("tags" => "my_tag:tag_value") + ["tags" => "my_tag:tag_value"] ); } @@ -1207,14 +1207,14 @@ public function testTelemetryDefault() $dog->gauge('metric', 42); $this->assertSame( - 'metric:42|g', + 'metric:42|g' . PHP_EOL, $this->getSocketSpy()->argsFromSocketSendtoCalls[0][1] ); } public function testTelemetryEnable() { - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->gauge('metric', 42); $this->assertSameWithTelemetry( @@ -1225,43 +1225,88 @@ public function testTelemetryEnable() public function testTelemetryAllDataType() { - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->timing('test', 21); $this->assertSameWithTelemetry('test:21|ms', $this->getSocketSpy()->argsFromSocketSendtoCalls[0][1]); $dog->gauge('test', 21); - $this->assertSameWithTelemetry('test:21|g', $this->getSocketSpy()->argsFromSocketSendtoCalls[1][1], "", array("bytes_sent" => 675, "packets_sent" => 1)); + $this->assertSameWithTelemetry( + 'test:21|g', + $this->getSocketSpy()->argsFromSocketSendtoCalls[1][1], + "", + ["bytes_sent" => 676, "packets_sent" => 1] + ); $dog->histogram('test', 21); - $this->assertSameWithTelemetry('test:21|h', $this->getSocketSpy()->argsFromSocketSendtoCalls[2][1], "", array("bytes_sent" => 676, "packets_sent" => 1)); + $this->assertSameWithTelemetry( + 'test:21|h', + $this->getSocketSpy()->argsFromSocketSendtoCalls[2][1], + "", + ["bytes_sent" => 677, "packets_sent" => 1] + ); $dog->distribution('test', 21); - $this->assertSameWithTelemetry('test:21|d', $this->getSocketSpy()->argsFromSocketSendtoCalls[3][1], "", array("bytes_sent" => 676, "packets_sent" => 1)); + $this->assertSameWithTelemetry( + 'test:21|d', + $this->getSocketSpy()->argsFromSocketSendtoCalls[3][1], + "", + ["bytes_sent" => 677, "packets_sent" => 1] + ); $dog->set('test', 21); - $this->assertSameWithTelemetry('test:21|s', $this->getSocketSpy()->argsFromSocketSendtoCalls[4][1], "", array("bytes_sent" => 676, "packets_sent" => 1)); + $this->assertSameWithTelemetry( + 'test:21|s', + $this->getSocketSpy()->argsFromSocketSendtoCalls[4][1], + "", + ["bytes_sent" => 677, "packets_sent" => 1] + ); $dog->increment('test'); - $this->assertSameWithTelemetry('test:1|c', $this->getSocketSpy()->argsFromSocketSendtoCalls[5][1], "", array("bytes_sent" => 676, "packets_sent" => 1)); + $this->assertSameWithTelemetry( + 'test:1|c', + $this->getSocketSpy()->argsFromSocketSendtoCalls[5][1], + "", + ["bytes_sent" => 677, "packets_sent" => 1] + ); $dog->decrement('test'); - $this->assertSameWithTelemetry('test:-1|c', $this->getSocketSpy()->argsFromSocketSendtoCalls[6][1], "", array("bytes_sent" => 675, "packets_sent" => 1)); + $this->assertSameWithTelemetry( + 'test:-1|c', + $this->getSocketSpy()->argsFromSocketSendtoCalls[6][1], + "", + ["bytes_sent" => 676, "packets_sent" => 1] + ); - $dog->event('ev', array('text' => 'text')); - $this->assertSameWithTelemetry('_e{2,4}:ev|text', $this->getSocketSpy()->argsFromSocketSendtoCalls[7][1], "", array("bytes_sent" => 676, "packets_sent" => 1, "metrics" => 0, "events" => 1)); + $dog->event('ev', ['text' => 'text']); + $this->assertSameWithTelemetry( + '_e{2,4}:ev|text', + $this->getSocketSpy()->argsFromSocketSendtoCalls[7][1], + "", + ["bytes_sent" => 677, "packets_sent" => 1, "metrics" => 0, "events" => 1] + ); $dog->service_check('sc', 0); - $this->assertSameWithTelemetry('_sc|sc|0', $this->getSocketSpy()->argsFromSocketSendtoCalls[8][1], "", array("bytes_sent" => 682, "packets_sent" => 1, "metrics" => 0, "service_checks" => 1)); + $this->assertSameWithTelemetry( + '_sc|sc|0', + $this->getSocketSpy()->argsFromSocketSendtoCalls[8][1], + "", + ["bytes_sent" => 683, "packets_sent" => 1, "metrics" => 0, "service_checks" => 1] + ); # force flush to get the telemetry about the last message sent $dog->flush(""); - $this->assertSameWithTelemetry('', $this->getSocketSpy()->argsFromSocketSendtoCalls[9][1], "", array("bytes_sent" => 675, "packets_sent" => 1, "metrics" => 0)); + $this->assertSameWithTelemetry( + '', + $this->getSocketSpy()->argsFromSocketSendtoCalls[9][1], + "", + ["bytes_sent" => 676, "packets_sent" => 1, "metrics" => 0] + ); } public function testTelemetryNetworkError() { - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $this->getSocketSpy()->returnErrorOnSend = true; $dog->gauge('test', 21); @@ -1270,32 +1315,52 @@ public function testTelemetryNetworkError() $this->getSocketSpy()->returnErrorOnSend = false; $dog->gauge('test', 22); - $this->assertSameWithTelemetry('test:22|g', $this->getSocketSpy()->argsFromSocketSendtoCalls[0][1], "", array("metrics" => 3, "bytes_dropped" => 1351, "packets_dropped" => 2)); + $this->assertSameWithTelemetry( + 'test:22|g', + $this->getSocketSpy()->argsFromSocketSendtoCalls[0][1], + "", + ["metrics" => 3, "bytes_dropped" => 1353, "packets_dropped" => 2] + ); # force flush to get the telemetry about the last message sent $dog->flush(""); - $this->assertSameWithTelemetry('', $this->getSocketSpy()->argsFromSocketSendtoCalls[1][1], "", array("bytes_sent" => 677, "packets_sent" => 1, "metrics" => 0)); + $this->assertSameWithTelemetry( + '', + $this->getSocketSpy()->argsFromSocketSendtoCalls[1][1], + "", + ["bytes_sent" => 678, "packets_sent" => 1, "metrics" => 0] + ); } public function testDecimalNormalization() { - $dog = new DogStatsd(array("disable_telemetry" => false, "decimal_precision" => 5)); + $dog = new DogStatsd(["disable_telemetry" => false, "decimal_precision" => 5]); $dog->timing('test', 21.00000); $this->assertSameWithTelemetry('test:21|ms', $this->getSocketSpy()->argsFromSocketSendtoCalls[0][1]); $dog->gauge('test', 21.222225); - $this->assertSameWithTelemetry('test:21.22223|g', $this->getSocketSpy()->argsFromSocketSendtoCalls[1][1], "", array("bytes_sent" => 675, "packets_sent" => 1)); + $this->assertSameWithTelemetry( + 'test:21.22223|g', + $this->getSocketSpy()->argsFromSocketSendtoCalls[1][1], + "", + ["bytes_sent" => 676, "packets_sent" => 1] + ); $dog->gauge('test', 2000.00); - $this->assertSameWithTelemetry('test:2000|g', $this->getSocketSpy()->argsFromSocketSendtoCalls[2][1], "", array("bytes_sent" => 682, "packets_sent" => 1)); + $this->assertSameWithTelemetry( + 'test:2000|g', + $this->getSocketSpy()->argsFromSocketSendtoCalls[2][1], + "", + ["bytes_sent" => 683, "packets_sent" => 1] + ); } public function testFloatLocalization() { $defaultLocale = setlocale(LC_ALL, 0); setlocale(LC_ALL, 'nl_NL'); - $dog = new DogStatsd(array("disable_telemetry" => false)); + $dog = new DogStatsd(["disable_telemetry" => false]); $dog->timing('test', 21.21000); $this->assertSameWithTelemetry('test:21.21|ms', $this->getSocketSpy()->argsFromSocketSendtoCalls[0][1]); @@ -1306,14 +1371,14 @@ public function testSampleRateFloatLocalization() { $defaultLocale = setlocale(LC_ALL, 0); setlocale(LC_ALL, 'de_DE'); - $dog = new DogStatsd(array("disable_telemetry" => true)); + $dog = new DogStatsd(["disable_telemetry" => true]); while (!array_key_exists(0, $this->getSocketSpy()->argsFromSocketSendtoCalls)) { $dog->timing('test', 21.21000, 0.3); } $this->assertSame( - 'test:21.21|ms|@0.3', + 'test:21.21|ms|@0.3' . PHP_EOL, $this->getSocketSpy()->argsFromSocketSendtoCalls[0][1] ); setlocale(LC_ALL, $defaultLocale); @@ -1321,18 +1386,35 @@ public function testSampleRateFloatLocalization() public function testMetricPrefix() { - $dog = new DogStatsd(array("disable_telemetry" => false, "metric_prefix" => 'test_prefix')); + $dog = new DogStatsd(["disable_telemetry" => false, "metric_prefix" => 'test_prefix']); - $dog->timing('test', 21.00); - $this->assertSameWithTelemetry('test_prefix.test:21|ms', $this->getSocketSpy()->argsFromSocketSendtoCalls[0][1]); + $dog->timing('test', 21.00); + $this->assertSameWithTelemetry( + 'test_prefix.test:21|ms', + $this->getSocketSpy()->argsFromSocketSendtoCalls[0][1] + ); - $dog->gauge('test', 21.22); - $this->assertSameWithTelemetry('test_prefix.test:21.22|g', $this->getSocketSpy()->argsFromSocketSendtoCalls[1][1], "", array("bytes_sent" => 687, "packets_sent" => 1)); + $dog->gauge('test', 21.22); + $this->assertSameWithTelemetry( + 'test_prefix.test:21.22|g', + $this->getSocketSpy()->argsFromSocketSendtoCalls[1][1], + "", + ["bytes_sent" => 688, "packets_sent" => 1] + ); - $dog->gauge('test', 2000.00); - $this->assertSameWithTelemetry('test_prefix.test:2000|g', $this->getSocketSpy()->argsFromSocketSendtoCalls[2][1], "", array("bytes_sent" => 691, "packets_sent" => 1)); + $dog->gauge('test', 2000.00); + $this->assertSameWithTelemetry( + 'test_prefix.test:2000|g', + $this->getSocketSpy()->argsFromSocketSendtoCalls[2][1], + "", + ["bytes_sent" => 692, "packets_sent" => 1] + ); } + private function get_default(&$var, $default = null) + { + return isset($var) ? $var : $default; + } /** * Get a timestamp created from a real date that is deterministic in nature diff --git a/tests/UnitTests/DogStatsd/TagSerializationTest.php b/tests/UnitTests/DogStatsd/TagSerializationTest.php index 9d43946..251466a 100644 --- a/tests/UnitTests/DogStatsd/TagSerializationTest.php +++ b/tests/UnitTests/DogStatsd/TagSerializationTest.php @@ -2,30 +2,26 @@ namespace DataDog\UnitTests\DogStatsd; -use PHPUnit\Framework\TestCase; use DataDog\DogStatsd; +use PHPUnit\Framework\TestCase; +use ReflectionMethod; class TagSerializationTest extends TestCase { - private function callPrivate($object, $method, $params) { - $reflectionMethod = new \ReflectionMethod(get_class($object), $method); - $reflectionMethod->setAccessible(true); - return $reflectionMethod->invoke($object, $params); - } - - /** - * @dataProvider tagProvider - * - * @param $tags - * @param $expected - */ - public function testTagSerialization($tags, $expected) { - $dog = new DogStatsd(); + /** + * @dataProvider tagProvider + * + * @param $tags + * @param $expected + */ + public function testTagSerialization($tags, $expected) + { + $dog = new DogStatsd(); - $this->assertsame( - $expected, - $this->callPrivate($dog, 'serializeTags', $tags) - ); + $this->assertsame( + $expected, + $this->callPrivate($dog, 'serializeTags', $tags) + ); } public function tagProvider() @@ -33,33 +29,41 @@ public function tagProvider() return [ 'without tags' => [ [], - '' + '', ], 'one string' => [ ['foo' => 'bar'], - '|#foo:bar' + '|#foo:bar', ], 'two strings' => [ ['foo' => 'bar', 'baz' => 'blam'], - '|#foo:bar,baz:blam' + '|#foo:bar,baz:blam', ], 'one string one int' => [ ['foo' => 'bar', 'baz' => 42], - '|#foo:bar,baz:42' + '|#foo:bar,baz:42', ], // https://github.com/DataDog/php-datadogstatsd/issues/118 'one string one true boolean' => [ ['foo' => 'bar', 'baz' => true], - '|#foo:bar,baz:true' + '|#foo:bar,baz:true', ], 'one string one false boolean' => [ ['foo' => 'bar', 'baz' => false], - '|#foo:bar,baz:false' + '|#foo:bar,baz:false', ], 'grab bag' => [ ['foo' => 'bar', 'baz' => false, 'nullValue' => null, 'blam' => 1, 'blah' => 0], - '|#foo:bar,baz:false,nullValue,blam:1,blah:0' - ] + '|#foo:bar,baz:false,nullValue,blam:1,blah:0', + ], ]; } + + private function callPrivate($object, $method, $params) + { + $reflectionMethod = new ReflectionMethod(get_class($object), $method); + $reflectionMethod->setAccessible(true); + + return $reflectionMethod->invoke($object, $params); + } } diff --git a/tests/socket_function_stubs.php b/tests/socket_function_stubs.php index 554f877..82b653e 100644 --- a/tests/socket_function_stubs.php +++ b/tests/socket_function_stubs.php @@ -19,6 +19,7 @@ * @param int $domain * @param int $type * @param int $protocol + * * @return resource */ function socket_create($domain, $type, $protocol) @@ -58,7 +59,7 @@ function socket_set_nonblock($socket) * @param string $addr * @param int $port */ -function socket_sendto($socket, $buf, $len, $flags, $addr, $port=null) +function socket_sendto($socket, $buf, $len, $flags, $addr, $port = null) { global $socketSpy; @@ -76,3 +77,15 @@ function socket_close($socket) $socketSpy->socketCloseWasCalled($socket); } + +/** + * Stub of built in global PHP function socket_last_error + * + * @param resource $socket + */ +function socket_last_error($socket) +{ + global $socketSpy; + + $socketSpy->socketLastErrorWasCalled($socket); +}