1 /*******************************************************************************
2
3 Contains a collection of prometheus stat collectors that acts as an
4 interface between the response handler, and stat collectors from every
5 individual metric and label set.
6
7 Copyright:
8 Copyright (c) 2019 dunnhumby Germany GmbH.
9 All rights reserved
10
11 License:
12 Boost Software License Version 1.0. See LICENSE.txt for details.
13
14 *******************************************************************************/
15
16 module ocean.util.prometheus.collector.CollectorRegistry;
17
18 /// ditto
19 public class CollectorRegistry
20 {
21 import ocean.meta.types.Qualifiers;
22 import ocean.util.prometheus.collector.Collector : Collector;
23
24 /// An alias for the type of delegates that are called for stat collection
25 public alias void delegate ( Collector ) CollectionDg;
26
27 /// An array of delegates that are called for stat collection
28 private CollectionDg[] collector_callbacks;
29
30 /// The collector to use for stat collection. This acts as the actual
31 /// parameter for the delegates in `collector_callbacks`.
32 private Collector collector;
33
34 /***************************************************************************
35
36 Constructor, that accepts an array of stat collection callbacks.
37
38 Params:
39 callbacks = An array of delegates to be called for stat collection.
40
41 ***************************************************************************/
42
43 public this ( scope CollectionDg[] callbacks )
44 {
45 this.collector = new Collector();
46 this.collector_callbacks = callbacks;
47 }
48
49 /***************************************************************************
50
51 Appends a stat collector callback to the list of existing ones.
52 May result in a re-allocation of the entire buffer storing the
53 callbacks.
54
55 Params:
56 collector = The stat collector to append.
57
58 ***************************************************************************/
59
60 public void addCollector ( scope CollectionDg collector )
61 {
62 this.collector_callbacks ~= collector;
63 }
64
65 /***************************************************************************
66
67 Collects stats by calling all delegates specified for stat collection,
68 formats them using `ocean.util.prometheus.collector.StatFormatter`
69 eventually, and finally returns the accumulated result. The
70 specifications of the format can be found at
71 `https://prometheus.io/docs/instrumenting/exposition_formats/`.
72
73 Returns:
74 The stats accumulated since last time this method was called.
75
76 ***************************************************************************/
77
78 public cstring collect ( )
79 {
80 this.collector.reset();
81
82 try
83 {
84 foreach (ref callback; this.collector_callbacks)
85 {
86 callback(this.collector);
87 }
88 }
89 catch (Exception ex)
90 {
91 throw ex;
92 }
93
94 return this.collector.getCollection();
95 }
96 }
97
98 version (unittest)
99 {
100 import ocean.core.Test;
101 import ocean.meta.types.Qualifiers;
102 import ocean.util.prometheus.collector.Collector;
103
104 ///
105 public struct Statistics
106 {
107 ulong up_time_s;
108 size_t count;
109 float ratio;
110 double fraction;
111 real very_real;
112 }
113
114 ///
115 public struct Labels
116 {
117 hash_t id;
118 cstring job;
119 float perf;
120 }
121
122 private class ExampleStats
123 {
124 Statistics test_stats;
125 Labels test_labels;
126
127 ///
128 public void setTestStats ( Statistics stats )
129 {
130 test_stats = stats;
131 }
132
133 ///
134 public void setTestLabels ( Labels labels )
135 {
136 test_labels = labels;
137 }
138
139 ///
140 public void collectDg1 ( Collector collector )
141 {
142 collector.collect!("id")(test_stats, 123.034);
143 }
144
145 ///
146 public void collectDg2 ( Collector collector )
147 {
148 collector.collect(test_stats, test_labels);
149 }
150 }
151 }
152
153 /// Test collection from a single delegate
154 unittest
155 {
156 auto stats = new ExampleStats();
157 auto registry = new CollectorRegistry([&stats.collectDg1]);
158
159 stats.setTestStats(Statistics(3600, 347, 3.14, 6.023, 0.43));
160
161 test!("==")(registry.collect(),
162 "up_time_s {id=\"123.034\"} 3600\ncount {id=\"123.034\"} 347\n" ~
163 "ratio {id=\"123.034\"} 3.14\nfraction {id=\"123.034\"} 6.023\n" ~
164 "very_real {id=\"123.034\"} 0.43\n");
165 }
166
167 /// Test collection from more than one delegates
168 unittest
169 {
170 auto stats = new ExampleStats();
171 auto registry = new CollectorRegistry([&stats.collectDg1]);
172 registry.addCollector(&stats.collectDg2);
173
174 stats.setTestStats(Statistics(3600, 347, 3.14, 6.023, 0.43));
175 stats.setTestLabels(Labels(1_235_813, "ocean", 3.14159));
176
177 test!("==")(registry.collect(),
178 "up_time_s {id=\"123.034\"} 3600\ncount {id=\"123.034\"} 347\n" ~
179 "ratio {id=\"123.034\"} 3.14\nfraction {id=\"123.034\"} 6.023\n" ~
180 "very_real {id=\"123.034\"} 0.43\n" ~
181
182 "up_time_s {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 3600\n" ~
183 "count {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 347\n" ~
184 "ratio {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 3.14\n" ~
185 "fraction {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 6.023\n" ~
186 "very_real {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 0.43\n");
187 }
188
189 /// Test that the collections are not accumulated upon more than one call to
190 /// the `collect` method.
191 unittest
192 {
193 auto stats = new ExampleStats();
194 auto registry = new CollectorRegistry([&stats.collectDg2]);
195
196 stats.setTestStats(Statistics(3600, 347, 3.14, 6.023, 0.43));
197 stats.setTestLabels(Labels(1_235_813, "ocean", 3.14159));
198
199 auto collected = registry.collect();
200
201 // Update the stats
202 stats.setTestStats(Statistics(4200, 257, 2.345, 1.098, 0.56));
203
204 // A 2nd call to `collect` should return the updated stats, without the
205 // previous values anywhere in it.
206 test!("==")(registry.collect(),
207 "up_time_s {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 4200\n" ~
208 "count {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 257\n" ~
209 "ratio {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 2.345\n" ~
210 "fraction {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 1.098\n" ~
211 "very_real {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 0.56\n");
212 }