55 |
57 |
56 if stats == nil then |
58 if stats == nil then |
57 log("error", "Error loading statistics provider '%s': %s", stats_provider, stats_err); |
59 log("error", "Error loading statistics provider '%s': %s", stats_provider, stats_err); |
58 end |
60 end |
59 |
61 |
60 local measure, collect; |
62 local measure, collect, metric, cork, uncork; |
61 local latest_stats = {}; |
|
62 local changed_stats = {}; |
|
63 local stats_extra = {}; |
|
64 |
63 |
65 if stats then |
64 if stats then |
66 function measure(type, name, conf) |
65 function metric(type_, name, unit, description, labels, extra) |
67 local f = assert(stats[type], "unknown stat type: "..type); |
66 local registry = stats.metric_registry |
68 return f(name, conf); |
67 local f = assert(registry[type_], "unknown metric family type: "..type_); |
|
68 return f(registry, name, unit or "", description or "", labels, extra); |
|
69 end |
|
70 |
|
71 local function new_legacy_metric(stat_type, name, unit, description, fixed_label_key, fixed_label_value, extra) |
|
72 local label_keys = array() |
|
73 local conf = extra or {} |
|
74 if fixed_label_key then |
|
75 label_keys:push(fixed_label_key) |
|
76 end |
|
77 unit = unit or "" |
|
78 local mf = metric(stat_type, "prosody_" .. name, unit, description, label_keys, conf); |
|
79 if fixed_label_key then |
|
80 mf = mf:with_partial_label(fixed_label_value) |
|
81 end |
|
82 return mf:with_labels() |
|
83 end |
|
84 |
|
85 local function unwrap_legacy_extra(extra, type_, name, unit) |
|
86 local description = extra and extra.description or "Legacy "..type_.." metric "..name |
|
87 unit = extra and extra.unit or unit |
|
88 return description, unit |
|
89 end |
|
90 |
|
91 -- These wrappers provide the pre-OpenMetrics interface of statsmanager |
|
92 -- and moduleapi (module:measure). |
|
93 local legacy_metric_wrappers = { |
|
94 amount = function(name, fixed_label_key, fixed_label_value, extra) |
|
95 local initial = 0 |
|
96 if type(extra) == "number" then |
|
97 initial = extra |
|
98 else |
|
99 initial = extra and extra.initial or initial |
|
100 end |
|
101 local description, unit = unwrap_legacy_extra(extra, "amount", name) |
|
102 |
|
103 local m = new_legacy_metric("gauge", name, unit, description, fixed_label_key, fixed_label_value) |
|
104 m:set(initial or 0) |
|
105 return function(v) |
|
106 m:set(v) |
|
107 end |
|
108 end; |
|
109 |
|
110 counter = function(name, fixed_label_key, fixed_label_value, extra) |
|
111 if type(extra) == "number" then |
|
112 -- previous versions of the API allowed passing an initial |
|
113 -- value here; we do not allow that anymore, it is not a thing |
|
114 -- which makes sense with counters |
|
115 extra = nil |
|
116 end |
|
117 |
|
118 local description, unit = unwrap_legacy_extra(extra, "counter", name) |
|
119 |
|
120 local m = new_legacy_metric("counter", name, unit, description, fixed_label_key, fixed_label_value) |
|
121 m:set(0) |
|
122 return function(v) |
|
123 m:add(v) |
|
124 end |
|
125 end; |
|
126 |
|
127 rate = function(name, fixed_label_key, fixed_label_value, extra) |
|
128 if type(extra) == "number" then |
|
129 -- previous versions of the API allowed passing an initial |
|
130 -- value here; we do not allow that anymore, it is not a thing |
|
131 -- which makes sense with counters |
|
132 extra = nil |
|
133 end |
|
134 |
|
135 local description, unit = unwrap_legacy_extra(extra, "counter", name) |
|
136 |
|
137 local m = new_legacy_metric("counter", name, unit, description, fixed_label_key, fixed_label_value) |
|
138 m:set(0) |
|
139 return function() |
|
140 m:add(1) |
|
141 end |
|
142 end; |
|
143 |
|
144 times = function(name, fixed_label_key, fixed_label_value, extra) |
|
145 local conf = {} |
|
146 if extra and extra.buckets then |
|
147 conf.buckets = extra.buckets |
|
148 else |
|
149 conf.buckets = { 0.001, 0.01, 0.1, 1.0, 10.0, 100.0 } |
|
150 end |
|
151 local description, _ = unwrap_legacy_extra(extra, "times", name) |
|
152 |
|
153 local m = new_legacy_metric("histogram", name, "seconds", description, fixed_label_key, fixed_label_value, conf) |
|
154 return function() |
|
155 return timed(m) |
|
156 end |
|
157 end; |
|
158 |
|
159 sizes = function(name, fixed_label_key, fixed_label_value, extra) |
|
160 local conf = {} |
|
161 if extra and extra.buckets then |
|
162 conf.buckets = extra.buckets |
|
163 else |
|
164 conf.buckets = { 1024, 4096, 32768, 131072, 1048576, 4194304, 33554432, 134217728, 1073741824 } |
|
165 end |
|
166 local description, _ = unwrap_legacy_extra(extra, "sizes", name) |
|
167 |
|
168 local m = new_legacy_metric("histogram", name, "bytes", description, fixed_label_key, fixed_label_value, conf) |
|
169 return function(v) |
|
170 m:sample(v) |
|
171 end |
|
172 end; |
|
173 |
|
174 distribution = function(name, fixed_label_key, fixed_label_value, extra) |
|
175 if type(extra) == "string" then |
|
176 -- compat with previous API |
|
177 extra = { unit = extra } |
|
178 end |
|
179 local description, unit = unwrap_legacy_extra(extra, "distribution", name, "") |
|
180 local m = new_legacy_metric("summary", name, unit, description, fixed_label_key, fixed_label_value) |
|
181 return function(v) |
|
182 m:sample(v) |
|
183 end |
|
184 end; |
|
185 }; |
|
186 |
|
187 -- Argument order switched here to support the legacy statsmanager.measure |
|
188 -- interface. |
|
189 function measure(stat_type, name, extra, fixed_label_key, fixed_label_value) |
|
190 local wrapper = assert(legacy_metric_wrappers[stat_type], "unknown legacy metric type "..stat_type) |
|
191 return wrapper(name, fixed_label_key, fixed_label_value, extra) |
|
192 end |
|
193 |
|
194 if stats.cork then |
|
195 function cork() |
|
196 return stats:cork() |
|
197 end |
|
198 |
|
199 function uncork() |
|
200 return stats:uncork() |
|
201 end |
|
202 else |
|
203 function cork() end |
|
204 function uncork() end |
69 end |
205 end |
70 |
206 |
71 if stats_interval or stats_interval_config == "manual" then |
207 if stats_interval or stats_interval_config == "manual" then |
72 |
208 |
73 local mark_collection_start = measure("times", "stats.collection"); |
209 local mark_collection_start = measure("times", "stats.collection"); |
74 local mark_processing_start = measure("times", "stats.processing"); |
210 local mark_processing_start = measure("times", "stats.processing"); |
75 |
211 |
76 function collect() |
212 function collect() |
77 local mark_collection_done = mark_collection_start(); |
213 local mark_collection_done = mark_collection_start(); |
78 fire_event("stats-update"); |
214 fire_event("stats-update"); |
|
215 -- ensure that the backend is uncorked, in case it got stuck at |
|
216 -- some point, to avoid infinite resource use |
|
217 uncork() |
79 mark_collection_done(); |
218 mark_collection_done(); |
80 |
219 local manual_result = nil |
81 if stats.get_stats then |
220 |
|
221 if stats.metric_registry then |
|
222 -- only if supported by the backend, we fire the event which |
|
223 -- provides the current metric values |
82 local mark_processing_done = mark_processing_start(); |
224 local mark_processing_done = mark_processing_start(); |
83 changed_stats, stats_extra = {}, {}; |
225 local metric_registry = stats.metric_registry; |
84 for stat_name, getter in pairs(stats.get_stats()) do |
226 fire_event("openmetrics-updated", { metric_registry = metric_registry }) |
85 -- luacheck: ignore 211/type |
|
86 local type, value, extra = getter(); |
|
87 local old_value = latest_stats[stat_name]; |
|
88 latest_stats[stat_name] = value; |
|
89 if value ~= old_value then |
|
90 changed_stats[stat_name] = value; |
|
91 end |
|
92 if extra then |
|
93 stats_extra[stat_name] = extra; |
|
94 end |
|
95 end |
|
96 fire_event("stats-updated", { stats = latest_stats, changed_stats = changed_stats, stats_extra = stats_extra }); |
|
97 mark_processing_done(); |
227 mark_processing_done(); |
98 end |
228 manual_result = metric_registry; |
99 return stats_interval; |
229 end |
|
230 |
|
231 return stats_interval, manual_result; |
100 end |
232 end |
101 if stats_interval then |
233 if stats_interval then |
102 log("debug", "Statistics enabled using %s provider, collecting every %d seconds", stats_provider_name, stats_interval); |
234 log("debug", "Statistics enabled using %s provider, collecting every %d seconds", stats_provider_name, stats_interval); |
103 timer.add_task(stats_interval, collect); |
235 timer.add_task(stats_interval, collect); |
104 prosody.events.add_handler("server-started", function () collect() end, -1); |
236 prosody.events.add_handler("server-started", function () collect() end, -1); |