Passed
Push — develop ( 111668...6ce8bd )
by Stan
01:51
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\Contracts\Storage;
12
use Krenor\Prometheus\Metrics\Histogram;
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
    public const COLLECTOR_BINDING_KEY = 'collect';
33
    public 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) {
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
            call_user_func(
139 14
                $this->binding(self::OBSERVER_BINDING_KEY, $metric),
140
                $metric,
141 14
                $this->labeled($metric, $labels),
142
                $value
143
            );
144 2
        } catch (LabelException $e) {
145 1
            throw $e;
146 1
        } catch (Exception $e) {
147 1
            $class = get_class($metric);
148
149 1
            throw new StorageException("Failed to observe [{$class}] with `{$value}`: {$e->getMessage()}", 0, $e);
150
        }
151 12
    }
152
153
    /**
154
     * {@inheritdoc}
155
     */
156 8
    public function set(Settable $metric, float $value, array $labels = []): void
157
    {
158
        try {
159 8
            $this->repository->set(
160 8
                "{$this->prefix}:{$metric->key()}",
161 8
                $this->labeled($metric, $labels)->toJson(),
162
                $value
163
            );
164 2
        } catch (LabelException $e) {
165 1
            throw $e;
166 1
        } catch (Exception $e) {
167 1
            $class = get_class($metric);
168
169 1
            throw new StorageException("Failed to set [{$class}] to `{$value}`: {$e->getMessage()}", 0, $e);
170
        }
171 6
    }
172
173
    /**
174
     * @return bool
175
     */
176
    public function flush(): bool
177
    {
178
        return $this->repository->flush();
179
    }
180
181
    /**
182
     * @param string $key
183
     * @param string $metric
184
     * @param string $binding
185
     *
186
     * @return self
187
     */
188 1
    public function bind(string $key, string $metric, string $binding): self
189
    {
190 1
        $this->bindings[$key][$metric] = $binding;
191
192 1
        return $this;
193
    }
194
195
    /**
196
     * @param string $key
197
     * @param Metric $metric
198
     *
199
     * @return callable
200
     */
201 40
    protected function binding(string $key, Metric $metric): callable
202
    {
203 40
        $bindings = Collection::make($this->bindings[$key]);
204 40
        $type = $bindings->keys()->first(fn(string $type) => is_subclass_of($metric, $type));
205
206 40
        if ($type === null) {
207 1
            throw new RuntimeException("Could not find [{$key}] binding for metric.");
208
        }
209
210 39
        return new $bindings[$type]($this->repository, "{$this->prefix}:{$metric->key()}");
211
    }
212
}
213