Skip to content

Commit

Permalink
Introduce "ignoreConnectionErrors" configuration option
Browse files Browse the repository at this point in the history
  • Loading branch information
robertlemke committed Apr 8, 2020
1 parent 080ff77 commit de53419
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 41 deletions.
123 changes: 82 additions & 41 deletions Classes/Storage/RedisStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
* (c) Flownative GmbH - www.flownative.com
*/

use Exception;
use Flownative\Prometheus\Collector\AbstractCollector;
use Flownative\Prometheus\Collector\Counter;
use Flownative\Prometheus\Collector\Gauge;
use Flownative\Prometheus\Exception\InvalidConfigurationException;
use Flownative\Prometheus\Sample;
use Flownative\Prometheus\SampleCollection;
use Predis;
use Predis\Connection\ConnectionException;

/**
* A storage which stores metrics in Redis using the phpredis PHP extension.
Expand Down Expand Up @@ -76,6 +78,11 @@ class RedisStorage extends AbstractStorage
*/
protected $keyPrefix = 'flownative_prometheus';

/**
* @var boolean
*/
protected $ignoreConnectionErrors = false;

/**
* @param array $options
* @throws InvalidConfigurationException
Expand All @@ -90,11 +97,16 @@ public function __construct(array $options = [])
case 'service':
$this->$key = (string)$value;
break;
case 'ignoreConnectionErrors':
$this->$key = ($value === true || $value === 'yes' || $value === 'true' || $value === 'on' || $value === 1);
break;
case 'sentinels':
if (is_string($value)) {
$this->sentinels = explode(',', $value);
} elseif(is_array($value)) {
} elseif (is_array($value)) {
$this->sentinels = $value;
} elseif (empty($value)) {
$this->sentinels = [];
} else {
throw new \InvalidArgumentException(sprintf('setSentinels(): Invalid type %s, string or array expected', gettype($value)), 1575969465);
}
Expand All @@ -112,18 +124,33 @@ public function __construct(array $options = [])

/**
* @return SampleCollection[]
* @throws Exception
*/
public function collect(): array
{
return $this->collectCountersAndGauges();
try {
return $this->collectCountersAndGauges();
} catch (ConnectionException $exception) {
if ($this->ignoreConnectionErrors === false) {
throw $exception;
}
return [];
}
}

/**
* @return void
* @throws ConnectionException
*/
public function flush(): void
{
$this->redis->flushDB();
try {
$this->redis->flushDB();
} catch (ConnectionException $exception) {
if ($this->ignoreConnectionErrors === false) {
throw $exception;
}
}
}

/**
Expand Down Expand Up @@ -153,6 +180,7 @@ public function registerCollector(AbstractCollector $collector): void
* @param Counter $counter
* @param CounterUpdate $update
* @return void
* @throws Exception
*/
public function updateCounter(Counter $counter, CounterUpdate $update): void
{
Expand All @@ -161,31 +189,38 @@ public function updateCounter(Counter $counter, CounterUpdate $update): void
throw new \InvalidArgumentException(sprintf('failed updating unknown counter %s (%s)', $counter->getName(), $identifier), 1574079998);
}

switch ($update->getOperation()) {
case StorageInterface::OPERATION_INCREASE:
$this->redis->hIncrByFloat(
$counter->getIdentifier(),
$this->encodeLabels($update->getLabels()),
$update->getValue()
);
break;
case StorageInterface::OPERATION_SET:
$this->redis->hSet(
$counter->getIdentifier(),
$this->encodeLabels($update->getLabels()),
$update->getValue()
);
break;
}
try {
switch ($update->getOperation()) {
case StorageInterface::OPERATION_INCREASE:
$this->redis->hIncrByFloat(
$counter->getIdentifier(),
$this->encodeLabels($update->getLabels()),
$update->getValue()
);
break;
case StorageInterface::OPERATION_SET:
$this->redis->hSet(
$counter->getIdentifier(),
$this->encodeLabels($update->getLabels()),
$update->getValue()
);
break;
}

$this->redis->hSet($counter->getIdentifier(), '__name', $counter->getName());
$this->redis->sAdd($this->keyPrefix . Counter::TYPE . self::KEY_SUFFIX, [$counter->getIdentifier()]);
$this->redis->hSet($counter->getIdentifier(), '__name', $counter->getName());
$this->redis->sAdd($this->keyPrefix . Counter::TYPE . self::KEY_SUFFIX, [$counter->getIdentifier()]);
} catch (ConnectionException $exception) {
if ($this->ignoreConnectionErrors === false) {
throw $exception;
}
}
}

/**
* @param Gauge $gauge
* @param GaugeUpdate $update
* @return void
* @throws Exception
*/
public function updateGauge(Gauge $gauge, GaugeUpdate $update): void
{
Expand All @@ -194,29 +229,35 @@ public function updateGauge(Gauge $gauge, GaugeUpdate $update): void
throw new \InvalidArgumentException(sprintf('failed updating unknown gauge %s (%s)', $gauge->getName(), $identifier), 1574259278);
}

switch ($update->getOperation()) {
case StorageInterface::OPERATION_INCREASE:
$this->redis->hIncrByFloat(
$gauge->getIdentifier(),
$this->encodeLabels($update->getLabels()),
$update->getValue()
);
break;
case StorageInterface::OPERATION_SET:
$this->redis->hSet(
$gauge->getIdentifier(),
$this->encodeLabels($update->getLabels()),
$update->getValue()
);
break;
try {
switch ($update->getOperation()) {
case StorageInterface::OPERATION_INCREASE:
$this->redis->hIncrByFloat(
$gauge->getIdentifier(),
$this->encodeLabels($update->getLabels()),
$update->getValue()
);
break;
case StorageInterface::OPERATION_SET:
$this->redis->hSet(
$gauge->getIdentifier(),
$this->encodeLabels($update->getLabels()),
$update->getValue()
);
break;
}
$this->redis->hSet($gauge->getIdentifier(), '__name', $gauge->getName());
$this->redis->sAdd($this->keyPrefix . Gauge::TYPE . self::KEY_SUFFIX, [$gauge->getIdentifier()]);
} catch (ConnectionException $exception) {
if ($this->ignoreConnectionErrors === false) {
throw $exception;
}
}

$this->redis->hSet($gauge->getIdentifier(), '__name', $gauge->getName());
$this->redis->sAdd($this->keyPrefix . Gauge::TYPE . self::KEY_SUFFIX, [$gauge->getIdentifier()]);
}

/**
* @return SampleCollection[]
* @throws Exception
*/
private function collectCountersAndGauges(): array
{
Expand All @@ -231,7 +272,7 @@ private function collectCountersAndGauges(): array

$samples = [];
foreach ($collectorRawHash as $sampleKey => $value) {
$value = (strpos($value,'.') !== false) ? (float)$value : (int)$value;
$value = (strpos($value, '.') !== false) ? (float)$value : (int)$value;
$samples[] = new Sample(
$collectorName,
$this->decodeLabels($sampleKey),
Expand All @@ -240,7 +281,7 @@ private function collectCountersAndGauges(): array
}
$this->sortSamples($samples);

$collector = $this->counters[$collectorKey] ?? $this->gauges[$collectorKey] ?? null;
$collector = $this->counters[$collectorKey] ?? $this->gauges[$collectorKey] ?? null;
if ($collector) {
$sampleCollections[$collectorKey] = new SampleCollection(
$collectorName,
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,21 @@ Flownative\Prometheus\Storage\RedisStorage:

Instead of providing sentinels as an array you can also set them as a comma-separated string.

The `RedisStorage` can be configured to ignore connection errors. This may protect your application against fatal errors at times
when Redis is not available. Of course, no metrics are stored while Redis connections fail.

```yaml
Flownative\Prometheus\Storage\RedisStorage:
arguments:
1:
value:
hostname: '%env:MY_REDIS_HOST%'
port: '%env:MY_REDIS_PORT%'
password: '%env:MY_REDIS_PASSWORD%'
database: 20
ignoreConnectionErrors: true
```

### Telemetry Path

The path, where metrics are provided for scraping, is "/metrics" by default. You can change this path by setting a respective
Expand Down

0 comments on commit de53419

Please sign in to comment.