StorageManager::increment()   A
last analyzed

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 array $bindings = [
38
        self::COLLECTOR_BINDING_KEY => [
39
            Counter::class   => CounterCollector::class,
40
            Gauge::class     => GaugeCollector::class,
41
            Histogram::class => HistogramCollector::class,
42
            Summary::class   => SummaryCollector::class,
43
        ],
44
        self::OBSERVER_BINDING_KEY  => [
45
            Histogram::class => HistogramObserver::class,
46
            Summary::class   => SummaryObserver::class,
47
        ],
48
    ];
49
50
    /**
51
     * StorageManager constructor.
52
     *
53
     * @param Repository $repository
54
     * @param string $prefix
55
     */
56 51
    public function __construct(protected Repository $repository, protected string $prefix = 'PROMETHEUS')
57
    {
58
        //
59 51
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64 37
    public function collect(Metric $metric): Collection
65
    {
66
        try {
67 37
            $items = $this->repository->get("{$this->prefix}:{$metric->key()}");
68 36
            $collector = $this->binding(self::COLLECTOR_BINDING_KEY, $metric);
69
70
            /** @var SamplesBuilder $builder */
71 35
            $builder = $collector($metric, $items);
72
73 35
            if (!$builder instanceof SamplesBuilder) {
74 1
                throw new RuntimeException("The collector did not resolve into a SamplesBuilder.");
75
            }
76
77 34
            return $builder->samples();
78 3
        } catch (LabelException $e) {
79
            throw $e;
80 3
        } catch (Exception $e) {
81 3
            $class = $metric::class;
82
83 3
            throw new StorageException("Failed to collect the samples of [{$class}]: {$e->getMessage()}", 0, $e);
84
        }
85
    }
86
87
    /**
88
     * {@inheritdoc}
89
     */
90 18
    public function increment(Incrementable $metric, float $value, array $labels = []): void
91
    {
92
        try {
93 18
            $this->repository->increment(
94 18
                "{$this->prefix}:{$metric->key()}",
95 18
                $this->labeled($metric, $labels)->toJson(),
96
                $value
97
            );
98 2
        } catch (LabelException $e) {
99 1
            throw $e;
100 1
        } catch (Exception $e) {
101 1
            $class = $metric::class;
102
103 1
            throw new StorageException("Failed to increment [{$class}] by `{$value}`: {$e->getMessage()}", 0, $e);
104
        }
105 16
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110 8
    public function decrement(Decrementable $metric, float $value, array $labels = []): void
111
    {
112
        try {
113 8
            $this->repository->decrement(
114 8
                "{$this->prefix}:{$metric->key()}",
115 8
                $this->labeled($metric, $labels)->toJson(),
116
                $value
117
            );
118 2
        } catch (LabelException $e) {
119 1
            throw $e;
120 1
        } catch (Exception $e) {
121 1
            $class = $metric::class;
122
123 1
            throw new StorageException("Failed to decrement [{$class}] by `{$value}`: {$e->getMessage()}", 0, $e);
124
        }
125 6
    }
126
127
    /**
128
     * {@inheritdoc}
129
     */
130 14
    public function observe(Observable $metric, float $value, array $labels = []): void
131
    {
132
        try {
133 14
            call_user_func(
134 14
                $this->binding(self::OBSERVER_BINDING_KEY, $metric),
135
                $metric,
136 14
                $this->labeled($metric, $labels),
137
                $value
138
            );
139 2
        } catch (LabelException $e) {
140 1
            throw $e;
141 1
        } catch (Exception $e) {
142 1
            $class = $metric::class;
143
144 1
            throw new StorageException("Failed to observe [{$class}] with `{$value}`: {$e->getMessage()}", 0, $e);
145
        }
146 12
    }
147
148
    /**
149
     * {@inheritdoc}
150
     */
151 8
    public function set(Settable $metric, float $value, array $labels = []): void
152
    {
153
        try {
154 8
            $this->repository->set(
155 8
                "{$this->prefix}:{$metric->key()}",
156 8
                $this->labeled($metric, $labels)->toJson(),
157
                $value
158
            );
159 2
        } catch (LabelException $e) {
160 1
            throw $e;
161 1
        } catch (Exception $e) {
162 1
            $class = $metric::class;
163
164 1
            throw new StorageException("Failed to set [{$class}] to `{$value}`: {$e->getMessage()}", 0, $e);
165
        }
166 6
    }
167
168
    /**
169
     * @return bool
170
     */
171
    public function flush(): bool
172
    {
173
        return $this->repository->flush();
174
    }
175
176
    /**
177
     * @param string $key
178
     * @param string $metric
179
     * @param string $binding
180
     *
181
     * @return self
182
     */
183 1
    public function bind(string $key, string $metric, string $binding): self
184
    {
185 1
        $this->bindings[$key][$metric] = $binding;
186
187 1
        return $this;
188
    }
189
190
    /**
191
     * @param string $key
192
     * @param Metric $metric
193
     *
194
     * @return callable
195
     */
196 40
    protected function binding(string $key, Metric $metric): callable
197
    {
198 40
        $bindings = Collection::make($this->bindings[$key]);
199 40
        $type = $bindings->keys()->first(fn(string $type) => is_subclass_of($metric, $type));
200
201 40
        if ($type === null) {
202 1
            throw new RuntimeException("Could not find [{$key}] binding for metric.");
203
        }
204
205 39
        return new $bindings[$type]($this->repository, "{$this->prefix}:{$metric->key()}");
206
    }
207
}
208