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 }