Completed
Push — develop ( 7e0ea6...010226 )
by Stan
02:03
created

StorageManager::increment()   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 51
    public function __construct(Repository $repository, ?string $prefix = 'PROMETHEUS')
61
    {
62 51
        $this->repository = $repository;
63 51
        $this->prefix = $prefix;
64 51
    }
65
66
    /**
67
     * {@inheritdoc}
68
     */
69 37
    public function collect(Metric $metric): Collection
70
    {
71
        try {
72 37
            $items = $this->repository->get("{$this->prefix}:{$metric->key()}");
73 36
            $collector = $this->binding(self::COLLECTOR_BINDING_KEY, $metric);
74
75
            /** @var SamplesBuilder $builder */
76 35
            $builder = $collector($metric, $items);
77
78 35
            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 34
            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 18
    public function increment(Incrementable $metric, float $value, array $labels = []): void
96
    {
97
        try {
98 18
            $this->repository->increment(
99 18
                "{$this->prefix}:{$metric->key()}",
100 18
                $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 16
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115 8
    public function decrement(Decrementable $metric, float $value, array $labels = []): void
116
    {
117
        try {
118 8
            $this->repository->decrement(
119 8
                "{$this->prefix}:{$metric->key()}",
120 8
                $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 6
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135 14
    public function observe(Observable $metric, float $value, array $labels = []): void
136
    {
137
        try {
138 14
            $this->binding(self::OBSERVER_BINDING_KEY, $metric)
139 14
                ($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 12
    }
148
149
    /**
150
     * {@inheritdoc}
151
     */
152 8
    public function set(Settable $metric, float $value, array $labels = []): void
153
    {
154
        try {
155 8
            $this->repository->set(
156 8
                "{$this->prefix}:{$metric->key()}",
157 8
                $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 6
    }
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 40
    protected function binding(string $key, Metric $metric): callable
198
    {
199 40
        $bindings = Collection::make($this->bindings[$key]);
200 40
        $type = $bindings->keys()->first(fn(string $type) => is_subclass_of($metric, $type));
201
202 40
        if ($type === null) {
203 1
            throw new RuntimeException("Could not find [{$key}] binding for metric.");
204
        }
205
206 39
        return new $bindings[$type]($this->repository, "{$this->prefix}:{$metric->key()}");
207
    }
208
}
209