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.transition; 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 Traits = ocean.core.Traits; 102 import ocean.transition; 103 import ocean.util.prometheus.collector.Collector; 104 105 /// 106 public struct Statistics 107 { 108 ulong up_time_s; 109 size_t count; 110 float ratio; 111 double fraction; 112 real very_real; 113 } 114 115 /// 116 public struct Labels 117 { 118 hash_t id; 119 cstring job; 120 float perf; 121 } 122 123 private class ExampleStats 124 { 125 Statistics test_stats; 126 Labels test_labels; 127 128 /// 129 public void setTestStats ( Statistics stats ) 130 { 131 test_stats = stats; 132 } 133 134 /// 135 public void setTestLabels ( Labels labels ) 136 { 137 test_labels = labels; 138 } 139 140 /// 141 public void collectDg1 ( Collector collector ) 142 { 143 collector.collect!("id")(test_stats, 123.034); 144 } 145 146 /// 147 public void collectDg2 ( Collector collector ) 148 { 149 collector.collect(test_stats, test_labels); 150 } 151 } 152 } 153 154 /// Test collection from a single delegate 155 unittest 156 { 157 auto stats = new ExampleStats(); 158 auto registry = new CollectorRegistry([&stats.collectDg1]); 159 160 stats.setTestStats(Statistics(3600, 347, 3.14, 6.023, 0.43)); 161 162 test!("==")(registry.collect(), 163 "up_time_s {id=\"123.034\"} 3600\ncount {id=\"123.034\"} 347\n" ~ 164 "ratio {id=\"123.034\"} 3.14\nfraction {id=\"123.034\"} 6.023\n" ~ 165 "very_real {id=\"123.034\"} 0.43\n"); 166 } 167 168 /// Test collection from more than one delegates 169 unittest 170 { 171 auto stats = new ExampleStats(); 172 auto registry = new CollectorRegistry([&stats.collectDg1]); 173 registry.addCollector(&stats.collectDg2); 174 175 stats.setTestStats(Statistics(3600, 347, 3.14, 6.023, 0.43)); 176 stats.setTestLabels(Labels(1_235_813, "ocean", 3.14159)); 177 178 test!("==")(registry.collect(), 179 "up_time_s {id=\"123.034\"} 3600\ncount {id=\"123.034\"} 347\n" ~ 180 "ratio {id=\"123.034\"} 3.14\nfraction {id=\"123.034\"} 6.023\n" ~ 181 "very_real {id=\"123.034\"} 0.43\n" ~ 182 183 "up_time_s {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 3600\n" ~ 184 "count {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 347\n" ~ 185 "ratio {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 3.14\n" ~ 186 "fraction {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 6.023\n" ~ 187 "very_real {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 0.43\n"); 188 } 189 190 /// Test that the collections are not accumulated upon more than one call to 191 /// the `collect` method. 192 unittest 193 { 194 auto stats = new ExampleStats(); 195 auto registry = new CollectorRegistry([&stats.collectDg2]); 196 197 stats.setTestStats(Statistics(3600, 347, 3.14, 6.023, 0.43)); 198 stats.setTestLabels(Labels(1_235_813, "ocean", 3.14159)); 199 200 auto collected = registry.collect(); 201 202 // Update the stats 203 stats.setTestStats(Statistics(4200, 257, 2.345, 1.098, 0.56)); 204 205 // A 2nd call to `collect` should return the updated stats, without the 206 // previous values anywhere in it. 207 test!("==")(registry.collect(), 208 "up_time_s {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 4200\n" ~ 209 "count {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 257\n" ~ 210 "ratio {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 2.345\n" ~ 211 "fraction {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 1.098\n" ~ 212 "very_real {id=\"1235813\",job=\"ocean\",perf=\"3.14159\"} 0.56\n"); 213 }