Passed
Push — develop ( 8c6599...c9bdac )
by Stan
04:29
created

StorageManager::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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