Passed
Push — develop ( cefedc...7e0ea6 )
by Stan
07:15
created

StorageManager::set()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
dl 0
loc 14
ccs 9
cts 9
cp 1
crap 3
rs 9.9332
c 1
b 0
f 0
eloc 10
nc 3
nop 3
1
<?php
2
3
namespace Krenor\Prometheus\Storage;
4
5
use Exception;
6
use RuntimeException;
7
use Krenor\Prometheus\Metrics\Gauge;
8
use Krenor\Prometheus\Metrics\Counter;
9
use Krenor\Prometheus\Metrics\Summary;
10
use Krenor\Prometheus\Contracts\Metric;
11
use Krenor\Prometheus\Metrics\Histogram;
12
use Krenor\Prometheus\Contracts\Storage;
13
use Tightenco\Collect\Support\Collection;
14
use Krenor\Prometheus\Contracts\Repository;
15
use Krenor\Prometheus\Contracts\SamplesBuilder;
16
use Krenor\Prometheus\Contracts\Types\Settable;
17
use Krenor\Prometheus\Exceptions\LabelException;
18
use Krenor\Prometheus\Contracts\Types\Observable;
19
use Krenor\Prometheus\Exceptions\StorageException;
20
use Krenor\Prometheus\Contracts\Types\Decrementable;
21
use Krenor\Prometheus\Contracts\Types\Incrementable;
22
use Krenor\Prometheus\Storage\Concerns\StoresMetrics;
23
use Krenor\Prometheus\Storage\Bindings\Collectors\GaugeCollector;
24
use Krenor\Prometheus\Storage\Bindings\Observers\SummaryObserver;
25
use Krenor\Prometheus\Storage\Bindings\Collectors\CounterCollector;
26
use Krenor\Prometheus\Storage\Bindings\Collectors\SummaryCollector;
27
use Krenor\Prometheus\Storage\Bindings\Observers\HistogramObserver;
28
use Krenor\Prometheus\Storage\Bindings\Collectors\HistogramCollector;
29
30
class StorageManager implements Storage
31
{
32
    const COLLECTOR_BINDING_KEY = 'collect';
33
    const OBSERVER_BINDING_KEY = 'observe';
34
35
    use StoresMetrics;
36
37
    protected Repository $repository;
38
39
    protected string $prefix;
40
41
    protected array $bindings = [
42
        self::COLLECTOR_BINDING_KEY => [
43
            Counter::class   => CounterCollector::class,
44
            Gauge::class     => GaugeCollector::class,
45
            Histogram::class => HistogramCollector::class,
46
            Summary::class   => SummaryCollector::class,
47
        ],
48
        self::OBSERVER_BINDING_KEY  => [
49
            Histogram::class => HistogramObserver::class,
50
            Summary::class   => SummaryObserver::class,
51
        ],
52
    ];
53
54
    /**
55
     * StorageManager constructor.
56
     *
57
     * @param Repository $repository
58
     * @param string|null $prefix
59
     */
60 45
    public function __construct(Repository $repository, ?string $prefix = 'PROMETHEUS')
61
    {
62 45
        $this->repository = $repository;
63 45
        $this->prefix = $prefix;
64 45
    }
65
66
    /**
67
     * {@inheritdoc}
68
     */
69 31
    public function collect(Metric $metric): Collection
70
    {
71
        try {
72 31
            $items = $this->repository->get("{$this->prefix}:{$metric->key()}");
73 30
            $collector = $this->binding(self::COLLECTOR_BINDING_KEY, $metric);
74
75
            /** @var SamplesBuilder $builder */
76 29
            $builder = $collector($metric, $items);
77
78 29
            if (!$builder instanceof SamplesBuilder) {
1 ignored issue
show
introduced by
$builder is always a sub-type of Krenor\Prometheus\Contracts\SamplesBuilder.
Loading history...
79 1
                throw new RuntimeException("The collector did not resolve into a SamplesBuilder.");
80
            }
81
82 28
            return $builder->samples();
83 3
        } catch (LabelException $e) {
84
            throw $e;
85 3
        } catch (Exception $e) {
86 3
            $class = get_class($metric);
87
88 3
            throw new StorageException("Failed to collect the samples of [{$class}]: {$e->getMessage()}", 0, $e);
89
        }
90
    }
91
92
    /**
93
     * {@inheritdoc}
94
     */
95 15
    public function increment(Incrementable $metric, float $value, array $labels = []): void
96
    {
97
        try {
98 15
            $this->repository->increment(
99 15
                "{$this->prefix}:{$metric->key()}",
100 15
                $this->labeled($metric, $labels)->toJson(),
101
                $value
102
            );
103 2
        } catch (LabelException $e) {
104 1
            throw $e;
105 1
        } catch (Exception $e) {
106 1
            $class = get_class($metric);
107
108 1
            throw new StorageException("Failed to increment [{$class}] by `{$value}`: {$e->getMessage()}", 0, $e);
109
        }
110 13
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115 7
    public function decrement(Decrementable $metric, float $value, array $labels = []): void
116
    {
117
        try {
118 7
            $this->repository->decrement(
119 7
                "{$this->prefix}:{$metric->key()}",
120 7
                $this->labeled($metric, $labels)->toJson(),
121
                $value
122
            );
123 2
        } catch (LabelException $e) {
124 1
            throw $e;
125 1
        } catch (Exception $e) {
126 1
            $class = get_class($metric);
127
128 1
            throw new StorageException("Failed to decrement [{$class}] by `{$value}`: {$e->getMessage()}", 0, $e);
129
        }
130 5
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135 12
    public function observe(Observable $metric, float $value, array $labels = []): void
136
    {
137
        try {
138 12
            $this->binding(self::OBSERVER_BINDING_KEY, $metric)
139 12
            ($metric, $this->labeled($metric, $labels), $value);
140 2
        } catch (LabelException $e) {
141 1
            throw $e;
142 1
        } catch (Exception $e) {
143 1
            $class = get_class($metric);
144
145 1
            throw new StorageException("Failed to observe [{$class}] with `{$value}`: {$e->getMessage()}", 0, $e);
146
        }
147 10
    }
148
149
    /**
150
     * {@inheritdoc}
151
     */
152 7
    public function set(Settable $metric, float $value, array $labels = []): void
153
    {
154
        try {
155 7
            $this->repository->set(
156 7
                "{$this->prefix}:{$metric->key()}",
157 7
                $this->labeled($metric, $labels)->toJson(),
158
                $value
159
            );
160 2
        } catch (LabelException $e) {
161 1
            throw $e;
162 1
        } catch (Exception $e) {
163 1
            $class = get_class($metric);
164
165 1
            throw new StorageException("Failed to set [{$class}] to `{$value}`: {$e->getMessage()}", 0, $e);
166
        }
167 5
    }
168
169
    /**
170
     * @return bool
171
     */
172
    public function flush(): bool
173
    {
174
        return $this->repository->flush();
175
    }
176
177
    /**
178
     * @param string $key
179
     * @param string $metric
180
     * @param string $binding
181
     *
182
     * @return self
183
     */
184 1
    public function bind(string $key, string $metric, string $binding): self
185
    {
186 1
        $this->bindings[$key][$metric] = $binding;
187
188 1
        return $this;
189
    }
190
191
    /**
192
     * @param string $key
193
     * @param Metric $metric
194
     *
195
     * @return callable
196
     */
197 34
    protected function binding(string $key, Metric $metric): callable
198
    {
199 34
        $bindings = Collection::make($this->bindings[$key]);
200
        $type = $bindings->keys()->first(function (string $type) use ($metric) {
201 34
            return is_subclass_of($metric, $type);
202 34
        });
203
204 34
        if ($type === null) {
205 1
            throw new RuntimeException("Could not find [{$key}] binding for metric.");
206
        }
207
208 33
        return new $bindings[$type]($this->repository, "{$this->prefix}:{$metric->key()}");
209
    }
210
}
211