307 lines
8.9 KiB
PHP
307 lines
8.9 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Sends statistics to an instance of the statsd daemon over UDP
|
|
*
|
|
* Make changes here: https://github.com/iFixit/statsd-php-client
|
|
* See: https://github.com/etsy/statsd
|
|
*
|
|
|
|
Copyright (c) 2010 Etsy
|
|
Copyright (c) 2012 iFixit
|
|
|
|
Permission is hereby granted, free of charge, to any person
|
|
obtaining a copy of this software and associated documentation
|
|
files (the "Software"), to deal in the Software without
|
|
restriction, including without limitation the rights to use,
|
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following
|
|
conditions:
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
**/
|
|
|
|
// File dated 2014-10-18
|
|
|
|
class StatsD {
|
|
|
|
/**
|
|
* Configuration (+ default values)
|
|
*
|
|
* @var array
|
|
*/
|
|
public static $config = array(
|
|
/**
|
|
* Name of our statsd-server
|
|
* @var string
|
|
*/
|
|
'host' => 'localhost',
|
|
|
|
/**
|
|
* UDP-port of the statsd-server
|
|
* @var string
|
|
*/
|
|
'port' => '8125',
|
|
);
|
|
|
|
/**
|
|
* Maximum payload we may cramp into a UDP packet
|
|
*/
|
|
|
|
const MAX_PACKET_SIZE = 512;
|
|
|
|
/**
|
|
* If true, stats are added to a queue until a flush is triggered
|
|
* If false, stats are sent immediately, one UDP packet per call
|
|
*
|
|
* @var bool
|
|
*/
|
|
protected static $addStatsToQueue = false;
|
|
|
|
/**
|
|
* Internal queue of stats to be sent
|
|
* @var array
|
|
*/
|
|
protected static $queuedStats = array();
|
|
|
|
/**
|
|
* Internal representation of queued counters to be sent.
|
|
* This is used to aggregate increment/decrements before sending them.
|
|
* @var array
|
|
*/
|
|
protected static $queuedCounters = array();
|
|
|
|
|
|
public function __construct($config = array()) {
|
|
|
|
foreach (array_keys(StatsD::$config) as $param)
|
|
{
|
|
if (isset($config[$param]) && $config[$param] != StatsD::$config[$param])
|
|
{
|
|
StatsD::$config[$param] = $config[$param];
|
|
}
|
|
//var_dump(StatsD::$config[$param]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log timing information
|
|
*
|
|
* @param string $stat The metric to in log timing info for.
|
|
* @param float $time The ellapsed time (ms) to log
|
|
* @param float $sampleRate the rate (0-1) for sampling.
|
|
**/
|
|
public static function timing($stat, $time, $sampleRate=1.0) {
|
|
static::queueStats(array($stat => self::num($time) . "|ms"), $sampleRate);
|
|
}
|
|
|
|
/**
|
|
* Report the current value of some gauged value.
|
|
*
|
|
* @param string|array $stat The metric to report on
|
|
* @param float $value The value for this gauge
|
|
*/
|
|
public static function gauge($stat, $value) {
|
|
// echo("$stat || $value \n");
|
|
static::queueStats(array($stat => self::num($value) . "|g"));
|
|
}
|
|
|
|
/**
|
|
* Increments one stats counter
|
|
*
|
|
* @param string $stat The metric to increment.
|
|
* @param float $sampleRate the rate (0-1) for sampling.
|
|
**/
|
|
public static function increment($stat, $sampleRate=1.0) {
|
|
static::updateStat($stat, 1, $sampleRate);
|
|
}
|
|
|
|
/**
|
|
* Decrements one counter.
|
|
*
|
|
* @param string $stat The metric to decrement.
|
|
* @param float $sampleRate the rate (0-1) for sampling.
|
|
**/
|
|
public static function decrement($stat, $sampleRate=1.0) {
|
|
static::updateStat($stat, -1, $sampleRate);
|
|
}
|
|
|
|
/**
|
|
* Pause and collect all reported stats until flushStatsOutput() is called.
|
|
*/
|
|
public static function pauseStatsOutput() {
|
|
static::$addStatsToQueue = true;
|
|
}
|
|
|
|
/**
|
|
* Send all stats generated AFTER a call to pauseStatsOutput()
|
|
* and resume immediate sending again.
|
|
*/
|
|
public static function flushStatsOutput() {
|
|
static::$addStatsToQueue = false;
|
|
static::sendAllStats();
|
|
}
|
|
|
|
/**
|
|
* Updates a counter by an arbitrary amount.
|
|
*
|
|
* @param string $stat The metric to update.
|
|
* @param float $delta The amount to increment/decrement the metric by.
|
|
* @param float $sampleRate the rate (0-1) for sampling.
|
|
**/
|
|
public static function updateStat($stat, $delta=1, $sampleRate=1.0) {
|
|
$deltaStr = self::num($delta);
|
|
if ($sampleRate < 1) {
|
|
if ((mt_rand() / mt_getrandmax()) <= $sampleRate) {
|
|
static::$queuedStats[] = "$stat:$deltaStr|c|@". self::num($sampleRate);
|
|
}
|
|
} else {
|
|
if (!isset(static::$queuedCounters[$stat])) {
|
|
static::$queuedCounters[$stat] = 0;
|
|
}
|
|
static::$queuedCounters[$stat] += $delta;
|
|
}
|
|
|
|
if (!static::$addStatsToQueue) {
|
|
static::sendAllStats();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deprecated, works, but will be removed in the future.
|
|
*
|
|
* @param string|array $stats The metric(s) to update. Should be either a string or an array of strings.
|
|
* @param float $delta The amount to increment/decrement each metric by.
|
|
* @param float $sampleRate the rate (0-1) for sampling
|
|
* @deprecated in favour of updateStat
|
|
*/
|
|
public static function updateStats($stats, $delta=1, $sampleRate=1.0) {
|
|
if (!is_array($stats)) {
|
|
self::updateStat($stats, $delta, $sampleRate);
|
|
return;
|
|
}
|
|
foreach ($stats as $stat) {
|
|
self::updateStat($stat, $delta, $sampleRate);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add stats to the queue or send them immediately depending on
|
|
* self::$addStatsToQueue
|
|
*
|
|
* @param array $data The data to be queued.
|
|
* @param float $sampleRate the rate (0-1) for sampling
|
|
*/
|
|
protected static function queueStats($data, $sampleRate=1.0) {
|
|
if ($sampleRate < 1) {
|
|
foreach ($data as $stat => $value) {
|
|
if ((mt_rand() / mt_getrandmax()) <= $sampleRate) {
|
|
static::$queuedStats[] = "$stat:$value|@". self::num($sampleRate);
|
|
}
|
|
}
|
|
} else {
|
|
foreach ($data as $stat => $value) {
|
|
static::$queuedStats[] = "$stat:$value";
|
|
}
|
|
}
|
|
|
|
if (!static::$addStatsToQueue) {
|
|
static::sendAllStats();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Flush the queue and send all the stats we have.
|
|
*/
|
|
protected static function sendAllStats() {
|
|
if (empty(static::$queuedStats) && empty(static::$queuedCounters))
|
|
return;
|
|
|
|
foreach (static::$queuedCounters as $stat => $value) {
|
|
$line = "$stat:$value|c";
|
|
static::$queuedStats[] = $line;
|
|
}
|
|
|
|
self::sendLines(static::$queuedStats);
|
|
|
|
static::$queuedStats = array();
|
|
static::$queuedCounters = array();
|
|
}
|
|
|
|
/**
|
|
* Squirt the metrics over UDP
|
|
*
|
|
* @param array $data the data to be sent.
|
|
*/
|
|
protected static function sendAsUDP($data) {
|
|
// Wrap this in a try/catch -
|
|
// failures in any of this should be silently ignored
|
|
try {
|
|
// FIXME, currently not validate hostname/ip and port
|
|
$host = StatsD::$config['host'];
|
|
$port = StatsD::$config['port'];
|
|
//var_dump(StatsD::$config);
|
|
$fp = fsockopen("udp://$host", $port, $errno, $errstr);
|
|
if (! $fp) { return; }
|
|
// Non-blocking I/O, please.
|
|
stream_set_blocking($fp, 0);
|
|
fwrite($fp, $data);
|
|
fclose($fp);
|
|
} catch (Exception $e) {
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send these lines via UDP in groups of self::MAX_PACKET_SIZE bytes
|
|
* Sending UDP packets bigger than ~500-1000 bytes will mean the packets
|
|
* get fragmented, and if ONE fragment doesn't make it, the whole datagram
|
|
* is thrown out.
|
|
*
|
|
* @param array $lines The lines to be sent to the stats-Server
|
|
*/
|
|
protected static function sendLines($lines) {
|
|
$out = array();
|
|
$chunkSize = 0;
|
|
$i = 0; $lineCount = count($lines);
|
|
while ($i < $lineCount) {
|
|
$line = $lines[$i];
|
|
$len = strlen($line) + 1;
|
|
$chunkSize += $len;
|
|
if ($chunkSize > self::MAX_PACKET_SIZE) {
|
|
static::sendAsUDP(implode("\n", $out));
|
|
$out = array($line);
|
|
$chunkSize = $len;
|
|
} else {
|
|
$out[] = $line;
|
|
}
|
|
$i++;
|
|
}
|
|
static::sendAsUDP(implode("\n", $out));
|
|
}
|
|
|
|
/**
|
|
* This is the fastest way to ensure locale settings don't affect the
|
|
* decimal separator. Really, this is the only way (besides temporarily
|
|
* changing the locale) to really get what we want.
|
|
*
|
|
* @param string $value the value to be "translated" to the needed locale
|
|
* @return string the "translated" value
|
|
*/
|
|
protected static function num($value) {
|
|
return strtr($value, ',', '.');
|
|
}
|
|
}
|