diff --git a/composer.json b/composer.json index 3d97c12..1126c79 100644 --- a/composer.json +++ b/composer.json @@ -15,11 +15,13 @@ "guzzlehttp/guzzle": "^6.2" }, "require-dev": { - "phpunit/phpunit": "4.1.0" + "phpunit/phpunit": "4.1.0", + "predis/predis": "^1.1" }, "suggest": { "ext-redis": "Required if using Redis.", - "ext-apc": "Required if using APCu." + "predis/predis": "Required if using Predis", + "ext-apcu": "Required if using APCu." }, "autoload": { "psr-0": { diff --git a/examples/flush_adapter.php b/examples/flush_adapter.php index 0c3862d..4c7eecf 100644 --- a/examples/flush_adapter.php +++ b/examples/flush_adapter.php @@ -1,17 +1,12 @@ REDIS_HOST)); - $redisAdapter->flushRedis(); -} elseif ($adapter === 'apc') { - $apcAdapter = new Prometheus\Storage\APC(); - $apcAdapter->flushAPC(); -} elseif ($adapter === 'in-memory') { - $inMemoryAdapter = new Prometheus\Storage\InMemory(); - $inMemoryAdapter->flushMemory(); +if ($adapter instanceof \Prometheus\Storage\Redis) { + $adapter->flushRedis(); +} elseif ($adapter instanceof \Prometheus\Storage\APC) { + $adapter->flushAPC(); +} elseif ($adapter instanceof \Prometheus\Storage\InMemory) { + $adapter->flushMemory(); } \ No newline at end of file diff --git a/examples/initialize.php b/examples/initialize.php new file mode 100644 index 0000000..406119d --- /dev/null +++ b/examples/initialize.php @@ -0,0 +1,21 @@ + isset($_SERVER['REDIS_HOST']) ? $_SERVER['REDIS_HOST'] : '127.0.0.1') + ); + case "predis": + return Prometheus\Storage\Redis::usingPredis( + new \Predis\Client(['host' => isset($_SERVER['REDIS_HOST']) ? $_SERVER['REDIS_HOST'] : '127.0.0.1']) + ); + case "apc": + return new Prometheus\Storage\APC(); + case "in-memory": + return new Prometheus\Storage\InMemory(); + default: + throw new \RuntimeException(sprintf('Adapter "" is not supported', $adapterName)); +} diff --git a/examples/metrics.php b/examples/metrics.php index fa89247..6742112 100644 --- a/examples/metrics.php +++ b/examples/metrics.php @@ -6,16 +6,8 @@ use Prometheus\RenderTextFormat; use Prometheus\Storage\Redis; -$adapter = $_GET['adapter']; +$adapter = require __DIR__ . '/initialize.php'; -if ($adapter === 'redis') { - Redis::setDefaultOptions(array('host' => isset($_SERVER['REDIS_HOST']) ? $_SERVER['REDIS_HOST'] : '127.0.0.1')); - $adapter = new Prometheus\Storage\Redis(); -} elseif ($adapter === 'apc') { - $adapter = new Prometheus\Storage\APC(); -} elseif ($adapter === 'in-memory') { - $adapter = new Prometheus\Storage\InMemory(); -} $registry = new CollectorRegistry($adapter); $renderer = new RenderTextFormat(); $result = $renderer->render($registry->getMetricFamilySamples()); diff --git a/examples/pushgateway.php b/examples/pushgateway.php index 984035d..b69e9af 100644 --- a/examples/pushgateway.php +++ b/examples/pushgateway.php @@ -4,16 +4,7 @@ use Prometheus\Storage\Redis; use Prometheus\CollectorRegistry; -$adapter = $_GET['adapter']; - -if ($adapter === 'redis') { - Redis::setDefaultOptions(array('host' => isset($_SERVER['REDIS_HOST']) ? $_SERVER['REDIS_HOST'] : '127.0.0.1')); - $adapter = new Prometheus\Storage\Redis(); -} elseif ($adapter === 'apc') { - $adapter = new Prometheus\Storage\APC(); -} elseif ($adapter === 'in-memory') { - $adapter = new Prometheus\Storage\InMemory(); -} +$adapter = require __DIR__ . '/initialize.php'; $registry = new CollectorRegistry($adapter); diff --git a/examples/some_counter.php b/examples/some_counter.php index 823bfac..3851e7c 100644 --- a/examples/some_counter.php +++ b/examples/some_counter.php @@ -5,16 +5,7 @@ use Prometheus\CollectorRegistry; use Prometheus\Storage\Redis; -$adapter = $_GET['adapter']; - -if ($adapter === 'redis') { - Redis::setDefaultOptions(array('host' => isset($_SERVER['REDIS_HOST']) ? $_SERVER['REDIS_HOST'] : '127.0.0.1')); - $adapter = new Prometheus\Storage\Redis(); -} elseif ($adapter === 'apc') { - $adapter = new Prometheus\Storage\APC(); -} elseif ($adapter === 'in-memory') { - $adapter = new Prometheus\Storage\InMemory(); -} +$adapter = require __DIR__ . '/initialize.php'; $registry = new CollectorRegistry($adapter); $counter = $registry->registerCounter('test', 'some_counter', 'it increases', ['type']); diff --git a/examples/some_gauge.php b/examples/some_gauge.php index b3e2382..a128cb2 100644 --- a/examples/some_gauge.php +++ b/examples/some_gauge.php @@ -8,16 +8,7 @@ error_log('c='. $_GET['c']); -$adapter = $_GET['adapter']; - -if ($adapter === 'redis') { - Redis::setDefaultOptions(array('host' => isset($_SERVER['REDIS_HOST']) ? $_SERVER['REDIS_HOST'] : '127.0.0.1')); - $adapter = new Prometheus\Storage\Redis(); -} elseif ($adapter === 'apc') { - $adapter = new Prometheus\Storage\APC(); -} elseif ($adapter === 'in-memory') { - $adapter = new Prometheus\Storage\InMemory(); -} +$adapter = require __DIR__ . '/initialize.php'; $registry = new CollectorRegistry($adapter); $gauge = $registry->registerGauge('test', 'some_gauge', 'it sets', ['type']); diff --git a/examples/some_histogram.php b/examples/some_histogram.php index 6b34809..0157c7c 100644 --- a/examples/some_histogram.php +++ b/examples/some_histogram.php @@ -7,16 +7,7 @@ error_log('c='. $_GET['c']); -$adapter = $_GET['adapter']; - -if ($adapter === 'redis') { - Redis::setDefaultOptions(array('host' => isset($_SERVER['REDIS_HOST']) ? $_SERVER['REDIS_HOST'] : '127.0.0.1')); - $adapter = new Prometheus\Storage\Redis(); -} elseif ($adapter === 'apc') { - $adapter = new Prometheus\Storage\APC(); -} elseif ($adapter === 'in-memory') { - $adapter = new Prometheus\Storage\InMemory(); -} +$adapter = require __DIR__ . '/initialize.php'; $registry = new CollectorRegistry($adapter); $histogram = $registry->registerHistogram('test', 'some_histogram', 'it observes', ['type'], [0.1, 1, 2, 3.5, 4, 5, 6, 7, 8, 9]); diff --git a/src/Prometheus/PushGateway.php b/src/Prometheus/PushGateway.php index a4a3fd2..8cbbac7 100644 --- a/src/Prometheus/PushGateway.php +++ b/src/Prometheus/PushGateway.php @@ -49,9 +49,9 @@ public function pushAdd(CollectorRegistry $collectorRegistry, $job, $groupingKey * @param $job * @param $groupingKey */ - public function delete($job, $groupingKey = null) + public function delete(CollectorRegistry $collectorRegistry, $job, $groupingKey = null) { - $this->doRequest(null, $job, $groupingKey, 'delete'); + $this->doRequest($collectorRegistry, $job, $groupingKey, 'delete'); } /** diff --git a/src/Prometheus/Storage/Redis.php b/src/Prometheus/Storage/Redis.php index f38391b..d433266 100644 --- a/src/Prometheus/Storage/Redis.php +++ b/src/Prometheus/Storage/Redis.php @@ -3,6 +3,7 @@ namespace Prometheus\Storage; +use Predis\Client; use Prometheus\Counter; use Prometheus\Exception\StorageException; use Prometheus\Gauge; @@ -13,55 +14,40 @@ class Redis implements Adapter { const PROMETHEUS_METRIC_KEYS_SUFFIX = '_METRIC_KEYS'; - private static $defaultOptions = array(); - private static $prefix = 'PROMETHEUS_'; + private $prefix = 'PROMETHEUS_'; - private $options; private $redis; - public function __construct(array $options = array()) + /** + * Redis constructor. + * @param RedisAdapter $redis + * @param string $prefix + */ + public function __construct(RedisAdapter $redis, $prefix = 'PROMETHEUS_') { - // with php 5.3 we cannot initialize the options directly on the field definition - // so we initialize them here for now - if (!isset(self::$defaultOptions['host'])) { - self::$defaultOptions['host'] = '127.0.0.1'; - } - if (!isset(self::$defaultOptions['port'])) { - self::$defaultOptions['port'] = 6379; - } - if (!isset(self::$defaultOptions['timeout'])) { - self::$defaultOptions['timeout'] = 0.1; // in seconds - } - if (!isset(self::$defaultOptions['read_timeout'])) { - self::$defaultOptions['read_timeout'] = 10; // in seconds - } - if (!isset(self::$defaultOptions['persistent_connections'])) { - self::$defaultOptions['persistent_connections'] = false; - } - if (!isset(self::$defaultOptions['password'])) { - self::$defaultOptions['password'] = null; - } - - $this->options = array_merge(self::$defaultOptions, $options); - $this->redis = new \Redis(); + $this->redis = $redis; } /** - * @param array $options + * @param string $prefix */ - public static function setDefaultOptions(array $options) + public function setPrefix($prefix) + { + $this->prefix = $prefix; + } + + public static function usingRedis(\Redis $redis, array $options) { - self::$defaultOptions = array_merge(self::$defaultOptions, $options); + return new self(RedisAdapter::forRedis($redis, $options)); } - public static function setPrefix($prefix) + public static function usingPredis(Client $redis) { - self::$prefix = $prefix; + return new self(RedisAdapter::forPredis($redis)); } public function flushRedis() { - $this->openConnection(); $this->redis->flushAll(); } @@ -71,7 +57,6 @@ public function flushRedis() */ public function collect() { - $this->openConnection(); $metrics = $this->collectHistograms(); $metrics = array_merge($metrics, $this->collectGauges()); $metrics = array_merge($metrics, $this->collectCounters()); @@ -83,29 +68,8 @@ function (array $metric) { ); } - /** - * @throws StorageException - */ - private function openConnection() - { - try { - if ($this->options['persistent_connections']) { - @$this->redis->pconnect($this->options['host'], $this->options['port'], $this->options['timeout']); - } else { - @$this->redis->connect($this->options['host'], $this->options['port'], $this->options['timeout']); - } - if ($this->options['password']) { - $this->redis->auth($this->options['password']); - } - $this->redis->setOption(\Redis::OPT_READ_TIMEOUT, $this->options['read_timeout']); - } catch (\RedisException $e) { - throw new StorageException("Can't connect to Redis server", 0, $e); - } - } - public function updateHistogram(array $data) { - $this->openConnection(); $bucketToIncrease = '+Inf'; foreach ($data['buckets'] as $bucket) { if ($data['value'] <= $bucket) { @@ -116,7 +80,7 @@ public function updateHistogram(array $data) $metaData = $data; unset($metaData['value']); unset($metaData['labelValues']); - $this->redis->eval(<<redis->evaluate(<<toMetricKey($data), json_encode(array('b' => 'sum', 'labelValues' => $data['labelValues'])), json_encode(array('b' => $bucketToIncrease, 'labelValues' => $data['labelValues'])), - self::$prefix . Histogram::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX, + $this->prefix . Histogram::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX, $data['value'], json_encode($metaData), ), @@ -139,12 +103,12 @@ public function updateHistogram(array $data) public function updateGauge(array $data) { - $this->openConnection(); + $metaData = $data; unset($metaData['value']); unset($metaData['labelValues']); unset($metaData['command']); - $this->redis->eval(<<redis->evaluate(<<toMetricKey($data), $this->getRedisCommand($data['command']), - self::$prefix . Gauge::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX, + $this->prefix . Gauge::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX, json_encode($data['labelValues']), $data['value'], json_encode($metaData), @@ -174,12 +138,11 @@ public function updateGauge(array $data) public function updateCounter(array $data) { - $this->openConnection(); $metaData = $data; unset($metaData['value']); unset($metaData['labelValues']); unset($metaData['command']); - $result = $this->redis->eval(<<redis->evaluate(<<toMetricKey($data), $this->getRedisCommand($data['command']), - self::$prefix . Counter::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX, + $this->prefix . Counter::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX, json_encode($data['labelValues']), $data['value'], json_encode($metaData), @@ -203,7 +166,7 @@ public function updateCounter(array $data) private function collectHistograms() { - $keys = $this->redis->sMembers(self::$prefix . Histogram::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX); + $keys = $this->redis->sMembers($this->prefix . Histogram::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX); sort($keys); $histograms = array(); foreach ($keys as $key) { @@ -277,7 +240,7 @@ private function collectHistograms() private function collectGauges() { - $keys = $this->redis->sMembers(self::$prefix . Gauge::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX); + $keys = $this->redis->sMembers($this->prefix . Gauge::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX); sort($keys); $gauges = array(); foreach ($keys as $key) { @@ -303,7 +266,7 @@ private function collectGauges() private function collectCounters() { - $keys = $this->redis->sMembers(self::$prefix . Counter::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX); + $keys = $this->redis->sMembers($this->prefix . Counter::TYPE . self::PROMETHEUS_METRIC_KEYS_SUFFIX); sort($keys); $counters = array(); foreach ($keys as $key) { @@ -347,7 +310,6 @@ private function getRedisCommand($cmd) */ private function toMetricKey(array $data) { - return implode(':', array(self::$prefix, $data['type'], $data['name'])); + return implode(':', array($this->prefix, $data['type'], $data['name'])); } - } diff --git a/src/Prometheus/Storage/RedisAdapter.php b/src/Prometheus/Storage/RedisAdapter.php new file mode 100644 index 0000000..c7d16ad --- /dev/null +++ b/src/Prometheus/Storage/RedisAdapter.php @@ -0,0 +1,119 @@ +options = array_replace([ + 'host' => '127.0.0.1', + 'port' => 6379, + 'timeout' => 0.1, + 'read_timeout' => 10, + 'persistent_connections' => false, + 'password' => null, + ], $options); + + return $adapter; + } + + /** + * @param Client $client + * @return RedisAdapter + */ + public static function forPredis(Client $client) + { + return new self($client); + } + + /** + * @param \Redis|Client $redisInstance + */ + private function __construct($redisInstance) + { + $this->redis = $redisInstance; + } + + /** + * @param string $script + * @param array $args + * @param int $numKeys + */ + public function evaluate($script, $args = array(), $numKeys = 0) + { + if ($this->redis instanceof Client) { + $this->redis->eval($script, $numKeys, ...$args); + return; + } + + $this->openConnection(); + $this->redis->eval($script, $args, $numKeys); + } + + /** + * @param string $key + * @return array + */ + public function sMembers($key) + { + $this->openConnection(); + return $this->redis->smembers($key); + } + + /** + * @param string $key + * @return array + */ + public function hGetAll($key) + { + $this->openConnection(); + return $this->redis->hgetall($key); + } + + public function flushAll() + { + $this->openConnection(); + $this->redis->flushall(); + } + + /** + * @throws StorageException + */ + private function openConnection() + { + if ($this->redis instanceof Client) { + return; + } + + try { + if ($this->options['persistent_connections']) { + @$this->redis->pconnect($this->options['host'], $this->options['port'], $this->options['timeout']); + } else { + @$this->redis->connect($this->options['host'], $this->options['port'], $this->options['timeout']); + } + if ($this->options['password']) { + $this->redis->auth($this->options['password']); + } + $this->redis->setOption(\Redis::OPT_READ_TIMEOUT, $this->options['read_timeout']); + } catch (\RedisException $e) { + throw new StorageException("Can't connect to Redis server", 0, $e); + } + } +} \ No newline at end of file diff --git a/tests/Test/BlackBoxPushGatewayTest.php b/tests/Test/BlackBoxPushGatewayTest.php index 26490c6..8fb793c 100644 --- a/tests/Test/BlackBoxPushGatewayTest.php +++ b/tests/Test/BlackBoxPushGatewayTest.php @@ -33,7 +33,7 @@ public function pushGatewayShouldWork() $metrics ); - $pushGateway->delete('my_job', array('instance' => 'foo')); + $pushGateway->delete($registry,'my_job', array('instance' => 'foo')); $httpClient = new Client(); $metrics = $httpClient->get("http://pushgateway:9091/metrics")->getBody()->getContents(); diff --git a/tests/Test/Prometheus/Predis/CollectorRegistryTest.php b/tests/Test/Prometheus/Predis/CollectorRegistryTest.php new file mode 100644 index 0000000..a6acda5 --- /dev/null +++ b/tests/Test/Prometheus/Predis/CollectorRegistryTest.php @@ -0,0 +1,10 @@ +adapter = Redis::usingPredis( + new Client(['host' => REDIS_HOST]) + ); + $this->adapter->flushRedis(); + } +} diff --git a/tests/Test/Prometheus/Redis/CollectorRegistryTest.php b/tests/Test/Prometheus/Redis/CollectorRegistryTest.php index ed2ad2b..7261005 100644 --- a/tests/Test/Prometheus/Redis/CollectorRegistryTest.php +++ b/tests/Test/Prometheus/Redis/CollectorRegistryTest.php @@ -1,16 +1,10 @@ adapter = new Redis(array('host' => REDIS_HOST)); - $this->adapter->flushRedis(); - } + use InitializationTrait; } diff --git a/tests/Test/Prometheus/Redis/CounterTest.php b/tests/Test/Prometheus/Redis/CounterTest.php index 0d78272..4e9bc15 100644 --- a/tests/Test/Prometheus/Redis/CounterTest.php +++ b/tests/Test/Prometheus/Redis/CounterTest.php @@ -1,9 +1,7 @@ adapter = new Redis(array('host' => REDIS_HOST)); - $this->adapter->flushRedis(); - } + use InitializationTrait; } diff --git a/tests/Test/Prometheus/Redis/GaugeTest.php b/tests/Test/Prometheus/Redis/GaugeTest.php index 17d0c44..952a259 100644 --- a/tests/Test/Prometheus/Redis/GaugeTest.php +++ b/tests/Test/Prometheus/Redis/GaugeTest.php @@ -1,9 +1,7 @@ adapter = new Redis(array('host' => REDIS_HOST)); - $this->adapter->flushRedis(); - } + use InitializationTrait; } diff --git a/tests/Test/Prometheus/Redis/HistogramTest.php b/tests/Test/Prometheus/Redis/HistogramTest.php index 57f73f6..e95b0b0 100644 --- a/tests/Test/Prometheus/Redis/HistogramTest.php +++ b/tests/Test/Prometheus/Redis/HistogramTest.php @@ -1,9 +1,7 @@ adapter = new Redis(array('host' => REDIS_HOST)); - $this->adapter->flushRedis(); - } + use InitializationTrait; } diff --git a/tests/Test/Prometheus/Redis/InitializationTrait.php b/tests/Test/Prometheus/Redis/InitializationTrait.php new file mode 100644 index 0000000..ffa5997 --- /dev/null +++ b/tests/Test/Prometheus/Redis/InitializationTrait.php @@ -0,0 +1,17 @@ +adapter = Redis::usingRedis( + new \Redis(), + array('host' => REDIS_HOST) + ); + $this->adapter->flushRedis(); + } +} diff --git a/tests/Test/Prometheus/Storage/RedisTest.php b/tests/Test/Prometheus/Storage/RedisAdapterTest.php similarity index 61% rename from tests/Test/Prometheus/Storage/RedisTest.php rename to tests/Test/Prometheus/Storage/RedisAdapterTest.php index 8eb1654..0db173c 100644 --- a/tests/Test/Prometheus/Storage/RedisTest.php +++ b/tests/Test/Prometheus/Storage/RedisAdapterTest.php @@ -4,7 +4,7 @@ namespace Prometheus\Storage; -class RedisTest extends \PHPUnit_Framework_TestCase +class RedisAdapterTest extends \PHPUnit_Framework_TestCase { /** * @test @@ -13,8 +13,8 @@ class RedisTest extends \PHPUnit_Framework_TestCase */ public function itShouldThrowAnExceptionOnConnectionFailure() { - $redis = new Redis(array('host' => 'doesntexist.test')); - $redis->flushRedis(); + $redis = RedisAdapter::forRedis(new \Redis, ['host' => 'doesntexist.test']); + $redis->hGetAll('test'); } }